作者:cq674350529
本文首發于信安之路,原文鏈接:https://mp.weixin.qq.com/s/5gwJpqj7ysue19OcoPI16A

前言

通常,在對IoT設備的固件進行分析時,固件中與提供服務如HTTPTelnetRTSPUPnP等相關的二進制程序是重點分析的對象。因為一旦在這些程序中發現漏洞,其很有可能會被遠程利用,進而帶來嚴重的安全隱患。

對固件二進制程序進行分析,常見的分析方法包括模糊測試、補丁比對、工具靜態掃描和人工審計等。其中,模糊測試方法具備簡單易用的特點,通常也比較有效,其在業界已被廣泛使用。

下面,以某型號路由器為例,基于Boofuzz框架,介紹對常見網絡協議進行fuzz的方法。

  • 除了網絡協議外,也可以采用類似的思路對其他協議如BLE、串口協議等進行fuzz。同時,該方法不僅局限于IoT設備,也可用于對常見的服務程序進行測試。

模糊測試簡介

模糊測試采用黑盒測試的思想,通過構造大量的畸形數據作為應用程序的輸入,來發現程序中可能存在的安全缺陷或漏洞。

模糊測試方法的分類有很多。根據測試用例生成方式的不同,可以分為基于變異的模糊測試和基于生成的模糊測試。根據對目標程序的理解程度,可分為黑盒模糊測試、灰盒模糊測試和白盒模糊測試。常見工具與方法的對應關系如下。

針對IoT設備,由于其資源受限和環境受限等特點,實際中常采用黑盒模糊測試的方式。在對網絡協議進行測試時,可以將常見的網絡協議分為兩類:一類屬于文本協議,如HTTP、FTP等,這類協議的特點是其數據包內容都是可見字符;另一類為二進制協議,其特點是數據包內容大部分是不可見字符,這類協議在工控設備如PLC中比較常見,通常屬于私有協議。針對文本協議,筆者常采用Sulley框架進行測試;而針對二進制協議,則常采用kitty框架進行測試。

  • Sulley框架和kitty框架均能夠對兩類協議進行測試。

另外,在對IoT設備進行模糊測試時,需要考慮如何對設備進行監控,以判斷是否出現異常。最簡單的方式通過設備服務的可用性進行判斷,如果設備提供的服務不可訪問,表明設備可能崩潰了。但這種監控方式粒度比較粗,容易漏掉一些異常行為。另外,當設備出現異常后,還需要對環境進行恢復,以便繼續進行測試。常見的方式就是重啟設備。現在很多設備崩潰之后都會自動重啟,如果測試目標設備沒用提供這種機制,則需要采用其他方式解決。

Boofuzz框架簡介

由于Sulley框架目前已經停止更新維護,而Boofuzz框架是Sulley的繼承者,除了修復一些bug之外,還增加了框架的可擴展性。下面對Boofuzz框架進行簡單介紹。

來源: Fuzzing Sucks! Introducing the sulley fuzzing framework. Pedram Amini & Aaron Portnoy. Black Hat US 2007

由上圖可知,該框架主要包含四個部分:

  • 數據生成:根據協議格式利用原語來構造請求

  • 會話管理/驅動:將請求以圖的形式鏈接起來形成會話,同時管理待測目標、代理、請求,還提供一個web界面用于監視和控制

  • 代理:與目標進行交互以實現日志記錄、對網絡流量進行監控等

  • 通常,代理是運行在目標設備上。但是,對于IoT設備而言,大部分情況下都無法在目標設備上運行代理程序。

  • 實用工具:獨立的命令行工具,完成一些其他的功能

其中,數據生成和會話管理/驅動是比較重要的2個模塊。對于數據生成模塊,Boofuzz框架提供了很多原語來定義請求,如最基礎的s_string()s_byte()s_static()等。對于會話管理/驅動模塊,其思想體現在下圖中。

來源: Fuzzing Sucks! Introducing the sulley fuzzing framework. Pedram Amini & Aaron Portnoy. Black Hat US 2007

在上圖中,節點ehlohelomail fromrcpt todata表示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()則負責測試前的一些預處理工作和測試后的一些清理工作。

理解了這幾個模塊的功能后,使用該框架進行測試的主要步驟如下:

  • 1.根據網絡數據包構造請求;
  • 2.設置會話信息(包括測試目標的ip地址和端口等),然后按照請求的先后順序將其鏈接起來;
  • 3.添加對目標設備的監控和設備重啟機制等;
  • 4.開始fuzz。

協議fuzz實戰

以某型號路由器為例,由于路由器上HTTP服務是最為常見的,故以http協議為例進行介紹。

  • 模糊測試屬于動態分析技術,因此需要有真實設備,或者采用對固件進行仿真的方式。

根據網絡數據包構造請求

首先,需要盡可能多地與設備進行交互,然后捕獲相應的http請求數據包,如下。

以登錄請求為例,對應的http請求報文示例如下。

