使用Python实现Fuzz测试的四种方法实现与对比
作者:Bruce_xiaowei
这篇文章将用Python实现一个类似的、存在漏洞的程序,并展示如何使用Python进行Fuzz测试,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下
下面我将用Python实现一个类似的、存在漏洞的程序,并展示如何使用Python进行Fuzz测试。需要注意的是,由于Python是内存安全的语言,我们无法完全复制C语言中的缓冲区溢出漏洞,但可以模拟类似的安全问题。
存在漏洞的Python程序
# vulnerable_parser.py
import sys
import json
def parse_data(input_data):
"""
一个存在安全问题的解析函数
1. 可能引发异常的处理逻辑
2. 可能存在代码注入漏洞
3. 可能存在正则表达式拒绝服务(ReDoS)
"""
try:
# 模拟不安全的数据处理
if input_data.startswith("{"):
# 尝试解析为JSON - 可能引发异常
data = json.loads(input_data)
return f"Parsed JSON: {data}"
elif "system" in input_data:
# 模拟命令注入漏洞
import os
# 危险操作:实际应用中绝不应该这样做!
result = os.popen(f"echo {input_data}").read()
return f"Command result: {result}"
elif "calc" in input_data:
# 模拟不安全的eval使用
return f"Calculation: {eval(input_data)}"
else:
# 模拟正则表达式ReDoS漏洞
import re
# 危险的正则模式,可能造成ReDoS
pattern = r"^(a+)+$"
if re.match(pattern, input_data):
return "Pattern matched"
return f"Processed: {input_data}"
except Exception as e:
return f"Error: {str(e)}"
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python vulnerable_parser.py <input_file>")
sys.exit(1)
with open(sys.argv[1], 'r', encoding='utf-8', errors='ignore') as f:
input_data = f.read().strip()
result = parse_data(input_data)
print(result)
使用Python进行Fuzz测试
有多种方法可以对Python程序进行Fuzz测试。以下是几种常见方法:
方法一:使用AFL++与Python包装
虽然AFL++主要针对本地代码,但可以通过Python扩展支持:
# 安装python-afl pip install python-afl # 使用AFL++运行Python Fuzz测试 PYTHON_AFL_PERSISTENT=1 python -m afl.vulnerable_parser
方法二:使用专门针对Python的Fuzzer - Atheris
Atheris是由Google开发的针对Python的覆盖率引导Fuzzer:
# fuzz_parser.py
import atheris
import sys
with atheris.instrument_imports():
import vulnerable_parser
def TestOneInput(data):
try:
# 将输入数据转换为字符串
input_str = data.decode("utf-8")
vulnerable_parser.parse_data(input_str)
except (UnicodeDecodeError, ValueError):
# 忽略编码错误,这是正常的Fuzz行为
pass
except Exception as e:
# 这里可以记录异常,但通常让Fuzzer自动处理
pass
if __name__ == "__main__":
atheris.Setup(sys.argv, TestOneInput)
atheris.Fuzz()
运行上面的Fuzzer:
pip install atheris python fuzz_parser.py
方法三:使用基于生成的Fuzzer - Hypothesis
Hypothesis是一个基于属性的测试库,也可以用于Fuzz测试:
# test_with_hypothesis.py
from hypothesis import given, strategies as st
import vulnerable_parser
@given(st.text())
def test_parse_data_with_random_input(text):
"""使用随机生成的文本测试解析器"""
try:
result = vulnerable_parser.parse_data(text)
# 这里可以添加一些断言
assert result is not None
except Exception:
# 对于Fuzz测试,异常是可以接受的
pass
if __name__ == "__main__":
test_parse_data_with_random_input()
运行测试:
pip install hypothesis python test_with_hypothesis.py
方法四:简单的自定义Fuzzer
# simple_fuzzer.py
import random
import string
import subprocess
import os
def generate_random_input(length=100):
"""生成随机输入"""
# 生成基本随机字符串
chars = string.ascii_letters + string.digits + string.punctuation + " "
result = ''.join(random.choice(chars) for _ in range(length))
# 有时添加一些特殊模式
if random.random() < 0.3:
result = "{" + result + "}" # 模拟JSON
if random.random() < 0.1:
result = "a" * length # 可能触发ReDoS
if random.random() < 0.05:
result = "system(" + result + ")" # 可能触发命令注入
return result
def run_fuzzer():
"""运行Fuzzer"""
crash_count = 0
hang_count = 0
total_tests = 10000
for i in range(total_tests):
# 生成测试输入
test_input = generate_random_input(random.randint(1, 1000))
# 写入临时文件
with open("temp_input.txt", "w") as f:
f.write(test_input)
try:
# 运行目标程序,设置超时
result = subprocess.run(
["python", "vulnerable_parser.py", "temp_input.txt"],
capture_output=True,
text=True,
timeout=5 # 5秒超时
)
# 检查结果
if result.returncode != 0:
crash_count += 1
print(f"Crash found with input: {test_input[:100]}...")
print(f"Error: {result.stderr}")
# 保存崩溃输入以供分析
with open(f"crash_{crash_count}.txt", "w") as f:
f.write(test_input)
except subprocess.TimeoutExpired:
hang_count += 1
print(f"Timeout with input: {test_input[:100]}...")
# 保存导致超时的输入
with open(f"hang_{hang_count}.txt", "w") as f:
f.write(test_input)
# 清理
if os.path.exists("temp_input.txt"):
os.remove("temp_input.txt")
print(f"Fuzzing completed. Total tests: {total_tests}")
print(f"Crashes: {crash_count}, Hangs: {hang_count}")
if __name__ == "__main__":
run_fuzzer()
分析与改进
通过上述Fuzz测试,我们可以发现Python程序中可能存在的多种问题:
- 异常处理 - 程序是否能够优雅地处理各种异常输入
- 代码/命令注入 - 程序是否存在eval或os.popen的不安全使用
- 正则表达式拒绝服务(ReDoS) - 是否存在效率低下的正则表达式
- 资源消耗 - 程序是否会因特定输入消耗过多资源
发现漏洞后,我们应该修复程序:
# fixed_parser.py
import sys
import json
import re
def safe_parse_data(input_data):
"""
修复后的安全解析函数
"""
# 限制输入大小
if len(input_data) > 1000:
return "Error: Input too long"
try:
if input_data.startswith("{"):
# 安全地解析JSON
data = json.loads(input_data)
return f"Parsed JSON: {data}"
else:
# 使用更安全的正则表达式
pattern = r"^a+$" # 简化模式,避免ReDoS
if re.match(pattern, input_data):
return "Pattern matched"
return f"Processed: {input_data}"
except json.JSONDecodeError:
return "Error: Invalid JSON"
except Exception as e:
return f"Error: {str(e)}"
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python fixed_parser.py <input_file>")
sys.exit(1)
try:
with open(sys.argv[1], 'r', encoding='utf-8', errors='ignore') as f:
input_data = f.read().strip()
# 移除危险功能(命令执行、eval等)
if any(cmd in input_data for cmd in ["system", "eval", "exec", "os.", "subprocess"]):
print("Error: Suspicious input detected")
sys.exit(1)
result = safe_parse_data(input_data)
print(result)
except Exception as e:
print(f"Error: {str(e)}")
总结
Python中的Fuzz测试与C/C++有所不同,主要关注:
- 异常处理 - 确保程序不会因异常输入而崩溃
- 代码注入 - 避免使用eval、exec等危险函数
- 正则表达式ReDoS - 使用效率更高的正则模式
- 资源限制 - 防止消耗过多内存或CPU时间
- 安全实践 - 遵循安全编码准则
通过结合使用专门的Python Fuzzer(如Atheris)和自定义Fuzz测试,可以有效发现Python应用程序中的各种安全问题。
到此这篇关于使用Python实现Fuzz测试的四种方法实现与对比的文章就介绍到这了,更多相关Python实现Fuzz测试内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
