热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入了解JsonWebToken之实战篇

在上一篇文章中,我介绍了JsonWebToken的相关概念。在这篇文章中,我主要介绍JWT的相关攻击

前言

在上一篇文章中,我介绍了 Json Web Token 的相关概念。在这篇文章中,我主要介绍JWT的相关攻击,并引用了一些CTF题目。

查看上一篇文章:

深入了解Json Web Token之概念篇: http://www.freebuf.com/articles/web/180874.html 。

0×00 环境准备

本来想用python DRF 的 JWT做,后来各种失败。最终尝试了用Php,发现非常便利。

PHP 版本 PHP 7.2.4-1+b2 (cli),也就是kali linux自带的php,至于composer的安装方法,以及各个库的使用方法在此不展开,需要的话可以自己查阅官方文档

php jwt库的评测

在jwt.io上有些php jwt的库,在此说一下使用下来的感觉。只取了评分前三的库:

firebase/php-jwt Star 3786

支持PHP5/7;

操作非常简单,但是不具备很多功能,不是很推荐。

lcobucci/jwt Star Star 2729

支持PHP5/7;

不具备JWE的方法,操作简单;

不具备多重JWS,JWE方法以及其对应序列化方法。

spomky-labs/jose Star 351

仅支持 PHP 7;

功能齐全,具有多重JWE,JWS,以及其对应序列化方法。以上两个都不具备。

0×01 JWT 攻击手段

JWT 的攻击手段包括以下内容:

参考网站: https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries// 。

1. 敏感信息泄露

当服务端的秘钥泄密的时候,JWT的伪造就变得非常简单容易。对此,服务端应该妥善保管好私钥,以免被他人窃取。

2. 将加密方式改为’none’

下文实战中的 Juice Shop JWT issue 1 便是这个问题。之前谈及过nonsecure JWT的问题。

签名算法确保恶意用户在传输过程中不会修改JWT。但是标题中的alg字段可以更改为none。一些JWT库支持无算法,即没有签名算法。当alg为none时,后端将不执行签名验证。将alg更改为none后,从JWT中删除签名数据(仅标题+’.'+ payload +’.')并将其提交给服务器。

解决对策:

不允许出现 none 的方法;

将开启 alg : none 作为一种额外的配置选项。

3.将算法RS256修改为HS256(非对称密码算法=>对称密码算法)

HS256使用密钥来签名和验证每个消息。而RS256使用私钥对消息进行签名并使用公钥进行认证。

如果将算法从RS256更改为HS256,则后端代码使用公钥作为密钥,然后使用HS256算法验证签名。由于攻击者有时可以获取公钥,因此攻击者可以将标头中的算法修改为HS256,然后使用RSA公钥对数据进行签名。

此时,后端代码就会使用RSA公钥+HS256算法进行签名验证,从而让验证通过。

解决对策:

不允许 HS256等对称加密 算法读取秘钥。jwtpy就是限制了这种方法。当读取到 类似于 “— xxx key —” 的参数的时候应抛出错误;

将秘钥与验证算法相互匹配。

4. HS256(对称加密)密钥破解

如果HS256密钥强度较弱,则可以直接强制使用,通过爆破 HS256的秘钥可以完成该操作。难度比较低。解决对策很简单,使用复杂的秘钥即可。

5. 错误的堆叠加密+签名验证假设

错误的堆叠加密

这种攻击发生在单个的或者嵌套的JWE中,我们想象一个JWE如下所示:

JWT RAW

    header : ...

    payload: "admin" : false

             "uid"   : 123

             "umail" : 123@126.com

             ...

JWE Main

    protected / unprotected

    recipients:

        en_key : key1

        en_key : key2

    cipher : xxx

在攻击者不修改秘钥的情况下,对于ciphertext进行修改。往往会导致解密的失败。但是,即使是失败,很多JWT的解密也是会有输出的,在没有附加认证数据(ADD)的情况下更是如此。攻击者对于ciphertext的内容进行修改,可能会让其他的数据无法解密,但是只要最后输出的payload中,有“admin”:true。 其目的就已经达到了。

