使用WebRTC开发Android Messenger:第1部分
這是一個(gè)由三部分組成的系列文章,內(nèi)容涉及:利用WebRTC中的BUG和利用Messenger應(yīng)用程序。本系列文章重點(diǎn)闡述了當(dāng)應(yīng)用程序不能應(yīng)用于WebRTC補(bǔ)丁程序以及通信和安全問題通知中斷時(shí)可能出問題的方面。
文 /?Natalie Silvanovich
原文鏈接:
https://googleprojectzero.blogspot.com/2020/08/exploiting-android-messengers-part-1.html
Part 1: First Attempts
WebRTC是一種開放源代碼視頻會(huì)議解決方案,可用于各種軟件。包括瀏覽器,消息客戶端和流媒體服務(wù)。雖然Zero Project過去曾報(bào)道過WebRTC中的多個(gè)BUG,但尚不清楚這些BUG是否可利用,尤其是在瀏覽器之外的BUG。我調(diào)查了流行的Android消息傳遞應(yīng)用程序中最近的兩個(gè)不知能否利用的bug。
The Bugs
我首先嘗試?yán)脙蓚€(gè)BUG:CVE-2020-6389和CVE-2020-6387。
這兩個(gè)BUG都在WebRTC的遠(yuǎn)程傳輸協(xié)議(RTP)的處理中。RTP是WebRTC用于從點(diǎn)對點(diǎn)傳輸音頻和視頻內(nèi)容的協(xié)議。RTP支持?jǐn)U展,擴(kuò)展是可以包含在每個(gè)數(shù)據(jù)包中的額外數(shù)據(jù)段,以便告訴目標(biāo)對等方如何顯示或處理數(shù)據(jù)。例如,存在一個(gè)擴(kuò)展,其中包含有關(guān)發(fā)送設(shè)備的屏幕方向的信息,而其中另一個(gè)包含音量級別。這兩個(gè)BUG都發(fā)生在2019年實(shí)現(xiàn)的WebRTC的擴(kuò)展中。
CVE-2020-6389發(fā)生在幀標(biāo)記擴(kuò)展中,該擴(kuò)展包含有關(guān)如何將視頻內(nèi)容拆分為幀的信息的內(nèi)容。BUG在于處理層信息的方式:WebRTC僅支持五層,但是層號在擴(kuò)展中是一個(gè)三位字段,這意味著它可以高達(dá)七層。這導(dǎo)致在以下代碼中寫越界。從擴(kuò)展名中的層號設(shè)置temporal_idx。
?
if (layer_info_it->second[temporal_idx] != -1 && AheadOf<uint16_t>(layer_info_it->second[temporal_idx], frame->id.picture_id)) { ????? // Not a newer frame. No subsequent layer info needs update. ???? ?break; ???} ??... ???layer_info_it->second[temporal_idx] = frame->id.picture_id; |
代碼的最后一行是發(fā)生越界時(shí)寫入的地方,因?yàn)樵摂?shù)組僅包含五個(gè)元素。這個(gè)BUG也有一些局限性,雖然從上面的代碼中看得不太明顯。首先,在寫的操作之前先進(jìn)行檢查,檢查內(nèi)存的當(dāng)前值(轉(zhuǎn)換為16位無符號整數(shù))是否大于當(dāng)前序列號。僅在為真時(shí)才執(zhí)行寫的操作。實(shí)際上,這并不是什么限制,當(dāng)我測試它時(shí),崩潰通常發(fā)生在兩到三遍之后。一個(gè)更為嚴(yán)重的限制是layer_info_it-> second字段具有64位整數(shù)類型,而frame-> id.picture_id是16位整數(shù)。這意味著盡管此BUG使攻擊者可以在固定大小的堆緩沖區(qū)之外最多寫入三個(gè)64位整數(shù),但是可以寫入的值非常有限,并且太小而無法表示指針。
CVE-2020-6387是前向糾錯(cuò)(FEC)如何處理視頻定時(shí)擴(kuò)展的錯(cuò)誤。? FEC復(fù)制傳入RTP數(shù)據(jù)包,然后在嘗試更正錯(cuò)誤時(shí)清除某些擴(kuò)展名。發(fā)生此BUG的原因是:在清除視頻定時(shí)類型的擴(kuò)展名之前,未驗(yàn)證它們是否具有預(yù)期的長度。導(dǎo)致此錯(cuò)誤的代碼如下:
case RTPExtensionType::kRtpExtensionVideoTiming: { ???????// Nullify 3 last entries: packetization delay and 2 network timestamps. ???????// Each of them is 2 bytes. ???????uint8_t* p = WriteAt(extension.offset) + VideoSendTiming::kPacerExitDeltaOffset; ???????memset( ???????????p, ???????????0, 6); ???????break; ?????} |
VideoSendTiming :: kPacerExitDeltaOffset的值為7,因此此代碼從數(shù)據(jù)包擴(kuò)展名的起始位置開始,將在偏移量7到偏移量13之間寫入六個(gè)零。但是,卻不檢查擴(kuò)展數(shù)據(jù)的長度是否超過13個(gè)字節(jié),甚至不檢查數(shù)據(jù)包是否剩下此字節(jié)數(shù)。該BUG的結(jié)果是,攻擊者可以在一個(gè)可變大小的堆緩沖區(qū)最多偏移七個(gè)字節(jié)的情況下,向堆中寫入最多六個(gè)零。該BUG在某些方面優(yōu)于CVE-2020-6389,但在其他方面則更糟。更好的是,可以溢出的堆緩沖區(qū)是可變大小的,這提供了更多關(guān)于此BUG可以覆蓋堆的選項(xiàng)。偏移量還為寫入零的位置提供了一定的靈活性,并且寫入時(shí)也不必對齊。而CVE-2020-6389需要64位對齊。該錯(cuò)誤更嚴(yán)重,因?yàn)閷懭氲闹当仨殲榱?#xff0c;并且可以寫入的區(qū)域的大小較小(六個(gè)字節(jié)對24個(gè)字節(jié))。
Moving the Instruction Pointer
我首先查看是否有可能使用這些BUG之一來移動(dòng)指令指針。現(xiàn)代Android使用jemalloc,這是一個(gè)平板分配器,它不使用內(nèi)聯(lián)堆頭,因此破壞堆元數(shù)據(jù)不是一種選擇。相反,我使用符號編譯了適用于Android的WebRTC,并將其加載到IDA中。然后,我瀏覽了可用的對象類型,以查看是否存在明顯可用于移動(dòng)指令指針或改善錯(cuò)誤功能的東西。結(jié)果,我什么都沒找到。
我以為也許我可以使用CVE-2020-6389覆蓋長度并導(dǎo)致更大的溢出,但這存在一些問題。首先,該BUG會(huì)寫入一個(gè)64位整數(shù),而很多長度字段都是32位整數(shù),這意味著該寫入操作還會(huì)覆蓋其他內(nèi)容,并且如果長度是64位對齊的,則只能寫入一個(gè)非零值。BUG在處理中的位置也是有問題的,因?yàn)樗鼤?huì)在即將處理的傳入數(shù)據(jù)包的末尾進(jìn)行覆蓋,這意味著在此之后許多對象將不再被訪問,因此任何覆蓋的內(nèi)存都將不再使用。? CVE-2020-6389還覆蓋了固定大小為80的堆緩沖區(qū),這限制了可能受此錯(cuò)誤影響的對象類型。我也不認(rèn)為CVE-2020-6387可以達(dá)到這個(gè)目的,因?yàn)樗荒軐懥?#xff0c;而這只能使長度變短。
?
我不確定現(xiàn)在要進(jìn)行什么操作,所以我在Android上觸發(fā)了數(shù)十次CVE-2020-6389,以查看是否存在超過16位寬的地址崩潰,希望它們能為我提供一些方法在除了覆蓋無效的16位值的指針之外,此錯(cuò)誤可能會(huì)影響代碼的行為。令我驚訝的是,它崩潰了,而且指令指針設(shè)置為一個(gè)值,該值顯然已從堆中讀取了大約20次。
?
分析崩潰后,結(jié)果發(fā)現(xiàn)在溢出區(qū)域之后分配了一個(gè)StunMessage對象。? StunMessage類的成員如下。
protected: ??std::vector<std::unique_ptr<StunAttribute>> attrs_; ?... ?private: ??... ??uint16_t type_; ??uint16_t length_; ??std::string transaction_id_; ??uint32_t reduced_transaction_id_; ??uint32_t stun_magic_cookie_; |
因此,在vtable之后,第一個(gè)成員是向量。向量如何在內(nèi)存中布置?原來它的前兩個(gè)成員如下。
? pointer __begin_; ??pointer __end_; |
這些指針指向內(nèi)存中向量內(nèi)容的開頭和結(jié)尾。在崩潰期間,__end_成員被一個(gè)小的16位整數(shù)覆蓋。向量迭代的工作方式是從__begin_指針開始,然后遞增直到達(dá)到__end_指針,因此,此更改意味著通常下次在析構(gòu)函數(shù)中對向量進(jìn)行迭代時(shí),它將超出范圍。由于此向量包含StunAttribute類型的虛擬對象,因此它將對每個(gè)元素執(zhí)行虛擬調(diào)用,以調(diào)用它的析構(gòu)函數(shù)。對越界內(nèi)存的虛擬調(diào)用正是為什么移動(dòng)指令指針的原因。
除以下的這個(gè)問題外,這似乎是控制指令指針的一種合理方法:在典型配置中,WebRTC連接一端的攻擊者無法將STUN發(fā)送給另一端的用戶,而是他們各自與自己的STUN服務(wù)器進(jìn)行通信。我問webrtchacks的Philipp Hancke是否知道某種方法。他建議使用此方法,該方法涉及將攻擊者控制的TCP服務(wù)器指定為兩個(gè)對等方(稱為ICE候選方)之間潛在的可路由路徑。然后,攻擊者和目標(biāo)設(shè)備都將通過此服務(wù)器進(jìn)行通信,包括STUN消息。
這使我能夠發(fā)送具有異常大量屬性的STUN消息。這是必要的,因?yàn)闉榱丝刂浦噶钪羔?#xff0c;我將需要能夠控制STUN屬性向量之后在內(nèi)存中顯示的內(nèi)容。? jemalloc分配相似大小的分配,這由連續(xù)內(nèi)存運(yùn)行中的預(yù)定義大小類確定。大小類使用的次數(shù)越少,相同大小類的兩個(gè)對象被一個(gè)接一個(gè)地分配的可能性就越大。
通常,STUN消息具有少量屬性,這些屬性轉(zhuǎn)換為32或64字節(jié)的向量緩沖區(qū)大小,它們都是非常常用的大小類。相反,我發(fā)送了具有128個(gè)屬性的STUN消息,這些消息轉(zhuǎn)換為1024字節(jié)的向量緩沖區(qū)大小,而這恰好是WebRTC中不常用的大小類。通過發(fā)送許多具有此數(shù)量屬性的STUN消息,同時(shí)發(fā)送大小為1024的RTP數(shù)據(jù)包,其中包含所需的指針值,并散布著包含BUG的數(shù)據(jù)包,我能夠?qū)υ撝羔樦颠M(jìn)行約1的虛擬調(diào)用五次。這足以在BUG利用中使用,所以我決定繼續(xù)攻克ASLR。
?
Breaking ASLR
在此攻擊中,有兩種可能的方法可以破解ASLR。?一種是使用上述BUG之一讀取內(nèi)存,然后以某種方式將其發(fā)送回攻擊者設(shè)備或TCP服務(wù)器,另一種是使用某種故障預(yù)兆來確定內(nèi)存布局。
?
我首先查看是否有可能使用這些BUG之一從目標(biāo)設(shè)備遠(yuǎn)程中讀取內(nèi)存。馬克·布蘭德(Mark Brand)建議,可以使用CVE-2020-6387來實(shí)現(xiàn)這一點(diǎn),方法是將指向傳出數(shù)據(jù)的指針的低位字節(jié)設(shè)置為零,從而導(dǎo)致發(fā)送越界數(shù)據(jù)而不是實(shí)際數(shù)據(jù)。這似乎是一種很有希望的方法,因此我使用IDA尋找潛在的對象。
?
結(jié)果發(fā)現(xiàn)有不少,他們都有問題。我花了一些時(shí)間在SendPacketMessageData和DataReceivedMessageData上。這些對象用于在隊(duì)列中存儲(chǔ)指向傳出RTP數(shù)據(jù)的指針。它們包含一個(gè)CopyOnWriteBuffer對象,并且它的第一個(gè)成員是指向rtc :: Buffer對象的引用計(jì)數(shù)的指針。使用CVE-2020-6387可以將此指針的最低字節(jié)設(shè)置為零。不幸的是,rtc :: Buffer的結(jié)構(gòu)使以這種方式顯示內(nèi)存具有挑戰(zhàn)性。
RefCountedObject vtable; size_t size_; size_t capacity_; std::unique_ptr<T[]> data_; |
我希望有可能使指向該結(jié)構(gòu)的裁剪指針指向堆上的其他對象,該對象在data_指針的位置具有一個(gè)指針,而該數(shù)據(jù)將被發(fā)送。但是,事實(shí)證明,在發(fā)送數(shù)據(jù)的過程中,上面對象的所有四個(gè)成員都可以訪問,并且需要合理有效。我遍歷了與rtc :: Buffer類相同大小的所有可用對象,但是找不到具有這些確切屬性的對象。
然后,我考慮使用一個(gè)已經(jīng)釋放的rtc :: Buffer對象,而不是使用其他對象,而使用特定的后備緩沖區(qū)大小,可以使用堆操作將其替換為包含指針的對象。這也沒有解決。這在很大程度上是可靠性的問題。首先,一個(gè)rtc :: Buffer對象是36個(gè)字節(jié),這在jemalloc中轉(zhuǎn)換為48個(gè)大小類,這意味著分配了48個(gè)字節(jié)。想象一下這種類型的一些連續(xù)分配,地址如下:
0x[...]0000? ? ? buffer 0 0x[...]0030? ? ? buffer 1 0x[...]0060? ? ? buffer 2 0x[...]0090? ? ? buffer 3 0x[...]00c0? ? ? buffer 4 0x[...]00f0 ? ? ? buffer 5 0x[...]0120? ? ? buffer 6 ... |
如果該BUG將緩沖區(qū)0到5的第一個(gè)字節(jié)設(shè)置為零,則它們將落在有效緩沖區(qū)上,但是如果緩沖區(qū)6設(shè)置為零,則它將不起作用,因?yàn)?56不會(huì)平均分配為48。所以結(jié)果是,每次BUG碰到SendPacketMessageData對象時(shí),只有三分之一的機(jī)會(huì)最終會(huì)指向有效的rtc :: Buffer。首先,擊中對象也是不可靠的,因?yàn)閃ebRTC正在進(jìn)行許多其他類似大小的分配。通過使用TCP服務(wù)器使連接非常慢,可以增加堆上這些對象的數(shù)量和發(fā)送它們之前的時(shí)間量,但即使這樣,我也只能在不到10%的時(shí)間內(nèi)命中結(jié)構(gòu)。必須操縱堆,以便首先在一行中有許多釋放的rtc :: Buffer對象,并且支持已被包含指針的東西替換。但這卻增加了更多的不可靠性。我最終放棄了這種方法,因?yàn)槲艺J(rèn)為我可能既無法做到足夠可靠,也無法通過合理的努力將其用于BUG利用程序中。同樣地,被攻擊的應(yīng)用程序的崩潰行為也很重要。這可能可以適用于在崩潰的情況下立即重生的應(yīng)用程序,但是對于停止重生的應(yīng)用程序?qū)嵱眯詤s要差很多,除非存在一定的延遲,而這在Android上很常見。
我還大量研究了WebRTC如何生成傳出數(shù)據(jù)包,尤其是對等端始終發(fā)送的遠(yuǎn)程傳輸控制協(xié)議(RTCP),即使它只是接收音頻或視頻。但是,大多數(shù)傳出數(shù)據(jù)包都是在堆棧上生成的,因此無法使用堆損壞BUG對其進(jìn)行更改。
我還考慮過使用崩潰Oracle來破解ASLR,但我認(rèn)為使用這些特定的錯(cuò)誤不太可能成功。首先,與它們進(jìn)行堆分配是不可靠的,因此很難判斷是由于特定情況還是僅由于BUG失敗而導(dǎo)致崩潰。考慮到這些BUG的功能有限,我還不確定是否有可能創(chuàng)建可檢測的條件。
我還考慮過使用CVE-2020-6387更改vtable或函數(shù)指針以讀取內(nèi)存,導(dǎo)致崩潰Oracle可以檢測到的行為或執(zhí)行不需要破壞ASLR的基于偏移的利用。我決定不走這條路,因?yàn)樽罱K結(jié)果將取決于哪些函數(shù)和vtables在以零結(jié)尾的位置上加載,而這在各個(gè)版本之間差異很大。使用此方法編寫的BUG利用程序需要進(jìn)行大量修改才能在WebRTC的稍微不同的版本上運(yùn)行,并且無法保證它完全可以運(yùn)行。
我決定在這一點(diǎn)上,我需要尋找可能破壞ASLR的新BUG,因?yàn)槲易罱l(fā)現(xiàn)的兩個(gè)BUG都無法輕易做到。
LiveVideoStackCon 2020?北京
2020年10月31日-11月1日
點(diǎn)擊【閱讀原文】了解更多詳細(xì)信息
總結(jié)
以上是生活随笔為你收集整理的使用WebRTC开发Android Messenger:第1部分的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 亚马逊团队在Interspeech 20
- 下一篇: 使用WebRTC开发Android Me