微信号:arrisec

介绍:JoMotto旗下棱安全团队,关注国内外网络安全,提供专业的网络安全服务.

一次特殊的验证码识别

2016-06-15 11:14 range

简单验证码识别的步骤一般是去噪->切割->对比识别,一般来说这种工作特别费时间,所以遇到一些特别的站的时候就要多想想其他方式了。

最近想做一个APP,把学校的教务,一卡通,流量查询等各种系统集中在一起,但是遇到了一个严重的问题:门户系统的验证码。

教务,一卡通,流量都是需要通过门户进入的,当然我也可以选择将验证码返回到客户端让用户自己识别,然而我还是想尝试一下的。


验证码是如下的图片:



这种验证码中间用了个扭曲的算法,将一部分图像扭曲了,如果直接识别,1和7的位置的不同会导致切割和识别出错,粗略计算了一下,如果按照这种方法,最终的识别率在百分之20左右,只能另寻生路。

其实,仔细看一下,这种验证码没有任何的随机噪点,然后对比一下第一排第二个验证码和第二排第二个验证码的最后一位数,两个“8”完全一模一样。
所以就可以考虑一下做个kv(key-value)映射表了。

经过大量的尝试,发现这些验证码里面只有1-9,所以一共有6561个验证码图片,而相同的验证码的图片也是一模一样的,所以这个kv映射表的key就是验证码图片的md5值,value就是验证码。

1.先来建立一个kv数据库,把所有的图片down下来,代码:


#!/usr/bin python

#coding: utf-8


import requests

import MySQLdb

import md5


conn=MySQLdb.connect(host='localhost',user='root',passwd='',port=3306)

count = 9000

while count <= 50000:

    print count 

    content = requests.get('图片地址.jpg').content

    m = md5.new()

    m.update(content)

    m = m.hexdigest()

    cur=conn.cursor()

    print m

    res = cur.execute("select md5 from md5.tables where md5='%s'"%m)

    print res

    if res == 0:

        cur.execute("insert into md5.tables(num, md5) values('%d','%s')" % (count, m))

        conn.commit()

        f = open('./data_2/' + str(count) + '.jpg', 'wb')

        f.write(content)

        f.close()

    count += 1

    cur.close()

conn.close()


2.然后就是需要识别6000多个图片,自己识别是很浪费时间的,所以我选择了一个打码平台,我使用的是云速:http://www.ysdm.net/6600个图片,当时他并没有开发文档,只有测试接口,要想识别,就只能从测试接口通过一个html-upload接口上传图片上去,这是代码,中间有用户名和密码等:

#coding: utf-8

import requests

import re

import MySQLdb


conn=MySQLdb.connect(host='localhost',user='root',passwd='',port=3306)

for num in range(5222,6000):#0,6508

    cur=conn.cursor()

    try:

        cur.execute("select num from md5.tables order by num limit %d,1" %num)

        n = cur.fetchall()[0][0]

        print n,num

        files={'username':(None,''),

            'password':(None,''),

            'softid':(None,'1'),

            'timeout':(None,'60'),

            'softkey':(None,''),

            'typeid':(None,'5000'),

            'image':('%s.jpg'%n,open(r'E:\tmp\data_2\%s.jpg'%n,'rb').read(),'image/jpeg')

         }

        headers = {'Cookie':'_': 'zh-CN,zh;q=0.8', 'Accept-Encoding': 'gzip,deflate', 'Referer': 'http://www.ysdm.net/home/OnTest', 'DNT': '1','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0', 'Origin': 'http://www.ysdm.net','Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8','Cache-Control': 'max-age=0'}

        a = requests.post("http://www.ysdm.net/home/OnTest",files=files,headers=headers)

        code = re.search(':([1-9]{4})"',a.content).group(1)

    except Exception,e:

        print e

        code = '0000'

    print code

    cur.execute('update md5.tables set val=%s where num=%s' %(code, n))

    cur.close()

    conn.commit()

conn.close()


3.等了一天以后,百分之80的验证码就down下来了,不用过多,百分之80的识别率就OK了,因为不论怎样,百分之百的识别是不可能实现的。

                              

这是最后的效果:




现在就是最后一步了,写个php来实现在线识别:


<?php

$mysqli = newmysqli('localhost','root','','md5');

if (mysqli_connect_errno()){

die('Unable to connect!').mysqli_connect_error();

}

$mysqli->query("SET NAMESutf8");

$md5 = $_GET['md5'];

$md5_white = '';

if(!preg_match('([0-9a-f]{32})',$md5,$md5_white))die("fuck");

$sql = "select val from tables wheremd5='$md5_white[0]'";

$query = $mysqli->query($sql);

while($rs=$query->fetch_array())

{

   if(!empty($rs['val'])) echo $rs['val'];

   else {

   echo "0";

    }

}

?>


这中间用了一些处理,只允许32位的md5传入,否则输出fu**

最后访问http://localhost/md5.php?md5=xxxxx就可以返回验证码值了,客户端只需要将图片的md5值传过来就可以获取到验证码值。




 
棱安全 更多文章 Disucz 插件漏洞挖掘 一次运气好的渗透测试 redis利用姿势收集 浅谈"丢失iPhone"的安全问题 谈一谈Httpoxy漏洞的利用场景
猜您喜欢 大数据让“因材施教”不再遥远 开源即时消息传输平台Openfire 2015年J2EE应用服务器市场占有哪家强? 一种灵活的数据架构 程序媛实习两个月,写在辞职的今天