学习了一段时间杂项以后打的第一个比赛 过程磕磕绊绊 对于很多知识点有混淆 一知半解 赛后甚至有点沮丧 复现也花了不少时间 后来真正能做出题目才体会到 Misc 的有趣
📎 相关: | 1.图片隐写 | Misc tools | 3.编码分析 | 4.压缩包分析 | 6.流量分析 | 1.1LSB隐写
星髓宝盒 星髓宝盒.pngbinwalk发现图片里包含着压缩包
foremost提取
获得你是优秀学生吗.txt``星髓宝盒.jpg加密的flag.txt
星髓宝盒.jpg属性看到jpg的信息 exiftool查看
你是优秀学生吗.txt我当时把它认为是零宽隐写 一无所获
这里是文本隐水印 第一次接触
https://www.guofei.site/pictures_for_blog/app/text_watermark/v1.html
第二层才是零宽隐写
5b298e6836902096e9316756d3b58ec4想到前面jpg图片属性提示md5
flag.txt*!!!@@@###123是flag.txt的密码
ISCTF{1e7553787953e74113be4edfe8ca0e59}
阿利维亚的传说 flag1
flag2 zsteg -a /home/kali/Desktop/TiTan.png
flag3 binwalk -eM /home/kali/Desktop/TiTan.png
获得加密的flag3.txt 直接爆破密码获得
ISCTF{VAN_WHlLE_TRUE}但不对
。。我看到wp了,,比赛的时候可能瞎了吧(揉眼睛
要从上往右读
ISCTF{DoNotTrustTitan_HopeYouMakeTherightChoice_FindMyGiftForYou}
木林森
20000824
base64解码结果里还有一张jpg图片 直接导出或者binwalk导出
....Mamba....
31EE9AB2DF104EE695824579140ADF39472BEB3316CF119A61A2CC460523B0618C794A934AFF3B90F4E036
我当时想着Ron’s Code应该指的是RC4解密
但没整出来
....Mamba....的意思是Mamba左右都要有四位 所以我们把20000824自然地砍半 插入Manba
结果就是2000Mamba0824
ISCTF{590CF439-E304-4E27-BE45-49CC7B02B3F3}
rc4脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 from Crypto.Cipher import ARC4 import sys if __name__ == "__main__": if len(sys.argv) != 3: print("Usage: python rc4.py <ciphertext_hex> <key>") sys.exit(1) ct_hex = sys.argv[1] key = sys.argv[2].encode() ct = bytes.fromhex(ct_hex) pt = ARC4.new(key).decrypt(ct) print(pt.decode(errors='replace'))
1 2 3 python rc4.py "31EE9AB2DF104EE695824579140ADF39472BEB3316CF119A61A2CC460523B0618C794A934AFF3B90F4E036" "2000Mamba0824"
美丽的风景图
红 jqW2
橙 Dg2C
黄7HLo8
绿6yRWh
青ZXw8T
蓝3CaEX
紫98Mz
青花瓷那张是青色 建筑物那张是蓝色 我给搞反了
古代从右往左读 于是与古代有关的红 橙 青 图 从右往左读
红 2Wqj
橙 C2gD
黄7HLo8
绿6yRWh
青 KEaC3
蓝 ZXw8T
紫 98Mz
2WqjC2gD7HLo86yRWhKEaC3ZXw8T98Mz
ISCTF{H0w_834u71fu1!!!}
小蓝鲨的二维码 纯粹的复现
enc.png末尾的字符串23xXvfzaibase58解码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 import numpy as np import cv2 import os def zigzag_indices(rows, cols): indices = [] for diag in range(rows + cols - 1): if diag % 2 == 0: r = min(diag, rows - 1) c = diag - r while r >= 0 and c < cols: indices.append((r, c)) r -= 1 c += 1 else: c = min(diag, cols - 1) r = diag - c while c >= 0 and r < rows: indices.append((r, c)) r += 1 c -= 1 return indices def zigzag_unscramble_2d(matrix_2d): rows, cols = matrix_2d.shape indices = zigzag_indices(rows, cols) flattened = matrix_2d.flatten() unscrambled = np.zeros_like(matrix_2d) for idx, (r, c) in enumerate(indices): unscrambled[r, c] = flattened[idx] return unscrambled def zigzag_unscramble_image(img): if img.ndim == 2: return zigzag_unscramble_2d(img) elif img.ndim == 3 and img.shape[2] == 3: unscrambled = np.zeros_like(img) for ch in range(3): unscrambled[:, :, ch] = zigzag_unscramble_2d(img[:, :, ch]) return unscrambled else: raise ValueError("仅支持灰度图或标准 RGB 彩色图") if __name__ == "__main__": encrypted_image_path = "enc.png" # 检查文件是否存在 if not os.path.exists(encrypted_image_path): print(f"错误:找不到加密图像文件 '{encrypted_image_path}'") print("请确保加密图像文件存在,或修改文件路径。") exit(1) img_bgr = cv2.imread(encrypted_image_path) if img_bgr is None: print("无法读取加密图像,请检查路径或格式是否支持") exit(1) img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) print(f"成功读取加密图像,尺寸: {img_rgb.shape}") decrypted_img = zigzag_unscramble_image(img_rgb) decrypted_bgr = cv2.cvtColor(decrypted_img, cv2.COLOR_RGB2BGR) output_path = "decrypted_zigzag.png" cv2.imwrite(output_path, decrypted_bgr) print(f"图像已解密并保存为: {output_path}")
获得图片decrypted_zigzag.png
derypted_zigzag.png与flag.png进行xor 或者在随波逐流进行xor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import cv2 import numpy as np def xor_images(): # 图片路径 img1_path = "flag.png" img2_path = "decrypted_zigzag.png" output_path = "xor_result.png" # 读取两张图片 img1 = cv2.imread(img1_path) img2 = cv2.imread(img2_path) if img1 is None: print(f"错误: 无法读取图片 {img1_path}") return if img2 is None: print(f"错误: 无法读取图片 {img2_path}") return # 检查尺寸是否一致 if img1.shape != img2.shape: print(f"错误: 图片尺寸不一致!") print(f" {img1_path}: {img1.shape}") print(f" {img2_path}: {img2.shape}") return # 执行按位 XOR xor_result = cv2.bitwise_xor(img1, img2) # 保存结果 cv2.imwrite(output_path, xor_result) print(f"XOR 结果已保存到: {output_path}") # 可选:显示图像(按任意键关闭窗口) cv2.imshow("Image 1", img1) cv2.imshow("Image 2", img2) cv2.imshow("XOR Result", xor_result) print("按任意键关闭所有窗口...") cv2.waitKey(0) cv2.destroyAllWindows() # 调用函数 if __name__ == "__main__": xor_images()
获得图片xor_result.png
https://merri.cx/qrazybox/
二维码分析恢复工具 数一下xor_result.png为29*29的二维码 找到文件头
用醒图的贴纸降一点透明度拼一下
可以扫了
ISCTF{fbf1a6d6-95e4-4a1c-95fd-7d2f03a16b20}
湖心亭看雪 1 2 3 4 5 a = b'*********' #这个东西你以后要用到 b = b'blueshark' c = bytes([x ^ y for x, y in zip(a, b)]) print(c.hex()) #c = 53591611155a51405e
异或(XOR)操作具有可逆性:如果 c = a ^ b,那么 a = b ^ c,同样 b = a ^ c
1 2 3 4 5 b = b'blueshark' c_hex = '53591611155a51405e' c = bytes.fromhex(c_hex) a = bytes(x ^ y for x, y in zip(c, b)) print(a)
15ctf2025
Miscrypto 1 2 3 4 5 6 7 8 9 10 11 12 13 #这是一道费马,对 from Crypto.Util.number import * flag = b'ISCTF{}' n = p*q phi = (p-1)*(q-1) m = bytes_to_long(flag) c = pow(m, e, n) e = 65537 #n= #c= #?但都不给要我怎么做啊!
三个文件名 另外两个的结果分别是n和c
7644027341241571414254539033581025821232019860861753472899980529695625198016019462879314488666454640621660011189097660092595699889727595925351737140047609这就是n
fXGWkWSnLSQSAKbSeTXlUVQTGRi7KVS7jCOKTKHSXXSjHjmTABnXGLH6L1jnYLKQamTGSUCSDaOKiqeLHyD7IFO2IQGGSGbzKBUQMTe=
CDABGHEFKLIJOPMNSTQRWXUVabYZefcdijghmnklqropuvstyzwx23016745+/89
base64换表吧
实际上还要加一步hex
755114994425250490088650711567597491113839217439840308448150555421 1619110839551091782778656892126244444160100583088287091700792873342921044046712035923917
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 from Crypto.Util.number import long_to_bytes, inverse import math import sys def is_square(n): if n < 0: return False root = math.isqrt(n) return root * root == n def fermat_factor(n): a = math.isqrt(n) if a * a < n: a += 1 iterations = 0 while True: b2 = a * a - n if is_square(b2): b = math.isqrt(b2) p = a + b q = a - b return p, q a += 1 iterations += 1 if iterations % 10000 == 0: print(f"已尝试 {iterations} 次...") def main(): try: # 获取用户输入的 n n_input = input("请输入 RSA 模数 n (十六进制加前缀0x,否则视为十进制): ").strip() # 转换为整数(支持 0x 前缀) if n_input.startswith('0x'): n = int(n_input, 16) else: n = int(n_input) print("正在使用费马分解法分解 n...") p, q = fermat_factor(n) print("分解成功!") print(f"p = {p}") print(f"q = {q}") # 可选:继续解密(如果你也想输入 c 和 e) # 这里我们只做分解,不自动解密,但你可以扩展 except ValueError: print("错误:输入的 n 不是有效的数字。") except Exception as e: print(f"发生错误:{e}") if __name__ == "__main__": main()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 # -*- coding: utf-8 -*- from Crypto.Util.number import long_to_bytes, inverse # 给定的 RSA 参数 p = 87430128338242598134172260625226774095596700493624565125749444668870272998101 q = 87430128338242598134172260625226774095596700493624565125749444668870272994709 e = 65537 c = 7551149944252504900886507115675974911138392174398403084481505554211619110839551091782778656892126244444160100583088287091700792873342921044046712035923917 # 重建 n 和 φ(n) n = p * q phi = (p - 1) * (q - 1) # 计算私钥 d = inverse(e, phi) # 解密 m = pow(c, d, n) flag = long_to_bytes(m).decode() print("Flag:", flag)
ISCTF{M15c_10v3_Cryp70}
小蓝鲨的周年庆礼物 一个png文件 搜不到隐写内容
一个无后缀名的文件 数据混乱 大小规整
在Veracrypt官网下载
1.选择挂载的盘符
2.选择要挂载的容器
3。加载容器
1.勾选密钥文件
2.密钥文件
1.添加文件
2.选择\E:Desktop\flag\nothing is here.png
3.点击确定
点击确定
现在可以获得flag.txt
实际字符数明显多于显示字符数 考虑零宽隐写
ISCTF{VC_15_s0OO0O0O_1n73r3571n6!!}
Abnormal log 一名 hacker 对小蓝鲨值守的服务器发起大量攻击,被工作人员拦下,得到一份日志,日志里藏着秘密(flag 用 ISCTF{} 包裹)。
附件:access.log,共 936 行,攻击者分段上传恶意文件。真正有用的是 File data segment 后面的 hex,其他 Random data injected/Uploaded data/ERROR 全是干扰。
日志结构(segment 1 为例):
1 2 3 4 5 6 [2025-09-11 20:48:36] [INFO] Attacker uploading segment 1... [2025-09-11 21:13:44] [DEBUG] Random data injected: AgtACjmKmUOcEBNRRfNd [2025-09-11 20:27:35] [INFO] File data segment: 327fb9aa22190501dfbff187e8080505050505055f05050505050505342d9d79 [2025-09-11 20:30:46] [ERROR] Upload failure, retrying... [2025-09-11 20:54:16] [INFO] Uploaded data: ypJNwHwHVnqWHfzohX4s ...
共 116 个 segment(segment 1 ~ segment 116),日志末尾显示:
1 2 3 [2025-09-11 21:02:21] [INFO] Attack complete. File upload successful. [2025-09-11 20:25:37] [DEBUG] Malicious file extracted and executed. [2025-09-11 20:38:31] [ERROR] Server crash detected after attack.
把每个 segment 的 File data segment 后面的 hex 按编号顺序拼起来。第一个 segment 的数据:
1 327fb9aa22190501dfbff187e8080505050505055f05050505050505342d9d79
前 6 字节 32 7f b9 aa 22 19,不像常见文件头。数据里大量出现 05,猜是 XOR 密钥 0x05。
验证:32 ^ 05 = 37,7f ^ 05 = 7a,b9 ^ 05 = bc,aa ^ 05 = af,22 ^ 05 = 27,19 ^ 05 = 1c
解密后前 6 字节 37 7A BC AF 27 1C,正好是 7z 压缩文件的魔数 。
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import re # 读取日志 with open('access.log', 'r') as f: content = f.read() # 提取所有 segment 及其对应的 hex 数据 segments = {} lines = content.split('\n') i = 0 while i < len(lines): m = re.search(r'uploading segment (\d+)', lines[i]) if m: seg_num = int(m.group(1)) for j in range(i+1, min(i+4, len(lines))): m2 = re.search(r'File data segment:\s*([0-9a-fA-F]+)', lines[j]) if m2: segments[seg_num] = m2.group(1) break i += 1 # 按编号顺序拼接 hex_data = '' for k in sorted(segments.keys()): hex_data += segments[k] # hex 转 bytes raw_bytes = bytes.fromhex(hex_data) # XOR 0x05 解密 decrypted = bytes([b ^ 0x05 for b in raw_bytes]) # 保存为 7z 文件 with open('extracted.7z', 'wb') as f: f.write(decrypted) print("Done! Saved to extracted.7z")
1 2 3 4 5 6 Found 116 segments Total hex length: 7374 Total bytes: 3687 First 6 bytes: 377abcaf271c Expected 7z: 377abcaf271c Saved to extracted.7z
解压7z文件获得flag.png
ISCTF{sabfndhjkashgfyiasdgfyusdguyfbknncxzbnj}
Guess! 二分法猜数字
ISCTF{9ueSs_thE_@N$weR}
Image_is_all_you_need Shamir 秘密共享 + 可逆神经网络隐写。给了 6 张 share(secret_1.png ~ secret_6.png)和两套源码:
share_secret.py:Shamir 把隐写图 secret.png 拆成 6 个 share
Steg/:INN 图像隐写,把 flag 嵌进 cover 图生成 secret.png
Shamir 恢复 share_secret.py 里是 GF(257) 上的 Shamir:像素值是多项式常数项,share 是 x=1..6 处的求值,Lagrange 在 x=0 处插值还原:
1 secret_img = (img + base) % 257
坑:像素值 256 在 PNG 里存不了(uint8 只到 255),被映射成 0 记在 tEXt chunk 里。恢复时先读 tEXt chunk 拿到这些索引,把对应位置的 0 还原成 256。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # 核心恢复逻辑 def lagrange_interpolation_at_zero(xs, ys, mod=257): n = len(xs) result = np.zeros(ys[0].shape, dtype=np.int64) for i in range(n): numerator = 1 denominator = 1 for j in range(n): if i != j: numerator = (numerator * (0 - xs[j])) % mod denominator = (denominator * (xs[i] - xs[j])) % mod basis = (numerator * pow(denominator, mod - 2, mod)) % mod result = (result + basis * ys[i]) % mod return result
INN 逆向 Steg/ 的编码流程:cover 和 payload(flag 的 bit 编码)分别做 DWT,拼接后送 INN 正向传播,取输出前 12 通道做 IWT 得到隐写图。
INV_block 正向:
1 2 3 4 t2 = f(x2) y1 = x1 + t2 s1, t1 = r(y1), y(y1) y2 = e(s1) * x2 + t1
逆过程:
1 2 3 4 s1, t1 = r(y1), y(y1) x2 = (y2 - t1) / e(s1) t2 = f(x2) x1 = y1 - t2
解码:恢复的 secret.png 做 DWT 取前 12 通道,后 12 通道补零,INN 逆向传播,取输出后 12 通道做 IWT 得到 payload。
Payload 解码 utils.py 的编码:text → zlib 压缩 → Reed-Solomon 编码 → bit 序列 → 重复填充至图像大小。INN 提的 bit 不完全精确,用多数投票跨重复周期纠错:
1 2 3 # 找到消息重复周期,对多个副本进行投票 chunks = bits_raw[:n_reps * msg_len].reshape(n_reps, msg_len) voted = (chunks.mean(axis=0) > 0.5).astype(int)
消息长度 1376 bits 时成功解码。
Flag 1 flag{Sh4r3_S3reCTTt_wiTh_Ai_H@@@@}
消失的flag 据说flag输出过,但是消失了
尝试直接SSH连接
1 2 3 4 5 6 7 8 9 C:\Users\13964>ssh -p 31439 qyy@challenge.imxbt.cn ___ ____ ____ _____ _____ |_ _/ ___| / ___|_ _| ___| | |\___ \| | | | | |_ | | ___) | |___ | | | _| |___|____/ \____| |_| |_| Connection to challenge.imxbt.cn closed.
将输出重定向到文件
既然直接看终端看不到,那就把SSH的所有输出重定向到文件中保存下来
在 PowerShell 中执行:
1 ssh -p 31439 qyy@challenge.imxbt.cn 2>&1 | Out-File -FilePath "$env:USERPROFILE\Desktop\output.txt" -Encoding utf8
这条命令的含义:
ssh -p 30648 qyy@challenge.imxbt.cn:SSH连接到目标服务器
2>&1:将标准错误重定向到标准输出,确保所有输出都被捕获
| Out-File -FilePath "$env:USERPROFILE\Desktop\output.txt" -Encoding utf8:通过管道将所有输出写入桌面的 output.txt 文件
打开桌面上的 output.txt,可以看到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ISCTF{95d3cb3e-e495-43d1-9712-67b5c64c28c7} ___ ____ ____ _____ _____ |_ _/ ___| / ___|_ _| ___| | |\___ \| | | | | |_ | | ___) | |___ | | | _| |___|____/ \____| |_| |_| ssh : Connection to challenge.imxbt.cn closed. 所在位置 行:1 字符: 1 + ssh -p 31439 qyy@challenge.imxbt.cn 2>&1 | Out-File -FilePath "$env:U ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (Connection to challenge.imxbt.cn closed.:String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError
服务器在SSH登录时通过 MOTD(Message of the Day) 或 Banner 机制输出了flag
登录后shell立即退出(可能 .bashrc 或 .profile 中配置了 exit 命令,或者用户的默认shell被设置为一个会立即退出的程序)由于连接瞬间断开,终端可能来不及渲染输出,或者输出被清屏操作覆盖,导致flag消失
将SSH输出重定向到文件,这样即使终端瞬间关闭,输出内容也会被完整保存下来