一、CDU-Lib-Robot项目实现过程

前言

​ 在某个夜晚,图书馆回来的ahao,突然有了新的idea:能不能实现图书馆座位系统全自动预约和签到签退呢?于是回到寝室便开始构思,一直到深夜。。。。。。 最后的结果是:ahao成功失眠了,这里省略十万个'cnm'又失眠了。

不过还是没有丝毫影响到第二天ahao,7点30起床,扛上我的小新pro去图书馆开始各种试探。

耗时三天的CDU-Lib-Robot项目的构建由此拉开序幕。。。。。。。。。。。。

项目地址:

GitHub链接: https://github.com/ahaox/CDU-Lib-Robot

Gitee链接: https://gitee.com/ahaox/CDU-Lib-Robot

废话不多说,切入正题。

1. 使用到的工具

小新13pro + Burp Suit + 火狐浏览器 + PyCharm + Charles

2. 实现思路

登录VPN 获取到Cookie ——> 携带VPN的Cookie访问图书馆座位预约系统 和 签到系统 ——> 构造各种需要的请求参数 ——> 座位预约,签到,签退

二、抓包分析

1. 校园VPN系统

因为图书馆的座位预约系统和签到系统,必须要使用校园网才能访问,非校园网是不能访问的。

好在学校提供的专门的VPN服务,即通过web端的VPN登录以后,可以访问校内资源(图书馆座位系统 和 签到系统)。

先看看学校的VPN系统,盲猜,VPN登录成功后,访问校园内的所有网站都会携带上 认证后返回的Cookie。

image-20210601214506227

①开始抓包

image-20210601214801999

还没有登录,还看不出哪个Cookie有用。

② 开始VPN登录抓包

好像返回信息,也没啥有用的,不过有注意到,请求头Cookie中携带了一个 TWFID,返回的XML中也有TwfID,而且两个的值一模一样。好奇心开始产生,这个TwfID 应该就是我们需要的VPN认证后的Cookie了。

image-20210603233826366

继续分析, 一般登录提交都是POST方式,但是我们刚刚登录抓的包却是GET方式,Why?这是不是感觉非常奇怪。

我想一定是姿势不对,换个姿势。哈哈哈,火狐浏览器的开发工具登上分析舞台。

打开开发工具里的Network,重新登录,果然发现了唯一个POST提交方式,

image-20210601220027653

点开瞧一瞧。一看请求头,又是这个TWFID,我的疑惑似乎已经快要落实了。

image-20210601220234770

继续看请求表单,看到svpn_name是学号,但是这个randcode哪里来的??还有这个密码怎么这么长???

根据多年实战经验,这个svpn_randcodesvpn_password 是在POST提交 之前获取到的。

image-20210601220412975

看看响应回来的内容,又看到这个TwfID,还是和之前一模一样,现在已经没有疑惑了,这个TwfID就是我们要的VPN认证标识,访问图书馆座位预约系统 和 签到系统。不需要校园网也能访问了。

现在已经分析出了这个TwfID的重要性了。下面开始用它去访问图书馆座位预约系统。

image-20210601224121923

③ 使用TwfID 访问图书馆

由于VPN有在线时限,又要重新获取新的TwfID,此时的TwfID是重新VPN认证后的Cookie,完美访问成功。

image-20210601231932301

不携带TwfID访问,

image-20210603233858268

携带非VPN认证后获取的TwfID,

image-20210603233913331

已经10000%确定,我们就是需要它。

1.1 继续深入分析VPN系统。

为什么使用Burp suit抓不到这个登录的POST提交呢???根据前面我们登录VPN抓的数据包是GET方式,而且发送后返回的XML内容如下,仔细一读,发现里面存在:CSRF_RAND_CODERSA_ENCRYPT_KEYRSA_ENCRYPT_EXP,还有一个:AES128-SHA。 看到AES128-SHA这个加密算法,我已经开始怀疑,POST提交的那个svpn_password 是POST执行之前在前端通过 该 加密算法加密后得到的 加密密码。

再一看, RSA_ENCRYPT_KEY : RSA加密密匙??? RSA_ENCRYPT_EXP: RSA加密扩展????

现在就非常确定就是 POST请求提交之前,在前端完成加密,组装到POST的表单中的。

image-20210603233928065

<?xml version="1.0" encoding="utf-8"?>
<Auth>
<ErrorCode>1</ErrorCode>
<Message>login auth success</Message>
<CSRF_RAND_CODE>1891159946</CSRF_RAND_CODE>
<StartAuth>1</StartAuth> <!-- return next auth type -->
<clientRunMode>0</clientRunMode>
<TwfID>21381bb00650c40a</TwfID> <!-- twfid from server -->
<RndImg>0</RndImg> <!-- image check -->

