Bootstrap

JS逆向——裁判文书网(详细图文步骤)

最新测试结果:

注意!!!!:

2023-10-26日:根据最新测试,以下教程仍然可用,代码测试无问题,以下为测试部分截图:

登录:

 接口请求与解密:

 解密结果对比原站点:

==================以下为本次教学===================== 

写在前面:本篇博客很长,都是手把手一步一步的教程,跟着做就能出结果,文章总共分为以下三部分,我也会在3个部分中详细的说明步骤

(1)接口请求头参数验证

(2)接口响应数据DES3加密

(3)登录状态验证

一、接口请求头参数验证

1、以“民事案件”专栏为例,切换到“民事案件”专栏,点击翻页,观察控制台中的网络请求,rest.q4w结尾的接口及为数据接口。

2、点击打开请求详情,观察请求和响应

3、请求中数据校验主要是 pageId、ciphertext、__RequestVerificationToken参数

 4、请求头中校验的主要是Cookie参数,session及登录状态,这里先拿固定的cookie做调试,数据加密解密完成后再研究登录

29c2c8e61d991de6a2c4b98850e73da1.png

5、通过观察首页不同案件类型,即可直到pageId与每个案件类型,一一对应,抓取某一块对应写死即可。

0956caa2658987a68a1cc3340faf916f.png

6、在控制台中对接口rest.q4w打断点

 7、再次请求接口,观察调用栈中的请求

97b82aa32d98d8e4b0adbc622e0441d1.png

8、观察可知此处生成了第二个参数ciphertext

466d24104d9e79e4dd5bd4715d84c6fc.png

9、跟进cipher()方法,使用Python还原此方法即可得到ciphertext

1281b4b5621cbb967f0a6eeeb1751782.png

10、 全局搜索参数 __RequestVerificationToken,跟进第三个

7f816cd1714e70f982fb0ab09b4652d9.png

11、参数是页面中生成的固定值,由base.random(24)生成

d76250482c5f639e9356f3d75f21f954.png

12、查找此方法,在website.js里1006行,找到了代码

random: function(size) {
            var str = ""
              , arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
            for (var i = 0; i < size; i++) {
                str += arr[Math.round(Math.random() * (arr.length - 1))];
            }
            return str;
        }

算法很简单,就是先定义一个空字符串str作为未来的返回值,然后有一个从0-9,到a-z到A-Z的数组作为字典,再用一个for循环,循环size次,把str的内容变成自身再加上arr字典里面的一个随机下标,这个随机下标就是一个随机数乘上这个字典的长度-1再四舍五入。这里解释一下:

- Math.round();是四舍五入

- Math.random();是随机数

最后根据输入的参数长度,返回对应长度的随机字符串

这里都是随机,理论上不用此方法,自己使用python任意产生随机数量的随机数,然后切片就行。

13、到此请求中的三个校验参数pageIdciphertext__RequestVerificationToken 都可获得,接着对响应的加密数据进行分析

二、接口响应数据DES3加密

1、观察响应数据,发现result很长,应该是返回的数据加密后的,还有一个secretKey参数,推测此加密为对称加密 AES或DES,secretKey为密钥,用于解密

2、全局搜索关键字AES,无结果,再次搜索DES,即可发现相关代码

 3aa0c42dc68ef7522e0893b9c30f600a.png

3、跟进第一处后可发现,此处非常可疑,在ajax方法的响应成功(sussecc:)回调中进行了DES3.decrypt(data.result, data.secretKey),decrypt及解密,参数也刚好是 1 中提到的resultsecretKey,打上断点继续调试看看

279e0fc86f35f5ddf9ebcec9a291a17d.png

4、果不其然,断点后参数正是数据接口的响应

96a8d6d87798f5eabddc9fee68690e05.png

5、跟进DES3.decrypt()方法看看,这里可看到加密算法是DES3,还有偏移量iv

797a855f1d07a11d6a69e08b3cdb6429.png

 6、查看整个DES3的声明,可得到vi的获取方法为 $.WebSite.formatDate(new Date(), "yyyyMMdd")

f15cc08e72e9e1a5699f972c0e03640a.png

7、通过以上过程可得以下结论

- 加密模式:CBC

- 填充模式:pkcs7(pkcs7padding)

- iv偏移量:CryptoJS.enc.Utf8.parse(a || DES3.iv())

iv偏移量计算原理,对a || DES3.iv() 的结果CryptoJS.enc.Utf8.parse()

CryptoJS.enc.Utf8.parse()是用来从UTF8编码解析出原始字符串的

a || DES3.iv()是一个短路逻辑运算符,运算规则如下:

假若a || b 则

- 当 a == true 时,无论b是什么,都返回a

- 当 a == false 时,无论b是什么,都返回b

