Erläuterung eines Zeigers im Exploit-Code

Lesezeit: 3 Minuten

Benutzeravatar von HuangJie
HuangJie

In einigen Exploits zum Abrufen der Root-Shell sehe ich häufig einen solchen Hinweis:

int i;
unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); 

Kann jemand diesen Zeiger ein wenig erklären? Ich denke, 8191 ist die Größe des Kernel-Stacks. p zeigt auf die Unterseite der Kernel-Stack? Hier ist, wie Zeiger p wird genutzt:

int i; 
unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); 
for (i = 0; i < 1024-13; i++) { 
    if (p[0] == uid && p[1] == uid && 
        p[2] == uid && p[3] == uid && 
        p[4] == gid && p[5] == gid && 
        p[6] == gid && p[7] == gid) { 
            p[0] = p[1] = p[2] = p[3] = 0; 
            p[4] = p[5] = p[6] = p[7] = 0; 
            p = (unsigned *) ((char *)(p + 8) + sizeof(void *)); 
            p[0] = p[1] = p[2] = ~0; 
            break; 
        } 
    p++; 
} 

  • Der Wert 8191 in binär ist 1111111111111und die long Typ ist 32 Bit. Ich denke, um Ihnen eine klare Antwort zu geben, müssten wir sehen, wie das geht *p Zeiger verwendet wird. Das & Operator ist wahrscheinlich eine Art Bitmaske.

    – Tim Biegeleisen

    23. Oktober 2015 um 6:26 Uhr


  • @TimBiegeleisen Danke für deine Antwort. Ich habe es bearbeitet.

    – HuangJie

    23. Oktober 2015 um 6:38 Uhr

Benutzeravatar von Mormegil
Mormegil

Der Code übernimmt die Adresse der lokalen Variablen i um einen Zeiger in den aktuellen Stapelrahmen zu bekommen. Dann richtet es die Adresse an der 8K-Seite aus (das machen Sie mit x & ~8191: 8191 ist 2^13 – 1 was bedeutet ~8191 sind alle Einsen außer den niedrigen 13 Bits, also löscht die UND-Verknüpfung mit einer Zahl die niedrigen 13 Bits, dh richtet die Zahl auf das nächste niedrigere Vielfache von 2^13 aus, mit anderen Worten, richtet sie an der 8K-Grenze aus).

Es nimmt dann diese Adresse und interpretiert sie als einen Zeiger auf einen Zeiger und lädt die gezeigte Adresse daraus. Weitere Informationen finden Sie unter Grundlegendes zum Abrufen des task_struct-Zeigers aus dem Prozess-Kernel-Stack.

Danach versucht es, eine bestimmte Struktur zu finden, die irgendwo hinter dieser Adresse gespeichert ist: Es durchsucht das Folgende 1024-13 unsigneds, der versucht, einen Ort im Speicher zu finden, an dem die aktuellen Prozessinformationen (wahrscheinlich) gespeichert sind: Wenn es ein Stück Speicher findet, das mehrere Kopien der aktuellen UID und GID enthält, nimmt es an, dass es es gefunden hat. In diesem Fall ändert es es so, dass der aktuelle Prozess UID und GID 0 erhält, wodurch der Prozess unter root läuft (und es speichert alle Einsen in den folgenden Capability-Flags).

Vgl. struct cred.

Joshuas Benutzeravatar
Josua

Ich werde noch eine weitere Antwort posten, weil es hier wirklich etwas hinzuzufügen gibt.

unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191); 

führt dazu, dass p der Zeiger auf den Beginn des 8192 Byte großen Speicherblocks ist. Allerdings ist der Code falsch. Wenn p über INT_MAX liegt (was es sein kann oder in unsigned, nicht unsigned long umgewandelt würde), werden die hohen Bits von der Maske abgeschert. Korrekter Code ist wie folgt:

unsigned *p = *(unsigned**)(((ptrdiff_t)&i) & ~(ptrdiff_t)8191);

oder mit uintptr_t:

unsigned *p = *(unsigned**)(((uintptr_t)&i) & ~(uintptr_t)8191U);

Es ist notwendig, in Integer und zurück in Pointer umzuwandeln, damit der Code funktioniert; Um jedoch einen int-großen Zeiger zu garantieren, ist die Verwendung von ptrdiff_t erforderlich (wir erinnern uns, dass sich signierte und unsignierte Werte für bitweise Operationen genau gleich verhalten). Warum schreiben sie sie nicht mit Hex-Konstanten, wen interessiert das? Die Typen, die solche Dinge tun, kennen ihre Kräfte von 2 auswendig. Es kann schneller sein, 8191 als 0x1FFF zu lesen.

  • Für gewissenhafte ISO-Korrektheit, uintptr_t Anstatt von ptrdiff_t an beiden Orten. Alle Linux-ABIs garantieren jedoch sizeof(unsigned long) == sizeof(T*) für alle T. Also die minimal Eine Änderung, um den Code im Zusammenhang mit einem Linux-Kernel-Exploit zu korrigieren, ist einfach & ~8191UL Anstatt von & ~8191.

    – zol

    23. Oktober 2015 um 20:09 Uhr

  • Wann ~8191Typ intwird in der verwendet &-Ausdruck mit einem unsigned long auf der linken Seite wird die Zeichenerweiterung angewendet. Daher werden die hohen Bits nicht von der Maske abgeschert. Dies ist der gleiche Grund, warum uint64_t x = -1; setzt alle 64 Bit auf 1.

    – mortehu

    23. Oktober 2015 um 20:13 Uhr


  • @mortehu: Ich wurde mit diesem Fragment an anderer Stelle verbrannt. Wenn int weniger Bits als unsigned long hat, machen die Konvertierungen das Falsche.

    – Josua

    23. Oktober 2015 um 21:05 Uhr

1408780cookie-checkErläuterung eines Zeigers im Exploit-Code

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy