He querido empezar a programar shellcodes, y he empezado por lo más básico, incrustar una syscall a exit() en asm en un programa C. El problema es que, tanto esta syscall, como otros pedazos de código que he utilizado para empezar a trastear, me sueltan una violación de segmento. Relato el proceso que he seguido:
1. Programo en ASM la llamada al syscall exit con un par de instrucciones extra que limpian los registros eax y ebx:
- Código: Seleccionar todo
section .text
global _start
_start:
xor eax, eax ; Limpiamos eax
xor ebx, ebx ; Limpiamos ebx
mov al, 0x01 ; Pasamos el argumento para syscall exit
int 0x80 ; Llamamos al manejador de interrupciones
2. Compilo, enlazo, ejecuto y sin problemas. Aparte he hecho un strace para ver que realmente se llama a exit(0):
- Código: Seleccionar todo
grtxo@blackjack:~/shellcoding/exit$ nasm -f elf exit.asm
grtxo@blackjack:~/shellcoding/exit$ ld -o salir exit.o
grtxo@blackjack:~/shellcoding/exit$ ./salir
grtxo@blackjack:~/shellcoding/exit$ strace ./salir
execve("./salir", ["./salir"], [/* 48 vars */]) = 0
_exit(0) = ?
3. Desensamblo el código para ver los opcodes:
- Código: Seleccionar todo
grtxo@blackjack:~/shellcoding/exit$ objdump -d salir
salir: file format elf32-i386
Disassembly of section .text:
08048060 <_start>:
8048060: 31 c0 xor %eax,%eax
8048062: 31 db xor %ebx,%ebx
8048064: b0 01 mov $0x1,%al
8048066: cd 80 int $0x80
de donde sacamos la siguiente secuencia de opcodes: "\x31\xc0\x31\xdb\xb0\x01\xcd\x80". Como vemos no hay bytes nulos, que según he leído, son perjudiciales para este propósito, ya que se considera que la cadena ha terminado. Si estoy equivocado, corregidme, por favor.
4. Incrustamos la cadena de opcodes en el programa de prueba en C que saqué de un tutorial para ir trasteando:
- Código: Seleccionar todo
char code[] = "\x31\xc0\x31\xdb\xb0\x01\xcd\x80";
int main (int argc, char **argv)
{
int (*func)();
func = (int (*)()) code;
(int)(*func)();
}
5. Compilo y ejecuto et... voilà, segfault al instante:
- Código: Seleccionar todo
grtxo@blackjack:~/shellcoding/exit$ gcc -o salir2 exit.c
grtxo@blackjack:~/shellcoding/exit$ ./salir2
Violación de segmento
Según he leído, el código tiene que estar en posiciones absolutas, no en posiciones relativas para poder ser reutilizable, pero creo que ese no es mi problema. También creo que puede ser porque el código se aloja en una zona de memoria de solo lectura donde no puedo escribir, pero me resulta raro igualmente.
Voy a postear también un poco de información de depuración:
- Código: Seleccionar todo
grtxo@blackjack:~/shellcoding/exit$ gcc -o salir2 -ggdb exit.c
grtxo@blackjack:~/shellcoding/exit$ gdb salir2
GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
Para las instrucciones de informe de errores, vea:
<http://www.gnu.org/software/gdb/bugs/>...
Leyendo símbolos desde /home/grtxo/shellcoding/exit/salir2...hecho.
(gdb) list
1 char code[] = "\x31\xc0\x31\xdb\xb0\x01\xcd\x80";
2
3 int main (int argc, char **argv)
4 {
5 int (*func)();
6 func = (int (*)()) code;
7 (int)(*func)();
8 }
(gdb) break 6
Punto de interrupción 1 at 0x804839d: file exit.c, line 6.
(gdb) run
Starting program: /home/razieliyo/shellcoding/exit/salir2
Breakpoint 1, main (argc=1, argv=0xbffff084) at exit.c:6
6 func = (int (*)()) code;
(gdb) step
7 (int)(*func)();
(gdb) step
Program received signal SIGSEGV, Segmentation fault.
0x080483a9 in main (argc=1, argv=0xbffff084) at exit.c:7
7 (int)(*func)();
(gdb)
Como vemos, el problema está en el código incrustado. Posteo la salida de un strace por si sirve de referencia:
- Código: Seleccionar todo
grtxo@blackjack:~/shellcoding/exit$ strace ./salir2
execve("./salir2", ["./salir2"], [/* 45 vars */]) = 0
brk(0) = 0x8a6f000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7777000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=123451, ...}) = 0
mmap2(NULL, 123451, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7758000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\220o\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1434180, ...}) = 0
mmap2(NULL, 1444360, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x718000
mprotect(0x872000, 4096, PROT_NONE) = 0
mmap2(0x873000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15a) = 0x873000
mmap2(0x876000, 10760, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x876000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7757000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb77578d0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0x873000, 8192, PROT_READ) = 0
mprotect(0x8049000, 4096, PROT_READ) = 0
mprotect(0x9b2000, 4096, PROT_READ) = 0
munmap(0xb7758000, 123451) = 0
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
+++ killed by SIGSEGV +++
Violación de segmento
A tanto ya no llego, y no entiendo la mitad de lo que dice ahí. Lo que si veo es que quizás haya problemas de permisos, muchos PROT_WRITE, DENYWRITE, PROT_READ, mprotect(), MAP_PRIVATE, que no sé que significan, pero huele a problema en permisos.
Ahí queda la cosa. Creo que quizás el problema se podría solucionar al pasarle al gcc a la hora de compilar algún argumento para que permita el alojamiento de código en la pila, o para dar permisos. También puede ser alguna opción que tengo que activar en el sistema. En resumen, no tengo mucha idea de momento en esto del shellcoding, y acabo de empezar con una simple llamada a exit(0), sin meterme todavía en la adquisición de shells para ir probando, y me encuentro con esto. La verdad tengo muchísimas ganas de aprender y he googleado al máximo, pero siempre había problemas de muchos tipos con respecto a los segfaults.
Espero que me puedan arrojar un poco de luz, seguramente el problema sea nimio y mi fallo, totalmente tonto. Saludos y gracias!








