mirror of
https://github.com/whyour/qinglong.git
synced 2025-12-13 07:25:05 +08:00
Fix subprocess bypass by wrapping child_process and subprocess modules
Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>
This commit is contained in:
parent
68d06acf6c
commit
38d1f67301
|
|
@ -212,7 +212,112 @@ if (sandboxEnabled) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent requiring the original fs module to bypass sandbox
|
// Wrap child_process to prevent sandbox bypass via subprocesses
|
||||||
|
let childProcessWrapped = false;
|
||||||
|
if (sandboxEnabled) {
|
||||||
|
// We need to get child_process before wrapping Module.prototype.require
|
||||||
|
const childProcess = require('child_process');
|
||||||
|
const originalSpawn = childProcess.spawn;
|
||||||
|
const originalExec = childProcess.exec;
|
||||||
|
const originalExecSync = childProcess.execSync;
|
||||||
|
const originalExecFile = childProcess.execFile;
|
||||||
|
const originalExecFileSync = childProcess.execFileSync;
|
||||||
|
const originalFork = childProcess.fork;
|
||||||
|
|
||||||
|
// Helper to ensure NODE_OPTIONS and PYTHONPATH are set for subprocesses
|
||||||
|
function ensureSandboxEnv(options = {}) {
|
||||||
|
const env = { ...process.env, ...options.env };
|
||||||
|
|
||||||
|
// Ensure NODE_OPTIONS includes the sandbox
|
||||||
|
const sandboxPreload = path.join(__dirname, 'sandbox.js');
|
||||||
|
if (!env.NODE_OPTIONS) {
|
||||||
|
env.NODE_OPTIONS = '';
|
||||||
|
}
|
||||||
|
if (!env.NODE_OPTIONS.includes(sandboxPreload)) {
|
||||||
|
env.NODE_OPTIONS = `-r ${sandboxPreload} ${env.NODE_OPTIONS}`.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure PYTHONPATH includes the sandbox directory
|
||||||
|
if (!env.PYTHONPATH) {
|
||||||
|
env.PYTHONPATH = '';
|
||||||
|
}
|
||||||
|
if (!env.PYTHONPATH.includes(__dirname)) {
|
||||||
|
env.PYTHONPATH = `${__dirname}:${env.PYTHONPATH}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...options, env };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap spawn
|
||||||
|
childProcess.spawn = function(...args) {
|
||||||
|
if (args[2]) {
|
||||||
|
args[2] = ensureSandboxEnv(args[2]);
|
||||||
|
} else if (args.length >= 3) {
|
||||||
|
args[2] = ensureSandboxEnv({});
|
||||||
|
}
|
||||||
|
return originalSpawn.apply(childProcess, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrap exec
|
||||||
|
childProcess.exec = function(...args) {
|
||||||
|
const callback = typeof args[args.length - 1] === 'function' ? args[args.length - 1] : undefined;
|
||||||
|
const optionsIndex = callback ? args.length - 2 : args.length - 1;
|
||||||
|
|
||||||
|
if (args[optionsIndex] && typeof args[optionsIndex] === 'object') {
|
||||||
|
args[optionsIndex] = ensureSandboxEnv(args[optionsIndex]);
|
||||||
|
} else if (optionsIndex > 0) {
|
||||||
|
args.splice(optionsIndex, 0, ensureSandboxEnv({}));
|
||||||
|
}
|
||||||
|
return originalExec.apply(childProcess, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrap execSync
|
||||||
|
childProcess.execSync = function(...args) {
|
||||||
|
if (args[1]) {
|
||||||
|
args[1] = ensureSandboxEnv(args[1]);
|
||||||
|
} else {
|
||||||
|
args[1] = ensureSandboxEnv({});
|
||||||
|
}
|
||||||
|
return originalExecSync.apply(childProcess, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrap execFile
|
||||||
|
childProcess.execFile = function(...args) {
|
||||||
|
const callback = typeof args[args.length - 1] === 'function' ? args[args.length - 1] : undefined;
|
||||||
|
const optionsIndex = callback ? args.length - 2 : args.length - 1;
|
||||||
|
|
||||||
|
if (args[optionsIndex] && typeof args[optionsIndex] === 'object') {
|
||||||
|
args[optionsIndex] = ensureSandboxEnv(args[optionsIndex]);
|
||||||
|
} else if (optionsIndex > 1) {
|
||||||
|
args.splice(optionsIndex, 0, ensureSandboxEnv({}));
|
||||||
|
}
|
||||||
|
return originalExecFile.apply(childProcess, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrap execFileSync
|
||||||
|
childProcess.execFileSync = function(...args) {
|
||||||
|
if (args[2]) {
|
||||||
|
args[2] = ensureSandboxEnv(args[2]);
|
||||||
|
} else if (args.length >= 3) {
|
||||||
|
args[2] = ensureSandboxEnv({});
|
||||||
|
}
|
||||||
|
return originalExecFileSync.apply(childProcess, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrap fork
|
||||||
|
childProcess.fork = function(...args) {
|
||||||
|
if (args[2]) {
|
||||||
|
args[2] = ensureSandboxEnv(args[2]);
|
||||||
|
} else if (args.length >= 3) {
|
||||||
|
args[2] = ensureSandboxEnv({});
|
||||||
|
}
|
||||||
|
return originalFork.apply(childProcess, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
childProcessWrapped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent requiring the original fs or child_process modules to bypass sandbox
|
||||||
const originalRequire = Module.prototype.require;
|
const originalRequire = Module.prototype.require;
|
||||||
Module.prototype.require = function(id) {
|
Module.prototype.require = function(id) {
|
||||||
const module = originalRequire.apply(this, arguments);
|
const module = originalRequire.apply(this, arguments);
|
||||||
|
|
@ -222,6 +327,9 @@ Module.prototype.require = function(id) {
|
||||||
return fs;
|
return fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For child_process, we already wrapped it above, so just return it
|
||||||
|
// (no need to re-require as that would cause recursion)
|
||||||
|
|
||||||
return module;
|
return module;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -324,3 +324,85 @@ if sandbox_enabled:
|
||||||
Path.chmod = sandboxed_path_chmod
|
Path.chmod = sandboxed_path_chmod
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Wrap subprocess to prevent sandbox bypass via subprocesses
|
||||||
|
try:
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
# Helper to ensure PYTHONPATH is set for subprocesses
|
||||||
|
def ensure_sandbox_env(env=None):
|
||||||
|
if env is None:
|
||||||
|
env = os.environ.copy()
|
||||||
|
else:
|
||||||
|
env = env.copy()
|
||||||
|
|
||||||
|
# Ensure PYTHONPATH includes the sandbox directory
|
||||||
|
sandbox_dir = os.path.dirname(__file__)
|
||||||
|
if 'PYTHONPATH' not in env:
|
||||||
|
env['PYTHONPATH'] = ''
|
||||||
|
if sandbox_dir not in env['PYTHONPATH']:
|
||||||
|
env['PYTHONPATH'] = f"{sandbox_dir}:{env['PYTHONPATH']}"
|
||||||
|
|
||||||
|
return env
|
||||||
|
|
||||||
|
# Store original functions
|
||||||
|
original_popen = subprocess.Popen
|
||||||
|
original_run = subprocess.run
|
||||||
|
original_call = subprocess.call
|
||||||
|
original_check_call = subprocess.check_call
|
||||||
|
original_check_output = subprocess.check_output
|
||||||
|
|
||||||
|
# Wrap Popen
|
||||||
|
class SandboxedPopen(subprocess.Popen):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
if 'env' in kwargs:
|
||||||
|
kwargs['env'] = ensure_sandbox_env(kwargs['env'])
|
||||||
|
else:
|
||||||
|
kwargs['env'] = ensure_sandbox_env()
|
||||||
|
original_popen.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
subprocess.Popen = SandboxedPopen
|
||||||
|
|
||||||
|
# Wrap run
|
||||||
|
def sandboxed_run(*args, **kwargs):
|
||||||
|
if 'env' in kwargs:
|
||||||
|
kwargs['env'] = ensure_sandbox_env(kwargs['env'])
|
||||||
|
else:
|
||||||
|
kwargs['env'] = ensure_sandbox_env()
|
||||||
|
return original_run(*args, **kwargs)
|
||||||
|
|
||||||
|
subprocess.run = sandboxed_run
|
||||||
|
|
||||||
|
# Wrap call
|
||||||
|
def sandboxed_call(*args, **kwargs):
|
||||||
|
if 'env' in kwargs:
|
||||||
|
kwargs['env'] = ensure_sandbox_env(kwargs['env'])
|
||||||
|
else:
|
||||||
|
kwargs['env'] = ensure_sandbox_env()
|
||||||
|
return original_call(*args, **kwargs)
|
||||||
|
|
||||||
|
subprocess.call = sandboxed_call
|
||||||
|
|
||||||
|
# Wrap check_call
|
||||||
|
def sandboxed_check_call(*args, **kwargs):
|
||||||
|
if 'env' in kwargs:
|
||||||
|
kwargs['env'] = ensure_sandbox_env(kwargs['env'])
|
||||||
|
else:
|
||||||
|
kwargs['env'] = ensure_sandbox_env()
|
||||||
|
return original_check_call(*args, **kwargs)
|
||||||
|
|
||||||
|
subprocess.check_call = sandboxed_check_call
|
||||||
|
|
||||||
|
# Wrap check_output
|
||||||
|
def sandboxed_check_output(*args, **kwargs):
|
||||||
|
if 'env' in kwargs:
|
||||||
|
kwargs['env'] = ensure_sandbox_env(kwargs['env'])
|
||||||
|
else:
|
||||||
|
kwargs['env'] = ensure_sandbox_env()
|
||||||
|
return original_check_output(*args, **kwargs)
|
||||||
|
|
||||||
|
subprocess.check_output = sandboxed_check_output
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user