# PraisonAI SandlockSandbox falls back to unrestricted subprocess execution when Landlock is unavailable

PraisonAI's SandlockSandbox silently falls back to plain SubprocessSandbox when Landlock is unavailable, letting sandboxed code read outside allowed paths and open network despite network=False.

- Payout: Research
- Severity: high
- Program: PraisonAI
- Found by: rexpository
- Disclosed: 2026-06-18
- CVE: CVE-2026-57144
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-6jcq-6546-qrrw
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-6jcq-6546-qrrw
- Tags: area:ai domain:ai domain:oss impact:data-leak impact:internal-access kind:research platform:ghsa sev:high tech:python technique:source-review vuln:access-control vuln:misconfiguration vuln:sandbox-escape

**Target:** Python sandbox backend (SandlockSandbox / SubprocessSandbox) in praisonai package
**Root cause:** SandlockSandbox.execute()/run_command() check self.is_available and, when Landlock is unavailable, silently delegate to SubprocessSandbox, which applies only rlimits and no filesystem/network boundary, while still reporting sandbox_type 'sandlock'.
**Steps:**
1. Install praisonai >=4.5.110,<4.6.61 on a host where the sandlock module imports but reports Landlock unavailable (or inject a fake `sandlock` module with `is_available=lambda: False`).
2. Create config via `SandboxConfig.native(writable_paths=[tenant_a], network=False)`.
3. Place a canary file outside tenant_a and start a localhost TCP listener.
4. Call `SandlockSandbox(config=config).execute(code)` where code reads the outside canary and connects to the listener.
5. Observe child output returns the canary contents and `local-ok` network reply, proving both boundaries crossed.
**How found:** Code review of the sandbox backend: noticed the `if not self.is_available` branch logs a warning and returns SubprocessSandbox instead of failing closed, then verified SubprocessSandbox installs no filesystem/network isolation and built a PoV forcing the unavailable branch.
**Exploitation:** Any caller relying on SandlockSandbox as the isolation boundary for untrusted code on a host lacking Landlock ABI support gets host-user-level execution: read arbitrary files reachable by the process (credentials, .env, SSH keys) and connect to loopback/internal services despite network=False.
**Escalation:** Sandboxed code execution becomes unsandboxed host-user code execution, enabling credential theft and internal network access from the sandbox.
**Insight:** When a security control has a 'graceful fallback' path, check whether the fallback preserves the same policy object's guarantees — here the config still says network=False and allowed_paths, but the fallback backend never reads them, so the metadata lies about the enforced boundary.

## Proof of concept (copied exactly from the original report)

**Reproduction command for PoV**

```
python3 poc/pov_poc.py \
  --repo /path/to/PraisonAI

The PoV:

1. injects a fake `sandlock` module that imports successfully but reports no usable Landlock support;
2. configures `SandboxConfig.native(writable_paths=[tenant_a], network=False)`;
3. creates `tenant-b-secret.txt` outside the configured path;
4. starts a localhost TCP listener;
5. executes code through `SandlockSandbox.execute()`.
```

**Observed vulnerable PoV output JSON**

```
{
  "child_output": {
    "network_reply": "local-ok",
    "outside_read": "TENANT_B_CANARY"
  },
  "configured_network": false,
  "outside_path_under_allowed": false,
  "sandlock_available": false,
  "sandbox_type": "sandlock",
  "status": "COMPLETED",
  "vulnerable": true
}
```

