jueves, 25 de agosto de 2016

Hack - shellcode en arm raspberry pi 3


Arranque probando un cross-compiler para arm y termine haciendo un shellcode para la rpi3 para no olvidarme, lo dejo anotado si alguien puede aportar lo nombro y agrego sus ideas.

Dejo el git de un sript para geany para poder compilar y subir el elf-arm a una rpi
git@github.com:pablinn/arm-pi.git

git clone git@github.com:pablinn/arm-pi.git


*Para conectarme a la RPI3 en forma remota
ssh ->shell remoto a mi rpi3
ssh pi@192.168.43.222
~ $

*Para ver puertos en la rpi3 para tranquilidad
nmap -v 192.168.43.222
22/tcp ssh (el servicio ssh activo)

 *Para pasar archivos de mi PC a la RPI3
 scp hello pi@192.168.43.222:/home/pi

 *Para ver la informacion de la cpu de la RPI3
cat /proc/cpuinfo

me muestra que es micro ARMV7 rev4 32bits de cuatro nucleos :)



*Para ver mi PC es lo mismo pero es un I7 de 8 nucleos 64bits
Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz


*Para compilar en ubuntu tenemos gcc pero para x486_64

gcc -dumpmachine
x86_64-linux-gnu

gcc -o  archivo archivo.c 


*Para compilar en rpi3 tenemos gcc pero para arm

gcc -dumpmachine
arm-linux-gnueabihf(cross-compiler-armlinux-con soporte coma flotante)


*Cross-compiler para linux x486

Instalarlo en ubuntu
sudo apt-get install gcc-arm-linux-gnueabi 
sudo apt-get install gcc-arm-linux-gnueabihf (version usada en rpi3 soporte de flotantes)


Modo de compilar 1

 Compila un archivo assembler en un objeto
arm-linux-gnueabi-as -o archivo.o archivo.s
 Compila el objeto en un ELF_ARM
arm-linux-gnueabi-gcc -o elf_arm archivo.o
 rm -vf *.o

Modo de compilar 2
Compila el *.C en un ELF_AR
arm-linux-gnueabi-gcc -o elf_arm archivo.c  -static
rm -vf *.o

Los programas compilados desde un x486 con el cross-compiler van a funcionar en un arm la rpi3 o en una maquina virtual como qemu.
por razones de proof (pruebas de concepto) voy a probar los archivo en la RPI3 para ver compatibilidades del CC.
Para automatizar la compilacion se puede usar los makefile el cual son un dolor de cabeza mas jodido q hacer el shellcode por lo q dejo un ejemplo pero voy a usar geany.


 Makefile para un cross-compiler en C solo hay que cambir el nombre de los archivos y listo tener en cuenta que MAKE usa TAB en el comienzo de los comandos no espacio. (make hdp @~$)


sudo nano makefile

********makefile*******************************
all: prueba1

en = as
CC = gcc
$OBJ = prueba1.o
$files = prueba1.s
$sal = prueba1

$(OBJ): $(files)
    $(en) -o $(OBJ) $(files)

$(sal): $(OBJ)
    $(CC) -o $(sal) $(OBJ)
    rm -vf *.o


************************************************
make

pero es un quilombo resumiendo

en un x486 pueden generar
1-elf_x486-64 o su version elf_x486-32 con GCC
2-elf_arm  ARM-GCC

el elf_arm lo emulan con qemu o lo copian a la RPI3 y lo prueban
scp elf_arm pi@192.168.43.222:/home/pi

Bueno hasta aca todo nuevo para mi y divertido, algunas herramientas a usar



geany - gdb -strace objdump ver syscall
asrl - dep nx

Un poco de teoria de procesadores ARM

16 rgistros enteros de 32bits

r0 .. r15
r0 .. r10 de uso general
r11 --->FP(frame pointer) puntero del stack
r12 --->IP(intra-procedure register) guarda datos temp de funciones
r13 --->SP(stack pointer) puntero a la parte mas alta de la pila  -4 crece +4 decrece

r14 --->LR(link register) puntero de retorno de una funcion
r15 --->PC(program counter) direccion de la siguiente instruccion a ejecutar

el CPSR o (current program status register) es un registro de 32bits

Se utiliza en operaciones de comparaciones bucles y saltos.

instrucciones aritmeticas :add - sub - rsb - sbc - adc - rsc
ej suma
add r1,r2,r3   /*  r1=r3+r2 */
add r1,r2,#120 /* r1=r2+120*/


