BooFuzz模糊測試


一、背景:

  對於物聯網設備,比如家用路由器模塊,UPNP、HTTP server、Telnet都是經常接觸的模塊,通常也能夠與外界交互,從而提供了入口。如果這些模塊或者使用的協議存在漏洞,往往能夠直接利用,到達遠程攻擊的效果。針對IoT設備的模糊測試,本文介紹BooFuzz。

  對物聯網設備的協議fuzz測試,不可丟失的一環是監控器,能夠發現bug是監控器作用所在。一般來說,大多數針對協議的fuzz測試,在每次發送變異報文后,通過ping或者其他協議檢測對方是否在線從而判斷設備是否出現異常。這種方法較為簡單,但是粗粒度的監控器帶來的后果是比較高的誤報率;另一種方式是ssh或者telnet登錄設備,實時觀察當前設備的狀態,這種方式優點是隨時捕捉異常信號,缺點是,針對不同設備可能要開發不同的監控API。所以當前諸多的協議Fuzz工具,大部分使用第一種,即通過目標設備是否可達來判斷崩潰情況。

二、BooFuzz框架介紹:

1、四大組件:

a) Data Generation 數據生成

b) Session 會話管理

c) Agents代理

d) Utilities獨立單元工具

其中,數據生成和會話管理是比較重要的兩個模塊。數據生成方式是基於generation-based的方式,需要對協議或者文件進行建模,數據生成的特點:

== 一個數據報文由基元(Primitives)、塊(Blocks)組成

== 多個基於可以組成塊,塊可以相互嵌套

== 在基元的基礎上我們可以創建自定義的特殊的負責基元(Legos,數據積木),例如Email的地址,IP地址等。

== 最后還有一些有用的工具,例如算length長度、校驗和、加密模塊等

會話模塊,根據已經構建好的Request,利用pgraph繪制:

在上圖中,節點ehlo、helo、mail from、rcpt to、data表示5個請求,路徑'ehlo'->'mail form'->'rcpt to'->'data''helo'->'mail from'->'rcpt to'->'data'體現了請求之間的先后順序關系。callback_one()和callback_two()表示回調函數,當從節點echo移動到節點mail from時會觸發該回調函數,利用這一機制,節點mail from可以獲取節點ehlo中的一些信息。而pre_send()和post_send()則負責測試前的一些預處理工具和測試后的一些清理工作。每個節點連接起來,組成狀態圖,可以對圖中的每個節點進行操作,也可以自定義callback回調函數。

2、BooFuzz API

Session對象是Fuzz測試的核心,當你創建Session的時候,你會傳遞一個Target對象,該對象本身接收一個Connection對象

session = Session(
         target = Target(
                 connection=SocketConnection("127.0.0.1", 8021, proto='tcp')))

准備好會話對象后,接下來需要在協議中定義消息。細節請參照靜態協議定義功能定義協議:https://boofuzz.readthedocs.io/en/stable/user/static-protocol-definition.html#static-primitives 每一條消息均以一個s_initialize函數開頭,比如fuzz FTP協議中的幾個消息定義:

s_initialize("user")
s_string("USER")
s_delim(" ")
s_string("anonymous")
s_static("\r\n")

s_initialize("pass")
s_string("PASS")
s_delim(" ")
s_string("james")
s_static("\r\n")

s_initialize("stor")
s_string("STOR")
s_delim(" ")
s_string("AAAA")
s_static("\r\n")

s_initialize("retr")
s_string("RETR")
s_delim(" ")
s_string("AAAA")
s_static("\r\n")

定義消息后,將使用剛剛創建的Session對象將它們連接到圖中:

session.connect(s_get("user"))
session.connect(s_get("user"), s_get("pass"))
session.connect(s_get("pass"), s_get("stor"))
session.connect(s_get("pass"), s_get("retr"))

之后,就可以進行fuzz測試了

session.fuzz()

每次運行的日志數據將保存在當前工作目錄下boofuzz-results目錄中的SQLite數據庫中。可以隨時通過以下任一方式在任何這些數據可上重新打開Web界面

boo open <run-*.db>

 3、Static Protocol Definition(靜態協議定義)

Request是信息,Blocks是消息中的塊,而Primitives是構成塊/請求的元素(字節,字符串,數字,檢驗和等)。

Blocks將獨立的primitives組建成有序的塊。Groups中包含了一些特定的primitives,一個Group和一個Block結合后,每次fuzzer調用Block的時候,都會將Group中的數據循環的取出,組成不同的Block。

Group允許你連接一個塊到指定的group原語,和一個組關聯的block必須為每個組中的值循環窮盡該block的所有空間。組原語是非常有用的,s_group定義一個組,並接受兩個必須的參數,第一個參數指定組名稱,第二個參數指定一個需要迭代的原始值列表。

a) 請求操作

s_initialize(名稱)

---初始化一個新的塊請求。此調用后生成的所有塊/原語都適用於指定的請求。使用s_switch()在工廠之間跳轉。

參數:name(str) - 請求名稱

s_get(名稱=無)

---如果未指定名稱,則返回具有指定名稱的請求或當前請求。使用它可以從全局函數樣式請求操作切換到直接對象操作。

s_num_mutations()

---確定我們將要進行的重復次數

返回類型:整數

s_switch(名稱)

---將當前請求更改為"name"指定的請求。

參數: name(str) - 請求名稱

b) 塊操作

s_block ( name = None , group = None , encoder = None , dep = None , dep_value = None , dep_values = None , dep_compare = '==' )

在當前請求下打開一個新塊。返回的實例支持"with"接口,因此它會自動為您關閉:

with s_block("header"):
    s_static("\x00\x01")
    if s_block_start("body"):
        ...

參數:

  • name ( str optional ) – 正在打開的塊的名稱

  • group ( str optional ) – (Optional, def=None) 與這個塊相關聯的組名

  • 編碼器函數指針可選)–(可選,def=None)指向函數的可選指針,在返回之前將呈現的數據傳遞給

  • dep ( str optional ) – (Optional, def=None) 可選原語,它的具體值取決於這個塊

  • dep_value ( Mixed optional ) – (Optional, def=None) 字段“dep”必須包含的值才能渲染塊

  • dep_values混合類型列表可選)–(可選,def=None)字段“dep”可能包含用於渲染塊的值

  • dep_compare ( str optional ) – (Optional, def="==") 依賴的比較方法 (==, !=, >, >=, <, <=)

s_block_start ( name = None , * args , ** kwargs )

在當前請求下打開一個新塊。此例程始終返回一個實例,因此您可以通過縮進使模糊器變得漂亮:(更喜歡直接使用 s_block 而不是這個函數)

s_block_end (名稱=無)

關閉最后打開的塊。可選地指定要關閉的塊的名稱(純粹出於美觀目的)

參數:name(str) - (可選,def-None)要關閉的塊的名稱。

c) 原始定義:

s_binary ( value , name = None )

將可變格式的二進制字符串解析為靜態值並將其壓入當前塊堆棧。

參數: 

  • value ( str ) – 可變格式二進制字符串

  • name ( str ) –(可選,def=None)指定名稱可以讓您直接訪問原語

s_delim ( value = ' ' , fuzzable = True , name = None )

將分隔符推送到當前堆棧上。

參數:

  • value ( Character ) – (可選,def="")原始值

  • fuzzable ( bool ) –(可選,def=True)啟用/禁用此原語的模糊測試

  • name ( str ) –(可選,def=None)指定名稱可以讓您直接訪問原語

s_group ( name = None , values = None , default_value = None )

這個原語代表一個靜態值列表,在突變時逐步遍歷每個值。您可以將一個塊綁定到一個組原語,以指定該塊應該循環遍歷組內每個值的所有可能的突變例如,組原語對於表示有效操作碼列表很有用。

參數:

  • name ( str ) –(可選,def=None)組名

  • 列表原始數據)–(可選,def=None)該組可以采用的可能原始值的列表。

  • default_value ( str or bytes ) – (可選,def=None) fuzzing() 完成時指定一個值

s_static(值=無,名稱=無)

將靜態值推送到當前塊堆棧上。

參數:
  • value ( Raw ) – 原始靜態數據

  • name ( str ) –(可選,def=None)指定名稱可以讓您直接訪問原語

s_string ( value = '' , size = None , padding = b'\x00' , encoding = 'ascii' , fuzzable = True , max_len = None , name = None )

將字符串壓入當前堆棧。

參數:

  • str)–(可選,def =””)默認字符串值

  • size ( int ) –(可選,def=None)此字段的靜態大小,為動態保留 None 。

  • 填充字符)-(可選,def=”x00”)用作填充以填充靜態字段大小的值。

  • encoding ( str ) –(可選,def=”ascii”)字符串編碼,例如: utf_16_le 用於 Microsoft Unicode。

  • fuzzable ( bool ) –(可選,def=True)啟用/禁用此原語的模糊測試

  • max_len ( int ) –(可選,def=None)最大字符串長度

  • name ( str ) –(可選,def=None)指定名稱可以讓您直接訪問原語

 

 

 

 

 

例如下面是官網給出的http.py中的代碼:

s_initialize("HTTP VERBS")
s_group("verbs", values=["GET", "HEAD", "POST", "TRACE", "PUT", "DELETE"]) 
if s_block_start("body", group="verbs"): 
    s_delim(" ") 
    s_delim("/") 
    s_string("index.html ") 
    s_delim(" ") 
    s_string("HTTP") 
    s_delim("/") 
    s_string("1") 
    s_delim(".") 
    s_string("1") 
 s_block_end()

模塊常用語法:

s_initialize('grammar')    # 初始化塊請求並命名
s_static("HELLO\r\n")     # 始終發送此消息
s_static("PROCESS")      # 在HELLO\r\n之后立即發送
s_delim("")                    # 使用s_delim()原語代替分隔符
s_string("AAAA")            # 這是我們的fuzz字符串
s_static("\r\n")               # 告訴服務器"done"

Connections

target = sessions.target("10.0.0.1", 5168)
target.netmon = pedrpc.client("10.0.0.1", 26001)
target.procmon = pedrpc.client("10.0.0.1", 26002)
target.vmcontrol = pedrpc.client("127.0.0.1", 26003)
target.procmon_options = \
{
"proc_name" : "SpntSvc.exe",
"stop_commands" : ['net stop "trend serverprotect"'],
"start_commands" : ['net start "trend serverprotect"'],
}
sess.add_target(target)
sess.fuzz()

下面的netmon(網絡監控代理) 、procmon(進程監控代理)、vmcontrol(VMware控制代理)為3個agent的子模塊,用來監測程序:

  • netmon子模塊:netmon子模塊主要負責捕捉網絡的雙向流量,並保存。 在向target發送數據之前,agent向target發送請求並記錄流量,數據傳送成功后,該代理子模塊將記錄的流量存入磁盤。
  • procmon子模塊:procmon子模塊主要負責檢測fuzz過程中發生的故障。 在向target發送數據之后,boofuzz聯系該代理以確定是否觸發了故障,如果產生故障,關於故障性質的hgih level信息將被傳送回session。錯誤信息會儲存在名為"crash bin"的文件中,我們也可以在web監控服務中看到發生crash時加載詳細的crash信息。
  • vmcontrol子模塊:vmcontrol子模塊主要用來控制虛擬機。一種常見的用法是把Target目標放在虛擬機中,使用這個子模塊來控制虛擬機的啟動、關閉、創建快照等,最重要的功能是能在目標出現崩潰的時候恢復主機的狀態。

下面簡單的介紹一個請求樣例:

session.connect(s_get("user"))
     session.connect(s_get("user"), s_get("pass"))
     session.connect(s_get("pass"), s_get("stor"))
     session.connect(s_get("pass"), s_get("retr"))

連接后,我們發送用戶名請求

在發送用戶名后,我們發送密碼

只有在發送密碼后我們才能發送stor或retr請求

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM