新手也能看懂的CTF入门:从蓝桥杯Web题“黑客密室逃脱”学Flask文件包含与自定义加密

张开发
2026/5/30 20:27:15 15 分钟阅读
新手也能看懂的CTF入门:从蓝桥杯Web题“黑客密室逃脱”学Flask文件包含与自定义加密
CTF入门实战从Flask文件包含到自定义加密算法破解第一次参加CTF比赛时面对满屏的加密字符串和陌生的Web界面我完全不知道从何下手。直到遇到那道改变我学习路径的黑客密室逃脱题目——它不仅教会了我如何系统性地拆解CTF赛题更让我理解了Web安全与密码学之间的奇妙联系。本文将用这道蓝桥杯经典Web题作为案例带你体验完整的CTF解题思维流程。1. 解题前的认知准备CTFCapture The Flag竞赛中的Web类题目通常模拟真实世界的Web应用漏洞。与渗透测试不同CTF题目往往会在看似复杂的场景中隐藏着明确的解题线索。对于初学者而言掌握以下三个核心认知至关重要漏洞的确定性每道CTF题目都设计有明确的漏洞点不会出现无解的模糊场景线索的连贯性题目提供的每个提示包括看似无关的界面文字都可能是关键线索技术的组合性单一知识点很少能直接解题需要组合应用多种技术以黑客密室逃脱为例题目描述中提到的数字钥匙和加密线索就暗示了我们需要寻找加密相关的漏洞点。2. 信息收集发现隐藏入口任何CTF解题过程都始于信息收集。对于Web题目我们首先需要系统性地探索目标网站的所有可见和不可见接口# 使用curl进行基础探测实际CTF中常用浏览器开发者工具 curl -X GET http://target-url/ curl -X GET http://target-url/robots.txt curl -X GET http://target-url/.git/HEAD在本题中通过点击界面上的【立即查看日志】按钮我们获得了第一个关键线索——一段加密字符串d9d1c4d9e0abc2a497df9a9a6c5fa4c9c9a592a8c39ccba6709b6b98a0c7c6d89cd994a39aae6f6f68af同时页面提示前往秘密区域这引导我们发现/file?namexxx这个关键接口。通过尝试常见文件名我们最终确定可以通过/file?nameapp.py获取到服务器源码。提示在真实CTF环境中常用文件包含探测列表包括app.pyindex.phpmain.jsconfig.ini.env3. 代码审计定位漏洞点获取到Flask应用源码后我们需要重点关注以下几个安全敏感区域路由定义检查所有app.route装饰的端点文件操作查找open()、os.path等文件系统调用加密实现识别自定义的加密算法实现本题的源码中暴露出两个关键漏洞点3.1 不安全文件包含app.route(/file) def file(): file_name request.args.get(name) 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, encodingutf-8) as f: content f.read() return render_template(file_content.html, contentcontent)这段代码虽然做了路径检查但允许读取SAFE_ROOT_DIR目录下的任意文件除了包含config的文件。通过这个接口我们可以获取到hidden.txt文件其中包含了加密密钥解密密钥: secret_key86723.2 自定义加密算法分析源码中实现的加密算法值得仔细研究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()这是一个典型的逐字节加法加密类似于Vigenère密码的变种其加密过程可以表示为步骤操作示例1取明文字符ASCII码A → 652取密钥对应字符ASCII码s → 1153相加取模(65 115) 1804转换为十六进制180 → 0xb44. 编写解密脚本理解加密算法后我们可以逆向设计解密函数。解密的核心在于逆向执行加密操作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)执行解密函数传入我们从日志获取的密文和hidden.txt中找到的密钥print(simple_decrypt( d9d1c4d9e0abc2a497df9a9a6c5fa4c9c9a592a8c39ccba6709b6b98a0c7c6d89cd994a39aae6f6f68af, secret_key8672))最终输出flag值flag{7c92fbd5-1df3-4d1f-8e4f-bcf7e5855791}5. 知识延伸Flask文件包含防护在真实开发中安全的文件包含实现应该考虑以下防护措施白名单校验只允许访问预定义的安全文件列表内容过滤对返回内容进行敏感信息过滤权限控制结合用户权限系统限制文件访问# 改进后的安全文件包含示例 ALLOWED_FILES {about.html, contact.html} app.route(/safe_file) def safe_file(): file_name request.args.get(name) if file_name not in ALLOWED_FILES: return Invalid file request, 403 try: with open(os.path.join(SAFE_ROOT_DIR, file_name), r) as f: content f.read() # 过滤敏感内容 filtered_content content.replace(SENSITIVE_INFO, ***REDACTED***) return render_template(file.html, contentfiltered_content) except FileNotFoundError: return File not found, 404这道题目完美展示了CTF学习的价值——它不仅考察单一技术点更训练我们建立系统性的安全思维。当我第一次成功解密出flag时那种从混沌中找到秩序的成就感至今记忆犹新。

更多文章