2025.12.1 ISCTF
2026-08-22 20:23:48

学习了一段时间杂项以后打的第一个比赛 过程磕磕绊绊 对于很多知识点有混淆 一知半解 赛后甚至有点沮丧 复现也花了不少时间 后来真正能做出题目才体会到 Misc 的有趣

📎 相关:
| 1.图片隐写
| Misc tools
| 3.编码分析
| 4.压缩包分析
| 6.流量分析
| 1.1LSB隐写

星髓宝盒

星髓宝盒.png

binwalk发现图片里包含着压缩包

20251201ISCTF-01

foremost提取

20251201ISCTF-02

获得你是优秀学生吗.txt``星髓宝盒.jpg加密的flag.txt

星髓宝盒.jpg

属性看到jpg的信息 exiftool查看

20251201ISCTF-03

你是优秀学生吗.txt

我当时把它认为是零宽隐写 一无所获

这里是文本隐水印 第一次接触

https://www.guofei.site/pictures_for_blog/app/text_watermark/v1.html

20251201ISCTF-04

20251201ISCTF-05

第二层才是零宽隐写

20251201ISCTF-06

20251201ISCTF-07

5b298e6836902096e9316756d3b58ec4想到前面jpg图片属性提示md5

20251201ISCTF-08

flag.txt*

!!!@@@###123flag.txt的密码

20251201ISCTF-09

ISCTF{1e7553787953e74113be4edfe8ca0e59}

阿利维亚的传说

flag1

20251201ISCTF-10

flag2

zsteg -a /home/kali/Desktop/TiTan.png

20251201ISCTF-11

20251201ISCTF-12

flag3

binwalk -eM /home/kali/Desktop/TiTan.png

20251201ISCTF-13

获得加密的flag3.txt 直接爆破密码获得

20251201ISCTF-14

ISCTF{VAN_WHlLE_TRUE}但不对

。。我看到wp了,,比赛的时候可能瞎了吧(揉眼睛

要从上往右读

ISCTF{DoNotTrustTitan_HopeYouMakeTherightChoice_FindMyGiftForYou}

木林森

20251201ISCTF-15

20251201ISCTF-16

20251201ISCTF-17

20000824

base64解码结果里还有一张jpg图片 直接导出或者binwalk导出

20251201ISCTF-18

20251201ISCTF-19

20251201ISCTF-20

....Mamba....

20251201ISCTF-21

31EE9AB2DF104EE695824579140ADF39472BEB3316CF119A61A2CC460523B0618C794A934AFF3B90F4E036

20251201ISCTF-22

我当时想着Ron’s Code应该指的是RC4解密

20251201ISCTF-23
但没整出来

20251201ISCTF-24

....Mamba....的意思是Mamba左右都要有四位 所以我们把20000824自然地砍半 插入Manba

结果就是2000Mamba0824

20251201ISCTF-25

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"

美丽的风景图

20251201ISCTF-26

20251201ISCTF-27

20251201ISCTF-28

20251201ISCTF-29

20251201ISCTF-30

20251201ISCTF-31

20251201ISCTF-32

20251201ISCTF-33

红 jqW2

橙 Dg2C

黄7HLo8

绿6yRWh

青ZXw8T

蓝3CaEX

紫98Mz

青花瓷那张是青色 建筑物那张是蓝色 我给搞反了

古代从右往左读 于是与古代有关的红 橙 青 图 从右往左读

红 2Wqj

橙 C2gD

黄7HLo8

绿6yRWh

青 KEaC3

蓝 ZXw8T

紫 98Mz

2WqjC2gD7HLo86yRWhKEaC3ZXw8T98Mz

20251201ISCTF-34

ISCTF{H0w_834u71fu1!!!}

小蓝鲨的二维码

纯粹的复现

enc.png末尾的字符串23xXvfzaibase58解码

20251201ISCTF-35

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

20251201ISCTF-36

derypted_zigzag.pngflag.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

20251201ISCTF-37

https://merri.cx/qrazybox/

二维码分析恢复工具 数一下xor_result.png29*29的二维码 找到文件头

20251201ISCTF-38

用醒图的贴纸降一点透明度拼一下

20251201ISCTF-39

可以扫了

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)

20251201ISCTF-40

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

20251201ISCTF-41

7644027341241571414254539033581025821232019860861753472899980529695625198016019462879314488666454640621660011189097660092595699889727595925351737140047609这就是n

20251201ISCTF-42

fXGWkWSnLSQSAKbSeTXlUVQTGRi7KVS7jCOKTKHSXXSjHjmTABnXGLH6L1jnYLKQamTGSUCSDaOKiqeLHyD7IFO2IQGGSGbzKBUQMTe=

20251201ISCTF-43

CDABGHEFKLIJOPMNSTQRWXUVabYZefcdijghmnklqropuvstyzwx23016745+/89

base64换表吧

20251201ISCTF-44

实际上还要加一步hex

20251201ISCTF-45

755114994425250490088650711567597491113839217439840308448150555421 1619110839551091782778656892126244444160100583088287091700792873342921044046712035923917

20251201ISCTF-46

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()

20251201ISCTF-47

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)

20251201ISCTF-48

ISCTF{M15c_10v3_Cryp70}

小蓝鲨的周年庆礼物

一个png文件 搜不到隐写内容

一个无后缀名的文件 数据混乱 大小规整

20251201ISCTF-49

在Veracrypt官网下载

1.选择挂载的盘符

2.选择要挂载的容器

3。加载容器

20251201ISCTF-50

1.勾选密钥文件

2.密钥文件

20251201ISCTF-51

1.添加文件

2.选择\E:Desktop\flag\nothing is here.png

3.点击确定

20251201ISCTF-52

点击确定

20251201ISCTF-53

现在可以获得flag.txt

20251201ISCTF-54

20251201ISCTF-55

实际字符数明显多于显示字符数 考虑零宽隐写

20251201ISCTF-56

20251201ISCTF-57

ISCTF{VC_15_s0OO0O0O_1n73r3571n6!!}

Abnormal log

一名 hacker 对小蓝鲨值守的服务器发起大量攻击,被工作人员拦下,得到一份日志,日志里藏着秘密(flag 用 ISCTF{} 包裹)。

附件:access.log,共 936 行,攻击者分段上传恶意文件。真正有用的是 File data segment 后面的 hex,其他 Random data injected/Uploaded data/ERROR 全是干扰。

20251201ISCTF-58

日志结构(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 = 377f ^ 05 = 7ab9 ^ 05 = bcaa ^ 05 = af22 ^ 05 = 2719 ^ 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

20251201ISCTF-59

ISCTF{sabfndhjkashgfyiasdgfyusdguyfbknncxzbnj}

Guess!

二分法猜数字

20251201ISCTF-60

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输出重定向到文件,这样即使终端瞬间关闭,输出内容也会被完整保存下来

上一页
2026-08-22 20:23:48
下一页