к.т.н., доцент Пархоменко И.И., Остапчук А.В.,
Национальный авиационный университет,
Украина
HTTP туннелирование
- взгляд изнутри
На
данный момент, защищенная и анонимная передача информации играет важную роль в жизни не только пользователей
интернета, но и в особенности, крупных компаний.
Web server - превосходная мишень для атаки. Именно с
такого сервера можно начать свое путешествие вглубь сети, даже не взирая на то,
если он защищен файрволом. Тут нам на помощь и приходит HTTP туннелирование.
HTTP туннелирование
позволяет клиенту инкапсулировать трафик в HTTP заголовки. Затем трафик
направляется к серверу на другом конце канала и там пакеты программой
обрабатываются обратно, извлекаются данные из заголовков, и только потом уже
редактируются к своей конечной цели. По такой схеме можно передавать как UDP,
так и ТСР данные, это обусловлено самой природой туннелирования.
В
RFC 2616 (HTTP 1.1) описаны следующие методы: OPTIONS, GET, HEAD, POST, PUT,
DELETE, TRACE, CONNECT.
О методе CONNECT можно
сказать следующее: "Спецификация резервирует имя метода CONNECT для
использования с прокси, которые могут динамически переключаться на работу в
качестве туннеля (например, туннелирование SSL)". Теперь
собственно о том, как это работает.
1. Клиент соединяется с
HTTP Proxy, поддерживающим метод CONNECT.
2. Клиент посылает серверу строку вида "CONNECT target_host:target_port
HTTP/1.x\r\nUser-Agent: User Agent\r\n"
3. В ответ на эту строку сервер может либо потребовать авторизацию, либо вернуть
состояние удачного соединения либо ошибку.
Открытый
тунель может использоваться не только как клиент-прокси канал, но и как
прокси-прокси канал.
Код будет
иметь следующий вид:
int len;
char buff[BUFF_SIZE];
bzero (buff,sizeof(buff));
sprintf(buff, "CONNECT %s:%d HTTP/1.0\r\nUser-Agent: ProxyChains
1.8\r\n", inet_ntoa( * (struct in_addr *) &ip), ntohs(port));
len = strlen (buff);
if(len!=send(sock,buff,len,0))
return SOCKET_ERROR;
//
bzero(buff,sizeof(buff));
len=0 ;
while(len<BUFF_SIZE)
{
if(1==read_n_bytes(sock,buff+len,1))
len++;
else
return SOCKET_ERROR;
if ( len > 4 &&
buff[len-1]=='\n' &&
buff[len-2]=='\r' &&
buff[len-3]=='\n' &&
buff[len-4]=='\r' ) break;
}
// если ответ не 200, значит либо порт закрыт, либо хост мертв
if ( (len==BUFF_SIZE) ||
! ( buff[9] =='2' &&
buff[10]=='0' &&
buff[11]=='0' )) return BLOCKED;
return SUCCESS;
В
конечном результате получаем туннель, который можно интерпретировать как
обычное соединение с target_host:target_port.