观察可知a是个参数,而在Ajax的请求中,却写的是:DES3.decrypt(data.result, data.secretKey),并没有传a,及a为undefined,所以iv的最后结果就是DES3.iv(),

也就是$.WebSite.formatDate(new Date(), "yyyyMMdd"),也就是当前日期的yyyyMMdd格式,看看这个formatDate方法也确实如此,例如今天是2022年9月20日,那么偏移量便是20220920

所以:

- 加密模式:CBC

- 填充模式:pkcs7(pkcs7padding)

- iv偏移量:当日的yyyyMMdd格式,例如:(20220920)

8、通过已知参数对加密结果在线解密,看看是否能得到正确结果,在线解密网址 http://tool.chacuo.net/crypt3des 输入参数 ,大家可用以下参数自行体验解密

3DES加密模式:CBC

填充:pkcs7padding

密码:izsGver6BoK18TE7I90zGvly

偏移量:20220920

输出:base64

字符集:utf8编码

待加密、解密的文本:因数据太长,应外单独存放 【腾讯文档】裁判文书响应体

解密结果如下:

9、到此加密解密已分析完毕,用python还原CBC解密,或者nodejs调用DES3.encrypt()解密即可。

三、登录状态验证

1、通过(1)和(2)的研究分析,已经可以正常请求接口和解密接口的数据,但是在最开始查看此网站时,发现此站是需要登录的,而在(1).4中分析也知请求头中需要验证Cookie,也就是登录后由服务器返回的session,可从登录接口开始进行分析。

2、在新打开的登录窗口中,输入账号,然后随便输入错误的密码,点击登录,开始调试

 3、点击登录以后调用了login接口,并返回密码错误

8cc3749a0a22f6f585024e7c7f89a176.png

4、点击查看接口信息,可发现在表单数据中,username是输入的手机号,而password则是经过加密后的字符串

5、 对login接口打断点,观察调用栈的每一步,查找密码加密的地方

ab8d317d8cdebf368dcf2a903b16ec3f.png

6、遗憾的是,经过层层调试,在调用栈的最底层,密码已经是加密后的状态。推测密码加密与登录并不是同步进行的,而是在执行登录之前,密码已经经过了加密;

7、按照上述思路,首先全局搜索password,尝试找加密的地方,经过漫长的调试过程,在main.js中发现了很多校验密码的代码

 8、第一行跟进后可发现对密码进行了一次编码encodePassword(),查看方法内容,有明显的RSA标志setPublickey,是典型的RSA加密,打上断点改下密码试试、

383607ca2df3835c08063bba67b0e47f.png

9、参数e与密码框中的密码一样,输出返回值及是加密后的字符串

48d22a94323fc5f334830fe54fdbba19.png

 10、python还原加密方法,或nodejs调用该方法即可

 aca95d5e235d36763ea88768ec60736c.png

11、由于登录后窗口跳转到主页,此处切换为fiddler抓包。先请求登录接口,可看到登录成功后,响应体中向Cookie中写入了HOLDONKEY=MjAyMDc3NmMtZGQwMS00MjM1LWE4NDItZGUyNjc5NzNhMzE1; Path=/; Secure; HttpOnly; SameSite=None

60d7fbe5c3e1ef13d442ba470c5d66cc.png

 12、登录成功后,在请求列表中的login下有个302跳转请求

我们先观察下302请求,发现请求体中带有上一步返回设置的cookie信息HOLDONKEY,以及响应头的相关信息

再看其重定向后的200请求,发现其请求中的cookie参数不再是HOLDONKEY了,而变成SESSION,但是在前两步都未曾设置过Cookie中的SESSION值,那这个SESSION是哪里来的?

 13、再思考这个问题中,又想到302请求的url又是哪里来的?往上继续翻找fiddler的请求列表,发现在login请求之前还有个/tongyiLogin/authorize 接口,返回值正是302请求的url,并且在响应头中设置了Cookie中的session值 SESSION=47a396b0-cc8e-4ffd-a841-57ed0c8a0ca7; Path=/; HttpOnly

30651cb5890e0082c54b6bd842e1036d.png

14、推测其登录权限校验逻辑如下

-请求/tongyiLogin/authorize接口,获取一个url并在Cookie中设置SESSION的值

-请求登录接口,获取登录后的Cookie值HOLDONKEY

-在Cookie中设置SESSION 和HOLDONKEY ,去请求第一步返回的url(其中302携带HOLDONKEY,跳转的200携带SESSION)对SESSION进行提权操作

15、根据以上逻辑还原登录过程,并保存SESSION的值

c41b9bdf7a603ed263fcdd4a1a880909.jpeg

16、使用保存的SESSION,+校验参数 请求接口,正确响应,并对结果解密,得到最终数据

17、至此,参数校验+结果解密+登录权限校验 完成

有问题欢迎评论、私信咨询。

;