登录华科校园网,我用Socket

登录华科校园网,我用Socket

导语:

找一个华科学生问一问,学校的网络怎么样?得到的大多数是负面回答。其实不论是从覆盖区域、网络稳定性、还是速度来说,华科做的都还是可以的(24:00断网除外)。可是有一点我从进校以来就一直不爽,那就是校园网的认证方式是有线锐捷+无线web页面组合,并且无线网不能输入MAC来指定无感认证设备。真的是非常的安(má)全(fàn)啊!

这就意味着像esp32这类MCU没法使用无线网,特别是大一学生不能开通有线也没法装路由器,当时想用esp32做点东西的我十分郁闷。我从来到华科的第一天就想搞它了。使用Socket直接模拟网页认证,让esp32也能直接联网。

补充 :

做完之后也看了网上类似的博客,其他学校的同学也用Socket进行过类似的认证,可大部分没有提及跳转重定向加密等重要部分,而且也都比较简短,没有分析整个认证过程,所以这一篇就尽量详细的还原整个过程,并且使用ESP32+micropython进行测试通过。所以,多图预警

工具 :

FireFox浏览器、WireShark、Python3


0x00 观察

登录过程

华科校园网认证页面

这就是认证页面,在手机端上的模样与电脑端大同小异。一般我们输入正确的用户名(学号)、密码再点击按钮就能跳转到认证成功的页面上去了:

认证成功页面

一般的,我会给电脑和手机开启无感认证,每次连接到校园网就不必手动认证,缺点在于不支持输入MAC进行无感认证。这就意味着设备必须支持浏览器才能进行认证,我们的目标也在于破除这一限制。

页面后台

单看网页前台能获得的信息十分有限,接下来就要去页面的实现代码上看一看了。按下F12,进入火狐的开发者工具:

页面html

因为页面非常的简洁,所以html内容较少,在调试器下我们能找到几个独立的JavaScript文件:

关键的三个js

不难发现,登录认证的核心在于红框内的三个文件。他们的名字非常的坦白明晰啊,authinterface应该是负责认证的接口,security可能是负责加密,login_bch肯定也和登录脱不了干系,统统拿下来研究。

交待一下,我之前从没接触过JavaScript,HTML也只是了解几个标签的运作方式,为了能看懂这几个js,就连夜预习最终达到了能看懂的水平😢。

其中security.js开头注释就说明了用途:

/*
 * RSA, a suite of routines for performing RSA public-key computations in JavaScript.
 * Copyright 1998-2005 David Shapiro.
 * Dave Shapiro
 * dave@ohdave.com 
 * changed by Fuchun, 2010-05-06
 * fcrpg2005@gmail.com
 */

后面我们能看到,这是对密码传输进行加密的RSA算法。

然而三个文件加起来超过2300行,并且注释量不多,我还是决定通过调试找出整个登录的函数调用路线,去除无关内容的干扰。

刚开始做东西的时候不爱用调试工具,这一两年却是越来越喜欢了。不论软硬件都是开发一小时,调试一整天😂。


0x01 尝试

网络监控

仅仅是打开这些网页就有如下网络请求:

网络请求1

我随机写了账号和密码,点击了登录按钮:

登录请求gif

出现了一个POST一个GET,通过类型我们能知道,GET是用来获取那张验证码图片的。那么重点就在于GET,其中一定包含了账户密码的上传认证。

POST

果不其然,在POST的请求里我们看到了我输入的账号“1234567”,以及加密后的密码。这几个字段里queryString包含了大量对于我本机的描述,IP、mac、网络名称等信息。最后一个字段passwordEncryptTure按照字面意思来讲是开启密码加密。

以为成功了?

据此,我做了一个尝试,利用浏览器的编辑并重发功能,将这条POST中的passworEncrypt改为false,并用我的账户替换了userId字段,用我的密码明文替换了password字段内容并重新发送。我发现我已经获得了互联网连接,只是页面不会自动跳转。

进一步为了验证登录过程对其他的步骤有没有依赖,确保只发送这一个POST就可以完成认证,我用WireShark把这个POST的TCP包拿了出来,并掏出了高中以后再没用过的Python试了一下:

import socket,time

