[SOLUCIONADO] Dudas sobre shellcode, objdump y null's

Iniciado por proxy_lainux, Agosto 11, 2016, 02:14:45 PM

Tema anterior - Siguiente tema

0 Miembros y 1 Visitante están viendo este tema.

Agosto 11, 2016, 02:14:45 PM Ultima modificación: Agosto 15, 2016, 09:54:58 PM por Gabriela
Hola

Estaba haciendo un shell en linux en nasm, saque los opcode y los agregue a la clásica shellcode en C para ejecutarlo, y no tuve problemas, pero me surgieron unas dudas que espero me puedan ayudar a entender.

La primera, es saber si, ¿cada vez que haga una shell y quiera sacar los opcode es necesario usar push y guardar en el stack todo?, ya que hice dos formas de ejecutar execv; una que es la que ronda por internet y otra hecha por mi. La que ronda por internet usan bastante push ya sea para /bin/sh como para los argumentos de execv, yo intenté usar una variable en section .data, lo que me di cuenta que necesito reemplazar las dirección que me da de section .data por /bin/sh en hexadecimal

entonces acabo haciendo lo mismo que usar directamente push /bin/sh

Pero recordé un código de una shell TCP y me di cuenta que también pushean todo, algo así

push 0x1
pop ebx
push 0x2
pop ecx

en vez de meterlo directamente

mov ebx, 0x1
mov ecx, 0x2

¿entonces es necesario meter en el stack todo en casos de shellcodes o será simplemente una coincidencia o una forma diferente de programar?

¿Y que pasa con los opcode "Cero" que si son necesarios?

por ejemplo, IP - 192.167.0.56

en hex quedaria C0A70038, pero si se dan cuenta hay cero "00", según en la shellcode no puede haber nulls y se deben quitar los ceros, y aunque en la ip no usa NULL sino es mas un cero de integer, objdump me lo quita al ser 0x00, entonces queda incompleto, en este caso se lo agregue manualmente y me funcionó, ¿pero que pasaría si hay más instrucciones con 00?, ¿tendría que buscarlos y agregarlos directamente en caso de que objdump me los elimine?, y ¿eso no perjudicaría la shellcode al tener que agregarle algún 0x00? porque tener que buscar en la shellcode byte por byte a ver si no me falto algo, creo no es muy optimo pero no se si sea normal.

Lo cual me llevó a otra duda; cuando busque el código de la shell, quise verificar su funcionamiento y saque los opcode, pero me di cuenta que objdump no me dio correctamente todo, el puerto solo me dio 1 byte de los 2

0x7968

y en la shellcode me dejo solamente

0x79

y de igual manera la shell de /bin/sh no me dio todo, le hizo falta 1

/x68/x73/x2f/x6e/x69/x62/x2f/x2f -- así debería quedar, pero me dio lo siguiente

/x68/x73/x2f/x69/x62/x2f/x2f

¿será algún detalle de que sea en 64 bits o algo parecido?, porque usé el comando de la página No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

cuando uso objdump -d me muestra bien todo, pero con el comando de commandlinefu me falló como les mencioné

bueno, espero haberme explicado lo mejor posible mis dudas.

Saludos

Agosto 12, 2016, 04:22:14 AM #1 Ultima modificación: Agosto 12, 2016, 04:28:01 AM por grep
No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
La primera, es saber si, ¿cada vez que haga una shell y quiera sacar los opcode es necesario usar push y guardar en el stack todo?, ya que hice dos formas de ejecutar execv; una que es la que ronda por internet y otra hecha por mi. La que ronda por internet usan bastante push ya sea para /bin/sh como para los argumentos de execv, yo intenté usar una variable en section .data, lo que me di cuenta que necesito reemplazar las dirección que me da de section .data por /bin/sh en hexadecimal

entonces acabo haciendo lo mismo que usar directamente push /bin/sh

Pero recordé un código de una shell TCP y me di cuenta que también pushean todo, algo así

push 0x1
pop ebx
push 0x2
pop ecx

en vez de meterlo directamente

mov ebx, 0x1
mov ecx, 0x2

¿entonces es necesario meter en el stack todo en casos de shellcodes o será simplemente una coincidencia o una forma diferente de programar?

Solo es una forma de llegar al objetivo, no existe una regla con respecto a usar push/pop en lugar de mov. Las diferencias que puedes encontrar es en el tamaño de opcode de cada instrucción y en la inserción de bytes necesarios para el correcto funcionamiento de la instrucción.

Por otro lado, las instrucciones que especificas como alternativas no son válidas para el shellcode ya que existen bytes 0x00. En el siguiente ejemplo de stackoverflow puedes ver la diferencia en los opcodes:

Código: asm

B8 01000000    MOV EAX,1          ; Set the register EAX to 0x000000001 To make the above instruction null free

; they've re-written it as follows

33C0           XOR EAX,EAX        ; Set the register EAX to 0x000000000
40             INC EAX           ; Increase EAX to 0x00000001

fuente: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
¿Y que pasa con los opcode "Cero" que si son necesarios?

por ejemplo, IP - 192.167.0.56