Instrucciones logicas: and - or eor - bic
Instrucciones de comparacion: cmp - cmn  utiliza los registros  N C Z  V
Instrucciones simd o de 16 y 8 bits UADD8
Instrucciones de memoria :LDR -STR
instrucciones de comparacion:

Instrucciones Branch : B - BL - BX - BLX


Bueno otra herramienta es GDB un excelente debuger



primer codigo de prueba1.c con el cross-compiler lo pasamos a la  rpi3
y funciona perfecto o compilamos desde la rpi3.

hello.c
****************************************************************
#include <stdio.h>

int main(int argc, char** argv) {
   printf("Usando el cross compiler para ARM\n");
   return 0;
}
****************************************************************
x486_64
GDB ./hello 
disass main















ARMV7
GDB ./hello
disass main






Con este comando vemos en las definiciones de las librerias en que direccion estan las funciones write() y exit()



ARM - RPI3 - raspbian
cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep write
#define __NR_write            (__NR_SYSCALL_BASE+  4)

cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep exit
#define __NR_exit            (__NR_SYSCALL_BASE+  1)

segun la code wiki
http://codewiki.wikidot.com/c:system-calls:write


Desde ubuntu linux con x486
arm-linux-gnueabihf-as -o bash.o bash.s
arm-linux-gnueabihf-gcc -o bash bash.o -STATIC

Este programa solo escribe lsusb y retorna nada mas tiene mucho comentario es cortito.


/*bash.s
compilado con geany 
arm-linux-gnueabihf-as -o %e.o %f
arm-linux-gnueabihf-gcc -o %e %e.o 

Este es solo un ejemplo solo escribe un texto, usa write y exit
size_t write(int fildes, const void *buf, size_t nbytes);
Por lo que deberiamos de usar 4 registros.
r2 (len cadena) =  6
r1 (cadena)        "lsusb\n" 
r0 (output)        1
r7 (syscall write) 0x04 
*/ 
 
 
**********************bash.s********************************************** 
 /*inicio*/
.section .text

.global main
main:
    /*write()*/
    mov  r2,#6      
    mov  r1,pc
    add  r1,#24
    mov  r0,$0x1 
    mov  r7,$0x4  
    svc  0            /*software interrupt o swi */
    
    /*exit()*/
    sub r0,r0,r0      /*0=1-1*/ 
    mov r7,$0x1
    svc 0
     
.ascii "lsusb\n"
/*fin*/ 
 ******************************************************************************** 
 
 
 Ahora traceamos el programa para ver lo que hace coloque una imagen porque es mucho
 
strace ./bash 
 
 
execve("./bash", ["./bash"], [/* 18 vars */]) = 0     -->ejecuta
write(1, "lsusb\n", 6lsusb                                      -->escribe
)                  = 6                                                     --->sale
exit(0)                                 = ?


Bueno usamos objdump para ver los opcodes y todo el codigo que se ejecuta nos importa solo lo que hay en main el resto no lo puse es lo que se va a jecutar si lo ponemos como shellcode en un puntero y lo llammos.
Pero este codigo tiene problemas no puede tener nulos 0x0 espacios 0x20 por lo que generalmente cambian instruciones por algunas parecidas para que en la salida no hayan nulos o opcodes no deseados.

objdump -d ./bash

0000840c <main>:
    840c:    e3a02006     mov    r2, #6
    8410:    e1a0100f     mov    r1, pc
    8414:    e2811018     add    r1, r1, #24
    8418:    e3a00001     mov    r0, #1
    841c:    e3a07004     mov    r7, #4
    8420:    ef000000      svc    0x00000000
    8424:    e0400000     sub    r0, r0, r0
    8428:    e3a07001     mov    r7, #1
    842c:    ef000000      svc    0x00000000
    8430:    7375736c     .word    0x7375736c
    8434:    0a62             .short    0x0a62

Encontre algo interesante en el paper de shellcode y en los datos de arm que tiene opciones de funcionar en modo de direccionamiento en memoria de 16 u 8 bit es el modo "thumb mode"  el codigo queda algo asi :

arm-linux-gnueabihf-as -mthumb -o bash.o bash.s
arm-linux-gnueabihf-gcc -o bash bash.o -STATIC
***************************prueba.s*********************************
.section .text

.global main
main:

.code 32                      /*modo thumb*/
    add r6,pc,#1
    bx  r6
   