Host='172.18.18.60'
Port=8080

context='内容'
byte=context.encode()

def connect(byte):
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect((Host,Port))
    print('[*]\r\n'+context)
    s.sendall(byte)
    time.sleep(0.5)
    re=s.recv(1024).decode()
    if("success" in re):
        s.close()
        return 1
    else:
        print('\r\n'+re)
        s.close()
        return 0


print(connect(byte))

结果还真的成了!?当时是晚上,我就挺高兴:没想到,这么个事这么快就解决了。

远远没有!

第二天晚上我再一次运行了脚本,却得到了如下信息:

{"userIndex":null,"result":"fail","message":"您当前使用的源IP与设备重定向地址中用户IP不一致,请重新认证!","forwordurl":null,"keepaliveInterval":0,"validCodeUrl":""}

果然人欢无好事,仅仅靠投机取巧获得的结论总是靠不住的。

感觉得出这与queryString里一长串的设备信息有关,要想发出正确的POST就必须有正确的queryString,可是queryString如何获取呢?要知道里面不仅仅有非常多的自身设备信息,更有AP设备的mac等信息,组织起来极其困难。除此以外,这些信息是加密过的,用站长工具尝试无果,除了一小部分字段是两次urlEncode,其他的加密方法即使到写这篇文章时都不得而知。

情况陷入了僵局,这就倒逼我回到我一度不愿意阅读的源码上去,毕竟源码之下无秘密。


0x02 调试

其实我从小就对浏览器按下F12的开发者工具很感兴趣,一直到这次摸索校园网登录才算是真正的用起来了。不由感叹网页的调试器真的很强大。

充分的运用搜索和倒推调用技巧之后,三个JavaScript文件的基本函数功能算是了解了,接着花了点时间学习浏览器的调试功能就直接上手了。

由于拙劣的技术,在打了无数个断点和中断事件之后,终于摸索出了整个流程。流程如下图所示:

调用流程图

  • 从上带下表示调用的先后顺序
  • 注意:图中对一些过程进行了简化,保留了核心功能,并不能完全代表整个过程。

流程分析

基本流程已经通过图片展示出来了,现在对图中标有数字序号的地方进行展开分析。

  1. document.location.search
  2. 隐藏的文本框
  3. passwordmac和encryptedpassword()
  4. 回调处理
1. document.location.search

这个值是对于当前的html页面来说的,也就是下图所示:

页面URL

把这些值给到了queryString

2. 隐藏的文本框

看似简洁的登陆页面,在后台可以发现不少隐藏的文本框:

隐藏文本框

图中展现三个带有初始值的文本框,将他们标签中的hidden去掉以后就可以看到了。

第一个框中的true是passwordEncrypt的值,也就是默认加密。

而后两个框中的值:1000194dd2a8675fb779e6b9f7103698634cd400f27a154afa67af6166a43fc26417222a79506d34cacc7641946abda1785b7acf9910ad6a0978c91ec84d40b71d2891379af19ffb333e7517e390bd26ac312fe940c340466b4a5d4af1d65c3b5944078f96a1a51a5a53e4bc302818b7c9f63c4a1b07bd7d874cef1c3d4b2f5eb7871组成了RSA加密的公钥,这就是华科校园网加密的公钥。

3. passwordmac和encryptedpassword()

这是一个变量的名称,它的定义是:var passwordMac = password+">"+macString;

也就是,接下来被处理的不是咱们的密码,而是:‘密码>mac’

这还没完,我们继续追踪下去追踪到在AuthInterFace.js里的Encryptedpassword()函数:

function encryptedPassword(password){//有删减

	var passwordEncode = password.split("").reverse().join("");//反转字符

	 var key = new RSAUtils.getKeyPair(publicKeyExponent, "", publicKeyModulus); //rsa加密公钥
	 var passwordEncry = RSAUtils.encryptedString(key,passwordEncode);//这里要对字符串进行反转,否则解密的密码是反的
	
	 return passwordEncry;
}

直观体会一下这个操作:

反转

没搞明白这么做的目的是什么。。。

我看不懂

4. 回调处理

