assignments/help/sicherheitsluecken.md

181 lines
5.0 KiB
Markdown
Raw Normal View History

2024-08-08 19:05:15 +02:00
# Typische Sicherheitslücken
Im Folgenden finden Sie eine kleine Auflistung von Sicherheitslücken, die sich gut in Crackmes verwenden können. Die Liste ist natürlich nicht vollständig - lassen Sie Ihrer Kreativität freien Lauf.
Gerne können Sie auch noch weitere Lücken vorschlagen.
## Buffer-Overflow
### Lokale Variablen überschreiben
Buffer auf dem Stack liegen dort zusammen mit allen anderen lokalen Variablen und können diese bei fehlender oder falscher Prüfung der Grenzen überschreiben (Buffer-Overflow).
```c
int i = 0;
char x[3];
printf("%d\n", i); // -> 0
strcpy(x, "Hell");
printf("%d\n", i); // -> 108
```
### Rücksprungadresse überschreiben
Buffer-Overflows können auch dazu benutzt werden, die Rücksprungadresse zur Aufrufenden Funktion zu überschreiben. Stack-Guards sollen dies verhindern, können aber manchmal übergangen oder explizit durch `-fno-stack-protector` beim Compilieren ausgeschaltet werden.
## Stackbasierte Schwachstellen
### Base-Pointer
Auf dem Stack liegt nicht nur die Rücksprungadresse, sondern auch der gespeicherte Base-Pointer (`EBP`) der vorhergehenden Funktion. Wenn man diesen durch einen Buffer-Overflow modifizieren kann, besteht die Möglichkeit lokale Variablen der aufrufenden Funktion zu verändern.
### Reste auf dem Stack
Der Stack wird bei einem Funktionsaufruf nicht gesäubert. Wenn die Funktion ihre Variablen nicht initialisiert, kann man möglicherweise alte Daten auf dem Stack entdecken.
```c
int a(void) {
short a = 0xbeef;
short b = 0xdead;
}
int b(void) {
int z;
printf("0x%x\n", z);
}
int main() {
a();
b(); // -> 0xbeefdead
}
```
## String-Funktionen
### Größe von String-Puffern
Wegen des Nullbytes muss der Buffer für einen String immer mindestens ein Zeichen länger sein, als der String selbst. Wenn man dies nicht berücksichtigt, kommt es Buffer-Overflows.
```c
char *text = "Hallo";
char *copy = alloc(strlen(text)); /* zu klein */
strcpy(copy, text); /* Buffer overflow */
```
### Länge von Strings
Die normalen String-Funktionen (`strcpy`. `strlen` ...) lesen bis zum ersten 0-Byte (`'\0'`) im `char*`-Array. Dies kann man eventuell ausnutzen, um mehr Daten zu verändern als vom Entwickler gewollt, weil man z.B. einen Puffer bis zum letzten Byte füllt.
```c
int k = 0x47554755;
char src[] = { 'H', 'a', 'l', 'l', 'o' };
char dest[30];
strcpy(dest, src);
printf("%s", dest); // -> HalloUGUGJ
```
### Puffer überschreiben
Funktionen wie `gets`, `strcpy` und `sprintf` überprüfen die Größe des Zielpuffers nicht und lassen sich deswegen hervorragend für Buffer-Overflows nutzen.
```c
int i = 0;
char b[5];
gets(b);
printf("%d\n", i);
```
Deswegen sollten die Funktionen nicht mehr genutzt werden. Findet man sie in den Imports eines Programms lohnt sich die Suche nach der Stelle, an der sie verwendet werden.
## Integer-Overflow
Alle Integer-Datentypen ohne `unsigned` (`char`, `short`, `int`, `long`, `long long`) sind vorzeichenbehaftet und laufen von Plus nach Minus über.
```c
int i = 2147483647;
i++;
printf("%d\n", i); // -> -2147483648
```
Alle Integer-Datentypen mit `unsigned` laufen zur `0` über und zum Max-Value unter.
```c
unsigned int i = 0;
i--;
printf("%u\n", i); // -> 4294967295
i++;
printf("%u\n", i); // -> 0
```
## Formatstring-Schwachstelle
Formatstringschwachstellen (`printf`) erlauben es, Variablen auf dem Stack zu lesen und zu schreiben.
```c
int main() {
long k = 0xcafebabe;
long *p = &k;
printf("0x%9$x"); // -> 0xcafebabe
}
```
## Funktionspointer
Funktionspointer auf dem Stack können, wenn sie durch eine der anderen Schwachstellen modifiziert werden können, dazu genutzt werden beliebige Funktionen aufzurufen.
```c
void f() {
puts("f called");
}
int main() {
void (*fp)(void);
fp = f;
fp(); // -> f called
}
```
## "malloc can never fail"
`malloc()` gibt `0` im Fehlerfall zurück, dies sollte eigentlich vom Programm geprüft werden. Wird dies nicht geprüft, arbeitet das Programm mit einem Pointer weiter, der die Adresse `0x00` hat.
```c
void *p = malloc(1717272222772);
printf("%p\n", p); // (nil)
```
## Use-After-Free
Mit `malloc` allozierter Speicher wird mit `free` wieder freigegeben. Wenn man den Speicher aber nach dem `free` noch weiter benutzt, können beliebige Daten von anderen Teilen des Programms dort landen, weil `malloc` den Speicher natürlich "recycled". Hierdurch kann man Daten exfiltrieren oder Funktionen sogar eigene Daten unterschieben.
```c
void printer() {
static char *text = 0;
if (!text) {
text = (char*) malloc(sizeof(char) * 9);
strcpy(text, "mutti123");
}
printf("%s\n", text); /* use after free */
free(text);
}
int main(int argc, char** argv) {
printer(); /* -> mutti123 */
char *pwnd = (char*) malloc(sizeof(char) * 9);
strcpy(pwnd, "pwned!");
printer(); /* -> pwned! */
}
```
## GOT.PLT
Wenn das Programm `-z,norelro` compiliert ist, kann man die Funktionen im global offset-table (GOT.PLT) ersetzen, so man durch eine andere Schwachstelle Zugriff darauf bekommt. Hierdurch kann man dann Standard-Funktionen der Library durch eigene ersetzen.