天天看點

《深入了解計算機系統》CSAPP_AttackLab

作業系統:linux

調試工具:gdb

目錄

AttackLab

Phase 1

Phase 2

Phase 3

Phase 4

Phase 5

Reference Linking

Conclusion

我們是攻擊者,也就是hack,其實我更喜歡駭客這個翻譯,而不是黑客。<code>phase1 ~ phase3</code>的攻擊方式都是運作<code>CTARGET</code>使用注入代碼技術。

作為一名駭客小白,我們可以通過<code>unix &gt; objdump -d ctarget &gt; ctarget.d</code>這段指令檢視彙編代碼,但我更喜歡用用gdb裡的disas指令展示函數。這樣更友善閱讀。(這裡就不需要打斷點了)

第一個攻擊任務是:

Your task is to get CTARGET to execute the code for touch1 when getbuf executes its return statement, rather than returning to test. 你的任務是運作CTARGET使得當getbuf運作結束後,運作touch1,這些行為要在test傳回之前完成。

原本主函數test調用了getbuf函數,test傳回時就結束了。但我們要在調用getbuf函數之後再多一個調用touch1函數。如何完成這個任務呢?

我們知道,當調用getbuf函數時,棧會給存留一個傳回位址棧幀<code>0x401976</code>,執行getbuf函數結束後,通過這個位址傳回主函數test。我們隻要把這個位址改為touch1的函數位址<code>0x4017c0</code>即可。

《深入了解計算機系統》CSAPP_AttackLab

如何改呢?我們利用棧溢出的特性,因為getbuf函數開辟了40(0x28)beytes的棧空間,我們多輸入8bytes的touch1的函數位址用來覆寫,之前的40bytes随意輸入。(棧的一行裡有8byte,傳回位址也是8bytes)

同時注意到intel使用的是小端法排序,我們無需用0x注明十六進制,CTARGET會自動轉為十六進制。

key:

(<code>c0 17 40</code>後續的零可填可不填,會自動補上的)

我們通過<code>unix&gt; ./hex2raw &lt; level1.txt | ./ctarget -q</code>進行攻擊。(<code>./ctarget -q</code>,在<code>./ctarget</code>之後加 <code>-q</code>的原因是我們沒法連接配接上CMU的伺服器,在本地運作驗證結果就行了;檔案<code>level1.txt</code>中存放着我們的答案)

先檢視代碼及對應彙編代碼:

Your task is to get CTARGET to execute the code for touch2 rather than returning to test. In this case, however, you must make it appear to touch2 as if you have passed your cookie as its argument. Phase2和Phase1類似,也是在test傳回前調用一次touch2函數。但是在touch2函數val的值必須和cookie相同才算touch2函數調用成功。

那麼得想辦法調用touch2函數,即注入touch2的函數位址。

advice: Recall that the first argument to a function is passed in register %rdi. <code>%rdi</code>中存儲touch2函數着第一個參數。 Your injected code should set the register to your cookie, and then use a <code>ret</code> instruction to transfer control to the first instruction in touch2. 你注入的代碼應該把寄存器<code>%rdi</code>設定為<code>cookie</code>,然後中使用<code>ret</code>指令轉移控制權到touch2中的第一條指令(即touch2的函數位址<code>0x4017ec</code>)。

根據advice及touch2源代碼可以知道:(要寫彙編代碼)

用<code>movq</code>指令,<code>$0x59b997fa</code>移入<code>%rdi</code>。進而<code>val == cookie</code>函數touch2執行成功。(在cookie.txt中存着cookie的值<code>0x59b997fa</code>)。

用<code>push</code>指令壓入touch2的函數位址,進而<code>ret</code>時會傳回為touch2的函數位址。

那麼有:

再将其轉化為機器代碼:使用<code>linux&gt; gcc -c level2.s</code>及<code>linux&gt; objdump -d level2.o</code>

進而得到部分注入代碼:(機器代碼已經使用小端法了)

那麼現在問題來了,從哪裡注入代碼?答案應該是從棧頂,在<code>getbuf</code>函數已經開辟棧空間的<code>%rsp</code>處注入。

原因呢?如下圖所示,注入的register必須在stack top之前。

《深入了解計算機系統》CSAPP_AttackLab

接下來我們查找<code>getbuf</code>開辟棧空間的<code>%rsp</code>對應位址。

好,最終我們有,key: (注意填充已經開辟的棧空間40bytes以及小端法)

最後手繪一下,注入代碼後的部分棧空間:(滑鼠畫的,字有點抽象)省略了<code>getbuf</code>的<code>return address</code>(注入順序和圖示順序相反)

《深入了解計算機系統》CSAPP_AttackLab

檢視(彙編)代碼:

先看看任務:

Your task is to get <code>CTARGET</code> to execute the code for <code>touch3</code> rather than returning to <code>test</code>. You must make it appear to <code>touch3</code> as if you have passed a string representation of your cookie as its argument. 你的任務是讓<code>CTARGET</code>執行<code>touch3</code>的代碼,而不是傳回<code>test</code>。你必須讓<code>touch3</code>在<code>test</code>之上(執行)看起來就像你(通過<code>test</code>)傳遞了一個cookie的字元串表示作為<code>touch3</code>的參數。

再康康建議:

You will need to include a string representation of your cookie in your exploit string. The string should consist of the eight hexadecimal digits (ordered from most to least significant) without a leading “<code>0x</code>.” 你将會用到cookie的字元表示。這個字元表示是連續的十六進制數字且沒有字首<code>0x</code>。 Recall that a string is represented in C as a sequence of bytes followed by a byte with value 0. Type “<code>man ascii</code>” on any Linux machine to see the byte representations of the characters you need. 在c語言中,字元串表示的結尾是1byte的0(也就是<code>'\0'</code>)。你可以在任何Linux機器中,使用<code>man ascii</code>指令檢視字元對應的ascii碼。 Your injected code should set register %rdi to the address of this string. 你注入的代碼應該把<code>%rdi</code>設定為字元串位址。 When functions <code>hexmatch</code> and <code>strncmp</code> are called, they push data onto the stack, overwriting portions of memory that held the buffer used by <code>getbuf</code>. As a result, you will need to be careful where you place the string representation of your cookie. 當函數<code>hexmatch</code>和<code>strncmp</code>被調用時,它們會push資料到棧上,覆寫部分調用<code>getbuf</code>時存儲的内容。是以,你應該仔細确定在哪裡放入你的cookie。

由此我們可以知道,和touch2類似,也是在使用test過程中先執行getbuf,在getbuf過程調用touch3,同時touch3會調用hexmatch,hexmatch會調用strncmp。我們通過彙編代碼可以知道它們分别開辟的棧空間是多少。

考慮到hexmatch開辟的110bytes棧空間,會覆寫部分原有的getbuf開辟的40bytes空間。是以我們不能直接輸入cookie的字元表示(也就是cookie的ASCII表示)。

不能輸入立即數,那我們可以用輸入位址的方式,在此之前将cookie的ASCII表示先放入該位址當中。

那麼,該放入哪個位址呢?我們這裡選擇的是放入test的棧頂位址當中,原因有兩個。一是因為test的棧頂位址可以臨時使用,當test開辟棧空間後,它立刻調用了getbuf函數,其棧空間實際上沒有存放任何資料。當調用結束後,才開始在棧空間處存放資料。二是因為友善輸入,test開辟的8bytes空間,恰好用來直接輸入cookie的ASCII表示。

由此得出,先輸入cookie的ASCII表示,然後引用test的棧頂位址,再調用touch3,最後傳回即可。接下來,我們按部就班的來。

先用<code>man ascii</code>指令查詢字元的ASCII表示:

​ 從左邊的16進制表示可以得到cookie的ASCII表示:

接下來查找test的棧頂位址:

​ 那麼test的棧頂位址為:<code>0x5561dca8</code>.

引用test的棧頂位址(作為位址存放在%rdi中,作為輸入),再調用touch3(<code>0x4018fa</code>,之前的彙編已經給出),最後傳回。得出如下彙編代碼:

反彙編得出機器代碼:

最終結合cookie的ASCII表示以及要注入的位置(getbuf棧頂位址:<code>0x5561dc78</code>)可以得出key:

(要将<code>ff 34 25</code>改為<code>68</code>才可通過,和标準答案編譯的不一樣,但不知道為啥,也許是我用<code>ubuntu</code>問題)

注意,任何字元表示的結尾都要加上<code>'\0'</code>,也就是16進制的<code>00</code>。也許會有人問,test隻開辟了8bytes的棧空間,這不會溢出到傳回位址,然後覆寫傳回位址内容嗎?當然會,但是經過查閱,發現<code>ctarget</code>的所有指令的位址<code>400c48 ~ 402d7c</code>都隻占據6byetes。進而test傳回時的位址最後2bytes是無效位,覆寫了也對傳回位址沒影響。

驗證一下:

最後手繪一下,注入代碼後的部分棧空間:(滑鼠畫的,字有點抽象)省略了函數的傳回位址R.A.

(省略了<code>getbuf, hexmatch</code>的<code>return address</code>;hexmatch棧空間實際已經覆寫了getbuf棧空間部分内容,但為了能看清楚,沒有展示出來)

《深入了解計算機系統》CSAPP_AttackLab

日期:21.11.7 ~ 21.11.8