在发出包含一切信息的POST之后,我们会收到验证服务器JSON格式的回应,成功也好,不成功也罢,需要对响应进行处理。比如:

{"userIndex":null,"result":"fail","message":"您当前使用的源IP与设备重定向地址中用户IP不一致,请重新认证!","forwordurl":null,"keepaliveInterval":0,"validCodeUrl":""}

通过result=fail能知道认证失败,message可以告诉我们原因。

分析结果

至此,我们弄明白了网页认证的主干流程,也明白我们要做什么:

发送一个内容正确的POST给认证服务器,所以就要组织出正确的queryString。

可是分析一圈下来我们知道queryString来自于网页的的URL,可是这页面也是自己弹出来的啊!

这让网络技术薄弱的我陷入思考。。。


0x03 重定向

在这个过程中,不断地用WireShark抓包,遇到了不少的困难,好在最后找到了重定向的地址。

既然这个登陆页面可以自己弹出来,那么我们的电脑是从哪里获得这个页面的网址?百度之后,结论如下:

连接WiFi之后,系统会自动访问一些地址,比如获取时间或者专门验证是否联网的页面,在Windows下这个网址是:

http://www.msftconnecttest.com/redirect如果有网络的话最终会被转到MSN中国的页面上去。

那如果AP设置了登录页面,就会在系统自动访问上述页面的时候,通过一些手段给客户端强制返回登陆页面(重定向),然后就是我们看见的登陆页面。

为了能亲眼看看这个过程,漫长的抓包开始了。

抓包

一开始并没有什么收获,因为HTTP包数量非常多,而且我们不知道重定向页面的IP地址,也就没法进一步筛选。于是我又转向了浏览器。

我发现,如果在地址框里直接输入172.18.18.60:8080,也能跳转到带有一长串queryString的页面,毫不犹豫我勾选了调试器中的“在任何网址处暂停”

调试断点

然后输入并访问172.18.18.60:8080。

页面暂停在了一个陌生的地址:123.123.123.123

123.123.123.123

这个网页没有其他内容,只有一句js:

<script>top.self.location.href='http://172.18.18.60:8080/eportal/index.jsp?wlanuserip=xxxxxxxxxxx&wlanacname=xxxxxxxxxxxxx&ssid=&nasip=xxxxxxxxxxx&snmpagentip=&mac=xxxxxxxxxxxx&t=wireless-v2&url=xxxxxxxxxxxxx&apmac=&nasid=xxxxxxxxxxxx&vid=xxxxxxxx&port=xxxxxxxxxx&nasportid=xxxxxxxxxxxxxxxx'</script>

这不就是queryString的来源吗!?

于是又发挥传统艺能验证了一下:

import socket

Host='123.123.123.123'
Port=80

con="GET / HTTP/1.1\r\nHost: 123.123.123.123\r\nUser-Agent: Python Socket\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\nAccept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\n\r\n"

byte=con.encode()


def connect(byte):
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect((Host,Port))

    s.sendall(byte)

    re=s.recv(1024).decode()
    s.close()
    if re != '' :
        print(re)
        href=re[(re.find('http://172')):(re.find('\'</script>'))]
        print('\r\n'+href)
        querystr=re[(re.find('wlanuserip')):(re.find('\'</script>'))]
        print('\r\n'+querystr)
        return('\r\n 1')


print(connect(byte))

结果喜人,验证通过。

除此以外,还发现123.123.123.123只能在未认证的情况下访问,在后面WireShark的抓包下,又发现了几个功能类似的地址,但是他们的地址显然没有123.123.123.123这么 讨人喜欢

一个重定向地址

另一个重定向地址


0x04 整合

将整个过程了解之后,实现自然是非常简单,将前文中的几个段落拼接修改之后不难得出最终版本:

直接使用Python3的socket与重定向和认证服务器建立TCP连接。

import socket,time


redirect_host='123.123.123.123'
redirect_port=80

login_host='172.18.18.60'
login_port=8080

redirect_request_str='GET / HTTP/1.1\r\nHost: 123.123.123.123\r\nUser-Agent: Python Socket\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\nAccept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\n\r\n'


login_str_line='POST /eportal/InterFace.do?method=login HTTP/1.1\r\n'

