forked from ree-lecture/assignments
181 lines
5.0 KiB
Markdown
181 lines
5.0 KiB
Markdown
|
# 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.
|