0x00 前言
由于被发配到了远离“驾校”本部的小区,导致网络认证非常难受,每天都会不定时的掉线。为了保证寝室的网络质量以及我的nuc的正常运行,决定对校园网的锐捷认证进行处理。
0x01 思路
之前的操作是通过F12直接抓取请求,获取加密密码和查询字符串,但最近F12的使用效果不佳,于是我使用了Fiddler这款抓包工具,具体的配置就不赘述了。我们先断网,看看请求的过程。
当我们断网并打开网页时,会被重定向到一个认证页面。无线网络和有线网络的请求方式可能有所不同,但问题不大。
接着,在Fiddler中访问以下URL,查看请求过程。
http://10.254.241.19/
我们发现前两个请求返回的状态码是302,随后有一个200状态的请求指向123.123.123.123,而第五个请求则是我们之前跳转后登录页面的地址。第二个图中显示的请求方法是“login”,这正是我们需要模拟的登录请求。
通过网页表单,我们了解到提交的是学号,密码则变成了一串难以辨认的字符,还有一些空字段。查询字符串看起来非常熟悉,实际上就是登录页面网址的后半部分,推测是与本机的MAC地址等信息有关的一个字符串,具体生成方式暂时不追究。最后一个passwordEncrypt
字段默认为true,说明现在默认是加密的。之前如果将其改为false就能直接用密码登录,现在则需要进行加密。
在查看源代码的JavaScript文件时,我发现了一些关键代码,其中调用了security.js
。因此,我们可以使用Python模拟这一过程,大致步骤是使用RSA进行加密,这里涉及两个重要参数:publicKeyExponent
和publicKeyModulus
。
这两个参数可以在登录页面上找到,但如何通过请求获取呢?在图2中的pageinfo
请求中就可以获取到这些信息。接下来,我们需要探索如何用代码获取查询字符串。仔细查看图1中第三个请求的返回数据,基本验证了我们的猜测。有线和无线的地址可能不同,无线网络似乎是178开头的,所以我们需要从上述请求的返回值中提取两个信息:192.***.50.***
和以wlanuserip
开头的字符串。
0x02 获取查询字符串的代码
我们首先判断网络状态,如果没有网络则从123.123.123.123获取自己的查询字符串,并提交数据。判断网络是否连接的代码如下:
class internetWorkingFine(Exception):
def __init__(self, ErrorInfo):
super().__init__(self)
self.ErrorInfo = ErrorInfo
def __str__(self):
return self.ErrorInfo
def get_info():
redirect_host = 'http://123.123.123.123/'
headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36",
}
info = {}
res = requests.get(redirect_host, headers=headers).content.decode()
info["querystr"] = res[(res.find("wlanuserip")):(res.find("'</script>"))].replace('=', '%3D').replace('&', '%26')
if not info["querystr"]:
raise internetWorkingFine("网络似乎正常~~")
info["url"] = res[res.find('http:'):res.find('eportal')]
return info
接下来的流程就比较清晰了。首先需要判断网络是否正常,如果断网则从123.123.123.123获取查询字符串,接着提交数据。
0x03 密码加密和重新认证
注意,部分学校的锐捷网页认证并没有开启加密,所以传参默认用户名和密码就行,例如“驾校”就是没有开启加密功能。这里的分析我们就用通用的。
接下来是加密密码和重新认证的代码,具体流程如下:
def get_password(pwd, exponent, modulus):
e = int(exponent, 16)
m = int(modulus, 16)
t = pwd.encode('utf-8')
input_nr = int.from_bytes(t, byteorder='big')
crypt_nr = pow(input_nr, e, m)
length = ceil(m.bit_length() / 8)
crypt_data = crypt_nr.to_bytes(length, byteorder='big')
return crypt_data.hex()
def relogin(uid, pwd):
session = requests.session()
pageinfo_url = "{url}eportal/InterFace.do?method=pageInfo".format(url=info["url"])
page_data = {
"queryString": info["querystr"]
}
req = session.post(pageinfo_url, headers=headers, data=page_data).json()
exponent = req["publicKeyExponent"]
modulus = req["publicKeyModulus"]
password = get_password(pwd, exponent, modulus)
login_url = "{url}eportal/InterFace.do?method=login".format(url=info["url"])
login_data = {
"userId": str(uid),
"password": password,
"service": "",
"queryString": info["querystr"],
"validcode": "",
"passwordEncrypt": "true"
}
req = session.post(url=login_url, headers=headers, data=login_data).json()
if req["result"] == "fail":
return "重新认证失败:" + req["message"]
return "重新认证成功~~~"
我们依次获取查询字符串、使用pageInfo
获取加密参数,然后通过加密算法生成加密后的密码,最后提交认证。
0x04 进一步探索
我们这里可以进一步猜想,能否伪造mac和ip地址,从而实现多拨的效果呢?当然如果你的寝室里正好有一台路由器,并且已经刷上了openwrt,那么恭喜你可以做到,使用MentoHUST插件,即可进行802.11x验证,加上负载均衡,即可实现,如下图所示:
但是很可惜,我的这台水晶路由器不支持刷固件。本来想着用nuc来顶一顶,结果网线接口又不够,没办法,只能当个废弃的旁路由来玩。
那我们就来到,伪造mac地址和ip地址,看看能否实现一线两拨。
我们从上文的分析中可以看到,访问认证页面时会先去访问http://123.123.123.123
生成对应的URL允许用户进行登录,那么我们可以从4层协议上进行篡改尝试。
我在本地测试的时候,增加了一个广播功能。当然,这应该是属于arp attack的范畴了,本人也不太想涉及这方面。
设置一个计划任务,N分钟运行效果更佳