一、网站分析
- 三年前的案例,我的原始文章
- 网站 ,如图我们直接点击标题进入到详情页,链接会发生跳转,且与我们在详情看到的链接,与在列表页看到的链接完全不一样,如果拿列表页的链接直接新建标签页打开的话,详情页也是403,
而只有触发点击的模式才能拿看到真实的详情链接
假详情链接
http://ggzy.zwfwb.tj.gov.cn:80/jyxxcggg/1025448.jhtml真详情链接
http://ggzy.zwfwb.tj.gov.cn/jyxxcggg/Hw5fFNS%5EhnOR3wD5T5hxxA.jhtml
二、定位监听
- 谷歌浏览器点中a标签
- 火狐浏览器有自带的event事件监听
三、熟悉AES-ECB
- 1、分析前,需要熟悉下aes加密是什么,熟悉的可能是md5加密,md5加密是哈希算法,不可逆不能从结果推出明文;
而aes是对称加密算法,区别之一可加密可解密,即可反推明文
- 2、
AES的ECB模式,只需要找到key密钥,就可以加密解密了
,在线调试AES加密解密
- 3、AES加密解密代码
四、调试分析
-
以谷歌浏览器为例子
-
开始添加断点,嗯~ 在定义变量这里打个断点试试,然后在点击列表链接,会发现js停在了断点处,然后咱们就一步一步的往下调试,会发现列表的url是如何加密的了,而其中最核心的加密算法,其实是用了CryptoJS的对称加密AES加密,并且使用的是ECB模式,Pkcs7填充,密钥在调试这部分的时候可以看到是何值
-
抠出js:将整个js文件全部复制下来,在console面板可直接运行,以下是将关键js扣出来并在新建js脚本运行后的结果,大功告成,下面我用python来还原
五、node运行js
- 前端定义的CryptoJS我并没有直接复制,而是通过本地的node直接导入CryptoJS库,此时只需要将关键代码扣出来即可;
- 本地需安装node环境,然后再安装crypto-js库:
npm install crypto-js -g
重要参数
:key是密钥;指定模式默认ECB模式;padding是用来填充数据的,如果需要加密的数据的字节码的长度不是块大小的整数倍就需要填充- 方式1:扣js,缺啥补啥
var CryptoJS = require('crypto-js'); var req = function(hh) { var s = "qnbyzzwmdgghmcnm"; var ee = "_blank"; var aa = hh.split("/"); var aaa = aa.length; var bbb = aa[aaa - 1].split('.'); var ccc = bbb[0]; var cccc = bbb[1]; var r = /^\+?[1-9][0-9]*$/; if (r.test(ccc) && cccc.indexOf('jhtml') != -1) { var srcs = CryptoJS.enc.Utf8.parse(ccc); var k = CryptoJS.enc.Utf8.parse(s); var en = CryptoJS.AES.encrypt(srcs, k, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }); var ddd = en.toString(); ddd = ddd.replace(/\//g, "^"); ddd = ddd.substring(0, ddd.length - 2); var bbbb = ddd + '.' + bbb[1]; aa[aaa - 1] = bbbb; var uuu = ''; for (i = 0; i < aaa; i++) { uuu += aa[i] + '/' } uuu = uuu.substring(0, uuu.length - 1); return uuu; } } console.log(req("http://ggzy.xzsp.tj.gov.cn:80/jyxxcggg/948547.jhtml"));
- 方式2:js自带的模块CryptoJS,加理解的逻辑
var CryptoJS = require("crypto-js"); var encrypt_req = function(key,text) { var l = CryptoJS.enc.Utf8.parse(text); var e = CryptoJS.enc.Utf8.parse(key); var a = CryptoJS.AES.encrypt(l, e, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }) return a.toString() // 此方式返回base64 // return a.ciphertext.toString() // 返回hex格式的密文 } // ECB模式加密base64 console.log(encrypt_req('qnbyzzwmdgghmcnm', '1025528'));
六、Python执行js
- python调用js三种方式:
- 要么用python现有的模块替换js相同的逻辑,即python还原
- 要么通过execjs/ py_mini_racer等执行
- 要么通过node部署服务开接口执行
- python的execjs库调用js
"""通过execjs执行js""" import execjs # pip install execjs from loguru import logger list_url = 'http://ggzy.zwfwb.tj.gov.cn:80/jyxxcgjg/1025528.jhtml' with open('./aes.js', "r", encoding='utf-8') as f: ctx = execjs.compile(f.read()) true_url = ctx.call('req', list_url) logger.info(f"详情的url:{list_url} >真实的url: {true_url}") #######分割线####### import execjs # pip install execjs from loguru import logger list_url = 'http://ggzy.zwfwb.tj.gov.cn:80/jyxxcgjg/1025528.jhtml' ccc = list_url.split('/')[-1].rstrip('.jhtml') with open('./aes.js', "r", encoding='utf-8') as f: ctx = execjs.compile(f.read()) suffix = ctx.call('encrypt_req', 'qnbyzzwmdgghmcnm', '1025528').replace('/', '^')[:-2] true_url = f"http://ggzy.zwfwb.tj.gov.cn:80/jyxxcgjg/{suffix}.jhtml" logger.info(f"详情的url:{list_url} >真实的url: {true_url}")
- python自带的aes库还原逻辑:
pip install pycryptodome
from loguru import logger from Crypto.Cipher import AES from Crypto.Util.Padding import pad import base64 def aes_ecb_encrypt_text(decrypt_text: str, key: str) -> str: """ 加密AES_ECB明文 :param decrypt_text: 待加密的字符串 :param key: 密钥 :return: 加密后的数据 """ aes2 = AES.new(key.encode('utf-8'), AES.MODE_ECB) encrypt_text = aes2.encrypt(pad(decrypt_text.encode('utf-8'), AES.block_size, style='pkcs7')) encrypt_text = str(base64.encodebytes(encrypt_text), encoding='utf-8').replace("\n", "") return encrypt_text list_url = 'http://ggzy.zwfwb.tj.gov.cn:80/jyxxcgjg/1025528.jhtml' ccc = list_url.split('/')[-1].rstrip('.jhtml') decrypt_str = ccc key_str = "qnbyzzwmdgghmcnm" encrypt_str = aes_ecb_encrypt_text(decrypt_str, key_str).replace('/', '^')[:-2] true_url = list_url.replace(decrypt_str, encrypt_str) logger.info(f"详情的url:{list_url} >真实的url: {true_url}")
七、知识星球-时光漫漫
- web端查看如下
- app端查看如下