NCTF2022-复现
比赛题目
比赛总结
本次比赛,web还是一无所获,自己还是差的很多。反观misc,这次比赛六道杂项题,做出来两道,然后在核对大佬们的wp后发现自己的思路完全正确,可惜的是自己不会写脚本。总的,本次比赛收获挺大的,做misc的思路更上了一步。个人感觉,这次misc是我做过最有意思的一次,非常好玩。
Misc
Signin
程序设计实验作业,但是签到题(听zys说建议把终端字体调小一点并且只需要输入一串来自35年前的神秘秘籍
神秘秘籍:上上下下左右左右ba,可以直接得到flag
NCTF{VVe1c0m3_T0_NCTF_2022!!!}
另一种解法
这道题,ida不太会用,先把参考的wp留着。
只因因
给了网站http://www.ncbi.nlm.nih.gov/BLAST/查询基因序列
简写 CFTR md5加密得到flag
NCTF{254e72a3768938792446548561412d43}
炉边聚会
听说炉石传说快关服了,zys很难过,决定在最后的时间里再组一套卡组玩玩,你能找到藏在里面的flag吗?
这题一开始做的时候不知道是什么东西,后来实在没思路就看着说明,搜了搜炉石传说,发现这段字符应该就是炉石的套牌代码,一下子就觉得只要找到卡组,flag肯定就没问题了。自己还装了一个炉石,结果代码复制进去完全没反应,然后搜的时候弹出了很多卡组代码的组成原理,于是就开始漫长的学习卡组代码的组成原理。
<?php
function read_varint(&$data) {
$shift = 0;
$result = 0;
do {
$c = array_shift($data);
$result |= ($c & 0x7f) << $shift;
$shift += 7;
}
while ($c & 0x80);
return $result;
}
function parse_deck($data) {
$reserve = read_varint($data);
if ($reserve != 0) {
printf("Invalid deckstring");
die;
}
$version = read_varint($data);
if ($version != 1) {
printf("Unsupported deckstring version %s", $version);
die;
}
$format = read_varint($data);
$heroes = [];
$num_heroes = read_varint($data);
for ($i = 0; $i < $num_heroes; $i++) {
$heroes[] = read_varint($data);
}
$cards = [];
$num_cards_x1 = read_varint($data);
for ($i = 0; $i < $num_cards_x1; $i++) {
$card_id = read_varint($data);
$cards[] = [$card_id, 1];
}
$num_cards_x2 = read_varint($data);
for ($i = 0; $i < $num_cards_x2; $i++) {
$card_id = read_varint($data);
$cards[] = [$card_id, 2];
}
$num_cards_xn = read_varint($data);
for ($i = 0; $i < $num_cards_xn; $i++) {
$card_id = read_varint($data);
$count = read_varint($data);
$cards[] = [$card_id, $count];
}
return [$cards, $heroes, $format];
}
$deckstring = "AAEDAZoFKIwGngXIBrwFzgnQBfIHygf0CIgJkAi+BogJ1gjMCPIHtgeeBeAD6AfyB7YHvgbgA+AD4AO2B7wFkgnMCMwI+ga2B/QImgi6BJAIiAn2BOIJAAA=";
#这是一个非常有趣的萨满卡组
$binary = base64_decode($deckstring);
$hex = bin2hex($binary);
$arr = str_split($hex, 2);
$arr = array_map("hexdec", $arr);
$arr = parse_deck($arr);
var_dump($arr);
执行结果
array(3) {
[0] =>
array(40) {
[0] =>
array(2) {
[0] =>
int(780)
[1] =>
int(1)
}
[1] =>
array(2) {
[0] =>
int(670)
[1] =>
int(1)
}
[2] =>
array(2) {
[0] =>
int(840)
[1] =>
int(1)
}
[3] =>
array(2) {
[0] =>
int(700)
[1] =>
int(1)
}
[4] =>
array(2) {
[0] =>
int(1230)
[1] =>
int(1)
}
[5] =>
array(2) {
[0] =>
int(720)
[1] =>
int(1)
}
[6] =>
array(2) {
[0] =>
int(1010)
[1] =>
int(1)
}
[7] =>
array(2) {
[0] =>
int(970)
[1] =>
int(1)
}
[8] =>
array(2) {
[0] =>
int(1140)
[1] =>
int(1)
}
[9] =>
array(2) {
[0] =>
int(1160)
[1] =>
int(1)
}
[10] =>
array(2) {
[0] =>
int(1040)
[1] =>
int(1)
}
[11] =>
array(2) {
[0] =>
int(830)
[1] =>
int(1)
}
[12] =>
array(2) {
[0] =>
int(1160)
[1] =>
int(1)
}
[13] =>
array(2) {
[0] =>
int(1110)
[1] =>
int(1)
}
[14] =>
array(2) {
[0] =>
int(1100)
[1] =>
int(1)
}
[15] =>
array(2) {
[0] =>
int(1010)
[1] =>
int(1)
}
[16] =>
array(2) {
[0] =>
int(950)
[1] =>
int(1)
}
[17] =>
array(2) {
[0] =>
int(670)
[1] =>
int(1)
}
[18] =>
array(2) {
[0] =>
int(480)
[1] =>
int(1)
}
[19] =>
array(2) {
[0] =>
int(1000)
[1] =>
int(1)
}
[20] =>
array(2) {
[0] =>
int(1010)
[1] =>
int(1)
}
[21] =>
array(2) {
[0] =>
int(950)
[1] =>
int(1)
}
[22] =>
array(2) {
[0] =>
int(830)
[1] =>
int(1)
}
[23] =>
array(2) {
[0] =>
int(480)
[1] =>
int(1)
}
[24] =>
array(2) {
[0] =>
int(480)
[1] =>
int(1)
}
[25] =>
array(2) {
[0] =>
int(480)
[1] =>
int(1)
}
[26] =>
array(2) {
[0] =>
int(950)
[1] =>
int(1)
}
[27] =>
array(2) {
[0] =>
int(700)
[1] =>
int(1)
}
[28] =>
array(2) {
[0] =>
int(1170)
[1] =>
int(1)
}
[29] =>
array(2) {
[0] =>
int(1100)
[1] =>
int(1)
}
[30] =>
array(2) {
[0] =>
int(1100)
[1] =>
int(1)
}
[31] =>
array(2) {
[0] =>
int(890)
[1] =>
int(1)
}
[32] =>
array(2) {
[0] =>
int(950)
[1] =>
int(1)
}
[33] =>
array(2) {
[0] =>
int(1140)
[1] =>
int(1)
}
[34] =>
array(2) {
[0] =>
int(1050)
[1] =>
int(1)
}
[35] =>
array(2) {
[0] =>
int(570)
[1] =>
int(1)
}
[36] =>
array(2) {
[0] =>
int(1040)
[1] =>
int(1)
}
[37] =>
array(2) {
[0] =>
int(1160)
[1] =>
int(1)
}
[38] =>
array(2) {
[0] =>
int(630)
[1] =>
int(1)
}
[39] =>
array(2) {
[0] =>
int(1250)
[1] =>
int(1)
}
}
[1] =>
array(1) {
[0] =>
int(666)
}
[2] =>
int(3)
}
其它wp脚本
这个要先转换为8位的二进制,根据卡组编码规则,把代码转为十六进制,再转二进制
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='')
脚本三;可以直接出
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?}
zystego
可以分离出一个zip,爆破密码
flag.txt是假的
somethin里有一堆不认识的东西,开头可以拿去搜一下,发现是PGP的私钥,然后这里就不会了,一直学PGP没搞懂,到底加密后的是什么,就给我个密钥怎么解。现在看了wp才发现,fd.png里还有东西。
fd.png右边一列奇怪的东西
提取RGB
看wp,可以用lsb找,但我一直找不出来,只能用脚本(但别说脚本真的香)
from PIL import Image
im = Image.open('fd.png')
pix = im.load()
width = im.size[0]
height = im.size[1]
for x in range(height):
for y in range(width-3,width):
r,g,b = pix[y,x]
print(r,g,b)
from PIL import Image
im = Image.open('fd.png')
pix = im.load()
width = im.size[0]
height = im.size[1]
for x in range(height):
for y in range(width-3,width):
r, g, b = pix[y,x]
#print(r,g,b)
if r%10 == 0:
print(0,end='')
else:
print(1,end='')
if g%10 == 0:
print(0,end='')
else:
print(1,end='')
if b%10 == 0:
print(0,end='')
else:
print(1,end='')
到这里我也不认识了(要学的东西还多)
一个dct算法,末尾是key
import numpy as np
import cv2
丁真 = np.float32(cv2.imread(r"fd.png",1))
for i in range(64):
for j in range(64):
for 芝士 in range(3):
小马珍珠 = 丁真[:, :, 芝士]
雪豹 = cv2.dct(小马珍珠[8*i:8*i+8, 8*j:8*j+8])
print(雪豹[7,7])
import numpy as np
import cv2
丁真 = np.float32(cv2.imread(r"fd.png",1))
for i in range(64):
for j in range(64):
for 芝士 in range(3):
小马珍珠 = 丁真[:, :, 芝士]
雪豹 = cv2.dct(小马珍珠[8*i:8*i+8, 8*j:8*j+8])
# print(雪豹[7,7])
if(雪豹[7,7] > 10):
print(1,end='')
elif(雪豹[7,7] < -10):
print(0,end='')
得到一个zip,即为加密文件
key:%$#%$#jhgasdfg76342t
NCTF{zys_1s_s0_V3g3T@13lE_qwq}
qrssssssss
有什么东西掩盖住了zys的双眼,好像在给他指明方向!(flag括号内的内容只有26位
这题,512个二维码,一开始以为会组成什么东西,扫完发现NCTF{}有这东西,那一想,二维码按顺序扫描,flag不就出来了。然后发现每个二维码名字是时间,于是想到会不会按时间顺序就能扫出来了。
这个脚本才让我明白这题的原理,时间竟然是非预期
from PIL import Image
import pyzbar.pyzbar as pyzbar
import os
def qrcode_parse_content(img_path):
'''
单张图片的二维码解析
'''
img = Image.open(img_path)
#使用pyzbar解析二维码图片内容
barcodes = pyzbar.decode(img)
#打印解析结果,从结果上可以看出,data是识别到的二维码内容,rect是二维码所在的位置
# print(barcodes)
# [Decoded(data=b'http://www.h3blog.com', type='QRCODE', rect=Rect(left=7, top=7, width=244, height=244), polygon=[Point(x=7, y=7), Point(x=7, y=251), Point(x=251, y=251), Point(x=251, y=7)])]
result = []
for barcode in barcodes:
barcode_content = barcode.data.decode('utf-8')
result.append(barcode_content)
return result
def load_imgs(folder):
'''
加载文件夹下的图片
'''
imgs = []
for img_path in os.listdir(folder):
ext = os.path.splitext(img_path)
if len(ext) > 1 and is_img(ext[1]):
imgs.append(img_path)
return imgs
def is_img(ext):
'''
判断文件后缀是否是图片
'''
ext = ext.lower()
if ext == '.jpg':
return True
elif ext == '.png':
return True
elif ext == '.jpeg':
return True
elif ext == '.bmp':
return True
else:
return False
if __name__ == "__main__":
imgs = load_imgs('./') # 打开图片文件夹,我这里是当前程序运行目录
contents = []
# for img in imgs:
# contents.extend(qrcode_parse_content(img))
# print( img)
# file = './result.txt' # 写入文件路径,我这里程序当前运行目录下的result.txt
# with open(file,'w',encoding='utf-8') as f:
# for c in contents:
# f.write(c + '\n')
for img in imgs:
print(qrcode_parse_content(img)[0],end='')
e1b19411bCT3ef91e-T6--5115-b-eNe5-01e-1-0}1a15-0e91e50N15-eTa3-0e}8bC16ee11feeeeb7{ae1eeNbC7Ta1T-5bFb--398a515-NbTfbb-8-7ef-}0C-Te4eF501f{6a51e4be--5-Fb0}Fb-5{b97ef8a-{fa11bC9fee-3-e9N{1-51T1NN01C41883{577-1e-T1603{C{-17-NNeC0f-0C39711514{9117F--6-F-e05a7517C3b4-F{F71f}-1fe70-34-{7805C}b{bCf1f-aT{e3aFe-f-0bT6C17000N78e-76}af7-}4N-907}03187b113T9eaeF--13ebeN71b-C9Te1eF-680-468675F17410674N910F51b1511--80FF565165-19}beT}N17N-1758e-}1a8-4C-0057e-5b7b0}5}{7eaN-{-b}36b}7eb0-606{15b4b01971T-59388-Te351eFC044417af
二维码纠错级别LMQH
参考文章:如何一眼看出二维码的纠错等级
另一种方法
脚本输出二维码内容和修改时间
# coding: UTF-8
from PIL import Image
import pyzbar.pyzbar as pyzbar
import os
def qrcode_parse_content(img_path):
'''
单张图⽚的⼆维码解析
'''
img = Image.open(img_path)
#使⽤pyzbar解析⼆维码图⽚内容
barcodes = pyzbar.decode(img)
#打印解析结果,从结果上可以看出,data是识别到的⼆维码内容,rect是⼆维码所在的位置
# print(barcodes)
# [Decoded(data=b'http://www.h3blog.com', type='QRCODE', rect=Rect(left=7, t
result = []
for barcode in barcodes:
barcode_content = barcode.data.decode('utf-8')
result.append(barcode_content)
return result
def load_imgs(folder):
'''
加载⽂件夹下的图⽚
'''
imgs = []
for img_path in os.listdir(folder):
ext = os.path.splitext(img_path)
if len(ext) > 1 and is_img(ext[1]):
imgs.append(img_path)
return imgs
def is_img(ext):
'''
判断⽂件后缀是否是图⽚
'''
ext = ext.lower()
if ext == '.jpg':
return True
elif ext == '.png':
return True
elif ext == '.jpeg':
return True
elif ext == '.bmp':
return True
else:
return False
if __name__ == "__main__":
# path = "C:/Users/KG/Desktop/1/"
path = 'C://Users/LENOVO/Desktop/NCTF2022/qrssssssss/qrssssssss/'
imgs = load_imgs(path) # 打开图⽚⽂件夹,我这⾥是当前程序运⾏⽬录
contents = []
for img in imgs:
print(img, end='\t')
old_name = img[:15]
# print(old_name)
print(qrcode_parse_content(path + img)[0], end='\t')
print(os.path.getatime(path + img), end='\t')
print(os.path.getctime(path + img), end='\t')
print(os.path.getmtime(path + img))
# new_name = old_name+'_'+qrcode_parse_content(path+img)[0]+'.png'
# print(old_name+'_'+new_name+'.png')
# print(qrcode_parse_content(path+img))
# os.rename(path+img, path+new_name)
# contents.extend(qrcode_parse_content(path+img))
脚本输出二维码的纠错级别
from PIL import Image
import pyzbar.pyzbar as pyzbar
import os
def qrcode_parse_content(img_path):
'''
单张图⽚的⼆维码解析
'''
img = Image.open(img_path)
#使⽤pyzbar解析⼆维码图⽚内容
barcodes = pyzbar.decode(img)
#打印解析结果,从结果上可以看出,data是识别到的⼆维码内容,rect是⼆维码所在的位置
# print(barcodes)
# [Decoded(data=b'http://www.h3blog.com', type='QRCODE', rect=Rect(left=7, t
result = []
for barcode in barcodes:
barcode_content = barcode.data.decode('utf-8')
result.append(barcode_content)
return result
def load_imgs(folder):
'''
加载⽂件夹下的图⽚
'''
imgs = []
for img_path in os.listdir(folder):
ext = os.path.splitext(img_path)
if len(ext) > 1 and is_img(ext[1]):
imgs.append(img_path)
return imgs
def is_img(ext):
'''
判断文件后缀是否是图片
'''
ext = ext.lower()
if ext == '.jpg':
return True
elif ext == '.png':
return True
elif ext == '.jpeg':
return True
elif ext == '.bmp':
return True
else:
return False
def get_level(i):
img = Image.open(i)
if img.load()[10, 170] == 0 and img.load()[30, 170] == 0:
return(i, 'L')
if img.load()[10, 170] == 0 and img.load()[30, 170] == 255:
return(i, 'M')
if img.load()[10, 170] == 255 and img.load()[30, 170] == 0:
return(i, 'Q')
if img.load()[10, 170] == 255 and img.load()[30, 170] == 255:
return(i, 'H')
if __name__ == "__main__":
path = "C://Users/LENOVO/Desktop/NCTF2022/qrssssssss/qrssssssss/"
# path = 'C://Users/KG/Downloads/qrssssssss/'
imgs = load_imgs(path) # 打开图⽚⽂件夹,我这⾥是当前程序运⾏⽬录
contents = []
for img in imgs:
old_name = img[:15]
print(old_name,end='\t')
print(qrcode_parse_content(path+img)[0],end='\t')
print(get_level(path+img)[1],end='\t')
print(os.path.getatime(path+img),end='\t')
print(os.path.getctime(path+img),end='\t')
print(os.path.getmtime(path+img))
# new_name = old_name+'_'+qrcode_parse_content(path+img)[0]+'.png'
# print(old_name+'_'+new_name+'.png')
# print(qrcode_parse_content(path+img))
# os.rename(path+img, path+new_name)
按纠错顺序排序即可得到flag
NCTF{737150-eeb-465-e91-110a8fb}
qrssssssss_revenge
LMQH
这道就是纯LMQH来排序了,没有非预期了
NCTF{62130783efd44b3692b4ddbecf}