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 -->ejecutawrite(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/