浅谈新形势下在线Web投票系统的攻防(2)

接上文浅谈新形势下在线Web投票系统的攻防(1),本文主要讨论一种验证码绕过的情况和利用Http请求头字段X-Forwarded-For的IP绕过。

一种验证码绕过的情况

上文用两种很不优雅的方式实现了案例二的刷票。当LZ同同伴准备把验证码识别和POST操作整合的时候,伙伴发现验证码原来可以不输入的,坑爹了,92%识别率的验证码破解工具派不上用场了。在仔细分析前端代码后我发现这个投票系统的验证码是js加载的:

刷票过程中模拟http post操作时直接把response的响应流读取出来进行处理,没有对html进行解析,这句 中的js函数压根就没有被执行,而这个getcode()的作用是加载并显示验证码文件/getcode.php。这样验证码就没有被读取,服务器那头用来校验验证码也是没有被更新过的(还是最近一次的值或者是初始化的值),看到这里如果你不懂怎么回事儿,建议仔细思考下验证码机制的工作原理。这也不能算是个bug,因为要是有意这样的效果也是不能避免的。在浏览器要产生这样的效果,有一种方法是不让浏览器加载/getcode.php这个页面,方法可以是Firefox的Autoproxy中对这个验证码图片页面url设置一个根本不存在的代理,这样这个图片就加载不了了。

但是!

但是这个漏洞是件郎有情,妾有意的事情,服务器为什么没有检测到验证码被禁用了没有被加载呢?后来拿到了那个系统的代码后我终于知道为什么了:

在处理投票post请求的代码中发现了这么个有bug的判断

上述$code是post过来的,是一个空的字符串,就是说$code=="";而$_SESSION['code']是不存在,空值,为$_SESSION['code']==null;而在php中null=="",两者是等价的!八阿哥就这么来了!

受该怎么办:

$_SESSION['code']初始化为一个非法的值,判断的时候再判断一下$_SESSION['code']的值是否合法。

利用X-Forwarded-For的IP绕过

终于讲到本文的正经内容了,写这篇文章初衷其实就是讨论下IP绕过,但是又觉得讲IP绕过不讲刷票有点欠缺了,纠结了很久。

IP绕过的核心是HTTP协议头X-Forwarded-For的伪造,前几天我闲着蛋疼,就把Wiki上的X-Forwarded-For词条英文版照抄全部翻译了一遍,在此,我想尽量表达完整英文版的意思,所以语言非常的生硬。关于这个协议字段头的官方文档在此

从HTTP层上简单地讲:如果你的IP是10.23.231.23,通过一台透明的代理服务器上网,比如代理地址是10.23.231.1:8080,你请求一个web页面,这个HTTP请求被发到代理服务器后,服务器对该请求报头至少做了如下处理:

  • HTTP请求头上添加了X-Forwarded-For:10.23.231.23这么一行
  • 如果特殊必要,HTTP请求头上添加了**|*CLIENT_IP:10.23.231.1这么一行*

对于服务端,限制IP用的IP获取代码大多类似:

上面总共有三个字段**HTTP_CLIENT_IP、\***|*HTTP_X_FORWARDED_FOR、REMOTE_ADDR **我在这里找到了解释并验证通过!验证过程如下:

感谢zscorpio同学借美国主机给我做测试:

测试代码:

<?php  
echo '<p>getevn(\'HTTP\_CLIENT\_IP\')='.getenv('HTTP\_CLIENT\_IP').';';  
echo '<p>getevn(\'HTTP\_X\_FORWARDED\_FOR\')='.getenv('HTTP\_X\_FORWARDED\_FOR').';';  
echo '<p>getevn(\'REMOTE\_ADDR\')='.getenv('REMOTE\_ADDR').';';  
echo '<p>$\_SERVER[\'REMOTE\_ADDR\']='.$\_SERVER['REMOTE\_ADDR'].';';  
?>  

通过一台普通非文艺非二逼的透明代理服务器访问页面,结果(三四行的结果一样,我隐藏了):

getevn('HTTP_CLIENT_IP')=;

getevn('HTTP_X_FORWARDED_FOR')=172.23.60.145;

getevn('REMOTE_ADDR')=###.##.171.119;

$_SERVER['REMOTE_ADDR']=###.##.171.119;

这种情况下getip()的值就是172.23.60.145,合法并且正确。

ADSL联网后直接访问页面:

getevn('HTTP_CLIENT_IP')=;

getevn('HTTP_X_FORWARDED_FOR')=;

getevn('REMOTE_ADDR')=###.200.51.#9;

$_SERVER['REMOTE_ADDR']=##.200.51.#9;

这种情况下getip()的值就是###.200.51.#9,合法并且正确。

ADSL联网后直接联网的状态下伪造http头(使用Firefox的Poster Addon,请求头Headers中加入一行X_FORWARDED_FOR=foo,如下图):

结果:

getevn('HTTP_CLIENT_IP')=;

getevn('HTTP_X_FORWARDED_FOR')=foo;

getevn('REMOTE_ADDR')=###.200.51.#9;

$_SERVER['REMOTE_ADDR']=###.200.51.#9;

伪造得非常好,这里我没有用一个合法的IPv4地址而是一个字符串,但是数据报还是顺利到达Web服务器并被识别。之后的测试中,我尝试在headers中加了CLIENT_IP,效果和XFF一样。随便网上找了一个在线代理,其中getevn('HTTP_X_FORWARDED_FOR')显示空的,这应该就是传说中的匿名代理了吧!

受该怎么办:

XFF的中文wiki中提到:

***|*完整起见,Web服务器应该记录请求来源的IP地址以及X-Forwarded-For 字段信息。

因此,请求来源的IP地址(也就是REMOTE_ADDR)和 X-Forwarded-For 都应该被记录下来。通常的刷票行为往往都是短时间内伪造大量投票请求的。虽然X-Forwarded-For是不一样的,但是REMOTE_ADDR往往是不变的。短时间内检测到含X-Forwarded-For信息的投票请求的REMOTE_ADDR的请求地址相同,就应该检查该代理服务器是否是合法的。就投票来说,另外一点要值得注意的是,X-Forwarded-For信息如果有的话,应该是一个内网地址,如果是一个外网地址,那这家伙也是企图刷票吧,虽然手段不文艺不普通。

全文完毕。

以上内容为原创内容,转载请注明出处,如对文中内容如有疑问,欢迎在下面评论!



关于 McKelvin

a hacker who's interested in `music computing` and `network security`.
此条目发表在 Work 分类目录,贴了 , , 标签。将固定链接加入收藏夹。
  • http://online.hfut.edu.cn dpriest

    楼主,打个小广告,要是想测试代码的话,可以直接把代码放到sae上,上面的访问速度还是不错的哦~~

  • http://www.zscorpio.com 蝎紫

    晚上断网了,不好下手,感觉这东西没有例子不怎么好下手啊.....求能用的例子.....
    不得不说这个很给力,对验证码的识别很感兴趣!你的工具很多....

  • Pingback: 浅谈新形势下在线Web投票系统的攻受(1) | McKelvin's Blog