记得在 2021-2022 上学期的时候,第一版学时通系统问世,这个系统主要是用来查学生的学时信息。当时发现了系统比较简陋,没有验证码防护,账号为学号,密码为学号后六位,让我感觉数据等同于在裸奔。
为了实现一些功能,我开始收集不同年级、不同专业、班级等学号规则信息,大概摸清了一些学号规则后写了一个简单爬虫。爬虫的主要功能就是检测学号是否存在,简言之也就是扫号。
爬虫程序跑了几次后,发现了有一个反扒机制:
- 防频繁提交,连续提交 3 次后,需要 60 秒后才能尝试。
- 针对一个内网 IP 进行封锁。
针对内网 IP 来受控,太 xxx 了!!!这就是你这么拽不加验证码的原因吗?
由于当时在长沙培训,为了比赛,后来就不了了之了。
当时的登录接口(现在停用了):
POST http://192.168.51.200/webapi/login/
请求(json)
{
"uid": "",
"password": ""
}
响应
{
"msg": "",
"uid": "",
"name": "",
"grade": "",
"academy": "",
"clazz": "",
"credit": "",
"sxdd_credit": "",
"cxcy_credit": "",
"fl_credit": "",
"wt_credit": "",
"xl_credit": "",
"token": ""
}
后来,第二版学时通系统上线了,看了一下,功能丰富了,安全提升了,体验也更好了,前端界面 UI 实属喜欢!
通知内容:
由于此前学时通服务器损坏,现已重新创建,另外也重新优化了一些功能。由于服务器更换,登录网址和账号发生改变。
网页(需在校园网进行登录):http://10.0.0.43/
账号:原账号不变(学生账号为学号)
密码:123456
温馨提醒:现在学时信息还在核实中,可能还有部分学时信息还没上传(这周会把上学期未上传的继续上传,如发现有遗漏的可在下周进行反馈),如有其他问题也可及时反馈,谢谢!
跟前一版本相比,仍然存在弱口令问题,只是多了验证码,只能防君子,个人感觉没有前一版本安全。
开始言归正传了,下面我将简称第二版学时通系统为学时通系统。
二、目的简单直接粗暴,扫号!
三、分析接口- 找验证码接口
- 找登录接口
打开浏览器 Chrome 开发者工具,刷新页面。
发现验证码图片来着 base64 图像。
继续往上找找接口,发现验证码 base64 图像数据来自 http://10.0.0.43:8848/learntime/sys/randomImage/1650700433317?_t=1650700433 接口。
这个接口是请求方式是 GET,只有两个参数,也就是时间戳,我们暂时不管。
Step2:找登录接口输入信息,验证码随便填,请求找到下面的接口:
curl 'http://10.0.0.43:8848/learntime/sys/login' \
-H 'Accept: application/json, text/plain, */*' \
-H 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7' \
-H 'Connection: keep-alive' \
-H 'Content-Type: application/json;charset=UTF-8' \
-H 'Origin: http://10.0.0.43' \
-H 'Referer: http://10.0.0.43/' \
-H 'tenant-id: 0' \
--data-raw '{"username":"***","password":"***","captcha":"1234","checkKey":1650700433317,"remember_me":true}' \
--compressed \
--insecure
看到请求了这些信息:
{
"username": "***",
"password": "***",
"captcha": "1234",
"checkKey": 1650700433317,
"remember_me": "true"
}
参数都是明文,没有加密。很简单的一个接口。
参数含义:账号、密码、验证码、生成验证码的时间戳(为了与生成验证码接口中的验证码对齐)。
再看看响应数据:
{
"success": false,
"message": "验证码错误",
"code": 500,
"result": null,
"timestamp": 1650700861260
}
根据上面的信息,我们可以手动构造信息进行登录,手动成功说明思路没有问题,接着再开始编码。
四、过验证码 几种识别思路- 手撸,用最古老的方法,写个分割对比算法。因为这类验证码干扰低,而且大小不变,简单处理就能获取高清二值化图像了。
- 调用第三方验证码识别接口(普遍收费)。
- 使用当前最流行的机器学习来预测。针对这类验证码,我们可以用分类算法,把每个数字字母切割来准备好样本训练,预测的时候也要切割,个人感觉丢丢麻烦。更好的方法就是使用卷积神经网络来卷,因为它有平移不变性,直接扔图进去就完事,工作量相对少。但无论哪种方式,都有共同的问题,要训练就需要样本和标签。手动打实在不可取。
找一个类似的验证码,扔进去模型训练。如果实在找不到,就自己写一个(前提是简单),没错,我就亲手写了一个!!!
我是怎样做的?
- 找一张验证码
- 上传到识字体网站
并手动标注字符。
- 找出验证码字体
找到一个类似的,虽然不 100% 像,但足够了。
- 开始写验证码
上图是原始,下图是生成。很像了吧!
def gen_cpatcha():
'''
生成验证码。该验证码类似「学时通系统 http://10.0.0.43/ 」的验证码。
目的:为了训练模型,去预测"真"验证码进行样本收集。
:return: 标签, 图像
'''
captcha_array = list('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
captcha_size = 4
font_size = 28
width = 105
height = 35
color = (0, 0, 0)
background = (255, 255, 255)
chars = ''.join(random.sample(captcha_array, captcha_size))
# chars = '3NrH'
font_path = "Cast W01 Heavy.ttf"
font = ImageFont.truetype(font_path, font_size)
im = Image.new('RGB', (width, height), background)
d = Draw(im)
dy = 6
d.fontmode = '1' # 锯齿
d.text((7, dy), chars[0:1], fill=color, font=font)
d.text((31, dy), chars[1:2], fill=color, font=font)
d.text((54, dy), chars[2:3], fill=color, font=font)
d.text((78, dy), chars[3:4], fill=color, font=font)
return chars, im
-
批量生成开始训练
-
拿训练好的模型去预测真实验证码
虽然成功率很低,在 10-20% 之间。为了提高准确率,我拿之前手动打的真实验证码样本(打了几百 /doge)进行了二次训练,准确了提高了 30%,总体在 50% 左右。
这样就具备了套白狼的条件了,直接去登录,预测成功的验证码保存起来,失败的也保存,就让程序跑个几千验证码出来,再拿去训练,以此类推去完善模型。最终模型准确率在 90% 以上。
为了得到验证码样本,可以空手套白狼。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)