渗透测试高级技巧(二):对抗前端动态密钥与非对称加密防护

news2024/9/24 1:23:29

在前文的技术分享中,我们描述了验签和静态对称加密(静态密钥 AES)的常见场景,大家我相信遇到类似的加解密清醒,基本都可以通过热加载的基本使用获得破解前端加密解密的方法,达到一个比较好的测试状态。

在本文中,我们在保持同样的通用适配度的同时,将会来接触更加复杂的前端加密与解密场景:

动态密钥:无法通过简单的阅读 JavaScript 直接获取加密密钥

非对称加密技术:使用 RSA / SM2 技术来加密通信数据以避免明文传输

案例:获取密钥和提交表单不在同一个请求中
这个场景可能初步听起来略微抽象,但是实际上却非常常见,我们使用基础的时序图将会很容易理解这个案例:我们首先请求/get-key接口获取一个密钥,然后开始用获取到的 KEY 加密表单,在 POST 中提交加密后的数据(可能并不携带密钥),服务器端可以通过服务器的密钥来解密数据

1698823491_6541fd433b50904ea6b1e.png!small?1698823491746

一般来说,这种加密方式并不是一个非常冷门的方式,反而很多网站会使用这种技术来防护自己的登录信息不会泄漏。为此,我们基于实际网站的前端加密混淆方式构建了一个非常经典的案例,来帮助大家掌握这些测试技术。我们在 Vulinbox 中可以获取到关于这个部分的靶场测试环境:

1698823507_6541fd532fcfde54b8a78.png!small?1698823507862

首先打开网站,抓包看一下通信流程发现网站请求完资源后会去加载一套证书:

1698823517_6541fd5d9e86cb01b30f4.png!small?1698823518152

因此我们需要去寻找 JavaScript 中的加密线索:

1698823527_6541fd678c7d00cbf6de5.png!small?1698823528314

那么基于此,加密的基本脉络就很清楚了,我们需要先发一个请求去服务端获取私钥用来解密签名的数据(来自于服务器),获取公钥来签名需要发送到后端的数据。但是实际上这还不够,我们知道有一些基本资料:

RSA-OAEP的最大加密长度取决于RSA的密钥长度和OAEP填充的长度。OAEP填充需要额外的空间,通常是两倍的哈希长度加上2(对于SHA-1和SHA-256,这将分别是42和66字节)。因此,对于一个n位的RSA密钥,最大的明文长度将是n/8减去OAEP填充的长度。

我们并不能直接使用 OAEP 去加密大段的数据,那这种情况下,我们知道 AES KEY 一般来说并不会太大,所以使用“混合加密(RSA-OAEP + AES)” 实际上是比较好的解决方案,否则我们就需要把大量数据进行切片传输,这实际上非常不符合常理。

那么我们继续从 JavaScript 中寻找线索:

1698823552_6541fd80b0bf694d4f1d9.png!small?1698823553612

捋清思路,准备动手
1697177464_6528df780aa558f22175c.png!small?1697177464022

在这个超复杂情况中,我们发现实际执行的时候,需要两个请求,这个操作恰好符合我们 Web Fuzzer 的请求序列的定义。

请求一:先执行GET /crypto/js/rsa/generator获取签名验证用的 RSA 密钥

使用数据提取功能,提取请求一中的公钥的 PublicKey,用来加密 KEY 和 IV

使用 AES 加密数据,并把加密后的 KEY 和 IV 一起发送到服务器

实际我们发现,这个结果处理起来也并不复杂,随机生成 KEY 和 IV 可以写死,这样用它加密真实数据即可,这个过程和我们在《渗透测试高级技巧(一)》中的操作非常类似,我们可以在热加载中很容易处理。

实际对我们来说,最大的工作量是把 KEY 和 IV 使用 RSA-OAEP 加密好,传输给服务器。这个步骤在 yaklang 中可以轻易使用codec.RSAEncryptWithOAEP(pemPublic, data)这个函数做到,因此我们的 Yaklang 热加载代码可以写成:

aesKey = “aaaaaaaaaaaaaaaa”
iv = “aaaaaaaaaaaa”
aesGCM = data => {
return codec.EncodeBase64(codec.AESGCMEncryptWithNonceSize12(aesKey, data, iv)~)
}
rsaOAEP_key = (pem) => {
return codec.EncodeBase64(codec.RSAEncryptWithOAEP(pem, aesKey)~)
}
rsaOAEP_iv = (pem) => {
return codec.EncodeBase64(codec.RSAEncryptWithOAEP(pem, iv)~)
}
对应的,我们的使用标签函数就可以是{{yak(rsaOAEP_key|{{params(publicKey)}})和{{yak(rsaOAEP_iv|{{params(publicKey)}})}},如果使用加密数据,而且我们的 AES KEY 是硬编码的,可以直接写成{{yak(aesGCM|…)}}`

到现在,基本核心功能有了非常好的实现,我们可以设置热加载代码并且完成我们的 Web Fuzzer 序列了。

首先我们在序列一中设置好数据提取器,数据提取器来提取服务端的 publicKey

请求一:获取签名(加密)用的公钥
1698823664_6541fdf0354007272c471.png!small?1698823664853

我们在第一个请求中,使用“数据提取器”添加一个配置项目,设置JQ(*)提取数据类型,范围设置成“响应体”,把提取字段名设置为publicKey,这样我们在后续的请求中,可以用{{param(publicKey)}}安全引用上一个请求中提取到的变量。

请求二:继承请求一中的密钥,加密请求中参数
1698825004_6542032c2f8d5f2bab3e8.png!small?1698825004662

我们在实际的发送的时候,发现 AES 密钥和 IV 可以硬编码,因此根据原数据包,我们把 JSON 中的关键字符串使用热加载 fuzztag 替换掉,形成如下效果:

{
“data”:“{{yak(aesGCM|abc)}}”,
“iv”:“aaaaaaaaaaaa”,
“encryptedIV”:“{{yak(rsaOAEP_iv|{{param(publicKey)}})}}”,
“encryptedKey”:“{{yak(rsaOAEP_key|{{param(publicKey)}})}}”
}
上述 Payload 中对应的热加载代码为:

aesKey = “aaaaaaaaaaaaaaaa”
iv = “aaaaaaaaaaaa”
aesGCM = data => {
return codec.EncodeBase64(codec.AESGCMEncryptWithNonceSize12(aesKey, data, iv)~)
}
rsaOAEP_key = (pem) => {
return codec.EncodeBase64(codec.RSAEncryptWithOAEP(pem, aesKey)~)
}
rsaOAEP_iv = (pem) => {
return codec.EncodeBase64(codec.RSAEncryptWithOAEP(pem, iv)~)
}

实际上就是把加密内容进行 Base64 写回原来数据包,并且 aes 和 iv 为了方便测试,已经写好了!

Wait a Minute,事情还没结束!
1698825130_654203aa79ec12e93d356.png!small?1698825131232

经过上述的操作,我们请求确实发送出去了,并且得到了正确的回应,但是我们似乎遇到了更大的问题:我们居然没有办法直接根据响应内容判断我们到底登陆成功了没有。

那么如何判断登陆成功了?我们发现服务端返回的信息中,包含了encryptedIV还有encryptedKey还有 data,大致直接可以推断出,返回的信息也被加密了。这也是一个非常棘手的问题

我们想要解决这种问题,那就需要找一个地方可以获取到相应的信息,并且可以加密解密。这里就需要进入我们本地技巧集的第二部分。

难点:解决 HTTP 响应加密问题
思路一:继续利用 Web Fuzzer 序列,添加第三个请求,处理第二个请求中响应的信息。

思路二:通过热加载afterRequest或mirrorHTTPFlow来编写代码解密响应中的信息

我们有了思路之后,可以尝试动手来做

使用 Fuzzer 序列来解密响应
类似 publicKey 的使用,我们可以在“请求一”中添加privateKey的提取配置,使用.privateKeyJQ 来提取初始化中的响应信息

1698825160_654203c8f11df56d85bb9.png!small?1698825161356

在第二个响应中,我们需要获取到响应的加密信息来解密响应中的数据,从而判断请求提交的真实结果,因此我们需要在请求二中设置提取encryptedIV / encryptedKey / data三个变量,通过数据提取器的配置:

1698825440_654204e0842986a0b04e0.png!small?1698825440898

1698825447_654204e7f28ebc7f05c54.png!small?1698825448379

可以很容易配置成功,接下来,通过第三个请求中使用热加载标签,执行这两个函数即可马上解密 RSA-OAEP 的内容。

oaepDec = (pem,data) => {
return string(codec.RSADecryptWithOAEP(pem, codec.DecodeBase64(data)))
}
dec = (key,data,iv) => {
dump(key, data, iv)
return string(codec.AESGCMDecryptWithNonceSize12(key, codec.DecodeBase64(data)~, iv)~)
}
相对来说,oaepDec 直接使用 PEM 和 Data 可以解密 KEY,和 IV,然后再使用 key 和 iv 去解密 data 中的内容最终就可以实现对响应的解密。

1698825482_6542050ad41968c9ac5eb.png!small?1698825483473

可以,非常棒,接下来我们就可以直接修改用户名和密码来进行爆破了对吧?利用这种特性,我们在第二个请求中,需要对用户名和密码进行爆破:

1698825503_6542051f081c3c2d993fb.png!small?1698825503593

在第二个请求的 web fuzzer 配置中,增加设置变量 user 和 pass,然后这两个变量实际上数据被加密,并且能把 encryptedIV 和 encryptedKey 设置正确。

我们配置 user 为admin|root配置 pass 为admin|666666|88888888|admin123|123456,实现了用户名和密码的爆破,总共应该有 2 * 5 个结果,点击序列执行查看效果:

1698825519_6542052f7cf461d93f74a.png!small?1698825519894

通过这种操作,我们实现了简单的“爆破功能”。真实情况是,这个场景已经非常复杂了,基本上属于“最严密防护”的那一类,在这种严密防护下,实际开发的过程也会非常艰辛,当然我们也很难实际真的遇到这种情况。但是如果这种情况我们可以解决,绝大多数弱于这种防护的策略我们都可以轻松突破。

优化流程:更好地处理响应中加密的部分
很多时候,响应的数据其实我们需要提炼一下信息或者认真处理一下加密的信息,不能总是使用序列来解决吧?那我们如何在一个 web fuzzer 流程中,处理好响应中的加密数据呢?其实并不是不可以解决的

Yaklang 的最新版本 1.2.7 及以上的版本,设置了一个热加载中的新 hook 函数帮助大家解决:

mirrorHTTPFlow可以给大家提供一种 “编程提炼 Web Fuzzer 请求和响应数据” 的新路径:

具体定义如下:

mirrorHTTPFlow = (reqBytes, rspBytes, params) => {
return {“key”: “value”}
}

// 使用案例如下
// 如果响应中包含 “Success”,就在提取的变量中输出一个“登陆”:“成功”的变量
mirrorHTTPFlow = (req, rsp, params) => {
data = {}
if string(rsp).Contains(“Success”) {
data[“登陆”] = “成功”
}
return data
}
我们可以在热加载中添加这个函数,来获取上下文,甚至执行对请求响应的解密。

所以我们可以整理一下,在上一个解决方案中,还可以做的更简单,直接使用热加载调制一个函数来实现加密解密。

mirrorHTTPFlow = (req, rsp, params) => {
// 获取私钥以解密响应数据
pem = params.privateKey

// 切割响应中的数据,作为 JSON 加载
_, body = poc.Split(rsp)
body = json.loads(body)

// 获取响应中的加密部分,data 为密文
// IV 和 KEY 分别是 AES-GCM 的加密密钥和 IV
data = body.data
ivEnc = body.encryptedIV
keyEnc = body.encryptedKey

obj = {}
// 使用 RSA-OAEP 解密 IV 和 KEY
iv = codec.RSADecryptWithOAEP(pem, codec.DecodeBase64(ivEnc))
key = codec.RSADecryptWithOAEP(pem, codec.DecodeBase64(keyEnc))

// 使用 AES-GCM 解密
obj[“data”] = codec.AESGCMDecryptWithNonceSize12(key, codec.DecodeBase64(data)~, iv)~
return obj
}
1698828419_654210832046bc895226a.png!small?1698828419824

通过我们新增加的mirrorHTTPFlow函数,我们同时处理请求,响应和序列变量三个部分,实现了对流量的解密,并且很方便的识别出我们的爆破到底成功没有。

总结
在本篇“高级技巧”中,我们明显和《渗透测试高级技巧(一)》中的验签有了非常明显的区分,我们大量使用了 Web Fuzzer 序列来实现有上下文关联的部分。并且在这个案例中,因为响应的加密,导致区分“爆破”结果变得十分困难,我们也给出了一些处理技巧和实际方案。

当然本篇内容的靶场和使用案例也并不是空想,而是来源于真实案例的总结和抽象。在实际使用中,加密算法和接口名称与当前案例略有差别,但是笔者相信,在完整实现过这个流程之后,你一定已经对 Web Fuzzer 序列的使用更加得心应手了。

CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

在这里插入图片描述

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

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

相关文章

无法创建 8192 MB 的匿名分页文件: 系统资源不足,无法完成请求的服务。

好久没用VMware Workstation,今天突然要用,发现所有的虚机在启动的时候提示都提示: 无法创建 XXXX MB 的匿名分页文件:页面文件太小,无法完成操作。 未能分配主内存。 模块"MainMem"启动失败。 未能启动…

[计算机网络实验]头歌 实验二 以太网帧、IP报文分析

目录 第1关:Wireshark基本使用入门 【实验目的】 【实验环境】 【本地主机、平台虚拟机之间数据传递】 wireshark基本用法】 1、wireshark主界面 2、抓取分组操作 3、Wireshark窗口功能 4、筛选分组操作 【实验操作】 ​编辑 第2关:Ethernet帧…

排序算法--冒泡排序

实现逻辑 ① 比较相邻的元素。如果第一个比第二个大,就交换他们两个。 ②对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。 ③针对所有的元素重复以上的步骤,除了最后一个。 ④…

怎么判断list是否为null

List<Entity> baseMess new ArrayList<>(); baseMess motiveService.getBaseMessage(machine.get(i),preDate,nowDate); System.out.println("获取Size"baseMess.size()); baseMess.removeIf(Objects::isNull); System.out.println("获取Size"…

NV080D语音芯片:让智能快递柜取件更便利

随着互联网的普及和电子商务的迅速发展&#xff0c;网购消费已经成为了越来越多人的选择。这也催生了一个庞大的“网购一族”&#xff0c;他们购买的各种商品会通过快递公司送到家门口。然而&#xff0c;收取快递往往也伴随着一系列问题。比如&#xff0c;派送时间和收件人取件…

使用Echarts.js绘制中国地图

使用Echarts.js绘制中国地图 一、页面效果 二、功能描述 ​ 1、展示中国所有省份&#xff0c;包括南海诸岛&#xff0c;确保领土完整&#xff0c;中国领土神圣且不可侵犯。 ​ 2、每个省份根据对应数据的不同渲染不同的颜色&#xff0c;根据数据从小到大&#xff0c;对应底部…

maven pom引入依赖不报红,但是项目Dependencies中没有引入jar包

前言 小编我将用CSDN记录软件开发求学之路上亲身所得与所学的心得与知识&#xff0c;有兴趣的小伙伴可以关注一下&#xff01; 也许一个人独行&#xff0c;可以走的很快&#xff0c;但是一群人结伴而行&#xff0c;才能走的更远&#xff01;让我们在成长的道路上互相学习&…

ROS2对比ROS1的一些变化与优势(全新安装ROS2以及编译错误处理)《1》

1、概述 我们在前面介绍的ROS&#xff0c;都是ROS1的版本&#xff0c;近期对机器狗进行学习的时候&#xff0c;发现版本是ROS2了&#xff0c;也发现平时习惯的一些命令都有了变化&#xff0c;改变还是挺大的&#xff0c;不过熟悉之后还是很习惯ROS2的写法。 ROS2不是在ROS1的基…

基于SSM的公司仓库管理系统(有报告)。Javaee项目

演示视频&#xff1a; 基于SSM的公司仓库管理系统&#xff08;有报告&#xff09;。Javaee项目 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring SpringMvc …

Sulfo-CY5 DBCO的荧光特点、激发发射-星戈瑞

**Sulfo-CY5 DBCO是一种近红外荧光标记探针&#xff0c;具有以下荧光特点&#xff1a; 激发波长&#xff1a;**Sulfo-CY5 DBCO的激发波长位于近红外区域&#xff0c;通常在650-670纳米之间。近红外光在生物体内具有较好的组织穿透性&#xff0c;能够减少组织自发荧光的干扰&…

ArkTS声明式开发范式

装饰器 用来装饰类、结构体、方法以及变量&#xff0c;赋予其特殊的含义&#xff0c;如上述示例中 Entry 、 Component 、 State 都是装饰器。 Component 表示这是个自定义组件&#xff1b; Entry 则表示这是个入口组件&#xff1b; State 表示组件中的状态变量&#xff0c;…

MySql表中添加emoji表情

共五处需要修改。 语句执行修改&#xff1a; ALTER TABLE xxxxx CONVERT TO CHARACTER SET utf8mb4;

全志R128芯片RTOS调试指南

RTOS 调试指南 此文档介绍 FreeRTOS 系统方案支持的常用软件调试方法&#xff0c;帮助相关开发人员快速高效地进行软件调试&#xff0c;提高解决软件问题的效率。 栈回溯 栈回溯是指获取程序的调用链信息&#xff0c;通过栈回溯信息&#xff0c;能帮助开发者快速理清程序执行…

线性代数 - 几何原理

目录 序言向量的定义线性组合、张成空间与向量基线性变换和矩阵线性复合变换与矩阵乘法三维空间的线性变换行列式矩阵的秩和逆矩阵维度变换点乘叉乘基变换特征值和特征向量抽象向量空间 序言 欢迎阅读这篇关于线性代数的文章。在这里&#xff0c;我们将从一个全新的角度去探索线…

NX二次开发UF_CAM_PREF_ask_logical_value 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_CAM_PREF_ask_logical_value Defined in: uf_cam_prefs.h int UF_CAM_PREF_ask_logical_value(UF_CAM_PREF_t pref, logical * value ) overview 概述 This function provides …

DC电源模块检测故障步骤有哪些

BOSHIDA DC电源模块检测故障步骤有哪些 DC电源模块检测故障步骤如下&#xff1a; 1. 检查输入电压&#xff1a;用万用表测量输入电压&#xff0c;确保其在规定范围内。 2. 检查输出电压&#xff1a;用万用表或示波器测量输出电压&#xff0c;确保其在规定范围内。 3. 检查输…

HPC 集群计算类型的注意事项

HPC 集群计算类型的注意事项 HPC 工作负载在 CPU &#xff0c;内存&#xff0c;网络和存储资源需求方面有不同的要求。 您可以从以下内容开始: 核心计数每个核心的内存网络带宽和等待时间处理器时钟速度 目标是选取返回最佳性价比的计算配置。 HPC 工作负载可以与单个核心作…

markdown常用命令说明,自己常用的,用到其他的再添加

对于要标红的字体 <font color"red">标签中的字会显示为红色</font> 之后的字不会再显示为红色注意: <font color"red">或者<font colorred>或者<font colorred>三种写法都可以

汽车智能座舱/智能驾驶SOC -1

看到华为&小康的 AITO问界M6、M7各种广告营销、宣传、测评、好评如潮水般席卷网络各APP平台。翻看了中信和海通对特斯拉M3和比亚迪元的拆解报告&#xff0c;也好奇华为的汽车芯片平台又能做出哪些新花样&#xff0c;下面是Mark开头&#xff0c;也学习下智能座舱和智能驾驶芯…