天翼校园网登录过程分析

yish 发布于 2024-10-29 237 次阅读 预计阅读时间: 9 分钟


AI 摘要

这篇文章讲述了如何通过分析校园网的登录流程,实现自动登录。重点在于抓取登录数据包、生成RSA公钥加密信息,以及使用OCR识别验证码。最终,作者展示了一个自动登录的客户端,简直是懒人必备!😂

前言

SEIG的校园网分为两种客户端,分别是未登录时会弹出的网页登陆界面,以及安装程序版的登录客户端。网页端只能登录教师账号,学生账号需要在后者登录。
本文将探索网页端登录过程,分析登录数据包。由于每次登录都要 *重新输入* 账号密码,以及只能拦截人类的 *验证码* ,所以实现自动登录也是本次的目标。

分析

访问登录界面

登录界面需要输入三个信息:
1、账号
2、密码
3、验证码

此时打开浏览器开发者工具(按下F12)切换到网络选项卡,然后正常输入账号密码,尝试抓取登录数据包。

分析登录数据包

当点击“提交登入”按钮后,会发送POST请求到服务器,发现界面中出现了Login字样的数据包,右键复制cURL(cmd),分析数据。

curl ^"http://125.88.59.131:10001/ajax/login^" ^
  -H ^"Accept: application/json, text/javascript, */*; q=0.01^" ^
  -H ^"Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6^" ^
  -H ^"Cache-Control: no-cache^" ^
  -H ^"Connection: keep-alive^" ^
  -H ^"Content-Type: application/x-www-form-urlencoded; charset=UTF-8^" ^
  -H ^"Cookie: JSESSIONID=a_LXXXXXXXXXXXXXXXXXXXXXXXXXXXZ6o9JNK2yG-efkG^!-42XXXXXXXX98^" ^
  -H ^"Origin: http://125.88.59.131:10001^" ^
  -H ^"Pragma: no-cache^" ^
  -H ^"Referer: http://125.88.59.131:10001/qs/index_gz.jsp?wlanacip=183.3.151.148^&wlanuserip=172.16.XX.XXX^" ^
  -H ^"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0^" ^
  -H ^"X-Requested-With: XMLHttpRequest^" ^
  --data-raw ^"loginKey=2897b82c1142XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXf8fef6ecae2362da468b76912171fff439532383dfd656c15c6127f8711bf55de381bb4f66138ca86acd5c309b971514daf2cdef5df07c7492c052dcced9d6b0b790b3e78447ecbXXXXXXXXXXXXXXXXXXXXXXXXXXXXXf2e6aa9da5778b5f^&wlanuserip=172.18.42.102^&wlanacip=183.3.151.148^" ^
  --insecure

可以发现,数据包中包含的重要参数:
1、登录的URL:http://125.88.59.131:10001/ajax/login
2、Referer中的wlanacip=183.3.151.148^&wlanuserip=172.16.XX.XXX (登录服务器ip和本机ip)
3、Cookie中的JESSIONID
4、loginKey (账号密码大概就在里面)

尝试直接将curl复制进cmd中执行,返回以下结果:

{"resultCode":"11063000","resultInfo":"验证码错误"}

尝试将loginkey打乱,再次发送,返回结果如下:

{"resultCode":"11000000","resultInfo":"账号或密码错误(11000000)"}

由此可知,登录的关键数据就是loginkey以及wlanacip、wlanuserip。其中loginkey里包含了账号密码以及验证码,但loginkey是一段加密数据,还需寻找加密过程。

这一大长串的内容大概就是RSA加密了...

分析Login.js

切换浏览器开发者工具的选项卡至“源代码”:

所幸js没有被混淆加密,可以看到登录过程的源代码,分析登录源码

function login() {     // 原本的js还有注释,这程序员真好!不过为了简洁,删除了原有的注释
	var username = $("#username").val();

	if (username == "") {
		alert("请输入上网帐号,@后面去掉");
		$("#username").focus();
		return;
	}

	var password = $("#password").val();

	if (password == "") {
		alert("请输入密码");
		$("#password").focus();
		return;
	}
	
	var code = $("#code").val();

	if (code == "") {
		alert("请输入验证码");
		$("#code").focus();
		return;
	}

	setMaxDigits(200); 
// 设置大数库的精度位数为200
	var key = new RSAKeyPair("10001","","b2867727e19e1163cc084ea57b9fa8406a910c6703413fa7df96c1acdca7b983a262e005af35f9485d92cd4c622eca4a14d6fd818adca5cae73d9d228b4ef05d732b41fb85f80af578a150ebd9a2eb5ececb853372ca4731ca1c8686892987409be3247f9b26cae8e787d8c135fc0652ec0678a5eda0c3d95cc1741517c0c9c3");         
// RSAKeyPair 是一个用于生成 RSA 公钥对的函数,10001是指数,后面的一大串是模数
	var loginKey = encryptedString(key, '{"userName":"' + username + '","password":"' + password + '","rand":"' + code + '"}');

// loginKey是由 userName: 用户名,password: 密码,code: 验证码 组成,然后用RSA公钥加密
//console.log(loginKey)

// 下面就是数据封包的逻辑了,还有根据服务器代码返回报错的一些信息
	var wlanuserip = $("#wlanuserip").val()
	var wlanacip = $("#wlanacip").val()

	$.ajax({
		type : 'POST',
		url : '/ajax/login',
		cache : false,
		data: {
			loginKey: loginKey,
			wlanuserip: wlanuserip,
			wlanacip: wlanacip
		},
		dataType : 'json',
		success : function(data, textStatus) {
			if (data.resultCode == "0" || data.resultCode == "13002000" ) {
				$("#login").css('display','none'); 
				$("#success").css('display','block');
				changeCode();
			} else {
				changeCode();
				if(data.resultCode == "13018000") {
					alert("已办理一人一号多终端业务的用户,请使用客户端登录");
				} else {
					alert(data.resultInfo);
				}
			}
		}
	});

}

