#!/usr/bin/php
<?php
/**
* Штука для перехвата HTTP/HTTPS-пакетов на линух-сервере и отправки их
* на какой-нибудь MITM-прокси типа mitmproxy или Fiddler
*
* Запускать так
*
* На хост-машине
* echo 1 > /proc/sys/net/ipv4/ip_forward
* iptables -F -t nat
* iptables -t nat -A PREROUTING -i eth0 -p tcp -j REDIRECT --dport 443 --to-ports 8443
* iptables -t nat -A PREROUTING -i eth0 -j ACCEPT
*
* Потом
* ./proxy_to_api.php -p 8443
* ./proxy_to_api.php -p 8080 --no-ssl
*
* На windows-клиенте
* route add 93.184.216.34 mask 255.255.255.255 192.168.1.14 metric 1
*
* Не забудьте включить на Fiddler'е "принимать подключения из вне", а на клиенте — импортнуть рутовый сертификат
*/
$options = getopt('d:p:', array(
'no-ssl'
));
$domain = array_key_exists('d', $options) ? $options['d'] : 'example.com';
$port = array_key_exists('p', $options) ? (int) $options['p'] : 8443;
$ssl_enabled = !array_key_exists('no-ssl', $options);
$input_server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($input_server === false) {
echo "can not spawn socket\n";
return;
}
if (@socket_bind($input_server, '0.0.0.0', $port) === false) {
echo "can not bind socket: ".socket_strerror(socket_last_error($input_server))."\n";
return;
}
if (@socket_listen($input_server, 5) === false) {
echo "can not listen socket: ".socket_strerror(socket_last_error($input_server))."\n";
return;
}
$our_proxy = gethostbyname('192.168.1.16');
$real_api_ip = gethostbyname($domain);
socket_set_nonblock($input_server);
/**
* @var resource[][] $socket_pairs
*/
$socket_pairs = array();
$start_session_time = time();
while (true) {
// Принимаем подключения
$input_socket = @socket_accept($input_server);
if ($input_socket !== false) {
echo "CONNEСT\n";
$output_socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$result = socket_connect($output_socket, $our_proxy, 8888);// Порт поменять, это до Фиддлера
if ($result === false) {
socket_shutdown($output_socket);
socket_close($output_socket);
socket_shutdown($input_socket);
socket_close($input_socket);
continue;
}
if ($ssl_enabled) {
$first_packet_bytes = @socket_recv($input_socket, $first_packet_buf, 2048, 0);
if (!preg_match('_([a-z0-9.-]{5,})_i', $first_packet_buf, $a)) {
echo "First input ssl packet malformed\n";
continue;
}
$real_domain = $a[1];
$init_string = "CONNECT {$real_domain}:443 HTTP/1.1\n".
"HOST: {$real_domain}\n\n";
socket_write($output_socket, $init_string, strlen($init_string));
$full_buf = '';
while (!preg_match('_\\r\\n\\r\\n_', $full_buf)) {
$bytes = @socket_recv($output_socket, $buf, 1000, 0);
$full_buf .= $buf;
if ($bytes == 0) {
echo "Connect-proxy malformed answer\n";
socket_shutdown($output_socket);
socket_close($output_socket);
socket_shutdown($input_socket);
socket_close($input_socket);
continue 2;
}
}
$a = explode("\r\n\r\n", $full_buf, 2);
if (!preg_match('_^HTTP/1\\.[01] ([0-9]+) _', $a[0], $b)) {
echo "Can not establish properly proxy connect\n";
socket_shutdown($output_socket);
socket_close($output_socket);
socket_shutdown($input_socket);
socket_close($input_socket);
continue;
} elseif ($b[1] != 200) {
echo "Can not establish properly proxy connect. HTTP Status {$b[1]}\n";
socket_shutdown($output_socket);
socket_close($output_socket);
socket_shutdown($input_socket);
socket_close($input_socket);
continue;
}
if ($first_packet_bytes > 0) {// Что очевидно, иначе бы сюда код не пришёл
socket_write($output_socket, $first_packet_buf, $first_packet_bytes);
}
if (strlen($a[1]) > 0) {
socket_write($input_socket, $a[1], strlen($a[1]));
}
echo "CONNEСT #".count($socket_pairs)." ESTABLISHED TO {$real_domain}\n";
unset($first_packet_buf, $first_packet_bytes, $a, $full_buf, $bytes);
$socket_pairs[] = array($input_socket, $output_socket, $real_domain);
} else {
$socket_pairs[] = array($input_socket, $output_socket);
}
unset($input_socket, $output_socket);
}
// Обрабатываем подключения (кидаем пакеты)
foreach ($socket_pairs as $pair_id => &$pair) {
if ($pair === null) {
continue;
}
$r = array($pair[0]);
$count = socket_select($r, $write = null, $except = null, 0);
if ($count === false) {
socket_shutdown($pair[1], 2);
socket_close($pair[1]);
socket_shutdown($pair[0], 2);
socket_close($pair[0]);
$pair = null;
echo "!{$pair_id}\tclose, I, count=false\n";
continue;
} elseif ($count > 0) {
$bytes = @socket_recv($pair[0], $buf, 2048, 0);
if ($bytes == 0) {
socket_shutdown($pair[1], 2);
socket_close($pair[1]);
socket_shutdown($pair[0], 2);
socket_close($pair[0]);
$pair = null;
echo "!{$pair_id}\tclose, I, bytes=0\n";
continue;
}
echo "!I{$pair_id}\t{$bytes}\n";
file_put_contents("/root/pta-{$start_session_time}-{$pair_id}-".microtime(true).'-i.dat', $buf);
socket_write($pair[1], $buf, $bytes);
}
$r = array($pair[1]);
$count = socket_select($r, $write = null, $except = null, 0);
if ($count === false) {
socket_shutdown($pair[1], 2);
socket_close($pair[1]);
socket_shutdown($pair[0], 2);
socket_close($pair[0]);
$pair = null;
echo "!{$pair_id}\tclose, O, count=false\n";
continue;
} elseif ($count > 0) {
$bytes = @socket_recv($pair[1], $buf, 2048, 0);
if ($bytes == 0) {
socket_shutdown($pair[1], 2);
socket_close($pair[1]);
socket_shutdown($pair[0], 2);
socket_close($pair[0]);
$pair = null;
echo "!{$pair_id}\tclose, O, bytes=0\n";
continue;
}
echo "!O{$pair_id}\t{$bytes}\n";
file_put_contents("/root/pta-{$start_session_time}-{$pair_id}-".microtime(true).'-o.dat', $buf);
socket_write($pair[0], $buf, $bytes);
}
$except = array($pair[1], $pair[0]);
$count = socket_select($read = null, $write = null, $except, 0);
if ($count === false) {
socket_shutdown($pair[1], 2);
socket_close($pair[1]);
socket_shutdown($pair[0], 2);
socket_close($pair[0]);
$pair = null;
echo "!{$pair_id}\tclose, E, count=false\n";
continue;
} elseif ($count > 0) {
echo "!E{$pair_id}\n";
socket_shutdown($pair[1], 2);
socket_close($pair[1]);
socket_shutdown($pair[0], 2);
socket_close($pair[0]);
$pair = null;
echo "!{$pair_id}\tclose, E, count>0\n";
continue;
}
}
};
socket_shutdown($input_server, 2);
socket_close($input_server);
?>
Comments