本次的目标是某行购物网站的加密算法分析,网站如下
aHR0cHM6Ly93d3cuemtoLmNvbS8=
(注:该网站从整体上看来,是webpack加密,但是本文的实现方式,不是扣js代码也不补环境,因为最后分析此网站用的都是标准加密算法,所以单纯从算法的角度去实现)
进入网站后,任意搜索一个关键词,打开网络请求
我们任意找一个请求,这个应该是显示价格的请求,这里看到,发送的参数和返回的参数都是加密过的,也就是我们最后的请求需要加密发送后,再把返回的字符串解密最后才能得到结果,接下里,我们先搜索下关键词,看下是否有结果
搜索有发现后,我们依次查找,发现这个地方非常可疑,还有对请求头的加密过程,我们在这里打个断点
成功断下断点后,发现这里就是请求参数的加密,将商品的编号列表加密,对密码学有基础的小伙伴应该知道,使用ECB加密模式,Pkcs7填充方式的加密都是对称加密,一般在加密网站中用的最多的就是AES加密,经过堆栈查找后,确实是AES加密,这里我就不追溯这个查找流程了,这个加密我们用js去还原,代码如下:
const crypto = require('crypto');
function padString(data) {
const blockSize = 16;
const paddingSize = blockSize - (data.length % blockSize);
const padding = Buffer.alloc(paddingSize, paddingSize);
return Buffer.concat([Buffer.from(data, 'utf8'), padding]);
}
function aesEncrypt(plainText, key) {
const iv = Buffer.alloc(0); // ECB 模式不需要 IV 向量
const cipher = crypto.createCipheriv('aes-256-ecb', key, iv);
cipher.setAutoPadding(false);
const paddedText = padString(plainText);
let encrypted = cipher.update(paddedText, 'utf8', 'base64');
encrypted += cipher.final('base64');
return encrypted;
}
接着,我们看headers中x-akac和x-rgn的变化
可以发现,这里用到了JSEncrypt这个加密库,我们跟栈进去看,发现是rsa加密,公钥是固定不变的,x-rgn参数是一个时间戳,我们再次还原这段代码
window = {}
const jsencrypt = require('jsencrypt')
function rsaenc(N){
let s = new jsencrypt();
s.setPublicKey('MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCCglz4D9dnGsJbt5HIjSjuM5KqBheaRZVBczbAJ1s5lkeWoOZHA7pbTo8lph5qj9fuVnUErY+XnlpMzMp7GtmnLPioxkY7zlcvOTsK90wnBxCxKN94/OvAtX/f4QivCR80B5KZYlvj4aVUEONVNls9hP6cHvt85gPFro8oeTs4fwIDAQAB')
const encryptedData = s.encrypt(N)
return encryptedData
}
基本上请求的加密参数就齐活了,接着我们看返回参数的解密过程,.接着跟栈寻找
发现这里也是用AES加密,使用的秘钥和发送请求的秘钥是一样的,这就好办了,加密发送出去,得到再回来解密,解密的js代码如下
function aesDecrypt(encryptedData, key) {
const iv = Buffer.alloc(0); // ECB模式不需要IV向量
const decipher = crypto.createDecipheriv('aes-256-ecb', key, iv);
decipher.setAutoPadding(false);
let decrypted = decipher.update(encryptedData, 'base64', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
最后,我们用python来组装代码后,正确得到了请求**(这里需要注意,上述分析步骤中提到的固定秘钥可能和当前会话有关,不一定是固定的,本文只分析整体的加密步骤)**
成功!!