手機(jī)版 | 網(wǎng)站導(dǎo)航
觀察家網(wǎng) > 消費(fèi) >

饒派杯 XCTF 車聯(lián)網(wǎng)挑戰(zhàn)賽 mqttsvr 復(fù)現(xiàn)-全球新資訊

博客園 | 2023-06-06 13:55:58

前言

IDA和Ghidra對(duì)mips64架構(gòu)的識(shí)別貌似不是很友好,賽場(chǎng)上由于反編譯實(shí)在難看,所以很難靜下心來(lái)去逆,于是賽后在期末考試前稍微花點(diǎn)時(shí)間做了一下復(fù)現(xiàn)。


(資料圖片僅供參考)

準(zhǔn)備

checksec一下,發(fā)現(xiàn)是mips64 大端,沒(méi)開(kāi)Canary RELRO,可以溢出,可以覆寫got表,這里開(kāi)了NX和PIE,NX對(duì)mips64這種異架構(gòu)來(lái)說(shuō)好像并不會(huì)起作用,如果服務(wù)端是用qemu起的,PIE好像也啥作用。

把文件拖進(jìn)IDA分析,發(fā)現(xiàn)我的IDA根本反編譯不了。

拖進(jìn)ghidra分析,乍看起來(lái)貌似并沒(méi)有什么問(wèn)題,仔細(xì)看一下會(huì)發(fā)現(xiàn)少了很多函數(shù)。

這可能是由于ghidra基址識(shí)別錯(cuò)誤導(dǎo)致的,于是我們把ghidra的加載地址由0x100000改為0x0,可以得到較為全面的反編譯。

逆向分析

從題目名字mqttsvr大概就能猜到這題基于mqtt協(xié)議,不過(guò)賽場(chǎng)上不能聯(lián)網(wǎng),不知道mqtt協(xié)議的大致格式,純靠逆向就很難受了,復(fù)現(xiàn)的時(shí)候可以上網(wǎng)查查,感覺(jué)還可以。一開(kāi)始會(huì)分配一個(gè)堆塊,然后進(jìn)入一個(gè)循環(huán),這個(gè)堆塊后來(lái)可以知道是存放標(biāo)志位用的。

函數(shù)FUN_000042fc比較長(zhǎng),容易關(guān)注到其中的switch,這個(gè)操作是對(duì)某個(gè)字節(jié)右移4,也就是取了該字節(jié)的高bit位,來(lái)作為選擇項(xiàng)。通過(guò)以下mqtt Connect報(bào)文,可大概推斷這個(gè)switch就是判斷報(bào)文的不同類型的請(qǐng)求,并且給出相對(duì)應(yīng)的處理。那么漏洞大概率就存在這些請(qǐng)求的處理函數(shù)中。

0x10             // 固定報(bào)頭:報(bào)文類型(Connect)和保留標(biāo)志位0x0C             // 可變報(bào)頭長(zhǎng)度0x00 0x04 0x4D 0x51 0x54 0x54 // MQTT協(xié)議名稱(長(zhǎng)度為4)0x05             // MQTT協(xié)議版本號(hào)0xCE             // 連接標(biāo)志0x00 0x3C        // Keep Alive時(shí)間(以秒為單位,此處為60秒)// 有效載荷0x00 0x04 0x63 0x6C 0x69 0x65  // 客戶端標(biāo)識(shí)符(Client Identifier)0x00 0x0A 0x2F 0x75 0x73 0x65 0x72 0x73 0x2F 0x31 0x32 0x33 // 遺囑主題(Will Topic)0x00 0x06 0x77 0x69 0x6C 0x6C 0x2F 0x31  // 遺囑消息(Will Message)0x00 0x04 0x75 0x73 0x65 0x72  // 用戶名(Username)0x00 0x06 0x70 0x61 0x73 0x73 0x77 0x6F  // 密碼(Password)