解决对策:

对于JWE而言,应当解密所有数据,而非从解密的结果中提取单个需要的数据。另外,利用附加认证数据ADD,也是非常好的选择。

签名假设验证

这种攻击发生嵌套的JWS中。我们想象一个嵌套的JWS,其包括了两层的部分,其结构如下:

JWT Main

    JWT Sub1

        payload

        Signature2

    Signature

现在,攻击者通过一定的方式,能够让外层的验证通过的时候,此时,系统还应该检查内层的签名数据,如果不检查,攻击者就可以随意篡改payload的数据,来达到越权的目的。

解决对策:

因此对于嵌套JWS而言,应当验证所有层面的签名是否正确,而非验证最外层的签名是否正确就足够。

6. 无效椭圆曲线攻击

椭圆曲线加密是一种非常安全的方式,甚至从某种程度上而言,比RSA更加安全。关于椭圆曲线的算法,在此不展开。

在椭圆曲线加密中,公钥是椭圆曲线上的一个点,而私钥只是一个位于特殊但非常大的范围内的数字。 如果未验证对这些操作的输入,那攻击者就可以进行设计,从而恢复私钥。

而这种攻击已在过去中得到证实。这类攻击被称为无效曲线攻击。这种攻击比较复杂,也设计到很多的数学知识。详细可以参考文档: critical-vulnerability-uncovered-in-json-encryption 。

解决对策:

检查传递给任何公共函数的所有输入是否有效是解决这类攻击的关键点。验证内容包括公钥是所选曲线的有效椭圆曲线点,以及私钥位于有效值范围内。

7. 替换攻击

在这种攻击中,攻击者需要至少获得两种不同的JWT,然后攻击者可以将令牌中的一个或者两个用在其他的地方。

在JWT中,替换共叽有两种方式,我们称他们为相同接收方攻击(跨越式JWT)和不同接收方攻击。

不同接收方攻击

我们可以设想一个业务逻辑如下:

Auth 机构,有着自己的私钥,并且给 App1 和 App2 发放了两个公钥,用于验证签名;
Attacker 利用自己的秘钥登录了 App1。

此时 Auth 机构给 Attacker 下发了一个 附带签名的JWT,其payload内容为:

{

    'uname':'Attacker'

    'role' :'admin'

}

此时,如果 Attacker 知道 App1 和 App2 的公钥是同一个Auth 签发的话,他可以利用这个JWT去登录 App2,从而获取Admin权限。

解决方法:

在jwt中带上 aud 声明,比如 aud : App1 这样。来限定该jwt只能用于App1。

相同接收方攻击/跨越式JWT same recipient/Cross JWT

我们可以设想一个业务逻辑如下:

在同一站点下,有两个应用程序,wordpress和phpmyadmin,他们都利用了相同的秘钥对和算法来验证JWT签名;

站点管理员知道 Different Recipient 的问题,所以给 wordpress 的应用增加了 aud 验证,但是 phpmyadmin 的用户人数较少,没有增加 aud 的验证;

Attacker 利用自己的秘钥登录了 wordpress。

此时 站点 给 Attacker 下发了一个 附带签名的JWT,其payload内容为:

{

    'uname':'Attacker'

    'role' :'writer'

    'aud' :'shaobaobaoer.cn/wordpress'

    'iss' :'shaobaobaoer.cn'

}

这个JWT看似非常安全,但这仅仅是对于 wordpress 的应用程序而言,。从而Attacker 可以以 writer 的身份登录 phpmyadmin。

解决方案:

为所有子应用程序增加 aud 的验证

8. 其他假想的攻击方式

JWT + SQL 注入

参考链接: https://github.com/greunion/ctf-write-ups/tree/master/2018-nullcon/web/400-web6 。

