【wp】2021DASCTF July X CBCTF 4th

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

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

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

Misc

red_vs_blue

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

image-20210801204741907

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

#!/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()

image-20210801212244559

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

funny_maze

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

image-20210801212517120

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

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

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

#!/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()

image-20210801213047317

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

ezSteganography

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

image-20210801213402811

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

image-20210801213526432

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

green0

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

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

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()

res

拿到了flag的另外半部分。

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

Just a GIF

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

所以拿脚本分离:

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

image-20210801215817427

就可以得到451帧的图片。

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

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

res

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

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

res2

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

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

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")

拿到了十一张图片:

res_000

res_001

res_002

res_003

res_004

res_005

res_006

res_007

res_008

res_009

res_010

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

写了个脚本拼接有:

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")

ans

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

DASCTF{6bb73086aeb764b5727529d82b084cce}

问卷题

直接做问卷拿flag

DASCTF{79f3bb47a2e2d46def82c052eccb7b80}

Crypto

Yusa的密码学签到——BlockTrick

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

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

image-20210801223008233

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

posted @ 2022-01-26 13:30  c10udlnk  阅读(104)  评论(0编辑  收藏  举报