前言
现在前端开发为了提高爬虫的难度及加强安全性,都会在数据包提交前进行加密,最典型的就是传参加密,相信大家在测试的时候都遇到过,那么我们在抓取数据包并修改之后,修改之后的参数无法通过后端程序数据完整性的校验,就无法进行进一步测试。如果我们逆向解析出加密的过程,就可以模拟出相同的密文,通过后端接口的校验。
最近由于工作需要,在搜索资料的时候,学到了很多爬虫大佬们的关于JS逆向、APK逆向、代码分析等方面的经验和技巧,后续会分部分记录并总结下来。
0x01 常见加密算法
比较简单的base64、hex等这些编码就不再说了。
1.1 对称加密
- 常用算法:DES、DES3、AES
- 根据密钥长度不同又分为:AES-128、AES-192、AES-256
- 其中AES-192和AES-256在Java中使用需获取无政策限制权限文件
- 加密/解密使用相同的密钥
- 加密和解密的过程是可逆的
1.2 非对称加密
- 常用算法:RSA
- 使用公钥加密,使用私钥解密
- 公钥是公开的,私钥保密
- 加密处理安全,但是性能极差,单次加密长度有限制
- RSA既可用于数据交换,也可用于数据校验
- 数据校验通常结合消息摘要算法 MD5withRSA 等
两种加密算法常见结合套路:1、随机生成密钥2、密钥用于AES/DES/3DES加密数据3、RSA对密钥加密4、提交加密后的密钥和加密后的数据给服务器
1.3 信息摘要算法/签名算法
- 常用算法:MD5、HMAC(HmacMD5、HmacSHA1、HmacSHA256)、SHA(SHA1、SHA256、SHA512)
- 不管明文多长,散列后的密文定长
- 明文不一样,散列后结果一定不一样
- 散列后的密文不可逆
- 一般用于校验数据完整性、签名 sign
- 由于密文不可逆,所以后台无法还原,也就是说他要验证,会在后台以跟前台一样的方式去重新签名一遍。也就是说他会把源数据和签名后的值一起提交到后台。所以我们要保证在签名时候的数据和提交上去的源数据一致,这种算法特喜欢在内部加入时间戳
0x02 JS逆向流程
以登录为例的基本流程:
- 如果网页有跳转,必须勾选
preserve log
(F12-Network)防止丢包 - 看一下有没有框架,右键查看框架源代码(弹出式登陆界面)
- 登陆尽量使用错误密码,防止跳转
- 查看关键登陆包,分析哪些参数是加密的
- 使用别的浏览器分析哪些参数是固定的值
- 初步猜测加密的方法
- 搜索
- 直接搜索参数,比如:
pwd=
,pwd =
,pwd:
,pwd :
- 密码框地方右键 检查 查看 id name type
- 直接搜索参数,比如:
- 找到加密的地方(重点)
- 进行代码调试
- 找出所有的加密代码
- 从最后一步开始写起,缺啥找啥
- 如果找的是函数的话,search 要带上 function xxx
- 如果看到加密的地方有个类,并且之后是用 prototype 把方法加在原生对象上的话,要把 所有加在原生对象上的方法都找出来
- 函数找多了没关系,只要不报错不会影响结果,但是不能找少了
0x03 实例操作
打开网站,抓包
修改个数据,比如修改num为1000
有些杠精该说了:我渗透测试改这有毛用。 是没用,只是以此为例。。。
可以看到,修改过参数数据后,后端数据校验之后不合法,所以没有返回数据。
打开测试网站 -> F12控制台 -> 切换至 XHR
这里我们看下需要做的有什么?
- 请求中有token(t明显是时间戳),token和数据不匹配后端不返回数据
- 返回的数据是加密的,需要解密
接下来我们就定位具体的加密函数和解密函数。
按照上面提到的流程步骤
打开控制台 -> source ->搜索
搜索加密参数名 token
根据搜索结果的文件名判断,基本上就是第二个文件,点击打开
token的生成代码
var token = md5(String(page) String(num) String(timestamp));
设置断点,刷新
成功进入断点,没毛病了
根据加密函数,编写脚本
可以看到,生成的token和URL中的一致,至此,加密部分完成。
解密部分同样的道理,搜索返回包中的参数,直接搜索list发现有点多,不太好观察,还有一种方法
可以看到数据部分html的id为ip-list
,再次搜索
成功找到解密数据包的代码
设置断点,进一步确认
没毛病,可以看到decode_str
后就开始出现我们需要的明文数据了,所以这里的 decode_str 就是我们要的解密方法。剩下就是分析代码,编写解密脚本了。
function decode_str(scHZjLUh1)
{ scHZjLUh1 = Base64["x64x65x63x6fx64x65"](scHZjLUh1);
key = 'x6ex79x6cx6fx6ex65x72';
len = key["x6cx65x6ex67x74x68"];
code = '';
for (i = 0; i < scHZjLUh1["x6cx65x6ex67x74x68"]; i )
{ var coeFYlqUm2 = i % len;
code = window["x53x74x72x69x6ex67"]["x66x72x6fx6dx43x68x61x72x43x6fx64x65"](scHZjLUh1["x63x68x61x72x43x6fx64x65x41x74"](i) ^ key["x63x68x61x72x43x6fx64x65x41x74"](coeFYlqUm2)) }
return Base64["x64x65x63x6fx64x65"](code)}
先运行下看看
报错,提示Base64
未定义,设置断点,找到Base64
的具体代码
复制粘贴进代码,再次运行
艹,提示Windows未定义,根据流程,缺啥补啥,debug,找对应的值
可以看到分别对应的是String
和fromCharCode
那就是调用了String.fromCharCode
方法了,替换掉,再次运行
bingo~
成功解密获取到明文数据。
0x04 Python实现加密方法合集
关于上述第一部分的常见加密算法,GitHub有对应的仓库,直接可以用的
GitHub:https://github.com/dhfjcuff/R-A-M-D-D3-S-M-H/
代码语言:javascript复制# -*- coding:utf-8 -*-import base64import rsafrom Crypto.Cipher import AESfrom Crypto.PublicKey import RSAfrom pyDes import des, CBC, PAD_PKCS5from Crypto.Cipher import DES3import hashlibimport hmac
class USE_AES: """ AES 除了MODE_SIV模式key长度为:32, 48, or 64, 其余key长度为16, 24 or 32 详细见AES内部文档 CBC模式传入iv参数 本例使用常用的ECB模式 """
def __init__(self, key): if len(key) > 32: key = key[:32] self.key = self.to_16(key)
def to_16(self, key): """ 转为16倍数的bytes数据 :param key: :return: """ key = bytes(key, encoding="utf8") while len(key) % 16 != 0: key = b'