HackTheBox Writeup - Layover
Layover is a Hard Linux machine on Hack The Box that rewards patience more than noisy scanning. The first useful credential did not open a shell on the target itself; it opened a contractor workstation over RDP. From there, unrestricted sudo access exposed a wireless interface and turned the box into a bridge into an internal airport network.
The next pivot was a captive portal hosted by portal.international.htb. Access through the portal led to a Craft CMS 5.9.8 administration surface, where an authenticated blind command-injection path could be turned into reliable code execution. The final stages were a careful credential-recovery pivot to a second host over SSH and a local CUPS privilege escalation using CVE-2026-34990.
Scope note: This writeup documents an authorized Hack The Box practice machine. Do not reproduce these techniques against systems you do not own or have explicit permission to test.
Synopsis
| Property | Value |
|---|---|
| Machine | Layover |
| Difficulty | Hard |
| Operating system | Linux |
| Initial access | Contractor credentials over RDP |
| First pivot | Airside workstation with unrestricted sudo |
| Network pivot | Wireless connection to HTB International |
| Web foothold | Craft CMS 5.9.8 authenticated blind RCE |
| User pivot | Decrypted mail-relay credential over SSH |
| Root path | CUPS 2.4.16 local privilege escalation |
The original instance was reset during verification. Its address changed, the SSH pivot could not be re-established before the session ended, and the original user-flag record was rejected by HTB. I therefore do not include either flag value here. Root ownership had previously been demonstrated in the lab, but no flag value is reproduced.
Enumeration
External Services
I started with the usual full TCP scan against the active instance:
1
nmap -sC -sV -p- --min-rate 5000 -oN nmap-full.txt <historical-target-ip>
The external surface exposed SSH and RDP. That combination suggested that the intended entry point might be a legitimate user account rather than a web exploit:
1
2
22/tcp open ssh
3389/tcp open ms-wbt-server
The machine was not simply a Linux server waiting for a direct SSH login. The RDP service was the important clue: Layover had an operational workstation in the path, and the credentials provided by the box’s attack surface made more sense there.
RDP Access to airside-ws01
I tested the available contractor credentials against RDP and landed on the airside workstation, airside-ws01. This was the first meaningful change in perspective. Instead of looking only at the server’s public ports, I now had a logged-in desktop with local network visibility and the permissions of a contractor account.
The first local check was deliberately simple:
1
2
3
4
whoami
hostname
ip addr
sudo -l
The contractor account could run commands through sudo without a meaningful restriction. That did not immediately give me the final host, but it changed what was possible from the workstation: network interfaces could be inspected, wireless tooling could be used, and the workstation could participate in networks that were not visible from the original external position.
The Wireless Pivot
Finding HTB International
From airside-ws01, I enumerated the available wireless networks:
1
2
nmcli device status
nmcli dev wifi list
The scan revealed HTB International WiFi. This was the pivot that made the machine’s name and airport context meaningful. The workstation was not just a jump host; it was positioned where an attacker could reach the internal airline network.
I connected the wlan2 interface and confirmed that DHCP assigned the interface an address in the internal range:
1
2
3
sudo nmcli dev wifi connect "HTB International WiFi" ifname wlan2
ip addr show wlan2
ip route
The historical lease was 10.13.37.182/24. I am keeping the address here only as context for the original instance; HTB changed the active address after the machine reset.
Captive Portal Discovery
Once wlan2 was online, I checked the new network path rather than assuming that a successful association meant unrestricted access. HTTP requests were redirected to the captive portal:
1
portal.international.htb/miles/
The portal was the next trust boundary. The RDP account had provided enough access to join the network, and the portal exposed the application surface that eventually led to the server-side foothold.
Miles and Craft CMS
Portal Access
The portal identified the user context as Miles and accepted the available access path. I mapped the routes and application behavior before trying to force an exploit. The important result was an authenticated Craft CMS administration interface rather than an anonymous public page.
The application reported Craft CMS 5.9.8. That version number mattered because it narrowed the research and made the element-search behavior worth testing, but version matching alone was not enough. The useful condition was authenticated access to the affected behavior.
Element Search and Blind Execution
The vulnerable request path involved Craft’s element-search handling. The injection was blind: a normal command-output response was not returned to the browser. The first useful proof was timing-based, using a harmless delay:
1
2
<authenticated element-search request with a sanitized timing proof>
sleep 5
The relevant behavior chain passed through:
1
2
yii\behaviors\AttributeTypecastBehavior
Psy\Readline\Hoa\ConsoleProcessus::execute
I used the timing difference to prove that the server was evaluating the injected command. This was an important checkpoint. Without it, a slow page or a queue delay could easily be mistaken for code execution.
The blind primitive was then converted into a reliable callback channel. Instead of depending on a one-off command response, I arranged for the target to bind a shell on TCP port 4445:
1
2
3
# Illustrative only: replace the redacted callback details with values from your lab.
<sanitized authenticated request that starts a bind shell on TCP 4445>
nc <target-ip> 4445
The shell landed as www-data. At this point I had repeatable execution and could inspect the application files locally rather than trying to infer everything from timing alone.
From www-data to aporter
Reading the Portal Configuration
The portal’s environment file and Craft database contained the configuration needed for the next pivot:
1
2
cd /var/www/portal
sed -n '1,160p' .env
The sensitive values are intentionally not reproduced. The important finding was that Craft’s mail-relay settings stored an encrypted password, and the application had enough information to decrypt it.
The relevant application logic was in MilesController.php. It showed two details that were easy to miss if I only searched for obvious plaintext credentials:
- The relay value was wrapped in base64 before being passed around.
- The application used Craft/Yii decryption with the site security key.
I parsed the exact CRAFT_SECURITY_KEY directly from .env on the target and used the application’s own decryption path. I did not transcribe or publish the key, encrypted blob, or decrypted credential. That distinction matters: copying a secret into a writeup turns a temporary lab observation into a reusable credential.
The resulting relay credential enabled SSH access to the internal host at 10.13.37.10 as aporter:
1
ssh aporter@10.13.37.10
The address is included as historical context for the original lab layout. After the HTB reset, the instance received a new address and I could not re-establish this pivot before the session ended.
Privilege Escalation with CUPS
Local Enumeration
On the aporter shell, I checked the operating system, kernel, running services, and package versions:
1
2
3
4
id
uname -a
ss -lntup
dpkg -l | grep -i cups
The host was running CUPS 2.4.16. That version was the key local lead. Rather than guessing at generic kernel exploits, I matched the installed service to CVE-2026-34990 and validated the affected behavior in the authorized lab environment.
Capturing the Privileged Token
The exploit path involved CUPS handling a privileged request and exposing a token that could be captured and reused by the local proof of concept. The commands below are intentionally schematic; the exploit artifact and token are not included:
1
2
# Illustrative only: use a verified lab copy of the CVE-2026-34990 PoC.
./cups-poc --target <local-cups-endpoint> --capture-token
The captured token allowed the exploit to write a sudoers rule. I verified the result rather than treating the file write as success:
1
2
sudo -l
id
The final check confirmed root:
1
uid=0(root) gid=0(root) groups=0(root)
This was the cleanest end state I could verify after the pivot. The original user-flag record was not accepted by HTB, the instance became unreachable, and the reset changed the target IP, so I am deliberately not claiming a verified user flag.
Attack Chain Summary
The complete chain was:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Contractor credentials
↓
RDP access to airside-ws01
↓
Unrestricted sudo on the workstation
↓
Wireless scan finds HTB International WiFi
↓
wlan2 receives 10.13.37.182/24
↓
Captive portal redirects to portal.international.htb/miles/
↓
Miles access reaches Craft CMS 5.9.8 admin
↓
Authenticated element-search behavior injection
↓
Timing proof, then www-data bind shell on TCP 4445
↓
Craft .env/database and MilesController.php reveal decryption path
↓
Decrypted relay credential enables SSH as aporter
↓
Reach 10.13.37.10
↓
CUPS 2.4.16 / CVE-2026-34990
↓
Captured token writes sudoers rule
↓
uid=0(root)
Lessons Learned
1. Treat a Workstation as an Attack Surface
The first login did not provide the final server shell, but it provided something more valuable: a real workstation with wireless hardware, desktop credentials, and local administrative capability. The correct question was not “what can this account do on the RDP host?” but “what networks can this host reach?”
2. Network Association Is Not the Same as Application Access
Joining HTB International WiFi only created the next opportunity. The captive portal still controlled access, and the redirect to /miles/ was the clue that identified the application and user context. Each network boundary had its own authentication and trust assumptions.
3. Blind RCE Needs a Measurable Proof
The element-search injection did not return command output. A five-second timing proof established that the behavior was real before I spent time building a shell channel. That small validation step prevented me from confusing normal application latency with execution.
4. Follow the Application’s Cryptography
The mail-relay password was not sitting in plaintext. MilesController.php explained the base64 wrapping and Craft/Yii decryption flow, while .env supplied the exact site key at runtime. Reusing the application’s own logic was more reliable than trying to guess a format or manually transform the value.
5. Re-Enumerate After Every Pivot
The external scan could not show the internal host, the internal host could not show the wireless network, and the web shell could not show the SSH target until the configuration files were inspected. Every pivot changed the visibility of the environment and deserved a fresh service and credential review.
Defensive Recommendations
- Do not expose authenticated search or element-query behavior to expression evaluation. Treat search attributes as data and use strict allowlists for filter names and types.
- Remove dangerous process-execution paths from production dependencies and ensure server-side debugging tools are not reachable from web requests.
- Do not let contractor workstation accounts run unrestricted
sudo. Use narrowly scoped commands, separate administrative roles, and strong audit logging. - Segment corporate WiFi and captive-portal infrastructure from management services and internal SSH targets. A workstation joining a wireless network should not automatically gain a route to sensitive hosts.
- Keep Craft security keys and database credentials out of broadly readable application paths, rotate them after exposure, and protect
.envfiles with least-privilege filesystem permissions. - Never store recoverable mail credentials unless the service genuinely requires them. Prefer short-lived, scoped credentials and a dedicated secret-management system.
- Patch CUPS promptly and restrict its network exposure. Local services still require hardening because a web compromise can become a local privilege-escalation problem.
- Monitor for unusual wireless associations, captive-portal access from administrative workstations, unexpected bind shells, and unauthorized sudoers changes.
Layover’s lesson is that the most important pivot was not a single payload. It was recognizing that each successful step changed the map: RDP exposed a workstation, the workstation exposed WiFi, WiFi exposed the portal, the portal exposed Craft, and Craft exposed the credentials that made the final host reachable.