所以登录的大致过程:

用户输入账号密码、验证码
→本地生成RSA公钥,加密输入的三个数据,即loginkey
→通过POST发送Json数据,包含loginKey、wlanuserip、wlanacip
→等待服务器返回结果

了解完登录过程,就可以用小学学过的Python尝试实现自动登录了!

实现自动登录

自动登录要模拟完成上面的登录流程,需要以下步骤:

生成RSA公钥
→将账号密码赋值给对应函数
→使用OCR识别验证码,识别后赋值给函数
→封装Loginkey数据包,使用RSA公钥加密
→构造请求参数
post_data = {
'loginKey': ,
'wlanuserip': ,
'wlanacip':
}
→发送POST请求

制作数据包

1、生成公钥

在“在线通过模数、指数生成公钥”网站中,通过已知的模数和指数生成RSA公钥

指数:10001
模数:b2867727e19e1163cc084ea57b9fa8406a910c6703413fa7df96c1acdca7b983a262e005af35f9485d92cd4c622eca4a14d6fd818adca5cae73d9d228b4ef05d732b41fb85f80af578a150ebd9a2eb5ececb853372ca4731ca1c8686892987409be3247f9b26cae8e787d8c135fc0652ec0678a5eda0c3d95cc1741517c0c9c3
(发现广东天翼的似乎都是这个)

生成的公钥:
rsa_public_key = """
    -----BEGIN PUBLIC KEY-----
    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyhncn4Z4RY8wITqV7n6hAapEM
    ZwNBP6fflsGs3Ke5g6Ji4AWvNflIXZLNTGIuykoU1v2Bitylyuc9nSKLTvBdcytB
    +4X4CvV4oVDr2aLrXs7LhTNyykcxyhyGhokph0Cb4yR/mybK6OeH2ME1/AZS7AZ4
    pe2gw9lcwXQVF8DJwwIDAQAB
    -----END PUBLIC KEY-----
    """

在Python中读取公钥:pub_key = rsa.PublicKey.load_pkcs1_openssl_pem(rsa_public_key.encode())

2、OCR识别验证码

在login.js中,还有一段与验证码有关的代码:
function changeCode() {
	var time = new Date().getTime();
	$("#image_code").attr("src", "/common/image_code.jsp?time=" + time);
}
通过一个带有当前时间的URL请求获取验证码图片

小插曲:

由于刚开始认为验证码是根据请求时间返回的,并在一段时间内有效,所以在完成了后续操作后,POST发送,但得到的结果一直是验证码错误,遂在群内询问大佬,在群里发的Github链接中找到了解决办法。
Pandaft/ESurfingPy-CLI: 基于 Python 实现登录和登出广东天翼校园网的命令行工具 原来已经有前辈做过这个了,通过Session保持连接再获取验证码。

先用Session访问登录链接:response = session.get(page_url, timeout=5, headers=headers),然后获取验证码:url = re.search(r'/common/image_code\.jsp\?time=\d+',
                            str(response.content)).group()

获取验证码图片后,将图片二值化降噪之后使用sml2h3/ddddocr通用验证码识别OCR识别,准确率非常高

    def preprocess_image(self, image): 
       # 转换为灰度图像
        image = image.convert("L")
        # 应用二值化
        threshold = 128
        image = image.point(lambda p: p > threshold and 255)
        image = image.filter(ImageFilter.MedianFilter(size=3))
        return image

3、封装loginkey,发送数据

        # 登录数据
        login_data = {
            "userName": username,
            "password": password,
            "rand": code
        }

        # 加密login_key
        login_key = self.encrypt_rsa(json.dumps(login_data), pub_key)

        # 构造请求头和Cookie
        headers = {
            "Origin": f"http://{esurfingurl}",
            "Referer": f"http://{esurfingurl}/qs/index_gz.jsp?wlanacip={wlanacip}&wlanuserip={wlanuserip}",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0",
        }

        # 构造请求参数
        post_data = {
            'loginKey': login_key,
            'wlanuserip': wlanuserip,
            'wlanacip': wlanacip
        }

        # 发送POST请求
        try:
            response = session.post(
                f'http://{esurfingurl}/ajax/login', timeout=5, headers=headers, data=post_data)
            if response.status_code == 200:
                data = response.json()
                if data['resultCode'] == "0" or data['resultCode'] == "13002000":
                    signature = response.cookies["signature"]
                    print("成功连接校园网!")

发现已经可以返回登录成功了

制作登录客户端

根据上述原理,制作一个登录客户端:Yish1/SEIG-Auto-Connect

原则上适用于所有广东天翼网页端账号

工具特色:

  •  支持一键登录网页端校园网账号
  •  支持自动获取登录参数
  •  自动登录功能,在启动软件时自动登录保存的账号
  •  自动识别验证码,如果失败5次,需要手动输入验证码
  •  看门狗,默认每600秒检测一次网络状态,若网络不通,自动重连
  •  对保存的密码低级加密
  •  按下最小化按钮可以隐藏进托盘