当解密JWT的秘钥很多的时候,往往需要通过kid来确定使用哪个秘钥,而keyid参数通过b64加密来保存,可以被篡改。当keyid要通过数据库API拿取得时候,往往就会联想到 sql 注入。

我们可以想象一下的攻击情况:

$keyID = $token-> getKeyID();

$keyCOntent= sqlAPI -> fromKeyidGetKeyContent($keyID)

###

class sqlAPI():

    function fromKeyidGetKeyContent($keyID){

        $result= Query("select key_content from keyTable where key_id = '$keyID'");

        return $result['key_content']

    }

###

if($token-> verify($JWA,$keyContetn)){

    echo $flag;

}

在下列比较简易的代码中,通过让数据库返回值为我们自定义的key_content。就可以达到破解JWT的目的。

通过注入:

' union select 'easy' limit 1,1--+

即可让秘钥改成easy。

0×03 实战练习

实战练习1 敏感信息泄露

我搜到了一个demo,在线演示地址为: http://demo.sjoerdlangkemper.nl/jwtdemo/hs256.php 。

GitHub地址为: https://github.com/Sjord/jwtdemo/ 。

使用firebase/jwt写的php-jwt的demo。可以用来完成后面的题目。

为了达到修改jwt的目的,我会把data字段改为:

"data":{

    "hacker":"shaobaobaoer"

    }

该题目地址为: http://demo.sjoerdlangkemper.nl/jwtdemo/rs256 。

在知道github项目的情况下,我们变相知道了公钥私钥的地址,直接访问:

http://demo.sjoerdlangkemper.nl/jwtdemo/private.pem

http://demo.sjoerdlangkemper.nl/jwtdemo/public.pem

可以拿到公钥私钥的内容,之后,利用我们自己的jwt库进行加密即可。

关键代码如下所示:

$keychain = new Keychain();

$sign = new Sha256();

$token = "eyJ0eXAiO...";

$token = (new Parser())->parse((string) $token);

$hacktoken = (new  Builder())

    ->setIssuer($token->getClaim('iss'))

    ->setIssuedAt($token->getClaim('iat'))

    ->setExpiration($token->getClaim('exp'))

    ->set("data",["hack"=>"shaobaobaoer"])

    ->sign($sign,$keychain->getPrivateKey('file://key_box/private.pem'))

    ->getToken();

echo $hacktoken.PHP_EOL;

var_dump($hacktoken->verify($sign,$keychain->getPublicKey('file://key_box/public.pem')));

可以看到,我们已经更改成功。

深入了解Json Web Token之实战篇

实战练习2 Juice Shop JWT issue 1

juice shop 是一个OWASP 的 vulnerable WEB 项目,后端语言为node.js。

当初我做的时候连jwt是什么都不知道,两道jwt的题目就此跳过了,现在已经掌握了这些概念,就可以拿出来回味一下。

关于juice shop 的大部分题解可以看 OWASP juice shop 实战报告 。

题目描述

Forge an essentially unsigned JWT token that impersonates the (non-existing) user jwtn3d@juice-sh.op.

实战过程

首先,先用万能登录,获取到jwt 如下所示:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6eyJpZCI6MSwiZW1haWwiOiJhZG1pbkBqdWljZS1zaC5vcCIsInBhc3N3b3JkIjoiMDE5MjAyM2E3YmJkNzMyNTA1MTZmMDY5ZGYxOGI1MDAiLCJjcmVhdGVkQXQiOiIyMDE4LTA4LTEyIDA3OjUzOjM4LjA2NCArMDA6MDAiLCJ1cGRhdGVkQXQiOiIyMDE4LTA4LTEyIDA3OjUzOjM4LjA2NCArMDA6MDAifSwiaWF0IjoxNTM0MDYwNTM5LCJleHAiOjE1MzQwNzg1Mzl9.Jivk7Pil6wukFkShzCCaHNq7qmxegvcyD83FkbglT0uYYP0azTW2rM-FH4R8WYneTu1A5gQmUjB6VdFJh8APz5Qej_AA4RP3Q6nH-9qbytxQ5cebiEuuhRSridDxbXxuS0-oquQ0PkRtpenJ75mLJFzVROeaBWgKFNNcFIrV9hs

