Presence of Absence
Presence of Absence
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Easy | OS: N/A | **Author: XESXOR Techniques: error_page_differential_analysis, exposed_asset_inspection, printable_strings_extraction
Summary
Walkthrough of the CUSTOM Web Presence of Absence. Task: an nginx site returned only custom 403 and 404 pages, but the less-damaged 404 page exposed an extra favicon asset. Solution: compare both error pages, fetch the missing favicon, extract printab
Description
Original task name: «Присутствие отсутствия»
Target: http://62.173.140.174:16006
The service looked broken at first glance: / returned a custom 403 page, while missing paths returned a custom 404 page. The key hint was that one page was "less damaged", so the correct approach was to compare the error pages and inspect any extra assets still referenced by the better-preserved page.
Analysis
Recon
The server identified itself as nginx/1.25.0.
Useful observations:
GET /returned 403 Forbidden with a custom HTML page.GET /nonexistentreturned 404 Not Found with a different custom HTML page.GET /robots.txtalso returned the same 404 page.
This already suggested that the challenge was not about bypassing access control, but about reading the pages carefully and comparing what survived in each template.
Why the 404 page mattered
The 403 and 404 pages were similar, but the 404 page still referenced more static files:
./css/bootstrap.css./js/bootstrap.bundle.js./assets/favicon.ico
The 403 page included the CSS and JavaScript files, but did not include the favicon reference. That difference matched the challenge hint: one page was less broken, so the missing clue was likely hidden in an asset only referenced there.
Asset inspection
Fetching /assets/favicon.ico returned a normal ICO file. There was no visible flag in the page source itself, so the next step was to inspect the icon as raw data.
Running strings on the favicon revealed three candidate strings:
text read-only • copy allowedCODEBY{IT IS NOT A FLAG BUT YOU CAN TRY} CODEBY{MAYBE LOWER ?} CODEBY{REDACTED}
The favicon image was a candy icon, which made the third candidate the only one that matched the embedded visual hint. The user confirmation matched that interpretation.
Solution
Step 1: Compare the error pages
bash read-only • copy allowedcurl -i http://62.173.140.174:16006/ curl -i http://62.173.140.174:16006/nonexistent curl -i http://62.173.140.174:16006/robots.txt
Expected result:
/->HTTP/1.1 403 Forbidden- missing pages ->
HTTP/1.1 404 Not Found
Step 2: Notice the extra asset on the 404 page
The 404 page source contained:
html read-only • copy allowed<link rel="stylesheet" href="./css/bootstrap.css"> <link rel="shortcut icon" href="./assets/favicon.ico" type="image/x-icon"> <script type="text/javascript" src="./js/bootstrap.bundle.js"></script>
The favicon reference was the important difference.
Step 3: Download and inspect the favicon
bash read-only • copy allowedcurl -O http://62.173.140.174:16006/assets/favicon.ico file favicon.ico exiftool favicon.ico strings favicon.ico
The file and exiftool checks were optional sanity checks. The crucial output came from strings:
text read-only • copy allowedCODEBY{IT IS NOT A FLAG BUT YOU CAN TRY} CODEBY{MAYBE LOWER ?} CODEBY{REDACTED}
Step 4: Select the semantically correct candidate
Because the favicon was a candy icon, the right flag was:
text read-only • copy allowedCODEBY{REDACTED}
Minimal Python reproduction
python read-only • copy allowed#!/usr/bin/env python3 import re import requests BASE = "http://62.173.140.174:16006" def printable_candidates(data: bytes): text = data.decode("latin-1", errors="ignore") return re.findall(r"CODEBY\{[^\r\n\}]{1,200}\}", text) index = requests.get(f"{BASE}/") missing = requests.get(f"{BASE}/nonexistent") print("/ status:", index.status_code) print("/nonexistent status:", missing.status_code) if "./assets/favicon.ico" in missing.text and "./assets/favicon.ico" not in index.text: print("404 page exposes an extra favicon reference") favicon = requests.get(f"{BASE}/assets/favicon.ico") candidates = printable_candidates(favicon.content) print("Candidates:") for candidate in candidates: print(candidate) print("\nFinal flag: CODEBY{REDACTED}")
Lessons Learned
- Compare all response variants, not just the main page.
- Error pages can leak clues through leftover asset references.
- Run
stringson downloaded binaries and images even when they look harmless. - When multiple candidate flags appear, use the visual or thematic hint to choose the correct one.
Key Takeaways / Lessons
- Mastering error_page_differential_analysis, exposed_asset_inspection, printable_strings_extraction was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-04-03 • CUSTOM • Presence of Absence • Difficulty: Easy