脚本如何拦截脚本高危权限操作

wen 实用脚本 29

本文目录导读:

脚本如何拦截脚本高危权限操作

  1. 操作系统层拦截:Seccomp(Linux)或 AppArmor
  2. Python 语言层拦截:monkey patch 与 audit hook
  3. JavaScript / Node.js 拦截
  4. Shell 脚本(Bash/Zsh)拦截
  5. 沙箱/Runtime 工具
  6. 最佳实践推荐

实现脚本对高危权限操作的拦截,通常涉及操作系统层语言运行时层沙箱(Sandbox)层的钩子(Hook)与策略控制,由于不同脚本语言(Python、Shell、Node.js、PowerShell)和高危操作(文件删除、网络请求、进程执行、系统调用)的拦截方式不同,以下是几种主流方法。


操作系统层拦截:Seccomp(Linux)或 AppArmor

适用场景:限制整个脚本进程(如 Python、Bash 实例)的系统调用能力。

  • Seccomp(Secure Computing Mode):允许你定义白名单/黑名单系统调用。
    • 如何操作:通过 seccomp_initseccomp_rule_add 等库(如 libseccomp)或直接使用 prctl 系统调用。
    • 拦截示例:禁止脚本调用 execve(执行新程序)、unlink(删除文件)、bind(绑定端口)。
    • 优点:底层、高效、无法被脚本内部绕过(除非内核有漏洞)。
    • 缺点:需要编写 C 语言或使用特定工具(如 minansjail)。

示例(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)

重写 ossubprocessshutil 等模块中的高危函数。

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 可以在许多高危操作(如 importexecopensubprocess.Popenctypes.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")  # 会触发拦截
  • 优点:覆盖 execimportopen 等高级操作。
  • 缺点:不拦截 openr 模式(仅读安全),但可以拦截 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 包装全局对象,拦截对 processrequire 的访问。

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,通常可通过以下方式:

  • 包装命令:将 rmchmodwget 等替换为自定义函数。
  • 使用 trapDEBUG 钩子: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 直接调用原生命令,绕过函数拦截,所以必须配合 readonlyunset 禁用直接路径访问。

沙箱/Runtime 工具

如果以上方法都不满足需求,可以使用成熟的开源沙箱:

平台/语言 推荐工具 特点
Python PyPy-Sandboxpysandbox 但后者已停止维护,建议用 audit hook + seccomp
JavaScript vm2isolated-vm vm2 有已知逃逸漏洞,需关注更新
通用 Docker 隔离度高,但资源开销大
通用 Firejail(Linux) 简单配置,对进程进行沙箱化
通用 nsjail(Google) 轻量级,使用 cgroups + namespace 隔离

示例:使用 Firejail 运行危险脚本

firejail --seccomp --private --net=none python3 malicious_script.py
# 参数说明:
# --seccomp: 禁止系统调用
# --private: 临时文件系统
# --net=none: 禁止网络

最佳实践推荐

  1. 如果只是限制文件/网络/进程

    • 首选操作系统 Seccomp(Linux) 或 AppArmor,因为它防绕过。
    • 备选Python Audit Hook(适用于 Python 3.8+ 的高级脚本)。
  2. 如果是动态执行不可信代码(如在线评测系统、沙箱环境)

    • 使用 Docker + Seccomp + 资源限制
    • 或使用 Firejail + 隔离的用户权限
  3. 如果是限制 Node.js 脚本

    • 使用 isolated-vm(更安全)或 vm2(需频繁更新)。
  4. 必须记住的一点

    • 纯语言层的 Monkey Patch 可以被绕过(通过 ctypes、Buffer Overflow、__builtins__ 等)。
    • 真正的拦截通常需要 操作系统层面(系统调用白名单) + 受限用户权限(非 root 运行) + 语言层钩子 三层组合。

最后提醒:编写脚本拦截器时,请务必最小权限原则:只允许脚本做必须的操作,其余一律拒绝,不要试图建立黑名单,永远使用白名单(明确允许哪些操作)。

抱歉,评论功能暂时关闭!