<SSLCipherSuite>
	<EC>AES128-SHA</EC>
</SSLCipherSuite>
<RSA_ENCRYPT_KEY>EBAC264AB6DC117778018C5D07AFD2C5656C47C0B18840B99313788B68D85CED763BFDB8B167E90F33627FA68CFA505584F684110795F0A3FEF46C085922EF2967DFF47AD86996175B1887309051F31B86B5083B570872429007CEFECF4CC1E87038FCD926CAD4E43CB1D898EE28E16A172FBE021D681E88FE752044EB31B1CA08EACFAD8C68C9D84C0975B7DC7AD41D0E51C95EB36E660E55D15BF82E93D17FFA8DFA1704F0BD2F45A832D7E79EA59C646414B26BF873B9116D59BA4F9A321878F40EB98FB3458E5B5CCCE15E04A8D8E939A2634B456F57FAD5B731F15C4FB44189E32DEE5655E9B8EA17D87B13A0581D30D48BC86BB1A44CA39A2DD846FA57</RSA_ENCRYPT_KEY>
<RSA_ENCRYPT_EXP>65537</RSA_ENCRYPT_EXP>

</Auth>

接下来就无限的尝试。在图书管坐了早上 + 一下午的我,开始尝试另外一种姿势——使用火狐开发者工具的 js调试模式。

image-20210602232749503

打开浏览器的调试界面,查看了下js文件,发现这个auth_psw.js 有点可疑,问号三连???它要验证密码?

点进去看看,果不其然,就是它,一定就是它,在看看流经BrupSuit的数据包,仔细一对比,登录POST提交的地址。就是它!!!熟悉 Ajax 和 Axios 的我,一眼看到这个 data: e ,即异步请求发送到目的path的数据。那么这个e里面肯定就是携带的 svpn_randcodesvpn_passwordsvpn_name等表单数据了。下面继续读这个js文件。

image-20210602234034785

image-20210602233528166

我去这么多e,读了半天,应该是混淆代码的,读了个大概,盲猜逻辑应该是这样的,先判断randCode是否存在,密码是否加密,如果这些没有,就....... 有点懵啊。不过看到最后也就是图中三个红框,瞬间又兴奋了。这就是表单提交的字段啊。后面分别对应,u, e, t, s 这四个变量,重点是ut, 接下来就开始 打断点,开始登录调试吧。

image-20210602233858401

哎呀,姿势不对,到这里已经被RSA加密了。换个姿势继续肝。

image-20210602235511276

又看了以下其他目录下的js,发现这个rsa.js, RSA???纳尼?这丫太可疑了,读了代码,发现有个函数RSAEncrypt 翻译为RSA加密, 老规矩,在这里打个断点,重新登录,

image-20210602235600385

image-20210602235756431

经过rsa.js里的断点后,返回到的是一个叫common.js的文件,而且还发现,监控的变量e里面的 id属性是我输入的密码!!!阿这,我感觉高潮就快到了丫, 还有一个extraCode???什么鬼。

再一看调用rsa.js的这个函数里面,new 了一个 RSAKey,而这RSAkey 就是rsa.js里面的函数。那这个s.encryptID 这个函数一定有料!!!浏览器里不太好读代码,把这个函数搞到PyCharm里面自动整理一下格式。

image-20210603000354379

image-20210603231817347

1.2 加密入口函数分析

哈哈哈,读完代码,坐在图书馆肝了一早上的我整个人都开心了起来。不过还是要吐槽一下,写前端的程序员就这么喜欢用三元运算符 X?A:B吗?现在我们来捋一捋这个加密逻辑。

image-20210603232919158

首先new了一个RSAKey,也就是新建一个RSA算法对象(第二行),然后就返回很多数据,我们一个一个的看。

n = '65537' || (o = SF.setting.getGlobal(
    [KEY_GLOBAL_ENCRYPT_EXP, KEY_GLOBAL_ENCRYPT_KEY, KEY_GLOBAL_EC_KEY],
    [0, ''])) [0],

||后面的函数是去获取全局变量,有没有很熟悉呢,没错,ENCRY_EXP, ENCRY_KEY, 在最开始我们登录抓包的时候,get了一次响应的xml中有这两个。

image-20210603233758226

再看下面这句,

e.type === c.ENCRYPT_TYPE_PSW && e.extraCode ? e.id = e.id + '_' + e.extraCode : r = o[2],

就是判断e.extraCode 是否存在,如果存在就执行 e.id = e.id + '_' + e.extraCode 即把e.id 拼接字符串加上这个extraCode, 眼前一亮,这个不就是上面在浏览器调试的时候看到的吗,id恰好是我的密码,仿佛上帝为我打开了一扇窗,那这句就是将我的密码和这个随机码使用下划线进行拼接。并替换掉原来的e.id