放到 jwt.io中去解密。可以看到数据的架构如下所示:

深入了解Json Web Token之实战篇

之前我们做过实验,当alg选择为 none 的时候,是不用对JWT进行签名的,这样的jwt也被称为 不安全的jwt。

这道题目的思路就是修改 alg。

当后端不限定alg的时候,这种方法就可以被利用。当然jwt.io是不会让你把alg改成none的。你需要自己手动改:

深入了解Json Web Token之实战篇

对头部稍微操作一下,得到的新token如下:

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6eyJpZCI6MSwiZW1haWwiOiJqd3RuM2RAanVpY2Utc2gub3AiLCJwYXNzd29yZCI6IjAxOTIwMjNhN2JiZDczMjUwNTE2ZjA2OWRmMThiNTAwIiwiY3JlYXRlZEF0IjoiMjAxOC0wOC0xMiAwNzo1MzozOC4wNjQgKzAwOjAwIiwidXBkYXRlZEF0IjoiMjAxOC0wOC0xMiAwNzo1MzozOC4wNjQgKzAwOjAwIn0sImlhdCI6MTUzNDA2MDUzOSwiZXhwIjoxNTM0MDc4NTM5fQ

将新的jwt发送,可以解决这个题目:

深入了解Json Web Token之实战篇

后面还有个jwt issue 2 ,我分解不了公钥,按照官方文档的做法也无从下手。做出来的可以交流一二。

实战练习3 加密方式更改

那个网站的后端代码是不能够演示加密方式修改的攻击方法的。那篇博客有些问题。这边就给出个样例。

后端的伪代码应该如下所示:

# sometimes called "decode"

verify(string token, string verificationKey){

    # returns payload if valid token, else throws an error

}

string token = $input

string verificatiOnKey= file_get_content('rsa_pub.key')

后端代码应该会判断jwt的加密方式,其实这种方法是比较局限的。首先对于一个优秀的JWT的库而言,RS256和SH256的认证不会放在一起。另外,HMAC应当禁止公钥作为secret。

例如:在pyjwt中,这种方法是被禁止的。会抛出错误。

jwt.exceptions.InvalidKeyError: The specified key is an asymmetric key or x509 certificate and should not be used as an HMAC secret.

大概写了个小脚本,利用了公钥来签名,如下所示:

$secret = file_get_contents("./key_box/public.pem");

//var_dump($secret);

$sign = new Sha256();

$token = "eyJ0eXAiO...";

$token = (new Parser())->parse((string) $token);

$hacktoken = (new  Builder())

    ->setIssuer($token->getClaim('iss'))

    ->setIssuedAt($token->getClaim('iat'))

    ->setExpiration($token->getClaim('exp'))

    ->set("data",["hack"=>"shaobaobaoer"])

    ->sign($sign,$secret)

    ->getToken();

echo $hacktoken.PHP_EOL;

var_dump($hacktoken->verify($sign,$secret));

实战练习4 HMAC秘钥爆破

参考链接: https://delcoding.github.io/2018/03/jwt-bypass/ 。

在这道题目中,访问web,可以返回一个jwt的字符串:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6ImZhbHNlIn0.oe4qhTxvJB8nNAsFWJc7_m3UylVZzO3FwhkYuESAyUM

将它解密,可以发现,算法是HS256,admin为flase。

{

  "alg": "HS256",

  "typ": "JWT"

}

{

  "admin": "false"

}

我们的目标很简单,只需要将admin转成true就可以了。而此刻能够做的只有爆破秘钥了。你当然可以写一个小脚本来爆破秘钥,这里推荐一个工具 c-jwt cracker 。

通过小工具,我们能迅速跑出秘钥来,我的VPS大概用了2m跑出了秘钥 54l7y

