0x01 前言

​ 我们学校的宽带在运营商办理之后和教务处账号进行绑定,每次上网插上网线之后需要进入一个认证页面进行登入认证,并且选择对应的运营商。

​ 本来这样挺好的,认证一下嘛也不麻烦。

​ 麻烦的是每次认证是有时效性的,认证之后2880分钟之后自动断网,但是认证并不会消失,需要学生手动点击注销按钮,再重新输入学号和密码进行认证,也就是说每过2天(2880min)就准时断网并且需要重新进行上网认证。

​ 现在场景换到你正在和朋友激情打枪,认证的2880分钟到了,突然断网,接下来你要做的是:

  • 切出浏览器
  • 进入172.16.2.100认证页面
  • 点击注销
  • 输入学号和密码
  • 选择运营商
  • 认证成功

运营商吃相难看,为了防止一个宿舍只办一条宽带,每个校园网账号只能两台设备上网,本文将提供绕过方法达到不限制设备数量联网

​ 现在都3202年了,还在用传统的上网认证吗?

image-20230618174826845

接下来我将使用Python进行定时自动认证宽带上网,解决手动认证的烦恼。

0x02 网络拓扑

​ 鄙人宿舍的网络拓扑结构如下:

网络拓扑

0x03 绕过设备数量限制

​ 一般情况下,当电脑连接校园网宽带认证之后,打开热点让其他设备连接热点,一旦数量超过两个就会自动断网,有时候甚至会被禁用宽带30分钟(十分的恶心)。

​ 细心的同学可能会发现,为什么我开虚拟机不会被识别多个设备联网呢?(当然前提是虚拟机是以Nat方式联网而不是Bridge桥接方式)

绕过设备限制原理:这里可以合理猜测校园网的认证系统会对上网设备的MAC进行过滤,而虚拟机的MAC地址较为特殊,是以00:为开头。所以当接入校园网的第一台设备的MAC地址为虚拟机MAC地址时,校园网宽带对设备数量的限制就可以无限制了。

​ 我的无线路由器为红米AX3000支持自定义MAC地址(实际上大部分无线路由器都可以):

image-20230618185030894

​ 成功达到无限设备数量的效果:

image-20230618185130186

这里我连了六台设备,均不出现断网断认证的情况,顺利联网。

0x04 Python脚本实现自动认证

​ 读者朋友可能注意到上方网络拓扑图中有一个连接小交换机软路由,其实所谓软路由就是有多个网口的小服务器,并且具有一定的路由转发功能。在宿舍我只把软路由当一个旁路由,软路由和其他设备同属一个网段,这样在其他设备中可以直接访问软路由上的服务,比如个人网盘系统、Clash等等。

​ 既然软路由可以24小时不间断运行,那只需要把自动认证的脚本丢进这个软路由,让它在某个不活跃(比如睡觉时候),进行注销-登录-认证的过程,这样就再也不需要自己进行手动认证这个校园网宽带了,也不怕打游戏打着打着断网了。

​ 我的软路由里面装有Ubuntu Linux系统:

image-20230618185945948

为了防止终端关闭导致Python脚本停止执行,这里使用screen进行后台运行脚本,安装方法如下:

1
sudo apt install screen

如果你是redhat系的Linux发行版则是以下命令:

1
sudo yum install screen

或者

1
sudo dnf install screen

如果你是macos:

1
sudo brew install screen