login_str_headers='Host: 172.18.18.60:8080\r\nUser-Agent: Python Socket\r\nAccept: */*\r\nAccept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2\r\nAccept-Encoding: gzip, deflate\r\nContent-Type: application/x-www-form-urlencoded; charset=UTF-8\r\nContent-Length: duetocontent\r\nOrigin: http://172.18.18.60:8080\r\nConnection: keep-alive\r\n\r\n'

login_str_content_head='userId=theuserid&password=thepassword&service=&queryString='
login_str_content_tail='&operatorPwd=&operatorUserId=&validcode=&passwordEncrypt=false'



def info_request(redirect_host,redirect_port,redirect_request_str):
    #在重定向处获取queryString
    print('[*]requesting redirection : \r\n')
    flag=0
    while(1):
        print('[*]trying \r\n')
        s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        s.connect((redirect_host,redirect_port))
        s.sendall(redirect_request_str.encode())
        re=s.recv(1024).decode()
        s.close()

        if(re == ''):
            flag=flag+1
            if(flag == 3):
                return 0
            continue
        else:
            querystr=re[(re.find('wlanuserip')):(re.find('\'</script>'))]
            print('[*]requesting success \r\n')

            print(querystr+'\r\n')
            return querystr


def login(login_host,login_port,querystr,id=None,pwd=None):

    if(id == None or pwd == None):
        print('[*]Please check the account.\r\n')
        return 0


    print('[*]trying to login  \r\n')
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect((login_host,login_port))


    global login_str_headers
    global login_str_content_head
    login_str_content_head=login_str_content_head.replace('theuserid',id).replace('thepassword',pwd)
    querystr=querystr.replace('=','%253D')
    querystr=querystr.replace('&','%2526')
    content=login_str_content_head+querystr+login_str_content_tail
    login_str_headers=login_str_headers.replace('duetocontent',str(len(content)))

    login_str=login_str_line+login_str_headers+content
    print(login_str+'\r\n')
    s.sendall(login_str.encode())
    #time.sleep(0.5)
    re=s.recv(1024).decode()
    s.close()
    if("success" in re):
        print('[*]login Successfully \r\n')
        return 1
    else:
        print('[*]login failed \r\n')
        print(re)
        return 0




query=querystr=info_request(redirect_host,redirect_port,redirect_request_str)
print(login(login_host,login_port,query,id='',pwd=''))

Python水平也蛮差的,也就只能应付这样的小场面了。。。。。


0x05 测试

为了不影响正常使用,我整个过程研究的都是2.4G的信号,这也正好符合咱们的目标是ESP32这一类设备。

image-20210708110429180

实际测试,两个信号都可以用脚本登录。

ESP32测试

不能忘记咱们是为什么开始的呀,ESP32才是事情的起源,也是咱们的目标。

esp32

micropython真的是非常方便,之前的代码几乎直接复制,再加一个WiFi连接就可以了,一遍过。

这个RT-Thread发布的micropython插件真的挺好用的,编辑器竟然支持代码补全。


0x06 总结

总体来讲,整个持续时间只有四五天,正值期末考试周。这个小项目成了我放(huá)松(shuǐ)的好机会。

第一次读JavaScript,第一次运用浏览器调试,第一次使用WireShark。有些过程在文章里展示的不多,但的确耗费了大量的时间。

这一次,对网络的认识又加深了几分。

在查找资料的时候,我了解到mentohust这个由10多年前的华科大神编写的校园网认证软件,了解到现在很多学校的学生都在用mentohust来进行锐捷认证,算是受到一些感召。期间,联系到一位今年刚毕业的华科计科学长,也非常感谢前辈们的帮助。

代码虽短,但也放到了GitHub上去了。如果有同好想移植到不同的平台或者用其他语言实现可以汇总起来方便查找。

仓库地址:https://github.com/HuXioAn/HUST_Wireless_login_by_socket

接下来可能会去折腾一下梅林固件、dd-wrt上的mentohust认证。


技术新人,水平一般,能力有限,如果文章中或者代码有任何疑问或者错误请一定指出,不吝赐教!

看更多相关文章或者联系我请来公众号,和我聊聊吧:

公众号


欢迎转载,请注明:

作者:胡小安 https://blog.csdn.net/qq_28039135?spm=1000.2115.3001.5343

原文链接:https://blog.csdn.net/qq_28039135/article/details/118573714

  • 13
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
华科科技大学锐捷平台有线网登陆 FAQ 1、手机和电脑使用什么信号比较好? 答:推荐使用HUST_WIRELESS_AUTO,其次是HUST_WIRELESS的无感知认证。 2、MAC电脑连接HUST_WIRELESS无法弹出认证页面如何处理? 答:首先要确保无线网卡的IP和DNS都是自动获取;之后断开网络,清理浏览器的Cookie后,尝试重新连接。 3、无线网频繁掉线如何处理? 答:无线网掉线的原因很多: a、无线网若是一段时间没有任何流量,会判定用户下线,这时需要重新认证连接; b、通常情况下,学生账号同一时刻,只能有一个设备在线,若是两个设备相互抢占,会导致频繁掉线,这时关闭一个设备校园网连接; c、教工账号同一时刻可以3台设备在线,超限也会被强制下线; d、更换认证方式,使用HUST_WIRELESS_AUTO或无感知认证; e、上述方式无法解决及时拨打报修电话。 4、手机在连接HUST_WIRELESS_AUTO的时候,Wi-Fi图标显示满格信号,但是微信是“未连接”状态,该怎么处理? 答:关闭手机Wi-Fi再打开,重新连接HUST_WIRELESS_AUTO。 5、笔记本连接无线频繁掉线,提示用户名或密码不匹配,重连能连上,该如何处理? 答:这是由于(或者是安装的Wi-Fi热点软件)勾选了“启用此网络的IEEE 802.1x身份验证”导致的环境冲突。处理方法:在“本地连接(或者以太网)”的属性的“身份验证”页中,取消“启用此网络的IEEE 802.1x身份验证”即可。 6、有时走在路上连上了校园无线网但无法正常使用,该怎么办? 答:无线网技术主要适用于室内,即使部署了无线网的室外区域也经常因为各种原因导致各种掉线或信号弱,此时无线网信号是部署有无线网的楼栋飘出的信号,建议此时关闭无线网络,到室内部署有无线网的区域进行无线上网。 7、东1/6/7/9-13舍、西8/9舍在断电之后恢复来电,宿舍有线网(实际是由无线AP上分出的有线)连接不上该怎么办? 答:将宿舍所有电脑上连接的网线全部拔下,等待5分钟,再将网线插上即可正常使用,如此方法无效,请及时报修。
对于计算机网络仿真组网校园网的综合组网,可以使用华为企业网络模拟平台(eNSP)进行实现。以下是一种可能的校园网组网方案: 1. 设备选择:选择合适的设备模拟器或虚拟机,如eNSP中的路由器、交换机等设备,用于模拟真实网络设备的功能。 2. 网络拓扑设计:根据校园网的需求和规模,设计出符合需求的网络拓扑结构,包括核心层、汇聚层和接入层的搭建。 3. IP地址规划:为各个网络设备分配合适的IP地址,并进行子网划分,确保网络中的每个子网都有足够的IP地址可供使用。 4. VLAN设计:根据需求,将不同的用户或服务划分到不同的VLAN中,以实现网络资源的隔离和管理。 5. 路由协议配置:根据具体需求选择合适的路由协议,如OSPF、BGP等,并在各个设备上进行路由配置,以实现网络间的通信和路径选择。 6. 交换机配置:配置交换机的端口、VLAN、STP等,以实现局域网内部的通信和数据转发。 7. 安全策略配置:配置网络设备的安全策略,如ACL、防火墙等,以保护网络免受恶意攻击和非法访问。 8. 服务配置:根据需求配置各种服务,如DHCP、DNS、Web服务器等,以提供网络中的各种服务功能。 9. 故障排除:模拟各种故障场景,并进行故障排除,以提高网络的可靠性和稳定性。 10. 性能评估:对组建好的校园网进行性能评估,如带宽利用率、延迟、丢包率等,以优化和改进网络设计。 以上是一个大致的组网流程,具体实施时还需根据实际需求和环境进行调整和配置。希望能对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值