深入了解Json Web Token之实战篇


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 我们


推荐阅读
  • 远程访问用户 Kindle通过电子书实现控制
    介绍自2007年以来,亚马逊已售出数千万台Kindle,令人印象深刻。但这也意味着数以千万计的人可能会因为这些Kindle中的软件漏洞而被黑客入侵。他 ... [详细]
  • 优化Nginx中PHP-FPM模块配置以提升性能
    通过调整Nginx与PHP-FPM之间的配置,可以显著提高Web服务器处理PHP请求的速度和效率。本文将详细介绍如何针对不同的应用场景优化PHP-FPM的各项关键参数。 ... [详细]
  • django项目中使用手机号登录
    本文使用聚合数据的短信接口,需要先获取到申请接口的appkey和模板id项目目录下创建ubtils文件夹,定义返回随机验证码和调取短信接口的函数function.py文件se ... [详细]
  • 本文介绍如何在Windows 7操作系统中配置支持HTML5多点触控功能的设备,同时探讨了HTML5在开发Windows桌面应用程序中的可能性。 ... [详细]
  • 本文旨在探讨Linux系统中两种重要的进程间通信(IPC)机制——System V和POSIX的标准及其特性,为开发者提供深入的理解。 ... [详细]
  • 2023年PHP处理请求超时的全面指南
    本文详细介绍了在PHP中处理请求超时的各种方法,包括设置脚本执行时间、处理file_get_contents函数超时以及优化AJAX请求等,适合开发者参考学习。 ... [详细]
  • 1.选择一个翻译页面,我选择的是有道词典(http:dict.youdao.com)2.随便输入一个英语单词进行翻译,然后查看源文件,找到 ... [详细]
  • 本文详细介绍了使用Apache的AB工具和Webbench进行服务器性能测试的过程,分享了实际操作中的测试结果,并探讨了测试过程中遇到的问题及可能的解决方案。 ... [详细]
  • 本文探讨了Thrift作为一款支持多语言的服务开发框架,其在体积、功能、扩展性以及多协议支持等方面的显著优势。特别地,Thrift作为一种RPC(远程过程调用协议)框架,非常适合用于构建可扩展且低耦合的分布式服务系统。文章通过多种编程语言对Thrift服务进行了性能测试,并提供了详细的测试结果。 ... [详细]
  • 浏览器、中间件与服务器的交互机制
    本文详细探讨了浏览器、中间件和服务器之间的交互过程,特别是HTTP请求的完整流程,包括DNS解析、TCP连接建立及数据传输等关键步骤。 ... [详细]
  • Linux 存储堆栈架构图
    本文提供了一张清晰的Linux存储堆栈架构图,并对比了多种iSCSI后端解决方案,如LIO和STGT。文中还提供了详细的资源链接,帮助读者深入了解这些技术。 ... [详细]
  • 本文介绍了FTP(文件传输协议)的基础知识,包括其定义、如何通过TCP建立控制和数据连接,以及主动模式与被动模式的区别。FTP作为一种重要的文件传输协议,在互联网数据交换中扮演着关键角色。 ... [详细]
  • Linux环境下的PHP7安装与配置指南
    本文详细介绍了如何在Linux操作系统中安装和配置PHP7,包括检查当前PHP版本、升级PHP以及配置MySQL支持等步骤,适合后端开发者参考。 ... [详细]
  • 利用Executor框架管理线程池
    本文介绍了如何使用Executor框架来管理和创建线程池,包括不同的线程池类型及其应用场景,以及如何通过Executors工厂类创建不同类型的Executor实例。 ... [详细]
  • 教程:如何打造令人印象深刻的GitHub个人主页Readme
    本文将指导您如何创建一个既专业又个性化的GitHub个人主页Readme,通过添加统计数据、常用语言和最近活动等元素,让您的主页更加吸引人。 ... [详细]
author-avatar
手机用户2502929415
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有