本文目录导读:

- 操作系统层拦截:Seccomp(Linux)或 AppArmor
- Python 语言层拦截:monkey patch 与 audit hook
- JavaScript / Node.js 拦截
- Shell 脚本(Bash/Zsh)拦截
- 沙箱/Runtime 工具
- 最佳实践推荐
实现脚本对高危权限操作的拦截,通常涉及操作系统层、语言运行时层或沙箱(Sandbox)层的钩子(Hook)与策略控制,由于不同脚本语言(Python、Shell、Node.js、PowerShell)和高危操作(文件删除、网络请求、进程执行、系统调用)的拦截方式不同,以下是几种主流方法。
操作系统层拦截:Seccomp(Linux)或 AppArmor
适用场景:限制整个脚本进程(如 Python、Bash 实例)的系统调用能力。
- Seccomp(Secure Computing Mode):允许你定义白名单/黑名单系统调用。
- 如何操作:通过
seccomp_init、seccomp_rule_add等库(如libseccomp)或直接使用prctl系统调用。 - 拦截示例:禁止脚本调用
execve(执行新程序)、unlink(删除文件)、bind(绑定端口)。 - 优点:底层、高效、无法被脚本内部绕过(除非内核有漏洞)。
- 缺点:需要编写 C 语言或使用特定工具(如
mina、nsjail)。
- 如何操作:通过
示例(Python 通过 ctypes 使用 Seccomp):
import ctypes
import os
# 加载 libseccomp
lib = ctypes.CDLL("libseccomp.so.2")
ctx = lib.seccomp_init(0) # 默认允许所有
# 禁止 execve (执行其他程序)
lib.seccomp_rule_add(ctx, 0, 59) if os.uname().machine == 'x86_64' else lib.seccomp_rule_add(ctx, 0, 11)
# 禁止 unlink (删除文件)
lib.seccomp_rule_add(ctx, 0, 87) if os.uname().machine == 'x86_64' else lib.seccomp_rule_add(ctx, 0, 10)
lib.seccomp_load(ctx)
# 后续脚本代码如果尝试执行系统操作会被 SIGKILL
# os.system("rm -rf /") # 会直接报错
Python 语言层拦截:monkey patch 与 audit hook
适合 Python 脚本自身需要防御恶意库或用户代码的场景。
A. 方法一:替换危险内置函数(Monkey Patch)
重写 os、subprocess、shutil 等模块中的高危函数。
import os
import builtins
# 保存原始函数
_original_import = builtins.__import__
# 示例:拦截子进程执行
def safe_subprocess_popen(*args, **kwargs):
raise PermissionError(f"高危操作被拦截: subprocess.Popen({args})")
import subprocess
subprocess.Popen = safe_subprocess_popen
# 拦截文件删除
def safe_os_remove(path):
raise PermissionError(f"删除操作被拦截: {path}")
os.remove = safe_os_remove
# 调用测试
# os.remove("/etc/passwd") # 触发异常
# subprocess.Popen("rm -rf /") # 触发异常
- 问题:如果恶意代码通过
ctypes直接调用 C 函数或使用os.system(内部走的 libc 的 system()),这种拦截无效。
B. 方法二:Python 审计钩子(Python 3.8+)
Python 内置的 sys.addaudithook 可以在许多高危操作(如 import、exec、open、subprocess.Popen、ctypes.CDLL)执行前进行拦截。
import sys
def audit_hook(event, args):
# 打印所有审计事件(调试用)
# print(f"审计事件: {event} -> {args}")
# 黑名单事件
forbidden_events = [
"os.system", # os.system()
"subprocess.Popen", # 子进程
"ctypes.dlopen", # 加载动态库
"import", # 导入模块(可限制模块名)
"exec", # exec()
"compile", # compile()
"builtins.input", # 读取输入(可能被利用注入)
]
if event in forbidden_events:
# 可以根据 args 进一步判断是否真正高危
print(f"高危操作被拦截: {event} ({args})")
raise RuntimeError(f"脚本安全策略禁止操作: {event}")
# 注册钩子
sys.addaudithook(audit_hook)
# 测试
# import os # 会触发拦截(因为 import 事件)
# os.system("ls") # 会触发拦截
- 优点:覆盖
exec、import、open等高级操作。 - 缺点:不拦截
open的r模式(仅读安全),但可以拦截w模式,不直接拦截特定文件路径,需要你在钩子内自行if "/etc/shadow" in args。
JavaScript / Node.js 拦截
适合 Web 渗透测试脚本或沙箱环境。
A. 沙箱化执行(vm2 或 Node.js 内置 vm)
const vm = require('vm');
// 危险的脚本内容
const code = `
const fs = require('fs');
fs.writeFileSync('/etc/hacked', 'evil');
`;
try {
// 创建一个隔离上下文,不包含 require 和 process
const sandbox = {};
vm.createContext(sandbox);
// 执行脚本,但脚本只能访问 sandbox 中的对象
vm.runInContext(code, sandbox);
} catch (e) {
console.log("拦截到高危操作:", e.message);
}
- 注意:原生
vm模块不安全,攻击者可通过constructor逃逸,建议使用vm2库(需定期更新)或isolated-vm。
B. Proxy 拦截(访问控制)
利用 JavaScript Proxy 包装全局对象,拦截对 process、require 的访问。
const sandbox = new Proxy(globalThis, {
get(target, prop, receiver) {
if (prop === 'require' || prop === 'process') {
throw new Error(`访问被禁止: ${prop}`);
}
return Reflect.get(target, prop, receiver);
}
});
// 在沙箱中执行
const code = "require('child_process').execSync('rm -rf /')";
try {
new Function('sandbox', `with(sandbox) { ${code} }`)(sandbox);
} catch (e) {
console.log("拦截:", e.message);
}
Shell 脚本(Bash/Zsh)拦截
Shell 本身没有原生的 audithook,通常可通过以下方式:
- 包装命令:将
rm、chmod、wget等替换为自定义函数。 - 使用
trap或DEBUG钩子:Bash 每次执行命令前都会触发 DEBUG 信号。
# 拦截所有 rm 命令
function rm() {
echo "[安全警告] 拦截 rm 操作: $@"
if [[ "$1" == "-rf" || "$1" == "--recursive" ]]; then
echo "禁止递归删除!"
return 1
fi
# 否则,执行真正的 rm(但有备份限制)
/bin/rm "$@" # 注意:需要 /usr/bin/rm 避免递归调用
}
# 也可以通过 DEBUG 钩子 + 环境变量禁用危险命令
shopt -s extdebug
trap 'case $BASH_COMMAND in rm*|sudo*|wget*) echo "高危命令被拦截: $BASH_COMMAND"; false;; esac' DEBUG
- 致命缺陷:脚本可以通过
/bin/rm直接调用原生命令,绕过函数拦截,所以必须配合readonly或unset禁用直接路径访问。
沙箱/Runtime 工具
如果以上方法都不满足需求,可以使用成熟的开源沙箱:
| 平台/语言 | 推荐工具 | 特点 |
|---|---|---|
| Python | PyPy-Sandbox、pysandbox |
但后者已停止维护,建议用 audit hook + seccomp |
| JavaScript | vm2、isolated-vm |
vm2 有已知逃逸漏洞,需关注更新 |
| 通用 | Docker |
隔离度高,但资源开销大 |
| 通用 | Firejail(Linux) |
简单配置,对进程进行沙箱化 |
| 通用 | nsjail(Google) |
轻量级,使用 cgroups + namespace 隔离 |
示例:使用 Firejail 运行危险脚本
firejail --seccomp --private --net=none python3 malicious_script.py # 参数说明: # --seccomp: 禁止系统调用 # --private: 临时文件系统 # --net=none: 禁止网络
最佳实践推荐
-
如果只是限制文件/网络/进程:
- 首选:操作系统 Seccomp(Linux) 或 AppArmor,因为它防绕过。
- 备选:Python Audit Hook(适用于 Python 3.8+ 的高级脚本)。
-
如果是动态执行不可信代码(如在线评测系统、沙箱环境):
- 使用 Docker + Seccomp + 资源限制。
- 或使用 Firejail + 隔离的用户权限。
-
如果是限制 Node.js 脚本:
- 使用
isolated-vm(更安全)或vm2(需频繁更新)。
- 使用
-
必须记住的一点:
- 纯语言层的 Monkey Patch 可以被绕过(通过
ctypes、Buffer Overflow、__builtins__等)。 - 真正的拦截通常需要 操作系统层面(系统调用白名单) + 受限用户权限(非 root 运行) + 语言层钩子 三层组合。
- 纯语言层的 Monkey Patch 可以被绕过(通过
最后提醒:编写脚本拦截器时,请务必最小权限原则:只允许脚本做必须的操作,其余一律拒绝,不要试图建立黑名单,永远使用白名单(明确允许哪些操作)。