INTRODUCCION
El uso de firewalls ha permitido separar estructuras de redes internas
del exterior de la misma. Muchos de estos firewalls no son equipos físicos
sino aplicaciones que actúan sobre la capa de aplicación
del modelo OSI, actuando como proxy entre las maquinas que se comunican
entre si.
El protocolo SOCKS fue creado para satisfacer esta necesidad que cada
vez era mayor, permitiendo una mejor autentificación entre las
maquinas y logrando así un mejor y mas fuerte control sobre el
acceso.
SOCKS permite a los servidores detrás de un firewall ganar acceso
a Internet. Reenvía los paquetes de los sitios de Internet al Server
interno, permitiendo la transferencia hacia atrás y hacia adelante.
Generalmente la aplicación cliente envía la petición
al servidor SOCKS, con la dirección de la maquina destino, el tipo
de conexión, y la identidad del usuario.
Esto lo podemos representar fácilmente en el siguiente diagrama:
.----------<--------.
| |
<------------------.
localhost
socks_server
Internet
| |
'----------------->
'---------->--------'
Después de que el servidor SOCKS recibe la petición, establece
un canal de comunicación con el servidor de la aplicación.
Un circuito se establece y el servidor SOCKS (que representaría
el cliente), reenvía los datos entre el cliente y el servidor.
SOCKS realiza diferentes funciones de negociación mientras el circuito
es establecido.
SOCKS 5 realiza cuatros operaciones básicas:
* negociación
* autentificación
* petición de conexión
* Establecimiento del circuito
* Reenvió de datos
El protocolo SOCKS 5 expande las características
de SOCKS 4, ya que además de incluir el protocolo TCP, también
soporta UDP y el formato de direcciones de IP V6.
Generalmente el demonio del servidor SOCKS estará corriendo en
el puerto 1080.
En el presente articulo veremos tan solo el procedimiento para clientes
TCP (siendo el mas común), pero si tu deseas profundizar en el
proceso para clientes UDP, puedes leer el RFC 1928 y/o visitar la pagina
oficial http://www.socks.nec.com
PROCEDIMIENTO PARA CLIENTES TCP
Negociación:
El cliente se conecta al servidor SOCKS, y envía el formato de
negociación, que puede verse así:
.-------.------------.-----------.
| VER | NMETHODS | METHODS |
+-------+------------+-----------+
| 1 | 1 |
1 a 255 |
'-------+------------+-----------'
Nota: Los números abajo indicados representan la
longitud en octetos de cada paquete.
Posibles valores de cada paquete:
VER
: 0x05 Versión del Protocolo
NMETHODS : [valor] Representa el numero de métodos.
METHODS :
0x00
Sin autentificacion
0x01
GSSAPI
0x02
Username/Password
0xff
Ningún método aceptable
En el proceso de negociación GSSAPI (Generic Security Service Application
Program Interface), los clientes negocian con el servicio SOCKS acerca
de la seguridad de los mensajes.
La integridad y privacidad son las opciones que pueden aplicarse al resto
de los mensajes, incluyendo las peticiones al proxy provenientes de la
aplicación cliente.
Peticiones:
Una vez que la subnegociacion de los métodos ha sido
completada, el cliente envía los detalles de la petición
como sigue:
.-------.-------.-------.--------.------------.------------.
| VER | CMD | RSV | ATYP
| DST.ADDR | DST.PORT |
+-------+-------+-------+--------+------------+------------+
| 1 | 1 | 0x00
| 1 | Variable | 2
|
'-------+-------+-------+--------+------------+------------+
Posibles valores de cada paquete:
VER
: 0x05 Versión del Protocolo
CMD
: Comando
0x01
Connect
0x02
Bind
0x03
UDP
RSV
: 0x00 Valor reservado
ATYP
: Tipo de dirección
0x01
dirección IP V4
0x03
Nombre de dominio
0x04
dirección IP V6
DST.ADDR : dirección
de destino
[valor]
Longitud de la dirección de dominio
[direcc]
Nombre de dominio o dirección de 4 octetos.
DST.PORT : Puerto
de destino.
SOCKS CHAINS
Nosotros podemos utilizar el protocolo SOCKS para actué
efectivamente como proxy/firewall en nuestras conexiones salientes.
Sockscap es una excelente utilidad para estos propósitos ya que
intercepta todas las salidas de nuestras maquinas por medio de un servidor
SOCKS, por lo que podemos utilizarlo para la mayoría de los clientes
(aun aquellos que no tengan la opción definida), logrando que nuestras
conexiones sean mas 'anónimas'.
Sockscap puede encontrarse en -> http://www.socks.nec.com/reference/sockscap.html
Un socks chainer puede encontrarse en -> http://www.ufasoft.com/socks
SOCKS EN PERL
Programar socks en Perl es relativamente sencillo ya que
ya existe un modulo creado para tal efecto, el autor es Clinton Wong y
el modulo puede encontrarse en CPAN http://www.perl.com/CPAN/modules/by-module/Net/
y actualmente solo soporta conexiones TCP.
Se carga el modulo Net::SOCKS, después se crea el constructor
así:
$sockout = new Net::SOCKS(socks_addr
=> '192.168.1.3',
socks_port => 1080,
user_id => 'the_user',
user_password => 'the_password',
force_nonanonymous => 1,
protocol_version => 5);
socks_addr = dirección
del servidor socks
socks_port = puerto del servicio socks
user_id = (opcional) nombre de usuario
user_password = (opcional) password de usuario
protocol_version = la versión del protocolo (4 o 5)
force_nonanonymous = fuerza la conexión a no ser anónima
Después nos conectamos mediante:
$sock->connect(peer_addr
=> '192.168.1.3', peer_port => 79);
peer_addr es la dirección
destino y peer_port es el puerto.
así, un cliente que haga finger en perl se vería así:
<++> socks/safefinger.pl
#!/usr/local/bin/perl -w
use strict;
use Net::SOCKS;
print "Attempting
to connect to 192.168.1.3 at port 79 using the socks\n";
print "server at 192.168.1.3 port 1080\n";
my $sock = new Net::SOCKS(socks_addr
=> '192.168.1.3',
socks_port => 1080,
#user_id => 'the_user',
#user_password => 'the_password',
#force_nonanonymous => 1,
protocol_version => 5);
my $f= $sock->connect(peer_addr => '192.168.1.3', peer_port =>
79);
print "connect status: ",
Net::SOCKS::status_message($sock->param('status_num')), "\n";
if ($sock->param('status_num') == SOCKS_OKAY) {
print $f "clintdw\n";
while (<$f>) { print }
$sock->close();
}
print "Attempting
to listen() using the server at 192.168.1.3 port 1080\n";
$sock = new Net::SOCKS(socks_addr
=> '192.168.1.3',
socks_port => 1080,
#user_id => 'the_user',
#user_password => 'the_password',
#force_nonanonymous => 1,
protocol_version => 5);
my ($ip, $ip_dot_dec,
$port) = $sock->bind(peer_addr => "192.168.1.3",
peer_port => 9999);
print "bind status: ",
Net::SOCKS::status_message($sock->param('status_num')), "\n";
if ($sock->param('status_num')
== SOCKS_OKAY) {
print "Listening at the IP of ", $ip_dot_dec, " at port
", $port, "\n";
$f= $sock->accept();
}
print "accept status: ",
Net::SOCKS::status_message($sock->param('status_num')), "\n";
if ($sock->param('status_num')
== SOCKS_OKAY) {
while (<$f>) { print }
}
$sock->close();
<-->
SOCKS EN C
Programar clientes SOCKS en C es mas complicado debido
a que tenemos que trabajar a un nivel mas bajo. así si recordamos
lo primero que debemos hacer es enviar la negociación (si vamos
a enviar una negociación de usuario y contraseña mirar el
RFC 1929) y luego enviar la petición.
Como ejemplo ponemos una aplicación que permite enviar correo mediante
un
servidor SOCKS:
<++> socks/rm-sm.c
/*
| Raza Mexicana Team 2001 - http://www.raza-mexicana.org
|
| (rm-sm.c) Sends e-mail thru a socks proxy server.
|
| Yo_Soy - <yo_soy@raza-mexicana.org>
|
| This program is free software; you can redistribute it and/or
| modify it under the terms of the GNU General Public License
| as published by the Free Software Foundation; either version
| 2 of the License, or (at your option) any later version.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "socks5.h"
void uso (void);
int main (int argc,
char *argv[]) {
struct sockaddr_in
sin;
unsigned int smtpuerto = 25,
socksport = 1080,
sockout;
char *mailfrom = argv[1],
*destino = argv[2],
*sockserv = argv[5],
*smtpserv = argv[4],
*mensaje,
respuesta[256],
sendbuff[256];
FILE *archivo;
if (argc < 5) uso();
printf ("[++]
-> Raza Mexicana - MailSock\n");
printf ("\n[+]
Mail from....: %s\n", mailfrom);
printf ("[+] Mail to......: %s\n", destino);
printf ("[+] Smtp Server..: %s\n", smtpserv);
printf ("[+] Socks server.: %s\n", sockserv);
if (argc > 5) {
socksport = atoi(argv[6]);
printf ("[+] Socks port...: %d\n", socksport); }
if ((archivo = fopen
(argv[3], "r")) == NULL) {
printf ("ERROR: Can't open file %s\n", argv[3]);
exit (EXIT_FAILURE); }
if ((sockout = socket
(AF_INET, SOCK_STREAM, 0)) == -1) {
printf ("ERROR: Can't create socket.\n");
exit (EXIT_FAILURE); }
sin.sin_family = AF_INET;
sin.sin_port = htons(socksport);
sin.sin_addr.s_addr = inet_addr(sockserv);
if ((connect(sockout,
(struct sockaddr *)&sin, sizeof(sin))) == -1) {
printf ("ERROR: Can't connect to host %s\n", sockserv);
exit (EXIT_FAILURE);
}
/* Negociacion */
sprintf (sendbuff, "%c%c%c", SOCKS_VER, 1, METHOD_NOAUTH);
send (sockout, sendbuff, sizeof(sendbuff), 0);
/* Request */
printf ("Sendbuff = %s\n", sendbuff);
sprintf (sendbuff, "%c%c%c%c%s%c%c", SOCKS_VER, CMD_CONNECT,
SOCKS_RSV, strlen(smtpserv), smtpserv,
(smtpuerto >> 8) & 0xFF, smtpuerto & 0xFF);
send (sockout, sendbuff, sizeof(sendbuff), 0);
/* Checamos las posibles respuestas del sock_server */
recv (sockout, respuesta, 100, 0);
switch (respuesta[1]) {
case R_SUCCEDED:
break;
case R_FAILURE:
printf ("ERROR: general socks server failure\n");
exit (EXIT_FAILURE);
break;
case R_NOALLOWED:
printf ("ERROR: connection not allowed\n");
exit (EXIT_FAILURE);
break;
case R_NET_UNR:
case R_HOST_UNR:
printf ("ERROR: network / host unreachable\n");
exit (EXIT_FAILURE);
break;
case R_REFUSED:
printf ("ERROR: connection refused\n");
exit (EXIT_FAILURE);
break;
case R_TTLEXPIRED:
printf ("ERROR: TTL expired\n");
exit (EXIT_FAILURE);
break;
case R_NOCMD:
printf ("ERROR: command not supported\n");
exit (EXIT_FAILURE);
break;
case R_NOADDR:
printf ("ERROR: address type not supported\n");
exit (EXIT_FAILURE);
break;
default:
printf ("ERROR: %d\n", respuesta[1]);
exit (EXIT_FAILURE);
break;
}
/* Si llegamos aqui,
todo esta bien */
/* ahora enviamos
el mensaje */
sprintf (sendbuff, "HELO RMHT\n");
send (sockout, sendbuff, sizeof(sendbuff), 0);
sprintf (sendbuff,
"MAIL FROM <%s>\n", mailfrom);
send (sockout, sendbuff, sizeof(sendbuff), 0);
sprintf (sendbuff,
"RCPT TO: <%s>\n", destino);
send (sockout, sendbuff, sizeof(sendbuff), 0);
sprintf (sendbuff,
"DATA\n");
send (sockout, sendbuff, sizeof(sendbuff), 0);
while (! feof(archivo))
{
if (fgets(mensaje, 255, archivo)) {
send (sockout, mensaje, sizeof(mensaje), 0); }
}
fclose (archivo);
sprintf (sendbuff,
"\n.\n");
send (sockout, sendbuff, sizeof(sendbuff), 0);
sprintf (sendbuff,
"QUIT\n");
send (sockout, sendbuff, sizeof(sendbuff), 0);
printf ("[+]
Message sent to %s!\n", destino);
return (0);
}
void uso(void) {
puts("\n[+] Raza Mexicana SocksMail - http://www.raza-mexicana.org
Usage:
rm-sm <from>
<to> <message> <smtp> <socks_proxy> <socks_port>
<from> from
mail address
<to> destination mail address
<message> the file with the message
<smtp> relaying smtp server
<socks_proxy> socks proxy server
<socks_port> default 1080 if not defined
");
exit(EXIT_SUCCESS); }
<-->
y la cabecera necesaria con las constantes definidas en el RFC (socks5.h):
<++> socks/socks5.h
/*
| Socks Protocol Versión 5 // RFC 1928
| by Yo_Soy <yo_soy@raza-mexicana.org>
*/
#define SOCKS_VER 0x05
/* socks protocol version 5 */
#define SOCKS_RSV 0x00 /* socks reserved */
enum METHODS {
METHOD_NOAUTH = 0x00, /* no authentication required */
METHOD_GSSAPI = 0x01, /* GSAAPI */
METHOD_AUTH = 0x02, /* username / password */
METHOD_NOACCM = 0xff /* no acceptable methods */
} sockmethods;
enum COMMANDS {
CMD_CONNECT = 0x01, /* connect */
CMD_BIND = 0x02, /* bind */
CMD_UDP = 0x03 /* udp associate */
} sockcmds;
enum ADDRESS {
ADDR_IPV4 = 0x01, /* IP V4 address */
ADDR_DOMAIN = 0x03, /* domain name */
ADDR_IPV6 = 0x04 /* IP V6 address */
} sockatyp;
enum REPLIES {
R_SUCCEDED = 0x00, /* succeeded */
R_FAILURE = 0x01, /* general socks server failure */
R_NOALLOWED = 0x02, /* connection not allowed */
R_NET_UNR = 0x03, /* network unreacheable */
R_HOST_UNR = 0x04, /* host unreacheable */
R_REFUSED = 0x05, /* connection refused */
R_TTLEXPIRED = 0x06, /* TTL expired */
R_NOCMD = 0x07, /* command not supported */
R_NOADDR = 0x08 /* address type not supported */
} sockreply;
<-->
|