Irgendwo im code von function_1(), z.B. einer webkit Funktion auf der PS3...
-------------------------------------------------------------------------------------------------
.text:0000000000053510 mr r3, r27 # *str
.text:0000000000053514 bl funktion_2 # (*str)
.text:0000000000053518 nop
.text:000000000005351C cmpwi cr7, r3, 0
.text:0000000000053520 beq cr7, loc_5352C
-------------------------------------------------------------------------------------------------
Register r27 enthält die Adresse eines strings der vom Benutzer kommt, z.B. ein Text den du in ein Eingabefeld einer Website eingibst.
Kann auch ein string, oder irgend ein anderer buffer sein, vom server auf desen Seite du bist kommt.
Mit der Instruktion mr(move register) wird die string Adresse in Register r3 kopiert.
Damit wird die string Adresse zum ersten, hier einzigen, Argument das an funktion_2() übergeben wird.
Jetzt kommt der call(Aufruf) der function_2() mit der Instruktion bl(branch then link).
Dabei passiert folgendes, die Adresse der nachfolgenden Instruktion, hier 0x53518
-------------------------------------------------------------------------------------------------
.text:0000000000053518 nop
-------------------------------------------------------------------------------------------------
wird in das LR(link register) gespeichert.
So weiß das Programm, dass es nach dem return von function_2() (also dem ausführen von function_2() von Anfang bis Ende)
an dieser Addresse in function_1() weiter machen soll. Dann wird zur function_2() gesprungen.
Nun sind wir in function_2()
Am Anfang gibt es den Funktions Prolog, der Funktions-stack wird aufgebaut, das ist das Zwischengedächtnis
der Funktion. Hier liegen z.B. lokale Variablen die wärend der Funktion gebraucht werden, aber auch die
Sicherungskopien von flüchtigen Registern deren Inhalt sonst verloren gehen würde.
Wenn du z.B. das Register r27 auch in function_2() brauchts, dann muss der alte Wert der der schon in r27 liegt,
hier die Adresse des strings in function_1(), gesichert werden. So kann am Ende von function_2(), im Epilog,
der ursprüngliche Wert von r27 wiederhergestellt werden damit function_1() nach dem return ungestört weitermachen kann.
Auch die Addresse im LR(link register) wird am Ende des stack gespeichert. Wie die flüchtigen
Register wird es einfach am Ende von function_2(), im Epilog, wiederhergestellt.
Sagen wir, das erste was in function_2() nach dem Prolog passiert, ist das kopieren des strings in eine
Hilfsvariable, quasi eine Kopie des strings die wir zum arbeiten benutzen. Die lokale Hilfsvariable liegt
natürlich am Anfang des stacks. Sagen wir, es ist vorgesehen das diese Variable 64 byte lang ist.
Also 63 Zeichen plus ein nullbyte welches das Ende des strings signalisiert.
Dann weißt du, dass der stack aus der 64 byte langen Hilfsvariable besteht, gefolgt von den gesicherten
Registern inklusive dem LR(link Register) am Ende des stacks.
Sagen wir, zum kopieren des strings von r3(dem Argument das wir function_2() übergeben haben, der *str)
in die Hilfsvariable auf dem stack wird eine unsichere Funktion wie strcpy() benutzt.
strcpy() kopiert solange wie der string Zeichen hat und hört erst beim Nullterminator auf,
Treffer, function_2() ist eine vulnerability function eine verwundbare Funktion.
Sie überprüft nicht ob der übergebene string wirklich nur 63 + 0 Zeichen lang ist.
Sie benutzt keine sichere Funktion zum kopieren des strings wie strncpy(), wo n angibt wie viel kopiert werden soll.
Wenn du nun dafür sorgst, das der von dir eingegebene string länger als 63 Zeichen + Nullterminator ist,
verursachts du einen buffer overflow, einen Pufferüberlauf der den stack, über die vorgesehene größe der Hilfsvariable
hinaus, überschreibt. Zuerst die gesicherten Register wie z.B. r27 und am Ende natürlich auch die gesicherte Adresse aus
dem LR(link register). Da am Ende von function_2(), im Epilog, alle gesicherten Register wieder mit den Kopien vom stack
restauriert werden kannst du über einen manipulierten string eine andere Adresse ins LR(link register) schreiben.
Du entscheidest, wo das Programm nach dem return von function_2() weitermacht.
Deshalb nennt sich das ganze ROP(Return Oriented Programming) rücksprungorientierte Programmierung.
Da du den stack mit LR kontrollierst, kannst du hinspringen wo du willst, z.B. zu sogenannten gadgets.
gadgets sind Enden von Funktionen die mit blr(branch on link register) also return enden.
Durch eine Aneinanderkettung von diesen gadgets kannst du den code ausführen den du willst. Quasi ein puzzel,
du bastelst dir den code den du haben willst aus einzelnen oder mehreren Instruktionen zusammen.
Diese Verkettung von gadgets nennt sich dann ROP-chain(Kette).
Du kannst Adressen in Register laden um von ihnen Werte zu lesen die du brauchst..
Du kannst auch den syscall ausführen um die IDPS bekommen.
Du kannst Argumente für Funktionen in Register laden und dann Funktionen wie fopen() fwrite() usw nutzen
um eine Datei zu erstellen in die du die IDPS speicherts, usw, usw ...
Es gib da natürlich noch etliches mehr zu dem Thema zu sagen, aber das hier sollte reichen um zu kapieren
worum es im Grunde geht.
Man braucht also einen Angriffspunkt um eingene Daten ins Programm zu bringen. Das kann ein string oder
irgend ein anderer buffer sein, das kann auch über eine Datei passieren... der erste, soweit ich mich erinnere,
exploit auf der PSP war ein TIFF exploit.
Hier wurde das ganze also über ein manipuliertes Bild und eine Schwäche in der TIFF Bibliothek gemacht.
Auf einer Konsole wie der PS3 kann man nicht viele "daten" einbringen, ab und an ein string über OnScreenKeybord,
und das OSK wird sicher genug sein eben weil es eine mögliche Schwachstelle ist. Das laden von Bildern und anderen
Dateien, wird aus den selben Gründen gut abgesichert sein. Aber der webbrowser + webkit, da gibt's viele Möglichkeiten.