并且我們不難發(fā)現(xiàn)除了case 1,其他在進(jìn)入處理函數(shù)前,都會(huì)有一個(gè)pbVar1[1] == 0的判斷,猜測(cè)這里的case 1是一個(gè)連接認(rèn)證的請(qǐng)求。直接進(jìn)入FUN_00003200看一下,但是發(fā)現(xiàn)ghidra識(shí)別不出這個(gè)函數(shù)。

我這里采用的方法是,直接到0x3200+0x4的地方,直接強(qiáng)行創(chuàng)建一個(gè)函數(shù),發(fā)現(xiàn)可以識(shí)別出來(lái)。還是有少許函數(shù)調(diào)用識(shí)別不出來(lái),對(duì)著匯編看看就行。

FUN_00003200函數(shù)大概流程是,獲取客戶端標(biāo)識(shí)符,用戶名和密碼并對(duì)其進(jìn)行檢查。這里的客戶端標(biāo)識(shí)符和用戶名經(jīng)過(guò)簡(jiǎn)單逆向可知應(yīng)該分別為Car_MQTT_Client和Car_Administrator。對(duì)于用戶密碼來(lái)說(shuō),在獲取到密碼之后會(huì)經(jīng)過(guò)FUN_000049f0,F(xiàn)UN_00004a68,F(xiàn)UN_00004c84進(jìn)行處理。進(jìn)入函數(shù)內(nèi)部看一下,可以得知實(shí)現(xiàn)了一個(gè)md5加密,同時(shí)把加密之后的數(shù)據(jù),通過(guò)strncmp"\x64\x1C\x73\x9C\x22\xC8\xF5\xC0\x67\xE1\x1F\xC3\x0B\xEC\x9D\x7A"循環(huán)異或0x73進(jìn)行比較,這里可以發(fā)現(xiàn),第三位會(huì)出現(xiàn)\x00截?cái)啵蔬@里可以通過(guò)爆破,得到一個(gè)加密后前3字節(jié)為\x17\x6f\x00的密碼即可,我這里爆破出一個(gè)\x00\xDD\x5E\x85。發(fā)送如下報(bào)文即可成功連接。

# Connectsleep(0.1)s.send(b"\x10" + b"\x34")VariableHeader = b"\x00\x04MQTT" + b"\x04\xC2" + b"\x43\x21" # \x04 + MQTT + \x04\x02 + \x43\x21VariableHeader+= b"\x00\x0F" + b"Car_MQTT_Client"VariableHeader+= b"\x00\x11" + b"Car_Administrator"VariableHeader+= b"\x00\x04" + b"\x00\xDD\x5E\x85"# Subscribe 1sleep(0.1)s.send(VariableHeader)

接下來(lái)分析case 3的函數(shù)FUN_000039b0,一開(kāi)始有一大堆的函數(shù),其中也有memcmp來(lái)進(jìn)行判斷,不過(guò)這里不太好看出來(lái)是和什么進(jìn)行的比較,我去調(diào)試了一下,可以知道是與IOTcar_topic進(jìn)行比較,這些數(shù)據(jù)會(huì)用全局變量null_ARRAY_00017360來(lái)存放其指針進(jìn)行保存,同時(shí)最后會(huì)進(jìn)入函數(shù)FUN_000037d8。

函數(shù)FUN_000037d8中也有一些memcmp,并且可以很清楚得之是與什么進(jìn)行的比較。

可以發(fā)現(xiàn),一個(gè)字段是與 IOTcar_topic,另一個(gè)是與 car_car進(jìn)行比較,并且如果 *** (calloc_ptr+ 2) == "\x01",*(calloc_ptr+ 3) == "\x05"**,那么就可以進(jìn)入函數(shù) FUN_0000250c,并且上面把 null_ARRAY_00017360 + local_30 * 0x20 + 0x13(存放字段car_car的堆地址)拷貝到 acStack_48上。后期經(jīng)過(guò)調(diào)試得知 FUN_0000250c本意可能是用來(lái)泄露出一個(gè)堆地址,不過(guò)由于我這里是 qemu啟動(dòng)的原因,堆地址都是 0x4000018000,會(huì)出現(xiàn) 00截?cái)嗟膯?wèn)題,不知道遠(yuǎn)程是不是用其他方式啟動(dòng)的,還是我的啟動(dòng)方式的原因。不過(guò)這里的 (calloc_ptr+ 2) == "\x01",(calloc_ptr+ 3) == "\x05"還并不滿足,我們需要對(duì)其他函數(shù)進(jìn)一步分析。

case 5,6的處理函數(shù)比較簡(jiǎn)單,同時(shí)并沒(méi)有起到什么作用,故分析略過(guò)。

case 8的函數(shù) FUN_00003dfc中,可以很明顯看出有一個(gè)堆溢出漏洞,它在申請(qǐng)堆塊時(shí),malloc_size=(size&0xff),并且如果其中的一個(gè)字段等于 IOTcar_topic,就會(huì)把 *(calloc_ptr+ 2)的值設(shè)置為 1,這是泄露堆地址的條件之一。

case 10的功能是吧 case 8申請(qǐng)的堆塊釋放掉,并且把 (calloc_ptr+ 2)的值設(shè)置為 0case 12*的功能是使得 *(calloc_ptr+ 3) = *(calloc_ptr+ 3)+1,通過(guò)發(fā)五次這個(gè)請(qǐng)求包可以滿足 *(calloc_ptr+ 3) == "\x05"。

漏洞分析

這題的漏洞有如下幾個(gè)。第一個(gè)是在連接時(shí),用戶密碼由于md5之后存在\x00截?cái)嗟膯?wèn)題,故可以爆破出一個(gè)滿足條件的密碼。第二個(gè)是訂閱IOTcar_topic主題,并且發(fā)送5次\xC0開(kāi)頭的請(qǐng)求,再發(fā)送特定的消息即可泄露堆地址。第三個(gè)是存在一個(gè)堆溢出。

漏洞利用

泄露出堆地址之后,就可以得到程序基地址。可以劫持got表,并且NX沒(méi)有效果,那么我們就可以把malloc等函數(shù)got表改為某一個(gè)堆地址,并且在這個(gè)堆地址上寫入shellcode即可。uclibc mips64的堆利用我參考https://eqqie.cn/index.php/search/mips64 ,發(fā)現(xiàn)有三個(gè)不同的malloc版本。但我并不知道怎么判斷當(dāng)前 uclibc是哪一個(gè)版本,我就用 malloc-standard這個(gè)版本的利用方法試了一下,直接覆蓋一個(gè)杯釋放堆塊的 fd,發(fā)現(xiàn)二次分配,可以申請(qǐng)到預(yù)期位置(malloc_got - 0x20)。

覆蓋成shellcode地址之后,我們即可執(zhí)行提前布置好的shellcode,從下圖可見(jiàn)我們成功跳轉(zhuǎn)到我們提前布置的shellcode上,不過(guò)雖然最后我可以走到execve("/bin/sh", NULL, NULL),但是似乎系統(tǒng)調(diào)用并沒(méi)起作用。有知道原因的師傅可以聯(lián)系我一下,我再改一下這里。

Poc

https://github.com/fxc233/CTF/blob/main/IOT/2023XCTFcar-mqttsvr/exp.py

標(biāo)簽:

  • 標(biāo)簽:中國(guó)觀察家網(wǎng),商業(yè)門戶網(wǎng)站,新聞,專題,財(cái)經(jīng),新媒體,焦點(diǎn),排行,教育,熱點(diǎn),行業(yè),消費(fèi),互聯(lián)網(wǎng),科技,國(guó)際,文化,時(shí)事,社會(huì),國(guó)內(nèi),健康,產(chǎn)業(yè)資訊,房產(chǎn),體育。

相關(guān)推薦