LOAD_NAME / LOAD_CONST opcode OOB Read
Last updated
Last updated
Lernen Sie & üben Sie AWS-Hacking:HackTricks Training AWS Red Team Expert (ARTE) Lernen Sie & üben Sie GCP-Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Diese Informationen wurden aus diesem Bericht** übernommen.**
Wir können die OOB-Lese-Funktion im LOAD_NAME / LOAD_CONST-Opcode verwenden, um ein Symbol im Speicher zu erhalten. Das bedeutet, dass Sie Tricks wie (a, b, c, ... hunderte von Symbolen ..., __getattribute__) if [] else [].__getattribute__(...)
verwenden können, um ein Symbol (wie einen Funktionsnamen) zu erhalten, den Sie möchten.
Dann erstellen Sie einfach Ihr Exploit.
Der Quellcode ist ziemlich kurz, enthält nur 4 Zeilen!
Wie kommt es zu dem Segfault?
Beginnen wir mit einem einfachen Beispiel, [a, b, c]
könnte in den folgenden Bytecode kompiliert werden.
Aber was passiert, wenn die co_names
ein leeres Tupel werden? Der LOAD_NAME 2
Opcode wird dennoch ausgeführt und versucht, den Wert von dieser Speicheradresse zu lesen, von der er ursprünglich stammen sollte. Ja, das ist ein Out-of-Bound Read "Feature".
Das Kernkonzept für die Lösung ist einfach. Einige Opcodes in CPython wie z.B. LOAD_NAME
und LOAD_CONST
sind anfällig (?) für OOB Reads.
Sie rufen ein Objekt aus dem Index oparg
aus dem consts
oder names
Tupel ab (das ist, wie co_consts
und co_names
unter der Haube genannt werden). Wir können uns den folgenden kurzen Ausschnitt über LOAD_CONST
ansehen, um zu sehen, was CPython tut, wenn es den LOAD_CONST
Opcode verarbeitet.
Auf diese Weise können wir das OOB-Feature verwenden, um einen "Namen" aus einem beliebigen Speicheroffset zu erhalten. Um sicherzustellen, welchen Namen es hat und welchen Offset es hat, versuchen Sie einfach LOAD_NAME 0
, LOAD_NAME 1
... LOAD_NAME 99
... Und Sie könnten etwas bei etwa oparg > 700 finden. Sie können auch versuchen, gdb zu verwenden, um sich natürlich die Speicherstruktur anzusehen, aber ich glaube nicht, dass es einfacher wäre?
Sobald wir diese nützlichen Offsets für Namen / Konstanten abgerufen haben, wie erhalten wir einen Namen / eine Konstante von diesem Offset und verwenden sie? Hier ist ein Trick für Sie:
Angenommen, wir können einen __getattribute__
-Namen vom Offset 5 (LOAD_NAME 5
) mit co_names=()
erhalten, dann führen Sie einfach die folgenden Schritte aus:
Beachten Sie, dass es nicht notwendig ist, es als
__getattribute__
zu benennen, Sie können es als etwas Kürzeres oder Seltsameres benennen
Sie können den Grund einfach erkennen, indem Sie sich den Bytecode ansehen:
Beachten Sie, dass LOAD_ATTR
auch den Namen aus co_names
abruft. Python lädt Namen aus demselben Offset, wenn der Name gleich ist, sodass das zweite __getattribute__
immer noch von Offset=5 geladen wird. Mit diesem Feature können wir einen beliebigen Namen verwenden, sobald der Name im Speicher in der Nähe ist.
Die Generierung von Zahlen sollte trivial sein:
0: not [[]]
1: not []
2: (not []) + (not [])
...
Ich habe keine Konstanten verwendet aufgrund des Längenlimits.
Hier ist zunächst ein Skript, um diese Offsets der Namen zu finden.
Und das Folgende dient zur Erstellung des tatsächlichen Python-Exploits.
Es macht im Grunde genommen die folgenden Dinge für die Zeichenfolgen, die wir aus der __dir__
Methode erhalten:
Lernen Sie AWS-Hacking:HackTricks Training AWS Red Team Expert (ARTE) Lernen Sie GCP-Hacking: HackTricks Training GCP Red Team Expert (GRTE)