POST /HNAP1/ HTTP/1.1
Connection: keep-alive
Content-Length: 400
HNAP_AUTH: E889FD5249E5D51C6C9424283DE3B5DB 1553349899
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36
Content-Type: text/xml; charset=UTF-8
Accept: */*
X-Requested-With: XMLHttpRequest
SOAPAction: "http://purenetworks.com/HNAP1/Login"
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8

<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><Login xmlns="http://purenetworks.com/HNAP1/"><Action>request</Action><Username>xxx</Username><LoginPassword>xxx</LoginPassword><Captcha></Captcha></Login></soap:Body></soap:Envelope>

利用該框架中提供的原語對http請求進行定義,部分示例如下。

s_initialize('login')   # 整個請求的名稱

# 對應 POST /HNAP1/ HTTP/1.1
s_string('POST', name='method')     # 對該字段進行fuzz
s_static(' ')
s_static('/HNAP1/', name='url')     # 不對該字段進行fuzz
s_static(' ')
s_static('HTTP/1.1')
s_static('\r\n')

# 對應 Content-Length: 400
s_static('Content-Length')
s_static(':')
s_size('data', output_format='ascii', fuzzable=True)    # size的值根據data部分的長度自動進行計算,同時對該字段進行fuzz
s_static('\r\n')

# 對應http請求數據
with s_block('data'):
    s_string('<?xml version="1.0" encoding="utf-8"?>')
    s_static('<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">')
    s_static('<soap:Body>')
    s_static('<Login xmlns="http://purenetworks.com/HNAP1/">')
    s_static('<Action>')
    s_string('login', max_len=1024)     # 字段變異后的最大長度為1024
    s_static('</Action>')
    # 省略部分內容
    s_static('</soap:Envelope>')

是否對某個字段進行fuzz需根據具體情況確定。對所有字段都fuzz,生成的畸形數據包會非常多,測試所耗費的時間比較長,但發現問題的可能性比較大;只對少部分字段進行fuzz,生成的畸形數據包會比較少,測試所耗費的時間更短,同時發現問題的可能性也比較小。

  • 字段的粒度大小可能也會對測試結果有所影響。比如,如果對<?xml version="1.0" encoding="utf-8"?>進行變異,是將其當作一個整體,還是拆分為更小的單元?

至于具體怎么對某個字段進行變異,如針對字符串的變異,該框架內已包含一些規則。當然,也可以自己增加規則。

類似的,對網絡數據包中的其他http接口請求進行同樣的定義。

  • 測試的接口越多,觸發問題的可能性越大。

設置會話信息

根據捕獲的數據包定義完請求后,設置與會話相關的信息,包括目標設備的ip地址、端口等。

host = '192.168.2.1'
port = 80

# 其他參數可以按需設置,比如添加fuzz_loggers來保存測試用例和結果等
session = Session(session_filename='http_session', receive_data_after_fuzz=True, ignore_connection_reset=True, restart_sleep_time=10)
target = Target(
    connection=SocketConnection(host, port, proto='tcp'),
    netmon=Remote_NetworkMonitor(host, port, proto='tcp'))  # 服務可用性監控

session.add_target(target)

然后將之前定義的請求按照一定的先后順序鏈接起來,部分示例如下。

session.connect(s_get('login'))     # 默認前置節點為root
session.connect(s_get('login'), s_get('setsysemailsettings'), callback=add_auth_callback)
session.connect(s_get('login'),s_get('setsyslogsettings'), callback=add_auth_callback)
session.connect(s_get('login'),s_get('setschedulesettings'), callback=add_auth_callback)

其中,由于setsysemailsettingssetsyslogsettingssetschedulesettings等請求需要在登錄之后才可以正常使用,所以需要在login請求之后發生。而setsysemailsettingssetsyslogsettingssetschedulesettings這幾個請求之間則沒有明確的先后關系。add_auth_callback為自定義的回調函數,主要用于從login請求中獲取用于登錄認證的信息如cookie,然后將其設置于setsysemailsettingssetsyslogsettingssetschedulesettings等請求中。

添加對目標設備的監控

這里通過設備HTTP服務的可用性來判斷目標設備是否發生異常。如果HTTP服務無法訪問,說明設備可能崩潰了。前面設置的Remote_NetworkMonitor()就是用于對服務的可用性進行監測,其核心代碼如下。

# 通過TCP全連接來判斷目標端口是否在監聽
if self.proto == "tcp" or self.proto == "ssl":
    try:
        self._sock.connect((self.host, self.port))
        self.alive_flag = 1
    except socket.error as e:
        self.alive_flag = 0
  • Remote_NetworkMonitor()為自行添加的代碼,不屬于Boofuzz框架。

前面也提到過,該監測方式的粒度比較粗,可能會存在漏報,可以采用或結合一些其他的方式進行改進。

  • 如果可能,在測試時對設備內部的輸出日志進行記錄,比如設備打印的一些輸出信息;
  • 如果可能,在gdb調試狀態下進行測試。

至于對環境進行恢復,由于該設備崩潰后會自行重啟,所以無須額外的操作,只需調用sleep()等待設備重啟后即可。

開始fuzz

最后調用session.fuzz()驅動整個過程,然后運行腳本即可。默認情況下,會在26000端口開啟一個web服務,用于控制或查看測試的進度及相關信息等。在測試完成后,可以通過查看測試記錄,看是否有測試用例造成目標設備出現異常,以進行進一步分析。

  • 筆者目前尚未對使用的Boofuzz框架進行更新。在最新的commit中,對web界面進行了改進,顯示的信息更豐富。

小結

本文以IoT設備為例,對模糊測試框架Boofuzz,以及利用該框架對網絡協議進行fuzz的基本流程進行了簡要介紹。如果想要獲得更好的效果,還需要對其中的細節進行進一步的優化與完善。

相關鏈接

boofuzz: Network Protocol Fuzzing for Humans

Fuzzing Sucks! Introducing Sulley Fuzzing Framework


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1626/