
Un kit de herramientas para escribir shellcode
Escribir shellcodes siempre ha sido muy divertido, pero algunas partes son extremadamente aburridas y propensas a errores. ¡Concéntrate solo en la parte divertida y usa ShellNoob!
Para una visión rápida, consulta las diapositivas de la charla en Black Hat Arsenal: link
¿Quieres contribuir? ¿Solicitud de funcionalidad? ¿Reporte de error? ¿Insultos? Todo comentario es bienvenido! (Pero algunos tipos de comentarios son más bienvenidos que otros :-)).
No dudes en contactarme en twitter @reyammer o enviarme un correo a yanick[AT]cs.ucsb.edu si tienes alguna pregunta!
21/01/2014 - ¡ShellNoob 2.1 está disponible! Incluye soporte completo para Python 3 y muchas correcciones de errores. ¡Todo el crédito es para Levente Polyak!
29/07/2013 - ¡ShellNoob 2.0 está disponible!
08/06/2013 - ¡ShellNoob fue aceptado en Black Hat Arsenal! Ver anuncio aquí: link.
--intel.--64.-c.--to-strace y --to-gdb!--file-patch, --vm-patch, ! (todos los detalles abajo)$ ./shellnoob.py -h
shellnoob.py [--from-INPUT] (input_file_path | - ) [--to-OUTPUT] [output_file_path | - ]
shellnoob.py -c (añadir un breakpoint (Advertencia: solo pocas plataformas/OS están soportadas!)
shellnoob.py --64 (modo de 64 bits, por defecto: 32 bits)
shellnoob.py --intel (modo sintaxis Intel, por defecto: att)
shellnoob.py -q (modo silencioso)
shellnoob.py -v (o -vv, -vvv)
shellnoob.py --to-strace (lo compila y ejecuta strace)
shellnoob.py --to-gdb (lo compila y ejecuta gdb y establece breakpoint en el punto de entrada)
Plugins "independientes"
shellnoob.py -i [--to-asm | --to-opcode ] (para modo interactivo)
shellnoob.py --get-const <const>
shellnoob.py --get-sysnum <sysnum>
shellnoob.py --get-errno <errno>
shellnoob.py --file-patch <exe_fp> <file_offset> <data> (en hex). (Advertencia: probado solo en x86/x86_64)
shellnoob.py --vm-patch <exe_fp> <vm_address> <data> (en hex). (Advertencia: probado solo en x86/x86_64)
shellnoob.py --fork-nopper <exe_fp> (esto nopea las llamadas a fork(). Advertencia: probado solo en x86/x86_64)
"Instalación"
shellnoob.py --install [--force] (esto solo copia el script en una ubicación conveniente)
shellnoob.py --uninstall [--force]
Formato INPUT soportado: asm, obj, bin, hex, c, shellstorm
Formato OUTPUT soportado: asm, obj, exe, bin, hex, c, completec, python, bash, ruby, pretty, safeasm
¡Todas las combinaciones de INPUT a OUTPUT están soportadas!
$ ./shellnoob.py --install
Esto solo copiará el script a /usr/local/bin/snoob. Eso es todo. (Ejecuta ./shellnoob.py --uninstall para deshacerlo).
$ snoob --from-asm shell.asm --to-bin shell.bin
Algunas alternativas equivalentes (la herramienta intentará adivinar lo que quieres dada la extensión del archivo..)
$ snoob --from-asm shell.asm --to-bin
$ snoob shell.asm --to-bin
$ snoob shell.asm --to-bin - > shell.bin
$ cat shell.asm | snoob --from-asm - --to-bin - > shell.bin
--intel para usar sintaxis Intel. (ver sección "asm como formato de salida" para más detalles)--from-shellstorm toma como argumento un <shellcode_id>. ShellNoob obtendrá el shellcode seleccionado de la base de datos de shellcodes de shell-storm, y lo convertirá al formato seleccionado.$ snoob -c shell.asm --to-exe shell
$ gdb -q shell
$ run
Reading symbols from ./shell...(no debugging symbols found)...done.
(gdb) run
Starting program: ./shell
Program received signal SIGTRAP, Trace/breakpoint trap.
0x08048055 in ?? ()
(gdb)
O puedes usar los nuevos modificadores --to-strace y --to-gdb!
$ snoob open-read-write.asm --to-strace
Convirtiendo open-read-write.asm (asm) a /tmp/tmpBaQbzP (exe)
execve("/tmp/tmpBaQbzP", ["/tmp/tmpBaQbzP"], [/* 97 vars */]) = 0
[ Process PID=12237 runs in 32 bit mode. ]
open("/tmp/secret", O_RDONLY) = 3
read(3, "thesecretisthedolphin\n", 255) = 22
write(1, "thesecretisthedolphin\n", 22thesecretisthedolphin
) = 22
_exit(0)
$ snoob open-read-write.asm --to-gdb
Convirtiendo open-read-write.asm (asm) a /tmp/tmpZdImWw (exe)
Reading symbols from /tmp/tmpZdImWw...(no debugging symbols found)...done.
(gdb) Breakpoint 1 at 0x8048054
(gdb)
¡Observa cómo ShellNoob establece automáticamente un breakpoint en el punto de entrada!
$ snoob --get-sysnum read
i386 ~> 3
x86_64 ~> 0
$ snoob --get-sysnum fork
i386 ~> 2
x86_64 ~> 57
$ snoob --get-const O_RDONLY
O_RDONLY ~> 0
$ snoob --get-const O_CREAT
O_CREAT ~> 64
$ snoob --get-const EINVAL
EINVAL ~> 22
$ snoob --get-errno EINVAL
EINVAL ~> Invalid argument
$ snoob --get-errno 22
22 ~> Invalid argument
$ snoob --get-errno EACCES
EACCES ~> Permission denied
$ snoob --get-errno 13
13 ~> Permission denied
$ ./shellnoob.py -i --to-opcode
asm_to_opcode selected
>> mov %eax, %ebx
mov %eax, %ebx ~> 89c3
>>
./shellnoob.py -i --to-asm
opcode_to_asm selected
>> 89c3
89c3 ~> mov %eax,%ebx
>>
$ python
>>> from shellnoob import ShellNoob
>>> sn = ShellNoob(flag_intel=True)
>>> sn.asm_to_hex('nop; mov ebx,eax; xor edx,edx')
'9089c331d2'
>>> sn.hex_to_inss('9089c331d2')
['nop', 'mov ebx,eax', 'xor edx,edx']
>>> sn.do_resolve_syscall('fork')
i386 ~> 2
x86_64 ~> 57
Cuando "asm" es el formato de salida, ShellNoob hará todo lo posible. Se usa objdump como desensamblador, pero su salida no es infalible. ShellNoob intenta aumentar el desensamblado añadiendo los bytes (notación .byte) y, cuando sea apropiado, mostrará el equivalente en ASCII (notación .ascii). Esto es útil cuando quieres modificar/ensamblar la salida de objdump pero necesitas hacer una corrección rápida.
Ejemplo con la notación .byte:
jmp 0x37 # .byte 0xeb,0x35
pop %ebx # .byte 0x5b
mov %ebx,%eax # .byte 0x89,0xd8
add $0xb,%eax # .byte 0x83,0xc0,0x0b
xor %ecx,%ecx # .byte 0x31,0xc9
Ejemplo con la notación .ascii:
das # .ascii "/"
je 0xac # .ascii "tm"
jo 0x70 # .ascii "p/"
jae 0xa8 # .ascii "se"
arpl %si,0x65(%edx) # .ascii "cre"
je 0xa0 # .ascii "tX
ShellNoob se publica bajo la licencia MIT. Consulta el archivo COPYRIGHT.
--fork-nopper