.code 16                   /*mode 16*/
    /*write()*/
    mov  r2,#6            /*longiud de la cadena*/
    mov  r1,pc             /*program counter*/
    add  r1,#12          /*program counter + 12*/
    mov  r0,$0x1        /*parametro 1*/
    mov  r7,$0x4        /*syscall write*/
    svc  0                    /*int*/
   
     /*exit()*/
     sub r0,r0,r0      /*0=1-1*/
     mov r7,$0x1     /*syscall exit*/
     svc 0                  /*int*/
    
.ascii "lsusb\n"
**************************************************************************
Vemos como queda la parte de 32bits y la de 16bits o sea funciona igual y esta reducido, pero tiene un par de nulos.

objdump -d ./bash

000083bc <main>:
    83bc:    e28f6001     add    r6, pc, #1
    83c0:    e12fff16     bx    r6
    83c4:    2206          movs    r2, #6
    83c6:    4679          mov    r1, pc
    83c8:    310c          adds    r1, #12
    83ca:    2001          movs    r0, #1
    83cc:    2704          movs    r7, #4
    83ce:    df00          svc    0
    83d0:    1a00          subs    r0, r0, r0
    83d2:    2701          movs    r7, #1
    83d4:    df00          svc    0
    83d6:    736c          .short    0x736c
    83d8:    0a627375     .word    0x0a627375

Otra modificacion para sacar nulos

********************lsusb.s*******************************
.section .text

.global main
main:

.code 32
    add r6,pc,#1
    bx  r6
   
.code 16    
    /*write()*/
    mov  r2,#6
    mov  r1,pc
    add  r1,#14
    mov  r0,$0x1
    mov  r7,$0x4
    svc  1
   
     /*exit()*/
     sub r4,r4,r4
     mov r0,r4     
     mov r7,$0x1
     svc 1
    
.ascii "lsusb\n"
*********************************************************
y el resultado quedaria bastante bien sin nulos ahora a armar el shellcode esto se puede automtizar

objdump -d ./prueba


000083bc <main>:
    83bc:    e28f6001     add    r6, pc, #1
    83c0:    e12fff16     bx    r6
    83c4:    2206          movs    r2, #6
    83c6:    4679          mov    r1, pc
    83c8:    310e          adds    r1, #14
    83ca:    2001          movs    r0, #1
    83cc:    2704          movs    r7, #4
    83ce:    df01          svc    1
    83d0:    1b24          subs    r4, r4, r4
    83d2:    1c20          adds    r0, r4, #0
    83d4:    2701          movs    r7, #1
    83d6:    df01          svc    1
    83d8:    7375736c     .word    0x7375736c
    83dc:    46c00a62     .word    0x46c00a62

Ahora la parte en rojo es lo que nos interesa con eso se arma el shellcode pero hay que rotar los opcodes


e28f6001  -> \x01\x60\x8f\xe2

asi sucesivamente hasta llegar al shellcode y agregarlo a un puntero y llamarlo

*************************lsusb2*****************************************************
#include <stdio.h>
char *SC =  "\x01\x60\x8f\xe2"
            "\x16\xff\x2f\xe1"
            "\x06\x22"
            "\x79\x46"
            "\x0e\x31"
            "\x01\x20"
            "\x04\x27"
            "\x01\xdf"
            "\x24\x1b"
            "\x20\x1c"
            "\x01\x27"
            "\x01\xdf"
            "\x6c\x73\x75\x73"
            "\x62\x0a\xc0\x46";

int main(void)
        {
                fprintf(stdout,"Longiud: %d\n",strlen(SC));
                (*(void(*)()) SC)();
        return 0;
        }

*********************************************************************************

 Lo compilamos
arm-linux-gnueabihf-gcc -o lsusb2 lsusb2.c

funciona y si lo traceamos
objdump -d ./lsusb2

execve("./lsusb2", ["./lsusb2"], [/* 18 vars */]) = 0
write(1, "Longiud: 36\n", 12Longiud: 36
)           = 12
write(1, "lsusb\n", 6lsusb
)                  = 6
exit(0)                                 = ?
+++ exited with 0 +++

Bueno hasta llegamos tratando de ejecutar el bendito

execve("/bin/sh", ["bin/sh"],0)
consigo violacion de segmento
o llamadas inesperadas jaja






protecciones
aslr - dep - nx
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
echo 1 | sudo tee /proc/sys/kernel/randomize_va_space
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space



Referencias
http://shell-storm.org/blog/Shellcode-On-ARM-Architecture/
http://shell-storm.org/shellcode/


No hay comentarios.:

Publicar un comentario