全世界最(不)好的目录
- 一、需求分析
- 二、实现过程
- 2.1 分析api
- 2.1.1 连接到校园网,自动弹出登录认证界面
- 2.1.2 先输入错误的账号密码,按F12看会获取哪些信息
- 2.1.3 api
- 2.2 分析加密
- 2.3 流程总结
- 三.模拟登录
- 3.1 编写配置文件
- 3.2.功能封装与定时
- 四.题外话
一、需求分析
指路:https://www.srun.com/cn/list/info?id=44
Srun4K 校园网解决方案,这群人自以为自己的系统搞得很好,可以帮助使用者偷懒的管理网络,但是对于用户端其实每天充斥着断网、卡顿、认证不跳等诸多问题,所以写个客户端便捷的进行断网重连。
二、实现过程
这里本来不想写的,但是某人要求教教他抓包,我就不点名了
由于人在公司,所以截图部分等我想弄的时候再弄吧
2.1 分析api
2.1.1 连接到校园网,自动弹出登录认证界面
http://172.19.0.5/srun_portal_success?ac_id=28&theme=pro&wlanacname=&wlanuserip=10.161.21.150
2.1.2 先输入错误的账号密码,按F12看会获取哪些信息
2.1.3 api
get_challenge
请求方式为get,地址为
http://172.19.0.5/cgi-bin/get_challenge
请求的数据为
参数 | 分析 |
---|---|
callback | jsonp解决跨域的参数 |
username | 校园网账户 |
ip | 本机自动获取到的ip |
_ | 当前时间戳(32)位 |
这里是不需要分析的。唯一需要注意的是challenge,因为太过诡异。
srun_portal
请求方式为get,地址为
http://172.19.0.5/cgi-bin/srun_portal
请求数据为
看到这一大串,可能不太好下手。确实参数有点多。
多观察几次,先找不变的参数,排除掉。
参数 | 分析 |
---|---|
callback | 和第一次的callback意思一样,排除 |
action | login |
username | 账户 |
ac_id | 1(意义不详,每次都一样可以排除了) |
ip | 自动获取的ip |
n | 200(和ac_id一样排除) |
type | 1(和ac_id一样排除) |
os | windows10 |
name | windows |
double_stack | 0 |
_ | 当前时间戳(13位) |
一共14个参数,一下子pass11个,还剩下3个参数分别是 password chksum info
接下来开始分析js文件,一共4个
搜索关键字:password
这个是什么,这个不就是我们请求的参数吗,一下全都出来了,继续搜索对应关键字。
hmd5就找出来了,是经过md5加密之后的密码,还做过加盐处理
至于token就是之前的challenge
接下来是chksum一样的方法,搜索关键字。
可以发现是简单的字符串拼接。然后用sha1加密得出chksum。
可是还有个i是什么呢。继续找吧
都出来了,对象转为字符串之后进行xEncode(天知道是什么加密方式)进行加密,反正也是加盐的加密。也用到了token。之后再进行一次base64加密。i就出来了
i出来了,chksum也出来了。顺序不能错。
处理顺序
参数 | 加密方式 |
---|---|
password | Hmac MD5加密 |
chksum | sha1处理 |
3个参数都需要用到token,至于加密方式更加是坑 | |
只有sha1和md5是可以直接用的。 | |
xencode更加不知道是什么鬼东西,base64是经过改动的。和正常的base64是不一样的。 |
2.2 分析加密
python版的xencode
import math
def force(msg):
ret = []
for w in msg:
ret.append(ord(w))
return bytes(ret)
def ordat(msg, idx):
if len(msg) > idx:
return ord(msg[idx])
return 0
def sencode(msg, key):
l = len(msg)
pwd = []
for i in range(0, l, 4):
pwd.append(
ordat(msg, i) | ordat(msg, i + 1) << 8 | ordat(msg, i + 2) << 16
| ordat(msg, i + 3) << 24)
if key:
pwd.append(l)
return pwd
def lencode(msg, key):
l = len(msg)
ll = (l - 1) << 2
if key:
m = msg[l - 1]
if m < ll - 3 or m > ll:
return
ll = m
for i in range(0, l):
msg[i] = chr(msg[i] & 0xff) + chr(msg[i] >> 8 & 0xff) + chr(
msg[i] >> 16 & 0xff) + chr(msg[i] >> 24 & 0xff)
if key:
return "".join(msg)[0:ll]
return "".join(msg)
def get_xencode(msg, key):
if msg == "":
return ""
pwd = sencode(msg, True)
pwdk = sencode(key, False)
if len(pwdk) < 4:
pwdk = pwdk + [0] * (4 - len(pwdk))
n = len(pwd) - 1
z = pwd[n]
y = pwd[0]
c = 0x86014019 | 0x183639A0
m = 0
e = 0
p = 0
q = math.floor(6 + 52 / (n + 1))
d = 0
while 0 < q:
d = d + c & (0x8CE0D9BF | 0x731F2640)
e = d >> 2 & 3
p = 0
while p < n:
y = pwd[p + 1]
m = z >> 5 ^ y << 2
m = m + ((y >> 3 ^ z << 4) ^ (d ^ y))
m = m + (pwdk[(p & 3) ^ e] ^ z)
pwd[p] = pwd[p] + m & (0xEFB8D130 | 0x10472ECF)
z = pwd[p]
p = p + 1
y = pwd[0]
m = z >> 5 ^ y << 2
m = m + ((y >> 3 ^ z << 4) ^ (d ^ y))
m = m + (pwdk[(p & 3) ^ e] ^ z)
pwd[n] = pwd[n] + m & (0xBB390742 | 0x44C6F8BD)
z = pwd[n]
q = q - 1
return lencode(pwd, False)
python版base64
_PADCHAR = "="
_ALPHA = "LVoJPiCN2R8G90yg+hmFHuacZ1OWMnrsSTXkYpUq/3dlbfKwv6xztjI7DeBE45QA"
def _getbyte(s, i):
x = ord(s[i]);
if (x > 255):
print("INVALID_CHARACTER_ERR: DOM Exception 5")
exit(0)
return x
def get_base64(s):
i=0
b10=0
x = []
imax = len(s) - len(s) % 3;
if len(s) == 0:
return s
for i in range(0,imax,3):
b10 = (_getbyte(s, i) << 16) | (_getbyte(s, i + 1) << 8) | _getbyte(s, i + 2);
x.append(_ALPHA[(b10 >> 18)]);
x.append(_ALPHA[((b10 >> 12) & 63)]);
x.append(_ALPHA[((b10 >> 6) & 63)]);
x.append(_ALPHA[(b10 & 63)])
i=imax
if len(s) - imax ==1:
b10 = _getbyte(s, i) << 16;
x.append(_ALPHA[(b10 >> 18)] + _ALPHA[((b10 >> 12) & 63)] + _PADCHAR + _PADCHAR);
else:
b10 = (_getbyte(s, i) << 16) | (_getbyte(s, i + 1) << 8);
x.append(_ALPHA[(b10 >> 18)] + _ALPHA[((b10 >> 12) & 63)] + _ALPHA[((b10 >> 6) & 63)] + _PADCHAR);
return "".join(x)
md5
import hmac
import hashlib
def get_md5(password,token):
return hmac.new(token.encode(), password.encode(), hashlib.md5).hexdigest()
sha1
import hmac
import hashlib
def get_md5(password,token):
return hmac.new(token.encode(), password.encode(), hashlib.md5).hexdigest()
2.3 流程总结
第一次get_challenge是获取token。
中间做了3个信息的处理
最后一步就是登录和认证了
三.模拟登录
完整代码如下:
#coding=utf-8
import ctypes
import hashlib
import json
import math
import random
import socket
import time
import requests
conf = ConfigParser()
conf.read("1.config",encoding='utf-8')
username = conf.get('mysql', 'username')
password = conf.get('mysql', 'password')
init_url="http://172.19.0.5"
def init_getip():
"""网络获得ip(似乎看起来更靠谱) """
init_res=requests.get(init_url,headers=header)
print("初始化获取ip")
ip=re.search('id="wlanuserip" value="(.*?)"',init_res.text).group(1)
return ip
def GetLocalIPByPrefix(prefix):
""" 多网卡情况下,根据前缀获取IP(Windows 下适用) """
localIP = ''
for ip in socket.gethostbyname_ex(socket.gethostname())[2]:
if ip.startswith(prefix):
localIP = ip
return localIP
local_ip=GetLocalIPByPrefix('10.')
#local_ip=init_getip()
class MD5(object):
'''MD5加密//根据js'''
def a(self, n):
r = ''
e = 32 * len(n)
for i in range(0,e,8):
r += chr((n[i >> 5] >> i % 32 & 0xffffffff >> i % 32) & 255)
return r
def d(self, n):
r = [0 for _ in range(len(n) >> 2)]
e = 8 * len(n)
for i in range(0,e,8):
if i >> 5 == len(r):
r.append(0)
r[i >> 5] |= ctypes.c_int32((255 & ord(n[i // 8])) << i % 32).value
return r
def t(self, n, t):
r = (65535 & n) + (65535 & t)
a = ctypes.c_int32(n >> 16).value
b = ctypes.c_int32(t >> 16).value
c = ctypes.c_int32(r >> 16).value
d = ctypes.c_int32(a + b + c << 16).value
return d | 65535 & r
def r(self, n, t):
return ctypes.c_int32(n << t).value | (n >> 32 - t & 0xffffffff >> 32 - t)
def e(self, n, e, o, u, c, f):
return self.t(self.r(self.t(self.t(e, n), self.t(u, f)), c), o)
def o(self, n, t, r, o, u, c, f):
return self.e(t & r | ~t & o, n, t, u, c, f)
def u(self, n, t, r, o, u, c, f):
return self.e(t & o | r & ~o, n, t, u, c, f)
def c(self, n, t, r, o, u, c, f):
return self.e(t ^ r ^ o, n, t, u, c, f)
def f(self, n, t, r, o, u, c, f):
return self.e(r ^ (t | ~o), n, t, u, c, f)
def i(self, n, r):
x = 14 + (r + 64 >> 9 << 4) + 1
n.extend([0 for _ in range(x - len(n))])
n[-1] = r
n[r >> 5] |= ctypes.c_int32(128 << r % 32).value
l = 1732584193
g = -271733879
v = -1732584194
m = 271733878
for j in range(0,len(n),16):
if j + 15 >= len(n):
x = j + 15 - len(n) + 1
for _ in range(x):
n.append(0)
i = l
a = g
d = v
h = m
l = self.o(l, g, v, m, n[j], 7, -680876936)
m = self.o(m, l, g, v, n[j + 1], 12, -389564586)
v = self.o(v, m, l, g, n[j + 2], 17, 606105819)
g = self.o(g, v, m, l, n[j + 3], 22, -1044525330)
l = self.o(l, g, v, m, n[j + 4], 7, -176418897)
m = self.o(m, l, g, v, n[j + 5], 12, 1200080426)
v = self.o(v, m, l, g, n[j + 6], 17, -1473231341)
g = self.o(g, v, m, l, n[j + 7], 22, -45705983)
l = self.o(l, g, v, m, n[j + 8], 7, 1770035416)
m = self.o(m, l, g, v, n[j + 9], 12, -1958414417)
v = self.o(v, m, l, g, n[j + 10], 17, -42063)
g = self.o(g, v, m, l, n[j + 11], 22, -1990404162)
l = self.o(l, g, v, m, n[j + 12], 7, 1804603682)
m = self.o(m, l, g, v, n[j + 13], 12, -40341101)
v = self.o(v, m, l, g, n[j + 14], 17, -1502002290)
g = self.o(g, v, m, l, n[j + 15], 22, 1236535329)
l = self.u(l, g, v, m, n[j + 1], 5, -165796510)
m = self.u(m, l, g, v, n[j + 6], 9, -1069501632)
v = self.u(v, m, l, g, n[j + 11], 14, 643717713)
g = self.u(g, v, m, l, n[j], 20, -373897302)
l = self.u(l, g, v, m, n[j + 5], 5, -701558691)
m = self.u(m, l, g, v, n[j + 10], 9, 38016083)
v = self.u(v, m, l, g, n[j + 15], 14, -660478335)
g = self.u(g, v, m, l, n[j + 4], 20, -405537848)
l = self.u(l, g, v, m, n[j + 9], 5, 568446438)
m = self.u(m, l, g, v, n[j + 14], 9, -1019803690)
v = self.u(v, m, l, g, n[j + 3], 14, -187363961)
g = self.u(g, v, m, l, n[j + 8], 20, 1163531501)
l = self.u(l, g, v, m, n[j + 13], 5, -1444681467)
m = self.u(m, l, g, v, n[j + 2], 9, -51403784)
v = self.u(v, m, l, g, n[j + 7], 14, 1735328473)
g = self.u(g, v, m, l, n[j + 12], 20, -1926607734)
l = self.c(l, g, v, m, n[j + 5], 4, -378558)
m = self.c(m, l, g, v, n[j + 8], 11, -2022574463)
v = self.c(v, m, l, g, n[j + 11], 16, 1839030562)
g = self.c(g, v, m, l, n[j + 14], 23, -35309556)
l = self.c(l, g, v, m, n[j + 1], 4, -1530992060)
m = self.c(m, l, g, v, n[j + 4], 11, 1272893353)
v = self.c(v, m, l, g, n[j + 7], 16, -155497632)
g = self.c(g, v, m, l, n[j + 10], 23, -1094730640)
l = self.c(l, g, v, m, n[j + 13], 4, 681279174)
m = self.c(m, l, g, v, n[j], 11, -358537222)
v = self.c(v, m, l, g, n[j + 3], 16, -722521979)
g = self.c(g, v, m, l, n[j + 6], 23, 76029189)
l = self.c(l, g, v, m, n[j + 9], 4, -640364487)
m = self.c(m, l, g, v, n[j + 12], 11, -421815835)
v = self.c(v, m, l, g, n[j + 15], 16, 530742520)
g = self.c(g, v, m, l, n[j + 2], 23, -995338651)
l = self.f(l, g, v, m, n[j], 6, -198630844)
m = self.f(m, l, g, v, n[j + 7], 10, 1126891415)
v = self.f(v, m, l, g, n[j + 14], 15, -1416354905)
g = self.f(g, v, m, l, n[j + 5], 21, -57434055)
l = self.f(l, g, v, m, n[j + 12], 6, 1700485571)
m = self.f(m, l, g, v, n[j + 3], 10, -1894986606)
v = self.f(v, m, l, g, n[j + 10], 15, -1051523)
g = self.f(g, v, m, l, n[j + 1], 21, -2054922799)
l = self.f(l, g, v, m, n[j + 8], 6, 1873313359)
m = self.f(m, l, g, v, n[j + 15], 10, -30611744)
v = self.f(v, m, l, g, n[j + 6], 15, -1560198380)
g = self.f(g, v, m, l, n[j + 13], 21, 1309151649)
l = self.f(l, g, v, m, n[j + 4], 6, -145523070)
m = self.f(m, l, g, v, n[j + 11], 10, -1120210379)
v = self.f(v, m, l, g, n[j + 2], 15, 718787259)
g = self.f(g, v, m, l, n[j + 9], 21, -343485551)
l = self.t(l, i)
g = self.t(g, a)
v = self.t(v, d)
m = self.t(m, h)
return [l, g, v, m]
def l(self, n, t):
o = self.d(n)
u = [0 for _ in range(16)]
c = [0 for _ in range(16)]
if len(o) > 16:
o = self.i(o, 8 * len(n))
for j in range(16):
u[j] = 909522486 ^ o[j]
c[j] = 1549556828 ^ o[j]
u.extend(self.d(t))
e = self.i(u, 512 + 8 * len(t))
c.extend(e)
r = self.a(self.i(c, 640))
return r
def s(self, n, t):
return self.l(n, t)
def g(self, n):
d = list('0123456789abcdef')
e = []
for c in list(n):
c = ord(c)
e.append(d[c >> 4 & 15])
e.append(d[15 & c])
r = ''.join(e)
return r
def C(self, n, t):
return self.g(self.s(n, t))
def __call__(self, password, token):
return self.C(token, password)
class BASE64:
#js:atob() base64加密
def __init__(self):
self.base64Alpha = 'LVoJPiCN2R8G90yg+hmFHuacZ1OWMnrsSTXkYpUq/3dlbfKwv6xztjI7DeBE45QA'
def encode(self, s):
r = []
x = len(s) % 3
if x:
s = s + '\0'*(3 - x)
for i in range(0,len(s),3):
d = s[i:i+3]
a = ord(d[0]) << 16 | ord(d[1]) << 8 | ord(d[2])
r.append(self.base64Alpha[a>>18])
r.append(self.base64Alpha[a>>12 & 63])
r.append(self.base64Alpha[a>>6 & 63])
r.append(self.base64Alpha[a & 63])
if x == 1:
r[-1] = '='
r[-2] = '='
if x == 2:
r[-1] = '='
return ''.join(r)
def getTime():
#取时间 等同于js里面的 Date.toValue()
t = time.time()
return int(round(t * 1000))
callback = 'jQuery{0}_{1}'.format(random.getrandbits(100), getTime())
def get_challenge():
#登录认证第一步
url = 'http://172.19.0.5/cgi-bin/get_challenge'
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36 Edg/95.0.1020.30'
}
#callback = 'jQuery{0}_{1}'.format(random.getrandbits(100), getTime())
params = {'callback':callback,
'username':username,
'ip':local_ip,
'_':getTime()}
r = requests.get(url, params=params, headers=headers)
#print(r.url)
print(r.text)
data = r.text[len(callback)+1:-1]
token = json.loads(data)['challenge']
return token
def statusTest():
#ping 百度来测试网络是否连通
import subprocess
ret = subprocess.run("ping baidu.com -n 1", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
s1 = str(ret.stdout, encoding='gbk')
return True if "0% 丢失" in s1 else False
def encode_userinfo(userinfo, token):
#对用户信息进行SRBX1
userinfo = json.dumps(userinfo).replace(' ','')
if len(userinfo) == 0:
return ''
sc = len(userinfo)
if sc % 4:
userinfo += '\0' * (4-(sc%4))
sv = []
for i in range(0, sc, 4):
while i >> 2 >= len(sv):
sv.append(0)
sv[i >> 2] = ord(userinfo[i]) | ctypes.c_int32(ord(userinfo[i+1]) << 8).value | \
ctypes.c_int32(ord(userinfo[i+2]) << 16).value | ctypes.c_int32(ord(userinfo[i+3]) << 24).value
sv.append(sc)
v = sv[:]
sc = len(token)
sv = []
for i in range(0, sc, 4):
while i >> 2 >= len(sv):
sv.append(0)
sv[i >> 2] = ord(token[i]) | ctypes.c_int32(ord(token[i+1]) << 8).value | \
ctypes.c_int32(ord(token[i+2]) << 16).value | ctypes.c_int32(ord(token[i+3]) << 24).value
k = sv[:]
while len(k) < 4:
k.append(0)
n = len(v) - 1
z = v[n]
y = v[0]
c = ctypes.c_int32(0x86014019 | 0x183639A0).value
q = math.floor(6 + 52 / (n + 1))
d = 0
m = None
e = None
while q > 0:
d = d + c & (0x8CE0D9BF | 0x731F2640)
d = ctypes.c_int32(d).value
e = (d >> 2 & 0xFFFFFFFF >> 2) & 3
for p in range(n):
y = v[p + 1]
m = (z >> 5 & 0xFFFFFFFF >> 5) ^ ctypes.c_int32(y << 2).value
m += (y >> 3 & 0xFFFFFFFF >> 3) ^ ctypes.c_int32(z << 4).value ^ (d ^ y)
m += k[p & 3 ^ e] ^ z
v[p] = ctypes.c_int32(v[p] + m & (0xEFB8D130 | 0x10472ECF)).value
z = v[p]
y = v[0]
m = (z >> 5 & 0xFFFFFFFF >> 5) ^ ctypes.c_int32(y << 2).value
m += (y >> 3 & 0xFFFFFFFF >> 3) ^ ctypes.c_int32(z << 4).value ^ (d ^ y)
m += k[(p + 1) & 3 ^ e] ^ z
v[n] =ctypes.c_int32(v[n] + m & (0xBB390742 | 0x44C6F8BD)).value
z = v[n]
q -= 1
lv = v[:]
ld = len(lv)
lc = ctypes.c_int32(ld - 1 << 2).value
for i in range(ld):
lv[i] = ''.join([chr(lv[i] & 0xff), chr((lv[i] >> 8 & 0xFFFFFFFF >> 8) & 0xff),
chr((lv[i] >> 16 & 0xFFFFFFFF >> 16) & 0xff),
chr((lv[i] >> 24 & 0xFFFFFFFF >> 24) & 0xff)])
l = ''.join(lv)
base64 = BASE64()
return r'{SRBX1}' + base64.encode(l)
def srun_portal(username,password):
#登录认证第二部
url = 'http://172.19.0.5/cgi-bin/srun_portal'
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.47'
}
#callback = 'jQuery{0}_{1}'.format(random.getrandbits(100),getTime())
md5 = MD5()
token= get_challenge()
hmd5 = md5(password, token)
userinfo = {'username':username,
'password':password,
'ip':local_ip,
'acid':'28',
'enc_ver': 'srun_bx1'
}
info = encode_userinfo(userinfo, token)
chkstr = token+username+token+hmd5+token+'28'+token+local_ip+token+'200'+token+'1'+token+info
sha1 = hashlib.sha1()
sha1.update(chkstr.encode())
chksum = sha1.hexdigest()
params = {'callback':callback,
'action':'login',
'username':username,
'password':r'{MD5}' + hmd5,
'os':'Windows10',
'name':'Windows',
'double_stack':0,
'chksum':chksum,
'info':info,
'ac_id':28,
'ip':local_ip,
'n':200,
'type':1,
'_':getTime()}
r = requests.get(url, params=params, headers=headers)
print(r.text)
if 'Login is successful' in r.text:
print('登录认证成功!')
elif 'ip_already_online' in r.text:
print("您已在线!")
elif statusTest()==True:
print("您虽然不在线但是有网")
else:
print('登陆失败,请检查acid或者密码是否正确')
def main():
if local_ip == '':
print('本机IP获取失败,请检查网络连接')
username=input("用户名")
password=input("密码")
print('登录用户名:{}'.format(username))
print('本机ipv4地址:{}'.format(local_ip))
srun_portal(username,password)
if __name__ == '__main__':
temp=local_ip
while True:
main()
time.sleep(3600)
3.1 编写配置文件
配置文件如下:
1.config
[mysql]
username=
password=
3.2.功能封装与定时
完成上面的步骤之后我们就可以开始对登陆代码进行封装了,然后定时一小时重新登陆一次即可。
四.题外话
校园网安全认证和提高信息技术水平有个毛关系?要么是为了限制设备数圈钱(说这的那的,网费和居民区一样贵服务还差),要么就是…
总之没见过这么对付的商业计划书。