Windows(略。

Python脚本如下:

ecjtu_AC_network/byRequests.py at master · q1jun/ecjtu_AC_network (github.com)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
import os
import re
import time
import requests
from bs4 import BeautifulSoup


def IdAndPasswd():
ispList = ["@cmcc", "@unicom", "@telecom"]
osList = ["win", "linux"]
try:
with open("/var/www/ecjtu_AC_network/.logindata.data", 'r') as f:
info = [a.rstrip('\n') for a in f]
if info.__len__() < 4:
os.remove("/var/www/ecjtu_AC_network/.logindata.data")
info.extend((str(input("输入学号:")), str(input("输入密码:"))))
print("1:中国移动 2:中国联通 3:中国电信")
number = int(input("选择运营商(1-3):"))
info.append(ispList[number - 1])
print("1:Windows 2:Linux(Macos)")
number = int(input("选择操作系统(1-2):"))
with open("/var/www/ecjtu_AC_network/.logindata.data", 'w') as f:
info.append(osList[number - 1])
f.write("\n".join(info))
except Exception:
info = [str(input("输入学号:"))]
info.append(str(input("输入密码:")))
print("1:中国移动 2:中国联通 3:中国电信")
number = int(input("选择运营商(1-3):"))
info.append(ispList[number - 1])
print("1:Windows 2:Linux(Macos)")
number = int(input("选择操作系统(1-2):"))
f = open("/var/www/ecjtu_AC_network/.logindata.data", 'w')
info.append(osList[number - 1])
with open("/var/www/ecjtu_AC_network/.logindata.data", 'w') as f:
f.write("\n".join(info))
return info


def GetIp():
HOST = "http://172.16.2.100/"
r = requests.get(HOST)
bsobj = BeautifulSoup(r.content, 'html5lib')
return re.findall(r"ss5=\"(.+?)\"", str(bsobj))[0]


def TestLog(requestObj):
print("************** test log start *******************")
print("Status Code: ", end="\t")
print(requestObj.status_code)
print("Url: ", end="\t")
print(requestObj.url)
print("Headers: ", end="\t")
print(requestObj.headers)
print("Cookies: ", end="\t")
print(requestObj.cookies)
print("History: ", end="\t")
print(requestObj.history)
print("-------------- All content ----------------------")
print(requestObj.text)
print("*************** test log end ********************")


def showLog(requestObj):
result = re.findall(r"认证成功页", requestObj.text)
if (len(result) > 0):
for _ in range(10):
print("*************** ok ********************")


def ACLogin(ip, id, passwd, isp):
url = "http://172.16.2.100:801/eportal/"
headers = {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3759.4 Safari/537.36",
"Referer": f"http://172.16.2.100/a70.htm?wlanuserip={ip}&wlanacip=null&wlanacname=null&vlanid=0&ip={ip}&ssid=null&areaID=null&mac=00-00-00-00-00-00",
"Connection": "keep-alive", "Host": "172.16.2.100:801", "Origin": "http://172.16.2.100"}

payload = {
"c": "ACSetting",
"a": "Login",
"protocol": "http:",
"hostname": "172.16.2.100",
"iTermType": "1",
"wlanuserip": ip,
"wlanacip": "null",
"wlanacname": "null",
"mac": "00-00-00-00-00-00",
"ip": ip,
"enAdvert": "0",
"queryACIP": "0",
"loginMethod": "1"
}
postData = {"DDDDD": f",0,{id}{isp}", "upass": passwd, "R1": "0", "R2": "0", "R3": "0", "R6": "0", "para": "00",
"0MKKey": "123456", "buttonClicked": "", "redirect_url": "", "err_flag": "", "username": "",
"password": "", "user": "", "cmd": "", "Login": ""}

cookiesData = {"ISP_select": isp, "areaID": "null", "ip": ip, "md5_login2": f"%2C0%2C{id}{isp}%7C{passwd}",
"program": "test", "ssid": "null", "vlan": "0"}

acRequest = requests.post(
url, params=payload, data=postData, cookies=cookiesData, headers=headers)
showLog(acRequest)


def getIpandMac_win():
ipmac = os.popen("ipconfig /all").read()
ip = re.findall(r"\d+.\d+.\d+.\d+", ipmac)[0]
mac = re.findall(r"\w+-\w+-\w+-\w+-+\w+-\w+", ipmac)[0]
dmac = ""
for a in mac.split("-"):
dmac += a
return (ip, dmac,)


def getIpandMac_linux():
ipmac = os.popen("ifconfig -a").read()
ip = re.findall(r"\d+.\d+.\d+.\d+", ipmac)[0]
mac = re.findall(r"\w+:\w+:\w+:\w+:+\w+:\w+", ipmac)[0]
dmac = ""
for a in mac.split(":"):
dmac += a
return (ip, dmac,)


def ACLogout(id, passwd, isp, os):
if os == "linux":
print(f'os = {os}')
ip = getIpandMac_linux()[0]
mac = getIpandMac_linux()[1]
else:
print(f'os = {os}')
ip = getIpandMac_win()[0]
mac = getIpandMac_win()[1]

url = "http://172.16.2.100:801/eportal/"
headers = {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3759.4 Safari/537.36",
"Referer": f"http://172.16.2.100/a70.htm?wlanuserip={ip}&wlanacip=null&wlanacname=null&vlanid=0&ip={ip}&ssid=null&areaID=null&mac=00-00-00-00-00-00",
"Connection": "keep-alive", "Host": "172.16.2.100:801", "Origin": "http://172.16.2.100"}

payload = {
"c": "ACSetting",
"a": "Logout",
# "protocol": "http:",
"hostname": "172.16.2.100",
"iTermType": "1",
"wlanuserip": "null",
"wlanacip": "null",
"wlanacname": "null",
"mac": mac,
"queryACIP": "0",
"port": ""
}

cookiesData = {"ISP_select": isp, "areaID": "null", "ip": ip, "md5_login2": f"%2C0%2C{id}{isp}%7C{passwd}",
"program": "test", "ssid": "null", "vlan": "0"}

acRequest = requests.post(
url, params=payload, cookies=cookiesData, headers=headers)
# TestLog(acRequest)


# info = IdAndPasswd()
# ACLogout(info[0], info[1], info[2], info[3])
# ip = GetIp()
# ACLogin(ip, info[0], info[1], info[2])

"""
定时
"""
import time
import schedule


def job():
print("[+]I'm logging in...now is :",end='')
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))
info = IdAndPasswd()
ACLogout(info[0], info[1], info[2], info[3])
ip = GetIp()
ACLogin(ip, info[0], info[1], info[2])


if __name__ == "__main__":
# schedule.run_all()
print('[+]正在执行定时项目......')
schedule.every().day.at("02:30").do(job)
while True:
schedule.run_pending()
time.sleep(1)

账号密码放在.logindata.data中,第一次运行将会要求你配置账号密码和运营商,自行填写即可。

然后使用:

1
screen -S ac

创建一个名为ac的后台窗口,默认创建之后自动进入该会话,启动完脚本直接关闭即可。

需要再次进入则输入:

1
screen -r ac

查看当前会话列表:

1
screen -ls

这里脚本设置是每天凌晨02:30自动进行认证,到这里配置就完成了,如有错误欢迎大家在评论区指正。