November 2, 2026
PHP2Shell Security Lab: Setting Up a Safe Vulnerability Testing Environment with Kali Linux and…
We’ll build an isolated penetration-testing laboratory to investigate PHP2Shell (CVE-2026–109618 and CVE-2026–109619) using Kali Linux as…

By Yasmine Bali
4 min read
We'll build an isolated penetration-testing laboratory to investigate PHP2Shell (CVE-2026–109618 and CVE-2026–109619) using Kali Linux as the attacker and Ubuntu as the vulnerable target.
PHP2Shell was disclosed on October 21, 2026. Both vulnerabilities affect PHP's internal memory handling and can potentially allow unauthenticated remote code execution (RCE). Both are rated Critical, CVSS 4.0: 9.2.
1. Lab Architecture
We'll use Kali Linux as our attacker machine and Ubuntu as our target machine, connected through a private VirtualBox Host-Only network.
Understanding each component
- VirtualBox — Hypervisor :
This is the software that allows you to create and run virtual computers (VMs) on your physical laptop.
- Kali Linux — Attacker
Your security testing machine. You will use tools such as Nmap, Burp Suite and curl to investigate the target.
- Ubuntu 24.04 LTS — Target
The second virtual machine. It will host the vulnerable web server and PHP application.
- Apache + PHP — Web server
Apache receives HTTP requests from your browser or Kali machine. PHP processes the application's server-side code.
Understanding the network
Host-Only: 192.168.56.0/24
Host-Only means the two VMs can communicate through a private virtual network without exposing the vulnerable target directly to your normal external network.
The target IP 192.168.56.120 is an example. We'll configure it explicitly below.
What do PHP 8.5.11 and PHP 8.5.12 mean?
This is probably the most important part.
You want to compare two PHP versions:
- PHP 8.5.11 : Vulnerable version (if affected by your selected CVE)
- PHP 8.5.12 : Patched comparison version (if the relevant fix is included)
2. Configure VirtualBox Networking
For both VMs, open Settings → Network.
Use the following settings:
- Adapter 1: Host-Only Adapter, attached to
vboxnet0(or VirtualBox Host-Only Ethernet Adapter on Windows). - Adapter 2: Disabled during vulnerability testing.
For initial package installation, you can temporarily use NAT, then disable it before launching the vulnerable service. Avoid Bridged Adapter networking.
Create a snapshot of both VMs before testing. Name the Ubuntu snapshot Clean-Ubuntu-Before-PHP2Shell.
3. Configure Ubuntu's IP Address
Inside Ubuntu, identify the Host-Only interface:
ip -br addrip -br addrIf your interface is enp0s8, assign the lab address:
sudo ip addr add 192.168.56.120/24 dev enp0s8
sudo ip link set enp0s8 upsudo ip addr add 192.168.56.120/24 dev enp0s8
sudo ip link set enp0s8 upReplace enp0s8 with the actual interface name, and make sure the address isn't already in use. This address assignment is temporary.
From Kali, verify connectivity:
ping -c 4 192.168.56.120ping -c 4 192.168.56.120You should receive ICMP responses before proceeding.
4. Install Docker on Ubuntu
Instead of installing an outdated PHP version directly on Ubuntu, we'll run it inside a disposable Docker container.
Temporarily enable NAT to download packages, then execute:
sudo apt update
sudo apt install -y docker.io docker-compose-v2 curl
sudo systemctl enable --now docker
sudo docker --versionsudo apt update
sudo apt install -y docker.io docker-compose-v2 curl
sudo systemctl enable --now docker
sudo docker --versionIf docker-compose-v2 isn't available, try docker-compose-plugin from Docker's official repository.
5. Create the Vulnerable PHP Server
We'll use PHP 8.5.11, which predates the 8.5.12 security fix. These are upstream PHP versions, not Ubuntu package revisions.
On Ubuntu:
mkdir -p ~/php2shell-lab/www
cd ~/php2shell-labmkdir -p ~/php2shell-lab/www
cd ~/php2shell-labCreate a minimal PHP application:
cat > www/index.php <<'EOF'
<?php
header('Content-Type: text/plain');
echo "PHP2Shell Security Lab\n";
echo "PHP Version: " . PHP_VERSION . "\n";
echo "Server: Apache\n";
echo "Status: Running\n";
EOFcat > www/index.php <<'EOF'
<?php
header('Content-Type: text/plain');
echo "PHP2Shell Security Lab\n";
echo "PHP Version: " . PHP_VERSION . "\n";
echo "Server: Apache\n";
echo "Status: Running\n";
EOFCreate the Docker Compose configuration:
nano compose.yamlnano compose.yamlPaste:
services:
vulnerable-php:
image: php:8.5.11-apache
container_name: php2shell-vulnerable
ports:
- "192.168.56.120:8080:80"
volumes:
- ./www:/var/www/html:ro
read_only: true
tmpfs:
- /tmp
- /var/run/apache2
- /var/lock/apache2
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
pids_limit: 128
mem_limit: 512m
restart: "no"services:
vulnerable-php:
image: php:8.5.11-apache
container_name: php2shell-vulnerable
ports:
- "192.168.56.120:8080:80"
volumes:
- ./www:/var/www/html:ro
read_only: true
tmpfs:
- /tmp
- /var/run/apache2
- /var/lock/apache2
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
pids_limit: 128
mem_limit: 512m
restart: "no"This requests the historical php:8.5.11-apache image tag. Historical image availability hasn't been verified, so the pull might fail. Don't substitute an unverified third-party image.
While NAT is temporarily enabled:
sudo docker compose pullsudo docker compose pullThen disable the NAT adapter again. Start the container:
sudo docker compose up -dsudo docker compose up -dVerify the actual PHP version and SAPI:
sudo docker exec php2shell-vulnerable php -v
sudo docker exec php2shell-vulnerable \
php -r 'echo php_sapi_name(), PHP_EOL;'sudo docker exec php2shell-vulnerable php -v
sudo docker exec php2shell-vulnerable \
php -r 'echo php_sapi_name(), PHP_EOL;'The version should be 8.5.11, with Apache as the web-serving runtime. Note that the CLI command itself reports the CLI SAPI.
If the image tag is unavailable, don't continue with a random image. Use a verified source-based build of PHP 8.5.11 instead.
6. Test Connectivity from Kali
Install testing tools:
sudo apt update
sudo apt install -y curl nmapsudo apt update
sudo apt install -y curl nmapCheck the Ubuntu target:
nmap -sV -p 8080 192.168.56.120nmap -sV -p 8080 192.168.56.120Then send a simple HTTP request:
curl -i http://192.168.56.120:8080/curl -i http://192.168.56.120:8080/Expected application response:
PHP2Shell Security Lab
PHP Version: 8.5.11
Server: Apache
Status: RunningPHP2Shell Security Lab
PHP Version: 8.5.11
Server: Apache
Status: RunningCheckpoint
At this point, you've established communication between Kali and the historical PHP runtime in an isolated environment. This confirms the installed version, not successful exploitation of either CVE.
7. Reproduce and Investigate the Vulnerability
The two CVEs have different technical causes:
CVE Research Objectives :
- CVE-2026–109618 Out-of-Memory Handling and Memory Corruption
Research objective: Investigate unsafe memory cleanup during request processing.
- CVE-2026–109619 Stale Global State and Use-After-Free
Research objective: Investigate request-state corruption caused by stale global state during request processing.
A normal request to index.php does not demonstrate either vulnerability. For a genuine reproduction, use an independently verified, CVE-specific test against the disposable target and compare its behavior against a patched version.
A useful technical reference is the TryHackMe PHP2Shell technical analysis, which explains the two mechanisms and their differing prerequisites.
Monitor the Target During Testing
On Ubuntu, open a second terminal:
sudo docker logs -f php2shell-vulnerablesudo docker logs -f php2shell-vulnerableOn Kali, capture traffic:
sudo tcpdump -i eth1 \
host 192.168.56.120 and port 8080 \
-w php2shell-lab.pcapsudo tcpdump -i eth1 \
host 192.168.56.120 and port 8080 \
-w php2shell-lab.pcapReplace eth1 with your Kali Host-Only interface.
For each controlled test, record the HTTP request, response, Apache errors, process termination, and whether the result is repeatable. A crash alone is not proof of remote code execution.
8. Compare with the Patched PHP Version
Once you've recorded the vulnerable baseline:
sudo docker compose downsudo docker compose downIn compose.yaml, change:
image: php:8.5.11-apacheimage: php:8.5.11-apacheto:
image: php:8.5.12-apacheimage: php:8.5.12-apacheTemporarily enable NAT, pull the patched image, disable NAT, and restart:
sudo docker compose pull
sudo docker compose up -dsudo docker compose pull
sudo docker compose up -dVerify the active runtime again:
sudo docker exec php2shell-vulnerable php -vsudo docker exec php2shell-vulnerable php -vRepeat the same CVE-specific tests against the patched build. A strong result shows the issue on the affected version, its absence on the patched version, and supporting logs or diagnostic evidence.
Remember: Ubuntu security packages can contain backported fixes, so an upstream version number alone is not sufficient to judge every Ubuntu installation.
9. Lab Completion Checklist
- Kali and Ubuntu connected using Host-Only networking
- Snapshots created for both VMs
- Docker installed on Ubuntu
- Historical PHP version verified
- HTTP request from Kali succeeds
- Packet capture and logs collected
- CVE-specific behavior tested
- Patched PHP compared
- Vulnerable container shut down
When you finish, shut down the target with:
cd ~/php2shell-lab
sudo docker compose downcd ~/php2shell-lab
sudo docker compose downRecommended Approach
Start with the two-VM isolated setup, confirm the vulnerable PHP build, then run a controlled reproduction of each CVE separately.
This gives you a useful vulnerability research exercise with reproducible observations, packet captures, and patch validation, rather than simply running an exploit without understanding it.