en hex quedaria C0A70038, pero si se dan cuenta hay cero "00", según en la shellcode no puede haber nulls y se deben quitar los ceros, y aunque en la ip no usa NULL sino es mas un cero de integer, objdump me lo quita al ser 0x00, entonces queda incompleto, en este caso se lo agregue manualmente y me funcionó, ¿pero que pasaría si hay más instrucciones con 00?, ¿tendría que buscarlos y agregarlos directamente en caso de que objdump me los elimine?, y ¿eso no perjudicaría la shellcode al tener que agregarle algún 0x00? porque tener que buscar en la shellcode byte por byte a ver si no me falto algo, creo no es muy optimo pero no se si sea normal.

No puedes tener el caracter '\0' (null) en el string del shellcode porque las funciones que procesan strings en C interpretan este byte como un fin de la cadena. Para el caso de valores de argumentos, existen operaciones aritméticas y lógicas entre números (cuya representación hexadecimal no contenga el byte 0x00) para evitar esto, te cuento algunas:

-- Operación xor entre dos números, por ejemplo:
Código: asm

mov ax, 0x5101
xor ax, 0x0101
push ax            ; 0x5101 xor 0x0101 = 0x5000 (el puerto 80 en network byte order)

fuente: No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

-- Resta de 0xfffffffff la cantidad que sea necesaria para obtener el número deseado.

Para el caso de las instrucciones, si algún opcode tiene el byte 0x00 debes buscar otra instrucción (o conjunto de instrucciones) equivalente para evitarlo. Esto se explica muy bien en "Smashing The Stack For Fun And Profit" (No tienes permitido ver los links. Registrarse o Entrar a mi cuenta) y el libro "Hacking: The Art of Exploitation". Por ejemplo mov $0, %eax produce b8 00 00 00 00 pero esto se puede cambiar por xor %eax, %eax que produce 31 c0.


No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
Lo cual me llevó a otra duda; cuando busque el código de la shell, quise verificar su funcionamiento y saque los opcode, pero me di cuenta que objdump no me dio correctamente todo, el puerto solo me dio 1 byte de los 2

0x7968

y en la shellcode me dejo solamente

0x79

y de igual manera la shell de /bin/sh no me dio todo, le hizo falta 1

/x68/x73/x2f/x6e/x69/x62/x2f/x2f -- así debería quedar, pero me dio lo siguiente

/x68/x73/x2f/x69/x62/x2f/x2f

¿será algún detalle de que sea en 64 bits o algo parecido?, porque usé el comando de la página No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

cuando uso objdump -d me muestra bien todo, pero con el comando de commandlinefu me falló como les mencioné

Por simple deducción supongo que el comando que utilizas no funciona correctamente.


Saludos

Citar¿cada vez que haga una shell y quiera sacar los opcode es necesario usar push y guardar en el stack todo?
Eres libre de programar tu shellcode como deses, puedes hacerlo bien o puedes hacerlo mal. Es muy recomendable guardar el contexto de la pila, lo cual se hace mediante las instrucciones que mencionaste, y contrastando empíricamente tu mismo te diste cuenta que la mayoría de shellcodes siguen dicho procedimiento.
Luego respecto a lo de empujar cadenas a la pila y demás depende del tipo de función que estés utilizando y cómo reciba los parámetros si los recibe por registro no sería necesario almacenarlos en la pila.
Citar¿entonces es necesario meter en el stack todo en casos de shellcodes o será simplemente una coincidencia o una forma diferente de programar?
Creo que esta duda ya te la aclaré.
Citar¿Y que pasa con los opcode "Cero" que si son necesarios?
Bueno aquí no está del todo bien lo que te dice el compañero @No tienes permitido ver los links. Registrarse o Entrar a mi cuenta , realmente no pasa nada en el shellcode porque contenga null bytes (\x00). Cada escenario es un mundo paralelo, normalmente se trata de evitar dichos null bytes por el tema de que en determinadas situaciones la gente se enfrenta a una función la cual interpreta un conjunto de bytes para su propio funcionamiento interno.
Un ejemplo sería strcpy la cual interpreta dicho null byte como un terminador de cadenas y a partir del mismo cesa la ejecución de los op codes consecutivos, como consecuencia tendremos una ejecución de código arbitrario fallida. Pero no necesariamente hay que temer a dichos null bytes a diferencia de strcpy, gets() te permitiría ejecutar tu shellcode con los tan odiados null bytes.
Con esto me reitero, cada escenario es un mundo.
Un saludo
Lo que sabemos es una gota de agua; lo que ignoramos es el océano.

No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
Citar¿Y que pasa con los opcode "Cero" que si son necesarios?
Bueno aquí no está del todo bien lo que te dice el compañero @No tienes permitido ver los links. Registrarse o Entrar a mi cuenta , realmente no pasa nada en el shellcode porque contenga null bytes (\x00). Cada escenario es un mundo paralelo, normalmente se trata de evitar dichos null bytes por el tema de que en determinadas situaciones la gente se enfrenta a una función la cual interpreta un conjunto de bytes para su propio funcionamiento interno.
Un ejemplo sería strcpy la cual interpreta dicho null byte como un terminador de cadenas y a partir del mismo cesa la ejecución de los op codes consecutivos, como consecuencia tendremos una ejecución de código arbitrario fallida. Pero no necesariamente hay que temer a dichos null bytes a diferencia de strcpy, gets() te permitiría ejecutar tu shellcode con los tan odiados null bytes.
Con esto me reitero, cada escenario es un mundo.

Correcto, mi respuesta sólo sirve en el contexto de funciones que interpretan el null byte como terminador de cadenas. Gracias @No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Saludos

que buenas explicaciones, muchas gracias, resolvieron mis dudas  ;D