php_bugs代码审计(一)

记录php_bugs代码审计。所有代码均来自:https://github.com/bowu678/php_bugs
这里仅作为本人二次消化、记录。此处为第一部分(1~3),后面待更。

0x01 extract变量覆盖

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
show_source(__FILE__);
$flag='xxx';
extract($_GET);
if(isset($shiyan)) {
$content=trim(file_get_contents($flag));
if($shiyan==$content) {
echo 'ctf{xxx}';
} else {
echo 'Oh.no';
}
}
?>

$shiyan==$content时输出flag$flag赋给$content,这里不知道$flag的值,所以可以get一个flag变量覆盖$flag,当getflag等于getshiyan时即可输出flag
构造payload:
?shiyan=&flag=

0x02 绕过过滤的空白字符

代码挺长的,就不放了,自行在github原项目上看。
主要满足四个点:

1
2
3
4
is_numeric($_REQUEST['number']) == false
$req['number'] == strval(intval($req['number']
intval($req["number"]) == intval(strrev($req["number"])
is_palindrome_number($req["number"]) == false

即可输出flag,构造如下:

  • 第一点is_numeric()判断变量是否为数字数字字符串,可以检查10进制16进制is_numeric()可以用空字符绕过,%00放在数值前、后都可以判断为非数值,而%20空字符只能放在数值后。
  • 第三点该整数值等于其反转整数值,第四点不为回文数,这两者看似矛盾,实则有多种绕过方法。

法一

来自php_bugs,先满足第三点回文数再Fuzzing绕过第四点,简化后端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
function is_palindrome_number($number) {
$number = strval($number); //strval — 获取变量的字符串值
$i = 0;
$j = strlen($number) - 1; //strlen — 获取字符串长度
while($i < $j) {
if($number[$i] !== $number[$j]) {
return false;
}
$i++;
$j--;
}
return true;
}
$a = trim($_GET['number']);
var_dump(($a==strval(intval($a)))&(intval($a)==intval(strrev($a)))&!is_palindrome_number($a))
?>

数值转成2位16进制加在回文数 191前面,Fuzzing如下:

1
2
3
4
5
import requests
for i in range(256):
r = requests.get(url="http://arch/php_bugs/02.php?number={}191".format("%%%02X"%i))
if '1' in r.text:
print("%%%02X"%i)

输出结果如下:

%0C
%2B

即可构成payload
?number=%00%2C191

法二

仅限于32位操作系统,利用intval()函数的溢出,Intval()最大的值取决于操作系统32 位系统最大带符号的 integer 范围是-21474836482147483647。举例,在这样的系统上, intval('1000000000000') 会返回 214748364764 位系统上,最大带符号的 integer 值是 9223372036854775807

如果在32位操作系统上我们可以构造payload?number=%002147483647

2147483647经过strrev()反转函数后为7463847412,又经过intval函数值又变为2147483647,故满足第三点条件,可以输出flag

64位操作系统最大integer 值是 9223372036854775807,经strrev()反转函数后为7085774586302733229反而变小了,未满足溢出条件,故不适用。

法三

因为要求不能为回文数,但又要满足intval($req["number"])=intval(strrev($req["number"]))所以我们采用科学计数法构造payload?number=0e-0%00,这样的话我们就可以绕过。

参考:

is_numeric()
%%%02x
法二、三来源

0x03 多重加密

源码中给出:

1
2
3
4
5
$login = unserialize(gzuncompress(base64_decode($requset['token'])));
//gzuncompress:进行字符串压缩
//unserialize: 将已序列化的字符串还原回 PHP 的值

if($login['user'] === 'ichunqiu'){echo $flag;}

有了加密方式,我们解密一下即可

1
2
3
4
5
<?php
$arr = array(['user'] === 'ichunqiu');
$token = base64_encode(gzcompress(serialize($arr)));
echo $token;
?>

eJxLtDK0qs60MrBOAuJaAB5uBBQ=

Thank you very much if you can.

欢迎关注我的其它发布渠道