CTF 总结:JWT 登录逻辑审计

记录 JWT 登录认证题中的源码线索、字段验证和 JavaScript 隐式类型转换问题。

  • #CTF
  • #JWT
  • #Koa
  • #身份认证

一、考察知识点

  • JWT 组成:Header、Payload、Signature
  • HS256 对称签名验证
  • Node.js(Koa)接口流程
  • JavaScript 隐式类型转换
  • 身份认证逻辑审计
  • 从源码寻找漏洞点

二、背景

这题由登录、注册和后台组成。可以正常注册账号并登录后台,但是在后台输入内容时会弹窗:

permission denied

三、分析过程

1. 检查是否可以触发 XSS

创建账号时,先检查输入能不能触发 XSS。

结果是内容在输出端全部被转换为大写并进行了转义。

2. 创建多个账号检查越权

创建多个账号,观察是否存在越权。

发现:

  • username 不能是 admin
  • 只有最新创建的用户可以登录

因此,username 是重点关注对象。

3. 从开发者工具寻找遗留信息

查看浏览器开发者工具(F12),检查是否存在遗留信息。

发现作者提示使用了 koa-static 架构,并且主动说明网站根目录的配置问题,最终获得 app.js 源码。

源码中关于 koa-static 和网站根目录配置的提示

Koa 项目的目录架构如下,其中包含控制器、数据库操作封装、数据模型、路由和项目入口等部分:

Koa 项目目录架构及各目录作用

根据架构图中 controllers 目录用于存放接口控制器这一线索,尝试直接访问题目环境中的控制器文件:

https://6a88a12301b5b94ce38d876c.http-ctf2.dasctf.com/controllers/api.js

该地址成功返回了 api.js 源码,从而进一步确认了静态文件目录配置不当导致的源码泄露问题。这个线索很关键,因为后续 JWT 注册和登录逻辑的分析都建立在获得源码的基础上。

通过 controllers 路径直接访问并获得 api.js 源码

4. 分析 JWT 登录验证

使用 Burp Suite 抓包后发现,登录数据不是根据 Session 验证,而是验证 JWT。

接着寻找 JWT 中哪些字段参与最终认证。单独修改 HS256 相关内容或者 secretid 都会返回错误,而且这些报错疑似是作者给出的侧面提示,不像正常业务中的报错。

只处理 HS256 相关内容时的响应:

只处理 HS256 相关内容时返回 no such secret id

只处理 secretid 时的响应:

只处理 secretid 时返回 secret or public key must be provided

由此得出:两个参数都需要修改。

根据源码,可以将 secretid 设置为空字符串。空字符串既不等于 undefined 或 null,又会在 <、>= 数值比较中被临时转换为 0,从而通过 secretid 的范围检查;变量本身仍然是字符串。

5. 修改 JWT 参数

使用在线 JWT 工具修改参数。源码中的签名逻辑为:

jwt.sign(
  { secretid, username, password },
  secret,
  { algorithm: "HS256" }
)

修改内容:

  • 将 JWT Header 中的 alg 从 HS256 改为 none
  • 将 secretid 设置为空字符串
  • 将 username 设置为 admin

通过以上修改,可以绕过注册限制,直接登录并获得 admin 权限。

使用工具

浏览器开发者工具(F12)

用于查看前端请求、响应和遗留信息,并寻找题目给出的架构提示。

Burp Suite

用于抓取登录请求,观察 JWT 数据,并修改请求参数进行验证。

在线 JWT 工具

用于解析 JWT 的 Header 和 Payload,并修改题目中的算法及认证字段。