NCTF2022 Misc Offical WriteUp
去年还是只能爆0的彩笔,今年却来出题了,还希望师傅们不要拷打我捏OrzOrzOrzOrz
Signin
描述一眼丁真,玩过游戏或者看过电视剧的都会想到一段秘籍:上上下下左右左右ba
直接输入就能拿到flag,直接人眼ocr
NCTF{VVe1c0m3_T0_NCTF_2022!!!}
qrssssssss
(被非预期了,好烦
非预期解:时间排序后扫码再手动去除冗余数据得到大致的flag,然后爆破
预期:这题是通过二维码data-masking的顺序来排flag里字符顺序的,大致是:L0~7 M0~7 Q0~7 H0~7
搜了一大圈没搜到好用的脚本,于是手动写了一个识别的,具体就是识别右边标记位的黑白顺序然后比对一下
exp如下
from PIL import Image
from pyzbar.pyzbar import decode
import os
def maskanalysis(img):
sign=''
for ii in range(510,670,20):
pi=img.getpixel((ii,170))
if(pi==0):
sign+='1'
if(pi==255):
sign+='0'
return sign
def scanqr(img):
decocdeQR = decode(img)
return decocdeQR[0].data.decode('ascii')
qrlist=os.listdir(r"C:\Users\16334\Desktop\qrssssssss_revenge")
flag=[0]*32
masklist=['11000100','11110011','10101010','10011101','00101111','00011000','01000001','01110110','00010010','00100101','01111100','01001011','11111001','11001110','10010111','10100000','01011111','01101000','00110001','00000110','10110100','10000011','11011010','11101101','10001001','10111110','11100111','11010000','01100010','01010101','00001100','00111011']
for i in qrlist:
img=Image.open(r"C:\Users\16334\Desktop\qrssssssss_revenge\{}".format(i))
qrmask=maskanalysis(img)
for j in range(32):
if(masklist[j]==qrmask):
flag[j]=scanqr(img)
print(''.join(flag))
masklist就是手动识别的掩码标记位,用的pyzbar和PIL实现扫码
NCTF{737150-eeb-465-e91-110a8fb}
炉边聚会
网上搜索一下炉石传说编码规则这样的就可以搜到
fflag=['10001100','00000110','10011110','00000101','11001000','00000110','10111100','00000101','11001110','00001001','11010000','00000101','11110010','00000111','11001010','00000111','11110100','00001000','10001000','00001001','10010000','00001000','10111110','00000110','10001000','00001001','11010110','00001000','11001100','00001000','11110010','00000111','10110110','00000111','10011110','00000101','11100000','00000011','11101000','00000111','11110010','00000111','10110110','00000111','10111110','00000110','11100000','00000011','11100000','00000011','11100000','00000011','10110110','00000111','10111100','00000101','10010010','00001001','11001100','00001000','11001100','00001000','11111010','00000110','10110110','00000111','11110100','00001000','10011010','00001000','10111010','00000100','10010000','00001000','10001000','00001001','11110110','00000100','11100010','00001001','00000000','00000000']
for i in range(40):
flag=fflag[2*i+1]+fflag[2*i][1:-1]+fflag[2*i][-1]
fla=int(flag,2)
fl=fla//10
print(chr(fl),end='')
(其实还有另一种解法
python甚至有一个库叫hearthstone,这是我万万没有想到的,直接拿来用就行
from hearthstone.deckstrings import Deck
deck = Deck.from_deckstring('AAEDAZoFKIwGngXIBrwFzgnQBfIHygf0CIgJkAi+BogJ1gjMCPIHtgeeBeAD6AfyB7YHvgbgA+AD4AO2B7wFkgnMCMwI+ga2B/QImgi6BJAIiAn2BOIJAAA=')
for card in deck.cards:
flag_part = int(card[0] / 10)
print(chr(flag_part), end='')
NCTF{HearthStone_C0de_S000_FunnY_ri9ht?}
qrssssssss_revenge
exp同qrssssssss
NCTF{62130783efd44b3692b4ddbecf}
zystego
(4解了脑洞部分应该不大
一眼丁真,图片尾部藏着一个压缩包,分析一下发现是真加密,拿去爆破得到密码是114514,直接就能拿到一个假flag((((
另一个文件something则是pgp的私钥,之后有用
再回到图片本身,结合宽高仔细看看可以判断出右边多出来3列像素,这里是解题的关键,于是提取出来看看,横着读它三通道
from PIL import Image, ImageDraw
import struct
width = 515
height = 512
img=Image.open(r"C:\Users\16334\Desktop\fd.png")
a=[]
for i in range(height):
for j in range(width-3,width):
pi=img.getpixel((j,i))
for k in range(3):
a.append(pi[k])
print(a)
这里可能有一点点的脑洞,可以发现这些数的个位都是5或0,容易联系到二进制,所以可以写个脚本转一下
from PIL import Image, ImageDraw
import struct
width = 515
height = 512
img=Image.open(r"C:\Users\16334\Desktop\fd.png")
a=[]
for i in range(height):
for j in range(width-3,width):
pi=img.getpixel((j,i))
for k in range(3):
a.append(pi[k])
for i in a:
j=i%10
if(j==5):
print(1,end='')
else:
print(0,end='')
提取出来的二进制直接每8位转字符,就可以得到盲水印脚本以及pgp加密的口令:%$#%$#jhgasdfg76342t
import secret
丁真 = np.float32(cv2.imread(r"C:\Users\16334\Desktop\fadian.png", 1))
for i in range(64):
for j in range(64):
芝士 = randint(0,2)
小马珍珠 = 丁真[:, :, 芝士]
雪豹 = cv2.dct(小马珍珠[8*i:8*i+8, 8*j:8*j+8])
if(secret[i*64+j] == '1'):
雪豹[7,7] = 20
elif(secret[i*64+j] == '0'):
雪豹[7,7] = -20
小马珍珠[8*i:8*i+8, 8*j:8*j+8] = cv2.idct(雪豹)
丁真[:, :, 芝士] = 小马珍珠
cv2.imwrite(r"C:\Users\16334\Desktop\fd.png", 丁真)
#a gift for you : %$#%$#jhgasdfg76342t
简单替换一下变量名就可以大致看出水印的逻辑,这里给出原脚本
from random import randint
import numpy as np
from math import *
import cv2
img = np.float32(cv2.imread(r"C:\Users\16334\Desktop\fadian.png", 1))
secret=open(r"C:\Users\16334\Desktop\secret.txt").read()
for i in range(64):
for j in range(64):
cho = randint(0,2)
imgch = img[:, :, cho]
dctt = cv2.dct(imgch[8*i:8*i+8, 8*j:8*j+8])
if(secret[i*64+j] == '1'):
dctt[7,7] = 20
elif(secret[i*64+j] == '0'):
dctt[7,7] = -20
imgch[8*i:8*i+8, 8*j:8*j+8] = cv2.idct(dctt)
img[:, :, cho] = imgch
cv2.imwrite(r"C:\Users\16334\Desktop\fd.png", img)
大致就是先把原图分成若干8x8的块,然后在这个块上随机选择rgb通道中的一个进行dct变换,然后根据secret.txt里是1还是0来改变dct矩阵右下角的值,然后合并进原图,最后保存
于是可以写出个简单的exp
import numpy as np
from math import *
import cv2
img = np.float32(cv2.imread(r"C:\Users\16334\Desktop\fd.png", 1))
for i in range(64):
for j in range(64):
for k in range(3):
imgg=img[:, :, k]
dctt = cv2.dct(imgg[8*i:8*i+8, 8*j:8*j+8])
if (dctt[7,7] >= 10):
print('1',end='')
elif(dctt[7,7] < -10):
print('0',end='')
由于dct逆变换会导致一些损失,所以判断的地方选择了与10和-10进行比较
将得到的结果转一下就能得到一个压缩包了
利用上面得到的pgp私钥和口令直接解密就可以得到flag力
NCTF{zys_1s_s0_V3g3T@13lE_qwq}
后记