【wp】2021DASCTF July X CBCTF 4th

第一天打dfjk去了,打完回来看发现逆向一道Golang一道lua,不太想啃,于是干脆光速转行做杂项= =

也没想到能差一题就ak杂项了(逃。学到很多,顺手总结总结(RE手要跑路啦)。

回头有空复现一下剩下的那道杂项和那个lua逆向(咕咕咕

Misc

red_vs_blue

连上去以后乱答了几次,发现在同一次连接的情况下(重来的时候按y),每一场的预测都是一样的。

所以直接pwntools连上去爆破即可:

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
#!/usr/bin/env python
# ------ Python2 ------
from pwn import *

# context.log_level='debug'
host="node4.buuoj.cn"
port=25384
r=remote(host,port)

myInput=''
pred='rb'
cnt=0

for _ in range(3):
r.recvline()

while True:
r.recvline()
r.recvline()
r.sendline(pred[cnt%2])
r.recvline()
r.recvline()
rslt=r.recvline()
print(pred[cnt%2],rslt)
if 'successful' in rslt:
myInput+=pred[cnt%2]
print("[input] -> "+myInput)
if len(myInput)==66:
break
else:
r.sendline('y')
for x in myInput:
r.recvuntil('\n')
r.recvline()
r.sendline(x)
r.recvline()
r.recvline()
r.recvline()
cnt+=1

r.interactive()

flag{783e1bfd-0803-47b6-b302-28591dcb9fca}

funny_maze

连上去发现是走迷宫,3和2都看了一下分别是:

要点在:输入路径的长度,并且有时间限制。

一开始以为这个and的意思是先输入路径再输入长度,结果写完脚本以后一直跑都不对,甚至跑去问工作人员了(x)。后来看到有解,才发现应该是自己理解出了问题XD

去年年底打zhb的时候也有一道杂项是远程迷宫(当时写过一版bfs),于是直接拿来改了,微调了一下recvline的次数:

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
#!/usr/bin/env python
# ------ Python2 ------
from pwn import *
# import time

# t=time.clock()
# context.log_level='debug'
host="node4.buuoj.cn"
port=29294
r=remote(host,port)

r.recvuntil('game\n')
r.sendline('1')
dirs=[[0,1],[1,0],[0,-1],[-1,0]]
flag_char='dsaw'
cntSuccess=0

while True:
if cntSuccess==0:
maze=r.recvuntil('answer:\n').split('\n')[:-1]
else:
maze=r.recvuntil('answer:\n').split('\n')[3:-1]
cntSuccess+=1
print("------")
for rows in maze:
print(''.join(rows))
print("------")
maze=[list(n) for n in maze]
row=len(maze)
col=len(maze[0])
path=[]
flag=""
book=[[(0,0,0) for _ in range(col)] for _ in range(row)]
head=0
tail=1

cnt=0
for i in range(len(maze)):
if 'S' in maze[i]:
start=(i,maze[i].index('S'))
cnt+=1
if 'E' in maze[i]:
end=(i,maze[i].index('E'))
cnt+=1
if cnt==2:
break

path.append(start)
book[start[0]][start[1]]=path[head]+('*',)
maze[start[0]][start[1]]='#'
isSuccess=0
while head<tail:
for i in range(4):
x,y=path[head][0]+dirs[i][0],path[head][1]+dirs[i][1]
if x not in range(row) or y not in range(col) or maze[x][y]=='#':
continue
maze[x][y]='#'
book[x][y]=path[head]+(flag_char[i],)
path.append((x,y))
tail+=1
if x==end[0] and y==end[1]:
isSuccess=1
break
if isSuccess==1:
break
head+=1
cur=book[end[0]][end[1]]
while cur[2]!='*':
flag+=cur[2]
cur=book[cur[0]][cur[1]]
flag=flag[::-1]
r.sendline(str(len(flag)+1))
print(flag)
if cntSuccess==4:
break
# print('time: ',round(time.clock()-t,2),'s')
r.interactive()

拿到flag:flag{a2189c60-2011-4e1d-a369-28f6417fe0f7}

ezSteganography

图片隐写题,拿到手第一步当然是拿Stegsolve翻啦~果然能翻到一些不同寻常的东西:

看到Green plane 0时黑点点突然密集了,感觉是LSB,于是解了一下:

看到开头有典型的png文件头,猜测是一张png,所以直接Save Bin然后改后缀名为png,得到:

拿到flag的前半部分flag{2e9ec6480d0515和提示关键词QIM quantizetion,查了一下是QIM量化,并且给出了步长为20。

在github上找了几个脚本都不能直接用,看了一眼相关论文,最后拿QuantizationIndexModulation/qim.py at master · pl561/QuantizationIndexModulation的脚本改了一下,对原图进行处理:

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
import numpy as np
from PIL import Image

class QIM:
def __init__(self, delta):
self.delta = delta

def embed(self, x, m):
x = x.astype(float)
d = self.delta
y = np.round(x/d) * d + (-1)**(m+1) * d/4.
return y

def detect(self, z):
shape = z.shape
z = z.flatten()
m_detected = np.zeros_like(z, dtype=float)
z_detected = np.zeros_like(z, dtype=float)
z0 = self.embed(z, 0)
z1 = self.embed(z, 1)
d0 = np.abs(z - z0)
d1 = np.abs(z - z1)
gen = zip(range(len(z_detected)), d0, d1)
for i, dd0, dd1 in gen:
if dd0 < dd1:
m_detected[i] = 0
z_detected[i] = z0[i]
else:
m_detected[i] = 255
z_detected[i] = z1[i]
z_detected = z_detected.reshape(shape)
m_detected = m_detected.reshape(shape)
return z_detected, m_detected.astype(int)

delta = 20 # quantization step
qim = QIM(delta)
filename = 'ezSteganography-flag.png'
image = Image.open(filename)
imdata = np.array(image)
z_detected, msg_detected = qim.detect(imdata)
im=Image.fromarray(np.uint8(msg_detected))
im.show()

拿到了flag的另外半部分。

最后拼一下flag有:flag{2e9ec6480d05150c211963984dcbc9f1}

Just a GIF

gif看不出什么,一般套路就是先把每一帧分离出来再说。

所以拿脚本分离:

1
2
3
4
5
6
7
8
9
10
from PIL import Image

im=Image.open('./Just_a_GIF.gif')
while True:
try:
cur=im.tell()
im.save('frames/frame_'+str(cur).rjust(3,'0')+'.png')
im.seek(cur+1)
except EOFError:
break

就可以得到451帧的图片。

观察可以知道,每11张图片就是一个循环,有451/11=41个这样的循环。

一开始想比对一下各组循环的同一个位置的图片是不是同一张(比如frame_000.pngframe_011.png),所以写了个脚本跑了一下,xor两张图的对应像素,把黑的地方((0,0,0))置成白色,方便看一点:

发现有一些黑点点,意识到不对劲+1。

而下一组(frame_011.pngframe_022.png)的xor结果里黑点点更多了:

所以猜测是把同一个位置的全部xor起来。

但是41组异或完肯定又变成原来这个图为底图(xor的奇数特性),所以异或前40组和后40组都试了一下,发现后40组的更清晰(猜测第一组是原图,没有魔改那种),所以写脚本xor:

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
from PIL import Image

def xorTuple(t1,t2,size):
ret=()
for i in range(size):
ret+=(t1[i]^t2[i],)
return ret

for x in range(11):
width=119
height=83
res=[[(0,0,0) for _ in range(height)] for _ in range(width)]
for pos in range(11+x,451,11): # 后40组
im=Image.open('frames/frame_'+str(pos).rjust(3,'0')+'.png')
img=im.convert('RGB')
for i in range(width):
for j in range(height):
res[i][j]=xorTuple(res[i][j],img.getpixel((i,j)),3)
resImg=Image.new("RGB",(width,height))
for i in range(len(res)):
for j in range(len(res[0])):
if res[i][j]!=(0,0,0):
resImg.putpixel([i,j],res[i][j])
else:
resImg.putpixel([i,j],(255,255,255))
resImg.save("res_"+str(x).rjust(3,'0')+".png")

拿到了十一张图片:

很明显就是要根据最后两张指示的位置来拼前九张图片。

写了个脚本拼接有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from PIL import Image

ans_list=[[7,9,5],[1,3,2],[6,4,8]] #横过来的

width=83
height=83
ans=[[(0,0,0) for _ in range(height*3)] for _ in range(width*3)]
for pi in range(3):
for pj in range(3):
im=Image.open('res_'+str(ans_list[pi][pj]-1).rjust(3,'0')+'.png')
img=im.convert('RGB')
for i in range(width):
for j in range(height):
ans[pi*83+i][pj*83+j]=img.getpixel((i,j))

ansImg=Image.new("RGB",(width*3,height*3))
for i in range(len(ans)):
for j in range(len(ans[0])):
ansImg.putpixel([i,j],ans[i][j])
ansImg.save("ans.png")

从之前学校ISCNU的科普(CTF知识树 | 第五期: 你不知道的那些二维码)可以知道是data matrix,用解码网站(Read Data Matrix Barcode Online)扫一下就能拿到flag:

DASCTF{6bb73086aeb764b5727529d82b084cce}

问卷题

直接做问卷拿flag

DASCTF{79f3bb47a2e2d46def82c052eccb7b80}

Crypto

Yusa的密码学签到——BlockTrick

不懂密码学啥攻击方式的人看着这个脚本真的一脸懵逼(

但是想了想……签到题嘛说不定能瞎猫碰上死耗子,加上这个for _ in range(2):有点奇怪,感觉是专门能用来复用信息的,于是把第一次的输出当成第二次的输入再丢进去,就成了= =

flag{edbffcb1-eb55-4ae3-8cc5-3b65663f1fec}

本文作者: c10udlnk
本文链接: https://c10udlnk.top/p/wpFor-2021JulyDASCTF/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 c10udlnk_Log (https://c10udlnk.top)!