前言
本文主要记录常规JS加密方法的判断以及如何定位加密方法,并进行扣取执行的方法等,如果有错误的地方,还望指正,不喜勿喷;
在日常渗透测试中,当遇到登录接口时,通常会进行弱口令以及注入等方法进行安全测试,然而当目标站点对敏感参数进行加密后,会增加我们安全测试的难度,因此需要对前端JS进行分析,跟踪加密过程、判断加密方法、判断是否加盐,从而编写脚本对接口进行安全测试。
在Burp Suite中有一些HASH的算法以及其他大佬编写的加密插件,可以满足一部分测试需求,但对于JS中加密方法DES、AES、RSA以及一些自研加密方法还是需要自己动手调试找key等操作,因此我也去学了一下,以下内容算是学习记录。
快速判断加密/散列算法
1、通过密文判断
仅列出常用的,以下为密文123456的密文(未加盐的情况下):
//散列
MD5:e10adc3949ba59abbe56e057f20f883e
SHA1:7c4a8d09ca3762af61e59520943dc26494f8941b(40位)
SHA256:8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92(64位)
SHA512:
ba3253876aed6bc22d4a6ff53d8406c6ad864195ed144ab5c87621b6c233b548baeae6956df346ec8c17f5ea10f35ee3cbc514797ed7ddd3145464e2a0bab413(128位)
//编码
BASE64:MTIzNDU2
BASE32:GEZDGNBVGY======
Unicode:\u0031\u0032\u0033\u0034\u0035\u0036
//加密算法(DES/AES/RSA)一般密文无规律,常见在前端输出时会进行base64编码后在输出,对称和非对称都是16进制的形式,最大的字母只到了F那就说明可能是。
2、通过F12
访问站点F12,当提交数据时查看network加载资源,有时会看到加密方法,如下:
还有一些加密库jsencrypt、crypto-js等都可用做判断加密算法。
如何快速定位加密JS代码位置
1、搜索关键字
使用浏览器自带的search功能,定位的位置比较准确, 搜到的位置比较多, 要自己进行筛选, 容易搜不到;
在搜索一个对象时可以使用以下定义方法进行搜索,可以过滤掉一些不必要的结果:
var object
function object
object:
object :
object =
object= //代码没格式化之前的样子
其他关键字:encrypt、passwd、password、MD5、AES、DES,登录加密的参数名是什么就先搜参数名,搜不到在搜其他关键字,框架的js文件(jquery等)可以不用看,重点看自写的JS文件,定位到大概位置后下断点跟值查看;
2、XHR断点
代码运行时间轴:加载html--加载js --运行js初始化 --用户触发了某个事件--调用了某段js--明文数据--调用加密函数 --加密数据--send请求(XHR--send)
执行比较靠后,距离加密函数相对比较近,可以根据栈快速定位,但是非XHR发送的数据包是断不住的。
example:
http://www.example.com/adminlogin
将后面固定的路径 adminlogin,添加到XHR/fetch Breakpoints中,在提交请求,此时会被断住,就可以通过栈进行分析;
方法栈(从下往上运行),它遵从后进先出(LIFO——Last In First Out)原则,通过跟栈可以快速定位到加密点,找到密文和明文之间的代码,大概率为加密方法的位置。
3、dom事件断点
代码运行时间轴:用户输入明文 -》经过一些方法 -》加密函数 -》拼接封包 -》发包函数 -》浏览器的发包函数
定位的位置比较靠前,定位到的通常是用户输入的明文,需要跟值才能跟到;
在event Listeners中查看事件,找到登录事件(找不到时候可以通过删除事件,点击登录查看是否登录按钮有效进行排查找出事件),从后面指向的js文件跟进去,简单分析后在比较像加密的地方下断点,点击登录后跟值。
定位以及扣取案例
找了几个站发现都是基于加密库jsencrypt、crypto-js的案例不太好,这里只做练习,不在乎有没有意义,苦笑;
提取的时候通常会直接把整个js先提取到本地,然后再提取加密方法出来,然后本地运行缺什么在补什么,另外webpack 框架的提取方式和常规的还有一定区别。
需要注意的点:
作用域的问题
var _hex_md5; //将局部变量导出才能在全局使用
!(function(){
var j = {
hex_md5:function(){
console.log("1");
}
}
_hex_md5 = j; //导出
})() //自执行
提取代码完整
var class = {
name:"test"
md5:function(args) //不能光扣md5这部分需要把this一起提取出来才有用,var开始到最后才是一个整体
{
console.log(args)
}
}
RSA
RSA特征:
new JSEncrypt();
a.setPublicKey("key");
a.encrypt(passwd);
new rsa的加密对象
xxx.set设置publickey //设置公钥
xxx.调用加密方法(“明文”) //加密
以上三步是RSA加密必须的,跟值得时候也需要在这三个地方打上断点,扣代码运行也要这三点;
在登录时,查看network可以看到password参数是加密的
个人觉得XHR定位比较方便,但是这里可以看到type是document,因此无法使用XHR断点进行跟栈;
使用搜索进行定位,使用network左下的search搜索关键字encrypt,可以看到有五个结果,最后一个JS文件中的是我们想要拿到的;
点击169行跟到JS文件中查看,并在三个特征处下断点,再次点击登录跟值查看,可以看到在166行的时候还是明文,在173行就输出了密文,因此加密方法是处于166-173行,当然这里也能很清楚的看到e.encrypt(passwd)方法对明文进行加密,可以使用console调用这个方法尝试一下加密(只有在断住后才能使用console进行调用),可以看到加密成功。
那么这里已经很清晰了,e调用了encrypt进行加密,而e = ze{},我们这里把ze取出来就可以了,选中JSEncrypt,根据指向的文件进入js文件中;
进入jsencrypt.min.js后,查看,注意在var的前面还有空格,那就说明它是在一个大方法中,不出意外的话,我们把这个大方法抠出来,在按照上诉特征三个步骤去调用就可以了;
鼠标放到var上,然后一直往上滑,直到看到一个顶格的function,然后扣出来就可以了,另外正常的RSA代码在2000行左右;
大方法拿出来后在JS调试工具中运行,运行会提示navigator is not defined以及window is not defined,我们在首行增加以下代码即可:
navigator = {},window = this;
接下来构造调用函数,比较方便的是学着目标网站构造调用函数就行了,网站怎么写你就怎么写,不会错:
function rsaPass(passwd)
{
var e = new JSEncrypt(); //RSA三大步
e.setPublicKey("key");
return e.encrypt(passwd);
}
构造出来后我们发现此处还缺少key,我们在到网站的JS里去找一下看看对方是怎么传的,找到调用位置,可以看到170行,它的含义是将ID为"e"的HTML元素的值作为公钥,并将其设置为某个对象的公钥。
我们可以到前端HTML进行查找,搜索#e,定位到key
最后调用测试,RSA加密同一字符串时,每次生成的密文通常是不一样的,这是因为RSA加密算法中,每次使用的随机数都是不同的,而随机数会影响到加密过程中的一些计算,从而导致密文的差异。
DES
DES特征
(function () { // 定义一个立即执行函数
const message = "123456"; // 要加密的明文
const key = "ffffffffffff"; // 密钥
// 将密钥从字符串转换为字节数组
const keyBytes = CryptoJS.enc.Utf8.parse(key);
// 使用 DES 加密算法加密消息
const encrypted = CryptoJS.DES.encrypt(message, keyBytes, {
mode: CryptoJS.mode.ECB, // 使用 ECB 模式
padding: CryptoJS.pad.Pkcs7, // 使用 PKCS#7 填充方式
});
填充方式以及加密模式不一致也会导致密文不一致;
请求类型是XHR
admin 123456的密文
这里使用XHR方法进行定位,在XHR/fetch Breakpoints添加adminlogin路径,再次提交请求,此时会被断住
根据栈往下看,先找到密文,在找到明文,在第三个栈(querySessionAttr)找到了密文,第二个栈(login)找到了明文以及密文,还有一串字符,那么加密代码的位置大概可能在这之间,但是这里比较清晰,我们直接在57、58行下断点,然后跟进去看;
跟进encryptByDES后可以看到DES的特征以及key,实际上到这里就可以将key拿到其他的DES算法环境中进行加密测试,看看密文是否相同,相同基本上就没有在跟下去的必要了。
这里我将key拿出来后,在自己的环境进行加密,密文相同。
AES
特征:
function Encrypt(xxx){
var xxx = xxx.enc.Utf8.parse('key');
var xx = xxx.enc.Utf8.parse(xxx);
var xxx = xxx.AES.encrypt(xxx);
}
其他操作也可des差不多。
测试脚本
代码功底太差,写了几个渗透测试用的简单小脚本,需要自己先把目标网站的key拿出来在使用脚本,不同的场景需要对脚本进行微调,主要实现功能如下:
DES、AES、RSA批量加密脚本,DES、AES解密
AES、DES 配合sqlmap的SQL注入脚本
项目地址:https://github.com/Small-ears/DES-AES_SQL_Scripts
后记
加密库的JS代码,提取出来放到VSvode里面用node运行时候会各种报错,比如ASN1 is not defined,我在目标网站找了半年都没有找到ASN,在https://github.com/travist/jsencrypt里找到了,但是在一些JS调试工具里面却能运行,不会报ASN1 is not defined,可能是环境问题,js代码拿出来调试时候需要注意。
使用加密库的网站只需要找到加密的key以及确认加密方式、填充方式是否有IV,就可以在自己的环境中运行,一般使用加密库也不太会改代码;