XXE (RSS Validity Checker Root-Me)

Anas Ibrahim
4 min readFeb 29, 2024
XML External Entity

What is XML external entity injection?

XML external entity injection (also known as XXE) is a web security vulnerability that allows an attacker to interfere with an application’s processing of XML data. It often allows an attacker to view files on the application server filesystem, and to interact with any back-end or external systems that the application itself can access.

Challenge Link

https://www.root-me.org/fr/Challenges/Web-Serveur/XML-External-Entity

Description

Find the administrator’s password.

Solution

While navigating the challenge’s web browser, I found a URL input field (vulnerable function), and a Submit Query button.

figure 1

Itested for XSS, but it was mitigated by using htmlspecialchars().

After attempting to test SSRF by inputting various URLs, I encountered an error indicating that the XML document is not valid. This suggests that the server might be attempting to parse the response from the requested URL as XML but is encountering issues due to the format not conforming to XML standards. Further investigation into the server-side code handling the SSRF requests could provide insights into the specific XML parsing mechanism and potential vulnerabilities therein.

figure 2

While opening the collaborator and clicking on ‘pull now’ I discovered a blind SSRF vulnerability. By injecting the Burp Collaborator URL and submitting the query, I successfully received requests from the web server. This vulnerability allows an attacker to make arbitrary requests on behalf of the server, potentially accessing internal resources or performing actions within the network.

figure 3

While attempting to exploit an SSRF vulnerability using the `gopher` PHP wrapper, I encountered the following error message:

Warning: simplexml_load_string(): Unable to find the wrapper “gopher” — did you forget to enable it when you configured PHP? in /challenge/web-serveur/ch29/index.php on line 32 Warning: simplexml_load_string(php://filter/read=convert.base64-encode/resource=gopher://127.0.0.1:80): failed to open stream: operation failed in /challenge/web-serveur/ch29/index.php on line 32

Then I tested for XXE to get the flag, Afterward, I speculated that it leverages cURL_init to initialize a new session, but only with xml documents. Consequently, I visited https://pastebin.com and uploaded a payload that returned the backend source code of index.php, encoded using base64.

You can access the payload here: https://pastebin.com/raw/Kyr9Rpu0

figure 4

Then I copied the raw link from Pastebin and injected it into the URL input.

figure 5

Now that I’ve obtained the encoded Base64 source code, I opened the decoder in Burp Suite and decoded it, resulting in the PHP source code.

<?php

echo '<html>';
echo '<header><title>XXE</title></header>';
echo '<body>';
echo '<h3><a href="?action=checker">checker</a>&nbsp;|&nbsp;<a href="?action=auth">login</a></h3><hr />';

if ( ! isset($_GET['action']) ) $_GET['action']="checker";

if($_GET['action'] == "checker"){

libxml_disable_entity_loader(false);
libxml_use_internal_errors(true);

echo '<h2>RSS Validity Checker</h2>
<form method="post" action="index.php">
<input type="text" name="url" placeholder="http://host.tld/rss" />
<input type="submit" />
</form>';


if(isset($_POST["url"]) && !(empty($_POST["url"]))) {
$url = $_POST["url"];
echo "<p>URL : ".htmlentities($url)."</p>";
try {
$ch = curl_init("$url");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 3);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT ,0);
$inject = curl_exec( $ch );
curl_close($ch);
$string = simplexml_load_string($inject, null, LIBXML_NOENT);
if ( ! is_object($string) || !$string || !($string->channel) || !($string->channel->item)) throw new Exception("error");

foreach($string->channel->item as $row){
print "<br />";
print "===================================================";
print "<br />";
print htmlentities($row->title);
print "<br />";
print "===================================================";
print "<br />";
print "<h4 style='color: green;'>XML document is valid</h4>";
}
} catch (Exception $e) {
print "<h4 style='color: red;'>XML document is not valid</h4>";
}

}
}

if($_GET['action'] == "auth"){
echo '<strong>Login</strong><br /><form METHOD="POST">
<input type="text" name="username" />
<br />
<input type="password" name="password" />
<br />
<input type="submit" />
</form>
';
if(isset($_POST['username'], $_POST['password']) && !empty($_POST['username']) && !empty($_POST['password']))
{
$user=$_POST["username"];
$pass=$_POST["password"];
if($user === "admin" && $pass === "".file_get_contents(".passwd").""){
print "Flag: ".file_get_contents(".passwd")."<br />";
}

}

}


echo '</body></html>';
print "Flag: ".file_get_contents(".passwd")."<br />";

So, to get the flag, I must retrieve the content of the .passwd file. Then, I created a new Pastebin link by simply changing index.php to .passwd in order to obtain the password.

figure 6

So, I copied the Pastebin link (https://pastebin.com/raw/tbCiMmPf) and pasted it into the vulnerable function, and the XML document was successfully validated.

figure 7

Now, I have successfully obtained the encoded base64 flag.

figure 8

Finally, I have completed the write-up detailing the solution to the XXE web security challenge on the RootMe platform. I hope you find it enjoyable.

The End

--

--