PyTorch机器学习识别验证码之旅:广商学时通系统

PyTorch机器学习识别验证码之旅:广商学时通系统,第1张

一、背景

记得在 2021-2022 上学期的时候,第一版学时通系统问世,这个系统主要是用来查学生的学时信息。当时发现了系统比较简陋,没有验证码防护,账号为学号,密码为学号后六位,让我感觉数据等同于在裸奔。

为了实现一些功能,我开始收集不同年级、不同专业、班级等学号规则信息,大概摸清了一些学号规则后写了一个简单爬虫。爬虫的主要功能就是检测学号是否存在,简言之也就是扫号。

爬虫程序跑了几次后,发现了有一个反扒机制:

  1. 防频繁提交,连续提交 3 次后,需要 60 秒后才能尝试。
  2. 针对一个内网 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
温馨提醒:现在学时信息还在核实中,可能还有部分学时信息还没上传(这周会把上学期未上传的继续上传,如发现有遗漏的可在下周进行反馈),如有其他问题也可及时反馈,谢谢!

跟前一版本相比,仍然存在弱口令问题,只是多了验证码,只能防君子,个人感觉没有前一版本安全。

开始言归正传了,下面我将简称第二版学时通系统为学时通系统。

二、目的

简单直接粗暴,扫号!

三、分析接口
  1. 找验证码接口
  2. 找登录接口
step1:找验证码接口

打开浏览器 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
}

根据上面的信息,我们可以手动构造信息进行登录,手动成功说明思路没有问题,接着再开始编码。

四、过验证码

几种识别思路
  1. 手撸,用最古老的方法,写个分割对比算法。因为这类验证码干扰低,而且大小不变,简单处理就能获取高清二值化图像了。
  2. 调用第三方验证码识别接口(普遍收费)。
  3. 使用当前最流行的机器学习来预测。针对这类验证码,我们可以用分类算法,把每个数字字母切割来准备好样本训练,预测的时候也要切割,个人感觉丢丢麻烦。更好的方法就是使用卷积神经网络来卷,因为它有平移不变性,直接扔图进去就完事,工作量相对少。但无论哪种方式,都有共同的问题,要训练就需要样本和标签。手动打实在不可取。
抖机灵!空手套白狼!

找一个类似的验证码,扔进去模型训练。如果实在找不到,就自己写一个(前提是简单),没错,我就亲手写了一个!!!

我是怎样做的?

  1. 找一张验证码

  1. 上传到识字体网站

并手动标注字符。

  1. 找出验证码字体

找到一个类似的,虽然不 100% 像,但足够了。

  1. 开始写验证码

上图是原始,下图是生成。很像了吧!

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
  1. 批量生成开始训练

  2. 拿训练好的模型去预测真实验证码

    虽然成功率很低,在 10-20% 之间。为了提高准确率,我拿之前手动打的真实验证码样本(打了几百 /doge)进行了二次训练,准确了提高了 30%,总体在 50% 左右。

    这样就具备了套白狼的条件了,直接去登录,预测成功的验证码保存起来,失败的也保存,就让程序跑个几千验证码出来,再拿去训练,以此类推去完善模型。最终模型准确率在 90% 以上。

五、学习总结

为了得到验证码样本,可以空手套白狼。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://www.outofmemory.cn/langs/723860.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-26
下一篇 2022-04-26

发表评论

登录后才能评论

评论列表(0条)

保存