作者:盤古實驗室
公眾號:https://mp.weixin.qq.com/s/yMQN3MciI-0f3mzz_saiwQ

在完成了對 FaceTime 的一系列漏洞挖掘與研究后,我們決定對微信的音視頻通信做一些分析。經分析后發現,當微信語音通話連接建立成功之后,微信客戶端將解析遠端發來的網絡報文并還原成多媒體流。在還原解析的過程中,如果處理遠端數據的代碼存在問題時就會形成一個遠程的攻擊面。

在針對這個攻擊面進行深入挖掘后我們發現了若干可以造成遠程內存破壞的漏洞。本篇文章我們將選擇一個比較有趣且復雜的漏洞進行深入的分析。該漏洞可以造成遠程寫溢出從而導致崩潰,其root cause隱藏的非常深,觸發流程也比較復雜。研究與分析該漏洞無論是對安全研究還是軟件開發的角度都有一定的價值。我們將在文章中詳細的分析漏洞成因和觸發流程。微信已經在最新版7.0.12中修復了該漏洞。

開胃小菜

首先我們先介紹兩個比較簡單的漏洞,一個屬于本地代碼執行,一個屬于遠程溢出。

本地代碼執行

Mac版本的微信客戶端處理粘貼操作時,沒有有效檢查粘貼板對象中內容,導致不安全的對象反序列化。當本地其他惡意應用設置粘貼板時,用戶在微信客戶端粘貼操作時,會導致任意對象的創建。

如下面截圖所示,Mac 版本的微信在反序列化粘貼板對象的過程中,并沒有使用secure coding 以及白名單等設置,導致任何可以響應 [initwithcoder:] 函數的 objective-c 對象都能被創建并使用,會引起很大的攻擊面。

Mac版本微信對剪切板的處理

具體攻擊結果可以參考Google Project Zero在iMessage中發現的大量不安全反序列化攻擊.

Mac版本微信已經對該漏洞進行了完全正確的修復,調用了 setRequiresSecureCoding: 函數,并作出了安全設置。

修復后的剪切板處理

遠程下溢出

微信視頻通話接通后,通話兩端建立網絡直連傳遞RTP報文。微信客戶端傳輸RTP包過程中,采用了一套加密機制。但是微信客戶端在RTP解密之前,沒有很好驗證RTP包長度。當攻擊者發送很短的RTP包的時候,會引起接受端處理RTP包過程中長度計算的整數下溢出,進而導致內存越界訪問。

RTP包長度驗證減法下溢出

有趣的是,GP0 研究員在微信 CAudioJBM::InputAudioFrameToJBM 函數中發現了類似的錯誤 (https://bugs.chromium.org/p/project-zero/issues/detail?id=1948)。這說明微信在在包長度驗證時存在一定共性缺陷。

這是一個非常明顯的下溢出,但是通過對這個問題的分析,我們認為遠程的攻擊面中可能存在風險更高的漏洞。

遠程寫溢出成因與分析

跳過前期復雜的協商互聯流程,我們在已經通過微信語音通話的狀態下,微信客戶端將收到遠端發送來的音頻數據。收到的原始數據會被層層分解處理,并根據不同的類型分發到不同的處理函數上。

RecvRtpPacketCng

在收到遠端的網絡數據后,RTP 數據包將被 RecvRtpPacketCng(__int64 XVEChannel, unsigned int *pData, __int16 len, void *a4) 函數處理,這里的參數 pData內容是語音通話的遠端完全可控的。該函數會根據網絡包中指定的過不同的代碼解析

當pkType類型為7或8時,該網絡包的類型為 RTPwithRsMd

當網絡包頭部的 subpkt 解析完成后會調用 ParaseRemoteLostRateParam 函數:

ParaseRemoteLostRateParam 函數中,根據遠端的 pData 中數據設置了XVEChannel+72 處對象的內部數據。通過參數 a2,在 pData 中讀取兩個字節,并最終設置到 m_RemoteLrParam 和 nFrmCnt 兩個成員變量中。

DevPutProcessRsMdCng

在接收遠端的語音數據的同時,也需要將自己的語音數據通過XVEChannel對象發送給遠端。

在 readRemoteLrParam 函數中,會將剛剛設置的 m_RemoteLrParam 和 nFrmCnt 讀取到棧上變量v92中。

在讀取RemoteLostRateParam到局部變量v92后,需要設置到相應的本地成員變量中

當數據準備好后將調用函數 CAudioRS::RsMdEncProcessCng,寫溢出就發生在這個函數中。

當 CAudioRS::RsMdEncProcessCng 剛開始執行時會通過 XVEChannel_72+9 作為 index 寫一個 byte.

并在 RsMdEncQueueSourcePktCng 函數中 XVEChannel_72 + 9 將做一次自增。

當 CAudioRS::RsMdEncProcessCng 退出前會根據當前的狀態更新成員變量。

[1] 通過update_data根據LocalExpectRSPara的值修改成員變量

[2] 如果XVEChannel_72+9處的值與XVEChannel_72+4處的值相同,則會觸發[3]處的代碼將XVEChannel_72+9處寫0.

因為 XVEChannel_72 + 9 可以根據 pData 中的數據設置成攻擊者可控的數據,當 XVEChannel_72 + 9 被設置為大于 XVEChannel_72 + 4 時,就必須一直自增且產生整數溢出后重新與 XVEChannel_72 + 4 相等時, 才能將 XVEChannel_72 + 9清零。

所以 XVEChannel_72 + 9 的取值范圍是0-255。又因為*(_BYTE *)(XVEChannel_72 + *(char *)(XVEChannel_72 + 9) + 1668) = a7; 使用的是有符號數作為index。最終覆蓋范圍是 XVEChannel_72+1668處的-128127處超過原本數據結構包含的內存。

觸發流程

RecvRtpPacketCng 從網絡報文中獲取 lrParam

DevPutProcessRsMdCng 根據 lrParam 設置 LocalExpectRSPara

RsMdEncProcessCng 根據 LocalExpectRSPara 中的參數修改成員變量作為數據修改的index (XVEChannel_72 + 9 )

修改成功后會對index自增并與本地的max值做比較,如果index達到最大值index_max時(XVEChannel_72 + 4)將index清零

如果通過遠數據端將index設置為大于index_max的情況,則index會一直自增直到發生整數溢出后才能滿足index==index_max的條件進入清零的邏輯

index在(-128,127)范圍內遍歷,產生越界寫。越界寫的范圍在 (-128,127)之間。

感謝

要特別感謝 TSRC 的認真負責。他們在我們上報漏洞后對漏洞響應及時,收到報告的次日就確認了漏洞并給出危險評級。并且在后續的漏洞修復與修復版本更新的工作中和我們保持聯系。

TimeLine

2019/11/28 發現漏洞

2019/12/02 完成漏洞分析并上報TSRC

2019/12/03 TSRC確認漏洞并修復

2020/03/23 文章發布

Credit:漏洞由盤古實驗室黃濤、王鐵磊發現和分析。


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