登陆加密
首先进入登陆页面,输入好登陆信息后点击登陆,查看f12,发现表单提交的密码被加密,多次进行提交,发现结果每次都不同,说明可能是rsa des等非对称加密
切换至源代码板块,添加xhr断点
找到断点位置数据
向上跟栈,找到请求位置,发现依然被加密
继续跟栈,发现在a函数中传入了原始的密码
在控制台中构造一个promise运行一下试试,果然这个a函数就是我们的加密函数
继续跟栈,发现了一个可疑函数
执行一下,果真拿到了数据
点进去后,很明显这段代码使用了RSA公钥加密算法来加密密码。具体操作是使用RSA公钥加密传入的密码 e
,然后 encodeURIComponent
对加密后的字符串进行URL编码。
我们可以使用python进行一下改写
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64
import urllib.parse
def encode_password(password):
public_key = """-----BEGIN PUBLIC KEY-----
此处为密钥
-----END PUBLIC KEY-----"""
rsa_key = RSA.import_key(public_key)
cipher = PKCS1_v1_5.new(rsa_key)
encrypted_password = cipher.encrypt(password.encode('utf-8'))
encoded_password = base64.b64encode(encrypted_password).decode('utf-8')
return urllib.parse.quote(encoded_password)
验证参数正确,登陆逆向到此完成
SESSION的获取
查看一下登陆接口返回的内容,在set-cookie字段中返回了HOLDONKEY,然而这并不是我们需要的参数
实际上,我们在请求具体的文书数据时,能够发现实际上是携带一个SESSION的cookie来作为鉴权参数的,然而,在/api/login
接口中,并没有返回相应的参数,我们将SESSION
的值进行搜索,检索结果中仅在/tongyiLogin/authorize
有一个Set-Cookie
,那么可以说明这个cookie的获取就是这个接口
请求接口后,除了返回了一个SESSION
外,还返回了一条url,直接使用接口返回的SESSION
是无法请求数据的
查看请求流程,返现这条url进行了一次302重定向,在整个请求过程中用到了HOLDONKEY
和SESSION
,那么这个可能就是我们的用户SESSION
激活接口了
编写python代码即可实现登陆
注:在请求数据时,有可能被waf拦截,这时除了SESSION
还需要携带wzws_cid
数据接口加密分析
响应体解密
发起列表页请求,查看请求url和参数
hook一下xhrhttps://wenshu.court.gov.cn/website/parse/rest.q4w
,向上寻找了,在getData
中便找到了我们需要的参数
很明显这里就是我们要找的响应体加密方式了
不过,还有另一个问题,虽然我们知道了加密方式,但是3DES除了明文和密钥以外,还需要以下三个参数
- 初始化向量(IV):在使用某些模式(如CBC模式)时,需要一个初始化向量来增加加密的随机性。
- 填充模式(padding):在不足某些位数时的填充方式
- 加密模式(Mode of Operation):常见的模式包括ECB(Electronic Codebook)和CBC(Cipher Block Chaining)。不同的模式会影响加密和解密的过程。
直接寻找太麻烦了,我们可以通过重写CryptoJS.TripleDES.encrypt
方法来hook传入的密钥等,直接定位加密的位置
// 保存原始的CryptoJS.TripleDES.encrypt方法
const originalEncrypt = CryptoJS.TripleDES.encrypt;
// 重写CryptoJS.TripleDES.encrypt方法
CryptoJS.TripleDES.encrypt = function() {
// 打印传入的参数
console.log('CryptoJS.TripleDES.encrypt called with arguments:', arguments);
// 打断点
debugger;
// 调用原始的encrypt方法
return originalEncrypt.apply(this, arguments);
};
用这种方式我们一下就找到了加密的位置和参数
由此我们可以得知
- 加密模式:CBC
- 填充模式:pkcs7(pkcs7padding)
- iv:CryptoJS.enc.Utf8.parse(a || DES3.iv())
再继续追寻一下iv的值,即a || DES3.iv()
,此处分析过程非常简单,直接放结论
最后可以得出,iv的值为当日日期,最后我们编写一下python代码解密响应
# 对响应中的result进行解密
def result_decode(crypto_text, key):
text = base64.b64decode(crypto_text)
des3 = DES3.new(key, mode=DES3.MODE_CBC, iv=get_iv())
result_hex: bytes = des3.decrypt(text)
result_str = result_hex.decode("utf-8")
return result_str
请求体加密
回顾一下请求体,我们只需要找出ciphertext的加密方式,因此最简便的方式还是回到刚刚的hook方式上,从xhr一层一层逐级向上寻找
依旧是老样子跟栈寻找,找到了疑似的生成函数
进入cipher所在的js文件,加密方式一目了然
function cipher() {
var date = new Date();
var timestamp = date.getTime().toString();
var salt = $.WebSite.random(24);
var year = date.getFullYear().toString();
var month = (date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date
.getMonth()).toString();
var day = (date.getDate() < 10 ? "0" + date.getDate() : date.getDate())
.toString();
var iv = year + month + day;
var enc = DES3.encrypt(timestamp, salt, iv).toString();
var str = salt + iv + enc;
var ciphertext = strTobinary(str);
return ciphertext;
}
function strTobinary(str) {
var result = [];
var list = str.split("");
for (var i = 0; i < list.length; i++) {
if (i != 0) {
result.push(" ");
}
var item = list[i];
var binaryStr = item.charCodeAt().toString(2);
result.push(binaryStr);
};
return result.join("");
}
cipher
函数通过3des生成一个加密的密文,并将其转换为二进制表示。strTobinary
函数将一个字符串转换为其二进制表示。
获取当前时间戳:
var date = new Date(); var timestamp = date.getTime().toString();
获取当前的时间戳,并将其转换为字符串。
生成随机盐值:
var salt = $.WebSite.random(24);
生成一个24字符长的随机盐值。
获取当前日期的年、月、日并生成初始化向量(IV):
var iv = year + month + day;
使用3DES加密:
var enc = DES3.encrypt(timestamp, salt, iv).toString();
使用3DES算法对时间戳进行加密,使用盐值作为密钥,IV作为初始化向量。将加密结果转换为字符串。
拼接最终的密文并转换为二进制表示:
var str = salt + iv + enc; var ciphertext = strTobinary(str); return ciphertext;
调用
strTobinary
函数,将拼接后的字符串转换为二进制表示,并返回结果。
而剩下的__RequestVerificationToken
经校验来自请求网站固定回传的值,并未进行校验,所以无需提交,至此我们的分析就结束了
wzws_cid怎么获取呢
不携带参数情况下,关闭重定向请求首页,就能获取到了