高校教务系统登录页面JS分析——安徽大学
高校教务系统密码加密逻辑及JS逆向
最近有粉丝说安徽大学的教务系统换了,之前用的是正方出品的系统,今天我来看看新版教务系统怎么模拟登录,总体来说,还是比较简单的,就是一个哈希加密了密码,其次就是一个滑块验证码,本文都会一一来介绍。
本文将介绍高校教务系统的密码加密逻辑以及使用JavaScript进行逆向分析的过程。通过本文,你将了解到密码加密的基本概念、常用加密算法以及如何通过逆向分析来破解密码。
本文仅供交流学习,勿用于非法用途。
一、密码加密基本概念
密码加密是一种保护信息安全的技术手段,它通过将明文(原始信息)转换为密文(加密后的信息),以防止未经授权的访问和篡改。常见的密码加密算法有MD5、SHA-1、SHA-256等。
1.1 加密过程
加密过程通常包括以下步骤:
- 密钥扩展:将密钥扩展为多个轮值,每个轮值都与明文的一部分有关。
- 初始轮值生成:将扩展后的密钥与轮常数进行某种运算,生成第一轮加密的密文。
- 多轮迭代:对密文进行多轮迭代操作,每轮操作都包括非线性函数、模运算和轮常数的变换。
- 最终密文:经过多轮迭代后,得到最终的密文。
1.2 解密过程
解密过程与加密过程相反,通过反向操作来恢复原始明文。通常需要知道加密时使用的密钥和算法。
二、高校教务系统密码加密逻辑分析
2.1 抓包
我们首先打开教务系统的登录页面,我们可以看到,只有学号和密码,安徽大学是错误一次密码,会验证验证码,我们后面再说。
ps:这里系统有个小问题,有个测试账号估计安徽大学忘记删了。
密码错误一次后会触发滑块验证码,都没用极验验证码等主流的滑块验证码,都没加密,好搞。
我们打开开发者工具,尝试登录抓包,网页会返回这样的数据接口。我们用户名和密码都是默认输入123456,你也可以输入其他的。
2.2 分析加密参数
我们接下来,就是来分析这个密码是怎么加密的。我们全局搜索password。定位到加密的位置。
发现上面有一行字符串,我们仔细一看,就是加密方法。这里很简单,就是普通的哈希加密。
三、JS逆向分析方法
逆向分析是指从已知的加密文本或程序中还原出原始信息的过程。在本例中,我们将使用JavaScript编写一个简单的逆向分析工具,用于逆向高校教务系统的密码。
环境使用
- python 3.9
- pycharm
- node
代码实现
我们就不去扣js代码了,直接用python实现。这里的salt就是服务器返回的值,也就是这个接口返回的密钥:https://jw.ahu.edu.cn/student/login-salt
import hashlib
def sha1_hash(text):
# 创建一个sha1 hash对象
sha1 = hashlib.sha1()
# 使用输入的文本更新hash对象
sha1.update(text.encode('utf-8'))
# 得到哈希摘要,并转换为十六进制字符串
hash_output = sha1.hexdigest()
return hash_output
new_password = salt + '-' + str("密码")
h_password = sha1_hash(new_password)
滑块验证码
我们今天来讲一下这个验证码,其实很简单,我们先看接口,总共有两个接口,一个是获取验证码图片的,一个是提交验证的。
login-captcha
我们先来看第一个接口,传入了两个参数,一个是ts,也就是时间戳,第二个是clientId。这个clientId是本地生成的,不难找到生成算法,我直接给大家了,原文算法是这样的:
uuid: function () {
var s = []
var hexDigits = '0123456789abcdef'
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)
}
s[14] = '4' // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1) // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23] = '-'
return s.join('')
}
能用python实现,我们就不要用js,下面是我们的python代码:
def generate_uuid():
hexDigits = '0123456789abcdef'
s = [''] * 36
for i in range(36):
if i in [8, 13, 18, 23]:
s[i] = '-'
else:
s[i] = hexDigits[random.randint(0, 0xf)]
# 设置特定的位以匹配JavaScript代码
s[14] = '4' # 设置版本号为4(0100)
# 设置时钟序列的变体部分
variant = int(s[19], 16) & 0x3 # 获取当前s[19]的低两位
s[19] = hexDigits[(variant | 0x8) & 0xf] # 设置变体为RFC 4122(1000)
return ''.join(s)
# 生成UUID
uuid = generate_uuid()
# print(uuid)
我们可以看到返回了验证码图片,这里是以base64数据返回的,我们保存到本地就可以。
下面是保存验证图片的代码,以便于我们后续比较:
import base64
def save_base64_image(base64_string, file_path):
"""
将 Base64 编码的图片保存到本地文件
:param base64_string: Base64 编码的图片字符串
:param file_path: 保存图片的文件路径
"""
# 将 Base64 字符串解码为二进制数据
image_data = base64.b64decode(base64_string)
# 将二进制数据写入文件
with open(file_path, "wb") as file:
file.write(image_data)
print(f"图片已保存到 {file_path}")
# 调用函数保存图片
save_base64_image(jigsawImageBase64, "jsgsaw.jpg")
save_base64_image(originalImageBase64, "originalImage.jpg")
login-captcha-check
我们看看第二个接口,这里传入了四个参数,一个是ts,一个是clientid,前面提及到过,token的值是上一个接口返回的,都不需要出,第四个参数,也就是我们要处理的值,首先,我们知道X是我们移动的距离,Y是0~100的随机值,其次,这些数据都没有加密,那就很简单了,我们直接算出X移动的距离就好了,简而言之,就是计算缺口图片缺口的位置。
我们这里用cv2库,直接实现,直接调用,没啥难度。
from PIL import Image
import cv2
import numpy as np
def pilImgToCv2(img: Image.Image, flag=cv2.COLOR_RGB2BGR):
return cv2.cvtColor(np.asarray(img), flag)
def getDistance(imgpath, sliceimgpath):
img = Image.open(imgpath)
slice = Image.open(sliceimgpath)
# 通过 pilImgToCv2 将图片置灰
# 背景图和滑块图都需要做相同处理
grayImg = pilImgToCv2(img, cv2.COLOR_BGR2GRAY)
# img.show(grayImg)
# showImg(grayImg) # 可以通过它来看处理后的图片效果
graySlice = pilImgToCv2(slice, cv2.COLOR_BGR2GRAY)
# 做边缘检测进一步降低干扰,阈值可以自行调整
grayImg = cv2.Canny(grayImg, 255, 255)
# showImg(grayImg) # 可以通过它来看处理后的图片效果
graySlice = cv2.Canny(graySlice, 255, 255)
# 通过模板匹配两张图片,找出缺口的位置
result = cv2.matchTemplate(grayImg, graySlice, cv2.TM_CCOEFF_NORMED)
maxLoc = cv2.minMaxLoc(result)[3]
# 匹配出来的滑动距离
distance = maxLoc[0]
# print(distance)
return distance
sliceimgpath = 'jsgsaw.jpg'
imgpath = 'originalImage.jpg'
x_dis= getDistance(imgpath, sliceimgpath)
验证码通过验证之后,会返回这样的结果:
{
"code": "0000",
"message": "成功",
"originalImageBase64": null,
"jigsawImageBase64": null,
"token": null,
"originalImageWidth": 0,
"success": true
}
全部代码
下面是这个模拟登录的全部代码,不排除后期这个系统维护升级导致代码失效。
#!/usr/bin/python3.9.6
# _*_ coding: utf-8 _*_
#
# Copyright (C) 2022 - 2024 BROKEN, Inc. All Rights Reserved
#
# @Time : 2025/1/5 16:34
# @Author : 爱吃饼干的小白鼠
# @File : 安徽大学教务系统(新).py
# @IDE : PyCharm
# @Blog :https://broken.blog.csdn.net/
import requests
import json
import time
import random
import hashlib
import base64
from PIL import Image
import cv2
import numpy as np
def sha1_hash(text):
# 创建一个sha1 hash对象
sha1 = hashlib.sha1()
# 使用输入的文本更新hash对象
sha1.update(text.encode('utf-8'))
# 得到哈希摘要,并转换为十六进制字符串
hash_output = sha1.hexdigest()
return hash_output
session = requests.Session()
headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Language": "zh-CN,zh;q=0.9",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Pragma": "no-cache",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-User": "?1",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.62",
}
url = "https://jw.ahu.edu.cn/student/login"
response = session.get(url, headers=headers)
url = "http://jw.ahu.edu.cn/student/login-salt"
salt = session.get(url, headers=headers).text
new_password = salt + '-' + str("123456")
h_password = sha1_hash(new_password)
def generate_uuid():
hexDigits = '0123456789abcdef'
s = [''] * 36
for i in range(36):
if i in [8, 13, 18, 23]:
s[i] = '-'
else:
s[i] = hexDigits[random.randint(0, 0xf)]
# 设置特定的位以匹配JavaScript代码
s[14] = '4' # 设置版本号为4(0100)
# 设置时钟序列的变体部分
variant = int(s[19], 16) & 0x3 # 获取当前s[19]的低两位
s[19] = hexDigits[(variant | 0x8) & 0xf] # 设置变体为RFC 4122(1000)
return ''.join(s)
# 生成UUID
uuid = generate_uuid()
timetemp = int(time.time()*1000)
url = "https://jw.ahu.edu.cn/student/login-captcha"
data = {
"ts": timetemp,
"clientId": uuid
}
response = session.post(url, json=data)
jigsawImageBase64 = response.json()['jigsawImageBase64']
originalImageBase64 = response.json()['originalImageBase64']
token = response.json()['token']
def save_base64_image(base64_string, file_path):
"""
将 Base64 编码的图片保存到本地文件
:param base64_string: Base64 编码的图片字符串
:param file_path: 保存图片的文件路径
"""
# 将 Base64 字符串解码为二进制数据
image_data = base64.b64decode(base64_string)
# 将二进制数据写入文件
with open(file_path, "wb") as file:
file.write(image_data)
# 调用函数保存图片
save_base64_image(jigsawImageBase64, "jsgsaw.jpg")
save_base64_image(originalImageBase64, "originalImage.jpg")
def pilImgToCv2(img: Image.Image, flag=cv2.COLOR_RGB2BGR):
return cv2.cvtColor(np.asarray(img), flag)
def getDistance(imgpath, sliceimgpath):
img = Image.open(imgpath)
slice = Image.open(sliceimgpath)
# 通过 pilImgToCv2 将图片置灰
# 背景图和滑块图都需要做相同处理
grayImg = pilImgToCv2(img, cv2.COLOR_BGR2GRAY)
# img.show(grayImg)
# showImg(grayImg) # 可以通过它来看处理后的图片效果
graySlice = pilImgToCv2(slice, cv2.COLOR_BGR2GRAY)
# 做边缘检测进一步降低干扰,阈值可以自行调整
grayImg = cv2.Canny(grayImg, 255, 255)
# showImg(grayImg) # 可以通过它来看处理后的图片效果
graySlice = cv2.Canny(graySlice, 255, 255)
# 通过模板匹配两张图片,找出缺口的位置
# 通过模板匹配两张图片,找出缺口的位置
result = cv2.matchTemplate(grayImg, graySlice, cv2.TM_CCOEFF_NORMED)
maxLoc = cv2.minMaxLoc(result)[3]
# 匹配出来的滑动距离
distance = maxLoc[0]
return distance
sliceimgpath = 'jsgsaw.jpg'
imgpath = 'originalImage.jpg'
x_dis= getDistance(imgpath, sliceimgpath)
url = "https://jw.ahu.edu.cn/student/login-captcha-check"
data = {
"point": {
"x": x_dis,
"y": random.randint(0, 99)# 生成一个0到99之间的随机整数
},
"token": token,
"ts": timetemp+23253,
"clientId": uuid
}
response = session.post(url, json=data)
print(response.text)
url = "https://jw.ahu.edu.cn/student/login"
data = {
"username": "123456",
"password": h_password,
"captchaToken": token
}
response = session.post(url,headers=headers, json=data)
print(response.text)
由于我们账号和密码是错误的(大家也可以用测试账号12345模拟登录,密码也是12345,看看效果吧),所以返回密码错误,但是会提示验证码正确("needCaptcha":true),就说明我们滑块验证码破解成功了,下面是运行结果:
四、总结
本文介绍了高校教务系统的密码加密逻辑以及使用JavaScript进行逆向分析的方法。通过学习这些知识,你可以更好地理解密码加密技术的原理,并掌握一定的逆向分析技巧。请注意,逆向分析可能涉及到法律问题,请在合法范围内进行研究和实践。
五、累计更新
争取到到底早日更新30所高校,大家可以在评论区留言。前期更的可能会多一点,有的学校教务系统都没有加密,我这里就不写了,还有,部分学校的教务系统已经和我之前写的是一样的,我也不重复赘述了。
往期作品可以查看专栏👇👇👇
全国高校教务系统登录页面JS分析_爱吃饼干的小白鼠的博客-CSDN博客