NCTF2022

时间: 2023-12-21 admin IT培训

NCTF2022

NCTF2022

文章目录

  • NCTF2022-复现
    • 比赛题目
    • 比赛总结
    • Misc
      • Signin
      • 只因因
      • 炉边聚会
      • zystego
      • qrssssssss
      • qrssssssss_revenge
    • 参考:

NCTF2022-复现

比赛题目

NCTF2022

比赛总结

本次比赛,web还是一无所获,自己还是差的很多。反观misc,这次比赛六道杂项题,做出来两道,然后在核对大佬们的wp后发现自己的思路完全正确,可惜的是自己不会写脚本。总的,本次比赛收获挺大的,做misc的思路更上了一步。个人感觉,这次misc是我做过最有意思的一次,非常好玩。

Misc

Signin

程序设计实验作业,但是签到题(听zys说建议把终端字体调小一点并且只需要输入一串来自35年前的神秘秘籍

神秘秘籍:上上下下左右左右ba,可以直接得到flag
NCTF{VVe1c0m3_T0_NCTF_2022!!!}

另一种解法
这道题,ida不太会用,先把参考的wp留着。

只因因

给了网站


简写 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//10print(chr(fl),end='')

脚本三;可以直接出

from hearthstone.deckstrings import Deckdeck = 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 Imageim = 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 Imageim = 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 osdef qrcode_parse_content(img_path):'''单张图片的二维码解析'''img = Image.open(img_path)#使用pyzbar解析二维码图片内容barcodes = pyzbar.decode(img)#打印解析结果,从结果上可以看出,data是识别到的二维码内容,rect是二维码所在的位置# print(barcodes)# [Decoded(data=b'', 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 resultdef 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 imgsdef is_img(ext):'''判断文件后缀是否是图片'''ext = ext.lower()if ext == '.jpg':return Trueelif ext == '.png':return Trueelif ext == '.jpeg':return Trueelif ext == '.bmp':return Trueelse:return Falseif __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 osdef qrcode_parse_content(img_path):'''单张图⽚的⼆维码解析'''img = Image.open(img_path)#使⽤pyzbar解析⼆维码图⽚内容barcodes = pyzbar.decode(img)#打印解析结果,从结果上可以看出,data是识别到的⼆维码内容,rect是⼆维码所在的位置# print(barcodes)# [Decoded(data=b'', type='QRCODE', rect=Rect(left=7, tresult = []for barcode in barcodes:barcode_content = barcode.data.decode('utf-8')result.append(barcode_content)return resultdef 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 imgsdef is_img(ext):'''判断⽂件后缀是否是图⽚'''ext = ext.lower()if ext == '.jpg':return Trueelif ext == '.png':return Trueelif ext == '.jpeg':return Trueelif ext == '.bmp':return Trueelse:return Falseif __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 osdef qrcode_parse_content(img_path):'''单张图⽚的⼆维码解析'''img = Image.open(img_path)#使⽤pyzbar解析⼆维码图⽚内容barcodes = pyzbar.decode(img)#打印解析结果,从结果上可以看出,data是识别到的⼆维码内容,rect是⼆维码所在的位置# print(barcodes)# [Decoded(data=b'', type='QRCODE', rect=Rect(left=7, tresult = []for barcode in barcodes:barcode_content = barcode.data.decode('utf-8')result.append(barcode_content)return resultdef 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 imgsdef is_img(ext):'''判断文件后缀是否是图片'''ext = ext.lower()if ext == '.jpg':return Trueelif ext == '.png':return Trueelif ext == '.jpeg':return Trueelif ext == '.bmp':return Trueelse:return Falsedef 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}

参考:

南邮-NCTF2022 WRITE UP

Scr1w战队wp(比赛群里师傅发的)

如何一眼看出二维码的纠错等级