image-20210603234335819

继续看下面一句

r && (s.setPublic(r, i), t = s.encrypt(e.id)),

调用加密算法的关键语句了,先调用了setPublic, 然后再调用encrypt加密 e.id (我的密码和随机码拼接后的),那么传的r,i这两个参数又是什么呢?调用加密算法后加密的结果是返回给t的,重新分析创建RSAKey之后的代码。

n = '65537' || (o = SF.setting.getGlobal(
    [KEY_GLOBAL_ENCRYPT_EXP,  KEY_GLOBAL_ENCRYPT_KEY, KEY_GLOBAL_EC_KEY],
    [0, ''])) [0],

这里o明显是个数组,而且第一个元素就是KEY_GLOBAL_ENCRYPT_EXP的值,而这个值我们抓包的时候看到它是等于:65537的,那么就明白了,如果 o 这个数据中的第一个元素KEY_GLOBAL_ENCRYPT_EXP不存在,那么就将 65537赋值给n,但是我们现在已经明确了KEY_GLOBAL_ENCRYPT_EXP存在并且为65537,

r = o[1], i = parseInt(n, 0).toString(c.ENCRYPT_LENGTH)

接下来这句就是把KEY_GLOBAL_ENCRYPT_KEY 赋值给 r。 i是通过这个函数计算出来的。我们先不用管它怎么计算的,直接浏览器调试看它的值,它的值为 "10001" , 它应该是个定值,抱着怀疑的态度,我又重新登录调试,果然没错。

image-20210603235842201

到此,我们已经分析出了加密密码的全部过程了。

突然想起,这个e.extraCode怎么来的呢?现在就差它了。

继续分析。。。。。。

image-20210604230939260

经过多次调试发现,每次登录之前都要去 请求这个 /login_auth.csp?apiversion=1

查看响应回来的xml,惊喜万分,,,,因为这里的CSRF_RAND_CODE 的值就是e.extraCode。TwfID就是之前分析出来的这次登录请求携带的Cookie。

image-20210604231414162

1.3 总结

现在VPN的登录完全摸透了。下面总结一下登录VPN的流程。

输入账号密码登录 ——> 携带TwfID,GET请求/login_auth.csp?apiversion=1 -——> 响应随机码extraCode, 请求携带的TwfID,RSA加密密匙RSA_ENCRYPT_KEY, 加密扩展RSA_ENCRYPT_EXP ——> 使用extraCode拼接密码,调用rsa.js加密算法,传入加密密匙,加密扩展,以及拼接好的密码字符串 ——> 完成加密,将加密密码传给 POST登录请求函数 ——> 登录成功 ——> TwfID登录状态生效。

2. 图书馆座位系统

2.1 登录分析

图书管登录,就没玩得那么花了,直接密码学号登录就ok。

image-20210604233516662

2.2 座位预约
image-20210604233812893

image-20210604234023563

参数是通过path传,看到path上面有座位id开始时间结束时间 ,无需多说。真的真的真的比过VPN简单了不知多少。

3. 签到签退系统

3.1 登录分析

因为每次签到都是通过手机扫二维码,然后有时候需要绑定学号,然后才出现签到得按钮,那么这个二维码里面肯定是存在链接的。拍了一张座位的二维码,解码了一下。得到签到系统的链接。

但是这个链接是有点恶心的,302重定向了3次,最后一次才到达登录系统。302重定向是拿不到cookie的,只能捕捉每次重定向的链接,通过这个链接去获取下一次重定向的链接再访问。最后到达登录界面时进行登录抓包,依然是携带TwfID, 完成登录的过程是仅次于VPN系统登录的,除了这个TwfID, 还需要图书馆登录后设置的Cookie,以及座位二维码链接第一次访问设置的path参数,在320重定向的过程中还涉及Cookie的设置,因为没法捕捉到,试了无数次都不行,差点放弃。

可是ahao从来不是轻易放弃的玩家,阴差阳错的,直接把最后一次登录的固定的Cookie全部拿过去,单独设置了其中一个变化的Cookie,居然成功了。

然后就是签到签退抓包分析了,也是比较简单的。

三、总结

本文是CDU-Lib-Robot 项目的完整分析过程,写这篇博文也是为了记录自己的学习过程,创建这个项目的初心也是为了 成大考研学子每天都泡图书馆 的同学 更加方便使用图书馆座位。

忠告:请勿将本项目用于包括但不限于恶意占座 等浪费图书馆公共资源等行为。

使用本项目之前请仔细阅读 免责声明注意事项


A student who writes the code of Python and Java.