TA AzeezCode

Case studies

XXE in a legacy XML import parser

A supplier feed parser resolved external entities, exposing server-side file contents. Remediated by disabling entity loading, switching to a safe parser configuration and validating against a schema.

high severity XXE Secure parsing Legacy code Retested

Context

A legacy PHP integration accepted XML supplier feeds and parsed them with a permissively configured parser. Reviewed as part of an authorised secure code review with a follow-up dynamic test in a lab copy of the application.

Scope

The XML ingestion path only: upload handling, parsing, and downstream mapping. No production data was used; the lab copy was seeded with synthetic records.

Vulnerability

The parser loaded external entities, allowing an XML document with a document type definition to reference local resources and include their contents in parsed output.

Root cause

The code used a parser configuration copied from an old tutorial, with entity substitution enabled and no schema validation. Nobody had revisited the integration since it was written, and there were no tests around it.

Impact

An attacker able to submit a feed could read server-side files readable by the web user and cause outbound requests from the server. In this codebase that included configuration paths, so the finding was rated High.

Evidence

Testing used a benign, non-sensitive marker file created for the purpose. No real configuration or credential file was read, and the proof of concept is not published.

Remediation

  • Disabled DTD processing and external entity loading explicitly rather than relying on defaults.
  • Removed entity substitution flags from the parsing call.
  • Validated every incoming document against an XSD schema before mapping.
  • Rejected documents containing a DOCTYPE declaration outright.
  • Added a size limit and an entity expansion guard against billion-laughs style resource exhaustion.
libxml_set_external_entity_loader(static fn () => null);

$document = new DOMDocument();
$document->loadXML($xml, LIBXML_NONET | LIBXML_NOENT ^ LIBXML_NOENT);

if ($document->doctype !== null) {
    throw new FeedRejected('DTD declarations are not accepted.');
}

Retesting

Retesting resubmitted the original document, a parameter-entity variant, an out-of-band variant pointing at a controlled lab listener, and a nested-entity expansion document. All were rejected before parsing. The listener recorded no connections. Valid supplier feeds continued to import correctly, verified by the regression suite added alongside the fix.

Lessons

  • Parser defaults vary by version and platform; secure configuration must be explicit and asserted in a test.
  • Rejecting DOCTYPE declarations is a cheap, high-value control for feeds that never legitimately need them.
  • Legacy integrations deserve the same review cadence as new features — age is not a security property.

Authorisation

This work was carried out under written authorisation or inside an authorised training environment. All evidence has been sanitised.

Continue reading

Related case studies

high severity Access control

IDOR: missing server-side authorisation in an invoice workflow

An authenticated user could modify an invoice identifier and attempt to access another customer's resource. The remediation enforced ownership at the database query layer and was verified with positive and negative authorisation tests.

IDOR Broken Access Control Authorisation PHP

5 min read

high severity SSRF

SSRF in a server-side document import feature

An authorised security lab examining how a user-controlled URL can cause a web server to make unintended requests to internal or restricted destinations, followed by layered remediation and retesting.

SSRF Web Security PHP Input Validation

9 min read