從Phase4起,用ROP(Return-Oriented Programming),以因為單單使用注入代碼(inject code)技術無法解決的棧随機化、二進制檔案被限制通路的問題。

Phase4的任務和Phase2一樣:将%rdi設定為cookie,調用touch2。

ROP要用到<code>gatgets</code>這個工具。

gadget:每一段<code>gadget</code>包含一系列指令位元組,而且以<code>ret</code>結尾,跳轉到下一個<code>gadget</code>,就這樣連續的執行一系列的指令代碼,對程式造成攻擊。

實驗老師已經很貼切的将我們需要用到的<code>gatget</code>放進<code>fram.c</code>檔案裡了。我們将其反彙編才可運用,終端指令如下:(注意是<code>-Og</code>,<code>O</code>要大寫,不然就會采用 stack frame pointer,而<code>rtarget</code>裡是沒有用該指針的,不加的話指令編碼會很複雜)

提示裡有句很重要的話:

When a gadget uses a <code>popq</code> instruction, it will pop data from the stack.

說明使用gadget中的popq指令可以将棧的元素彈出,能夠彈出的位置當然隻能是棧頂了。

再結合我們之前在Phase2注入cookie使用的是movq指令,可以猜測,這裡應該彙編代碼應該是彈出cookie再将其移入<code>%rdi</code>,即:(gadget中的指令都是以<code>ret</code>結尾,是以彈出之後應該放在<code>%rax</code>裡)

再參照表格:(這個圖檔在Phase 5處)

得出上述彙編代碼十六進制表示:

再于<code>farm.d</code>檔案中查找對應<code>gadget</code>指令(直接ctrl+f查找<code>48 89 c7</code>)。我們是要把cookie加進去,是以我們選擇addvel這一組如下:

參考連結1 參考連結2(連結是關于<code>endbr64</code>的解釋)

最後在終端使用如下指令查找它們十六進制代碼:

可以得出對應movq、popq對應的起始位址:<code>0x4019a0 + 2 = 0x4019a2</code>,<code>0x4019a7 + 4 = 0x4019ab</code>。

靈魂畫手又來啦。直接把gatget的指令的注入,pop之後會有一個資料,這個資料就是cookie。

《深入了解計算機系統》CSAPP_AttackLab

key:

21.11.20

phase 5和phase 3的相同任務,但同樣考慮到棧随機化、二進制檔案被限制通路的問題。我們使用ROP解決。

Before you take on the Phase 5, pause to consider what you have accomplished so far. In Phases 2 and 3, you caused a program to execute machine code of your own design. If CTARGET had been a network server, you could have injected your own code into a distant machine. In Phase 4, you circumvented two of the main devices modern systems use to thwart buffer overflow attacks. Although you did not inject your own code, you were able inject a type of program that operates by stitching together sequences of existing code. You have also gotten 95/100 points for the lab. That’s a good score. If you have other pressing obligations consider stopping right now.

很喜歡老師非常誠懇的一段話。

回顧phase 3任務,也是在<code>tset</code>傳回前執行<code>touch3</code>,但還要記得插入個<code>cookie</code>的ascii代碼

由于思路堵塞,這裡直接參考這位部落客的思路:

因為開啟了棧随機化,是以不能直接把代碼插入到絕對位址,必須找一個基準,我們就隻能找%rsp。 因為touch3會開辟一個很大的buffsize,若把資料插到touch3下面的棧空間,有關記憶體之後基本就會被重寫,是以要存在touch3的更高位址處。是以要在%rsp上加一個bias才可以,即字元串位址是%rsp + bias。 沒有直接的加法指令,那就找兩個寄存器互相加,找到一個放在下面 具體的操作可以是這樣: 把<code>%rsp</code>裡的棧指針位址放到<code>%rdi</code> 拿到bias的值放到<code>%rsi</code> 利用<code>lea x, y</code>,把棧指針位址<code>%rdi</code>和bias(<code>%rsi</code>)加起來放到<code>%rax</code>,再傳到<code>%rdi</code> 調用<code>touch3</code>

關于寄存器的轉化,需要自己查表配出來。

我們先畫(寫出= =)出要執行的棧幀。

因為<code>bias</code>的取值要根據插入的ROP指令數量來決定,<code>bias = 8byte * 9 = 72 = 0x48</code>。(注意,直接用0x48覆寫彈出的%rax的值也算一條ROP指令)

具體的查找指令對應十六進制代碼的過程就不詳述了,可以參考<code>Phase 4</code>,直接放出答案。

系統入門程式設計

leyN的CS學習之旅

myk的CS學習之旅

attack的要求蠻高,經常要找參考答案。

英文真的很重要(是以最近要用心、用腦備考六級了)

想到在補充

如有謬誤,敬請指正。

上一篇: form表單控件