使用Python實(shí)現(xiàn)Fuzz測(cè)試的四種方法實(shí)現(xiàn)與對(duì)比
下面我將用Python實(shí)現(xiàn)一個(gè)類似的、存在漏洞的程序,并展示如何使用Python進(jìn)行Fuzz測(cè)試。需要注意的是,由于Python是內(nèi)存安全的語(yǔ)言,我們無(wú)法完全復(fù)制C語(yǔ)言中的緩沖區(qū)溢出漏洞,但可以模擬類似的安全問題。
存在漏洞的Python程序
# vulnerable_parser.py
import sys
import json
def parse_data(input_data):
"""
一個(gè)存在安全問題的解析函數(shù)
1. 可能引發(fā)異常的處理邏輯
2. 可能存在代碼注入漏洞
3. 可能存在正則表達(dá)式拒絕服務(wù)(ReDoS)
"""
try:
# 模擬不安全的數(shù)據(jù)處理
if input_data.startswith("{"):
# 嘗試解析為JSON - 可能引發(fā)異常
data = json.loads(input_data)
return f"Parsed JSON: {data}"
elif "system" in input_data:
# 模擬命令注入漏洞
import os
# 危險(xiǎn)操作:實(shí)際應(yīng)用中絕不應(yīng)該這樣做!
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:
# 模擬正則表達(dá)式ReDoS漏洞
import re
# 危險(xiǎn)的正則模式,可能造成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進(jìn)行Fuzz測(cè)試
有多種方法可以對(duì)Python程序進(jìn)行Fuzz測(cè)試。以下是幾種常見方法:
方法一:使用AFL++與Python包裝
雖然AFL++主要針對(duì)本地代碼,但可以通過Python擴(kuò)展支持:
# 安裝python-afl pip install python-afl # 使用AFL++運(yùn)行Python Fuzz測(cè)試 PYTHON_AFL_PERSISTENT=1 python -m afl.vulnerable_parser
方法二:使用專門針對(duì)Python的Fuzzer - Atheris
Atheris是由Google開發(fā)的針對(duì)Python的覆蓋率引導(dǎo)Fuzzer:
# fuzz_parser.py
import atheris
import sys
with atheris.instrument_imports():
import vulnerable_parser
def TestOneInput(data):
try:
# 將輸入數(shù)據(jù)轉(zhuǎn)換為字符串
input_str = data.decode("utf-8")
vulnerable_parser.parse_data(input_str)
except (UnicodeDecodeError, ValueError):
# 忽略編碼錯(cuò)誤,這是正常的Fuzz行為
pass
except Exception as e:
# 這里可以記錄異常,但通常讓Fuzzer自動(dòng)處理
pass
if __name__ == "__main__":
atheris.Setup(sys.argv, TestOneInput)
atheris.Fuzz()
運(yùn)行上面的Fuzzer:
pip install atheris python fuzz_parser.py
方法三:使用基于生成的Fuzzer - Hypothesis
Hypothesis是一個(gè)基于屬性的測(cè)試庫(kù),也可以用于Fuzz測(cè)試:
# 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):
"""使用隨機(jī)生成的文本測(cè)試解析器"""
try:
result = vulnerable_parser.parse_data(text)
# 這里可以添加一些斷言
assert result is not None
except Exception:
# 對(duì)于Fuzz測(cè)試,異常是可以接受的
pass
if __name__ == "__main__":
test_parse_data_with_random_input()
運(yùn)行測(cè)試:
pip install hypothesis python test_with_hypothesis.py
方法四:簡(jiǎn)單的自定義Fuzzer
# simple_fuzzer.py
import random
import string
import subprocess
import os
def generate_random_input(length=100):
"""生成隨機(jī)輸入"""
# 生成基本隨機(jī)字符串
chars = string.ascii_letters + string.digits + string.punctuation + " "
result = ''.join(random.choice(chars) for _ in range(length))
# 有時(shí)添加一些特殊模式
if random.random() < 0.3:
result = "{" + result + "}" # 模擬JSON
if random.random() < 0.1:
result = "a" * length # 可能觸發(fā)ReDoS
if random.random() < 0.05:
result = "system(" + result + ")" # 可能觸發(fā)命令注入
return result
def run_fuzzer():
"""運(yùn)行Fuzzer"""
crash_count = 0
hang_count = 0
total_tests = 10000
for i in range(total_tests):
# 生成測(cè)試輸入
test_input = generate_random_input(random.randint(1, 1000))
# 寫入臨時(shí)文件
with open("temp_input.txt", "w") as f:
f.write(test_input)
try:
# 運(yùn)行目標(biāo)程序,設(shè)置超時(shí)
result = subprocess.run(
["python", "vulnerable_parser.py", "temp_input.txt"],
capture_output=True,
text=True,
timeout=5 # 5秒超時(shí)
)
# 檢查結(jié)果
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]}...")
# 保存導(dǎo)致超時(shí)的輸入
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()
分析與改進(jìn)
通過上述Fuzz測(cè)試,我們可以發(fā)現(xiàn)Python程序中可能存在的多種問題:
- 異常處理 - 程序是否能夠優(yōu)雅地處理各種異常輸入
- 代碼/命令注入 - 程序是否存在eval或os.popen的不安全使用
- 正則表達(dá)式拒絕服務(wù)(ReDoS) - 是否存在效率低下的正則表達(dá)式
- 資源消耗 - 程序是否會(huì)因特定輸入消耗過多資源
發(fā)現(xiàn)漏洞后,我們應(yīng)該修復(fù)程序:
# fixed_parser.py
import sys
import json
import re
def safe_parse_data(input_data):
"""
修復(fù)后的安全解析函數(shù)
"""
# 限制輸入大小
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:
# 使用更安全的正則表達(dá)式
pattern = r"^a+$" # 簡(jiǎn)化模式,避免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()
# 移除危險(xiǎn)功能(命令執(zhí)行、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)}")
總結(jié)
Python中的Fuzz測(cè)試與C/C++有所不同,主要關(guān)注:
- 異常處理 - 確保程序不會(huì)因異常輸入而崩潰
- 代碼注入 - 避免使用eval、exec等危險(xiǎn)函數(shù)
- 正則表達(dá)式ReDoS - 使用效率更高的正則模式
- 資源限制 - 防止消耗過多內(nèi)存或CPU時(shí)間
- 安全實(shí)踐 - 遵循安全編碼準(zhǔn)則
通過結(jié)合使用專門的Python Fuzzer(如Atheris)和自定義Fuzz測(cè)試,可以有效發(fā)現(xiàn)Python應(yīng)用程序中的各種安全問題。
到此這篇關(guān)于使用Python實(shí)現(xiàn)Fuzz測(cè)試的四種方法實(shí)現(xiàn)與對(duì)比的文章就介紹到這了,更多相關(guān)Python實(shí)現(xiàn)Fuzz測(cè)試內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
linux mint中搜狗輸入法導(dǎo)致pycharm卡死的問題
這篇文章主要介紹了linux mint中搜狗輸入法導(dǎo)致pycharm卡死的問題,這篇文章給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10
Python面向?qū)ο笾蓡T相關(guān)知識(shí)總結(jié)
通過面向?qū)ο筮M(jìn)行編程時(shí),會(huì)遇到很多種情況,也會(huì)使用不同的成員來實(shí)現(xiàn),接下來我們來逐一介紹成員特性和應(yīng)用場(chǎng)景,需要的朋友可以參考下2021-06-06
Python技巧分享之如何將字符串轉(zhuǎn)回DataFrame格式
平常我們使用pandas,一般使用的是DataFrame和Series,但個(gè)別交換數(shù)據(jù)的時(shí)候,只能使用字符串,那如何再將字符串再轉(zhuǎn)回DataFrame格式呢,本文就來和大家講講解決辦法2023-06-06
Python使用Requests請(qǐng)求網(wǎng)頁(yè)方式
這篇文章主要介紹了Python使用Requests請(qǐng)求網(wǎng)頁(yè)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
基于django channel實(shí)現(xiàn)websocket的聊天室的方法示例
這篇文章主要介紹了基于基于django channel實(shí)現(xiàn)websocket的聊天室的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-04-04
如何使用Python實(shí)現(xiàn)PPT批量轉(zhuǎn)圖片
這篇文章主要為大家詳細(xì)介紹了如何使用Python開發(fā)一個(gè)帶有圖形界面的PPT批量轉(zhuǎn)圖片工具,文中的示例代碼講解詳細(xì),有需要的小伙伴可以了解下2025-02-02

