2026MISC躲猫猫题目复盘
前言
感觉这个比赛关注的人应该不多,题目也比较小众。
最近笔者发现笔者的博客走歪了,有关网络攻防的文章比较少了,全是些工具介绍。
前端时间笔者简直就是脚本小子,只知道工具使用(事实上大家都是这个趋势,尤其是AI时代的到来)。
但是笔者内心确实十分的惭愧,没有了一种学技术的初心。
最近也是沉迷游戏起来了,除了必要的学习时间几乎都在打游戏,该用的时间就叫人见笑了。
这是在hvv时间抽空写的,过段时间也会写篇hvv小经验。
估计这个文章没啥人会去读,因为题目大家估计都没有,事实上题目就是封面,直接传上来了,仅用作学习交流。
secret
笔者后来把这题喂给GPT5.5,也就跑到这里就直接躺了,笔者还以为是flag格式问题。实际上是笔者不会这题罢了。
图片的边框上黑白块是按固定采样点编码的 bit 序列,不知道大家注意没有。
从上到右到下到左排序。
注意是一条完整的序列,直接自己读就行了,也不用啥脚本。
当然AI分析或许会好一些。
AI的脚本给出来了:
fromPILimportImage# ====================== 你只需要改这里!======================IMAGE_PATH="你的图片名字.png"# 把图片放在同一个文件夹,改这里# ============================================================defread_border_bits(img_path):# 打开图片,转成纯黑白模式(黑=0,白=1)img=Image.open(img_path).convert("1")w,h=img.size# ---------------------- 按题目规则读取 4 条边 ----------------------# 顶边:从左到右 46 个点top=[1ifimg.getpixel((x,0))==255else0forxinrange(46)]# 右边:从上到下 32 个点right=[1ifimg.getpixel((w-1,y))==255else0foryinrange(32)]# 底边:从右到左 46 个点bottom=[1ifimg.getpixel((x,h-1))==255else0forxinrange(45,-1,-1)]# 左边:从下到上 32 个点left=[1ifimg.getpixel((0,y))==255else0foryinrange(31,-1,-1)]# ---------------------- 题目规定的拼接公式 ----------------------stream=top+right[1:]+bottom[1:]+left[1:]# 把 01 比特流 → 转成字符串bit_str=''.join(map(str,stream))result=""foriinrange(0,len(bit_str),8):byte=bit_str[i:i+8]result+=chr(int(byte,2))returnresult# 运行if__name__=="__main__":password=read_border_bits(IMAGE_PATH)print("✅ 边框解码结果:",password)我自己都没跑,要试试的可以试试。
按比特编译:
thesecretis20250810当时以为这个是flag,实际上不是。
第一段flag
密码是20250810:
用的工具:
https://tools.jb51.net/aideddesign/img_add_info得到第一段flag:
flag{cf78e26a
还有一个网站:
https://sekao.net/pixeljihad/也可以:
PixelJihad,实际上就是文字隐写术。
有专门的GitHub仓库:
https://github.com/oakes/PixelJihad你要是走这个的话,得到的是SJCL JSON:
{"iv":"7RVtgnQxxwFEQa26zadUOQ","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"2crKWORWtmM","ct":"5B6fxSZZ6/EpfIsDIyJKpujAq66r"}这个字符串是用密码 AES 加密过的,所以要你输入密码才能解。
同样的密码再来一遍,也能得到flag。
第二段flag
没线索了,就直接 010 分析。
自打用了AI,好久没有这么手搓的分析了。大模型在消磨本就不高的能力啊。
这里应该在IEND这里结束了,但是IEND后面还有字节。
异或不知道的情况下可以遍历。
#!/usr/bin/env python3""" 文件异或暴力破解 - 增强版 在异或后的数据中,扫描前 N 个字节的任意偏移,匹配常见文件格式魔数 """importosimportsysfrompathlibimportPath# 扫描深度(前多少个字节内查找魔数)SCAN_DEPTH=8# 常见文件格式的魔数(支持偏移0和偏移>0)MAGIC_SIGNATURES=[# (魔数字节序列, 格式名称)(b'PK\x03\x04','ZIP'),(b'PK\x05\x06','ZIP_CDIR'),(b'\x89PNG\r\n\x1a\n','PNG'),(b'%PDF','PDF'),(b'GIF87a','GIF87'),(b'GIF89a','GIF89'),(b'\xff\xd8\xff','JPEG'),(b'BM','BMP'),(b'RIFF','RIFF'),(b'\x7fELF','ELF'),(b'MZ','EXE'),(b'<!DOCTYPE','XML'),(b'<?xml','XML'),(b'{\\rtf','RTF'),(b'OggS','OGG'),(b'fLaC','FLAC'),(b'ID3','MP3'),(b'ftyp','MP4'),]defxor_bytes(data:bytes,key:int)->bytes:"""单字节异或"""returnbytes([b^keyforbindata])deffind_magic_in_scan_range(data:bytes,magic:bytes,scan_bytes:int)->int|None:""" 在前 scan_bytes 字节内查找魔数 返回匹配的偏移位置,未找到返回 None """max_offset=min(scan_bytes,len(data)-len(magic))foroffsetinrange(max_offset+1):ifdata[offset:offset+len(magic)]==magic:returnoffsetreturnNonedefidentify_file_format_in_scan_range(data:bytes,scan_bytes:int=SCAN_DEPTH)->tuple[str|None,int|None]:""" 在扫描范围内识别文件格式 返回: (格式名称, 魔数偏移) """formagic,fmt_nameinMAGIC_SIGNATURES:offset=find_magic_in_scan_range(data,magic,scan_bytes)ifoffsetisnotNone:returnfmt_name,offsetreturnNone,Nonedeftry_xor_and_match(input_path:Path,key:int,output_dir:Path)->tuple[bool,str|None,int|None,Path|None]:""" 尝试用指定密钥异或,并在前 SCAN_DEPTH 字节内匹配格式 返回: (是否匹配, 格式名称, 魔数偏移, 输出文件路径) """try:withopen(input_path,'rb')asf:original_data=f.read()exceptExceptionase:returnFalse,None,None,Noneiflen(original_data)==0:returnFalse,None,None,None# 异或xor_result=xor_bytes(original_data,key)# 在扫描范围内匹配魔数fmt,offset=identify_file_format_in_scan_range(xor_result,SCAN_DEPTH)iffmt:# 导出整个异或后的文件(不截取,保留完整)output_path=output_dir/f"{input_path.stem}_xor_{key:02X}_{fmt}_offset_{offset}.bin"withopen(output_path,'wb')asf:f.write(xor_result)returnTrue,fmt,offset,output_pathreturnFalse,None,None,Nonedefmain():iflen(sys.argv)<2:print("用法: python xor_bruteforce.py <文件路径> [起始密钥] [结束密钥]")print("示例: python xor_bruteforce.py encrypted.bin")print("示例: python xor_bruteforce.py encrypted.bin 0x00 0xFF")print(f"扫描深度: 前{SCAN_DEPTH}字节内任意偏移")sys.exit(1)input_file=Path(sys.argv[1])ifnotinput_file.is_file():print(f"错误: 文件不存在 -{input_file}")sys.exit(1)# 密钥范围iflen(sys.argv)>=3:start_key=int(sys.argv[2],16)end_key=int(sys.argv[3],16)iflen(sys.argv)>=4elsestart_keyelse:start_key=0end_key=255ifstart_key<0orend_key>255orstart_key>end_key:print("错误: 密钥范围必须在 0x00 到 0xFF 之间")sys.exit(1)# 输出目录output_dir=input_file.parent/f"{input_file.stem}_xor_output"output_dir.mkdir(exist_ok=True)print(f"输入文件:{input_file}")print(f"文件大小:{input_file.stat().st_size}字节")print(f"密钥范围: 0x{start_key:02X}- 0x{end_key:02X}")print(f"扫描深度: 前{SCAN_DEPTH}字节")print(f"输出目录:{output_dir}")print("-"*60)matched_count=0forkeyinrange(start_key,end_key+1):matched,fmt,offset,out_path=try_xor_and_match(input_file,key,output_dir)ifmatched:matched_count+=1print(f"[+] 密钥 0x{key:02X}→ 格式{fmt:8s}(偏移{offset}) →{out_path.name}")# 额外显示前16字节用于调试withopen(input_file,'rb')asf:data=f.read()xor_result=xor_bytes(data,key)print(f" 前16字节:{xor_result[:16].hex().upper()}")print("-"*60)print(f"完成! 共找到{matched_count}个匹配格式")ifmatched_count==0:print("\n提示: 没有找到已知格式,可能原因:")print(" 1. 异或密钥不在 0x00-0xFF 范围内")print(" 2. 文件被多层加密(需要先解密其他层)")print(" 3. 魔数在更后面的位置(可以修改 SCAN_DEPTH 变量)")print(" 4. 文件格式不在支持列表中(可手动添加魔数)")if__name__=="__main__":main()理论上只要导出最后223字节,我导出来227字节,也是可以识别的。
解压下来得到:
第二段flag:
-f5ba-4d78-aa2d
第三段flag
碟中谍,在这个zip文件结束后,依然有一段文字多出。
(这里参考网上的WP了,卡在这里了)
WP是这么说的:
对这部分继续按同样方式处理,可得到一段 Base64 文本,再进行后续解码, 通过先做 base64 解码,得到一串文本,再把文本反转。
反转后的内容还要再做一次 base64.urlsafe_b64decode(),最后按 utf-16be 解码。
最终得到:
-901338eaa22e得到:
flag{cf78e26a-f5ba-4d78-aa2d-901338eaa22e}