应用控件如何使用 PowerShell

本文介绍了应用控制 for Business 如何保护 PowerShell 及其施加的限制。 PowerShell 的安全行为因使用的 Windows 和 PowerShell 版本而异。

PowerShell 如何检测系统锁定策略

PowerShell 可检测适用于企业的 AppLocker应用控制系统范围策略。 AppLocker 已弃用。 应用控件是 Windows 的首选应用程序控制系统。

旧版应用控制策略强制检测

PowerShell 使用旧版应用控制 WldpGetLockdownPolicy API 发现两项内容:

  • 系统范围的策略强制实施:NoneAuditEnforce
  • 单个文件策略: NoneAudit(策略允许)、Enforce(策略不允许)

所有版本的 PowerShell (v5.1 - v7.x)都支持此应用控制策略检测。

最新的应用控制策略强制检测

应用控件在最新版本的 Windows 中引入了新的 API。 PowerShell 从版本 7.3 开始使用新的 WldpCanExecuteFile API 来决定如何处理文件。 Windows PowerShell 5.1 不支持此新 API。 新 API 优先于单个文件的旧 API。 但是,PowerShell 会继续使用旧 API 获取系统范围的策略配置。 如果新 API 不可用,PowerShell 会回退到旧 API 行为。

新 API 提供每个文件的以下信息:

  • WLDP_CAN_EXECUTE_ALLOWED
  • WLDP_CAN_EXECUTE_BLOCKED
  • WLDP_CAN_EXECUTE_REQUIRE_SANDBOX

锁定策略下的 PowerShell 行为

PowerShell 在交互式和非交互式模式下都可以运行。

  • 在交互式模式下,PowerShell 是命令行应用程序,将用户命令行输入作为命令或脚本来运行。 返回结果向用户显示。
  • 在非交互式模式下,PowerShell 无需用户输入就会加载模块并运行脚本文件。 结果数据流将被忽略或重定向到某个文件。

在策略强制实施下运行交互模式

PowerShell 在 ConstrainedLanguage 模式下运行命令。 此模式可防止交互用户运行某些命令或执行任意代码。 有关此模式下限制的详细信息,请参阅本文的锁定策略下的 PowerShell 限制一节。

在策略强制实施下运行非交互模式

当 PowerShell 运行脚本或加载模块时,它使用应用控制 API 获取文件的策略强制实施。

PowerShell 版本 7.3 或更高版本会使用 WldpCanExecuteFile API(如果可用)。 此 API 会返回以下结果:

  • WLDP_CAN_EXECUTE_ALLOWED:该文件经策略批准,并在 FullLanguage 模式下使用,但存在一些限制。
  • WLDP_CAN_EXECUTE_BLOCKED:该文件未经策略批准。 运行或加载此文件时,PowerShell 会引发错误。
  • WLDP_CAN_EXECUTE_REQUIRE_SANDBOX:该文件未经策略批准,但仍可以在 ConstrainedLanguage 模式下运行或加载。

在 Windows PowerShell 5.1 中或是 WldpCanExecuteFile API 不可用时,每个文件的 PowerShell 行为如下:

  • None:该文件会在 FullLanguage 模式下运行或加载,但存在一些限制。
  • Audit:该文件会在 FullLanguage 模式下运行或加载,不存在限制。 在 PowerShell 7.4 或更高版本中,策略会将限制信息记录到 Windows 事件日志。
  • Enforce:该文件会在 ConstrainedLanguage 模式下运行或加载。

锁定策略下的 PowerShell 限制

当 PowerShell 检测到系统处于应用控制锁定策略下时,即使脚本受信任且在 FullLanguage 模式下运行,也会应用限制。 这些限制可预防 PowerShell 发生已知问题,这些问题可能会导致锁定系统上的任意代码执行。 此锁定策略会强制实施以下限制:

  • 限制模块点源引用使用通配符导出函数

    任何使用脚本点源引用和导出使用通配符名称的函数的模块都会导致错误。 阻止通配符导出可防止恶意用户将点源引用的不受信任脚本植入到受信任的模块中。 如果这样做,恶意脚本会获得权限,可以访问受信任模块的专用函数。

    安全建议:切勿在模块中使用脚本点源引用,并且始终导出具有显式名称(无通配符)的模块函数。

  • 限制嵌套模块使用通配符导出函数

    如果父模块使用通配符函数名称导出函数,PowerShell 会从函数导出列表中删除嵌套模块中的任何函数名称。 阻止嵌套模块使用通配符导出可防止通过通配符名称匹配意外导出危险的嵌套函数。

    安全建议:始终导出具有显式名称(无通配符)的模块函数。

  • 交互式 shell 参数类型转换

    系统锁定后,交互式 PowerShell 会话在 ConstrainedLanguage 模式下运行,以阻止任意代码执行。 会话中加载的受信任模块会在 FullLanguage 模式下运行。 如果受信任的模块 cmdlet 使用复杂类型的参数,但跨信任边界转换不受允许,则参数绑定期间的类型转换可能会失败。 如果 PowerShell 尝试运行类型构造函数来转换值时,就会出现此问题。 类型构造函数不允许在 ConstrainedLanguage 模式下运行。

    在此示例中,通常允许参数绑定类型转换,但在 ConstrainedLanguage 模式下运行时转换会失败。 不允许 ConnectionPort类型构造函数:

    PS> Create-ConnectionOnPort -Connection 22
    Create-ConnectionOnPort: Cannot bind parameter 'Connection'. Cannot convert the "22"
    value of type "System.Int32" to type "ConnectionPort".
    
  • 不允许 Enter-PSHostProcess cmdlet

    Enter-PSHostProcess cmdlet 已禁用,如果使用,会引发错误。 此命令用于连接和调试会话。 它允许连接到本地计算机上的其他 PowerShell 会话。 禁用该 cmdlet 可以防止信息泄露和任意代码执行。

受约束语言模式下的 PowerShell 限制

应用控制策略未批准的脚本或函数不受信任。 运行不受信任的命令时,PowerShell 会阻止该命令运行(新行为),或者在 ConstrainedLanguage 模式下运行该命令。 ConstrainedLanguage 模式会存在以下限制:

  • 不允许 Add-Type cmdlet

    阻止 Add-Type 可防止任意 .NET 代码执行。

  • Import-LocalizedData cmdlet 受限

    阻止 Import-LocalizedDataSupportedCommand 参数可防止任意代码执行。

  • Invoke-Expression cmdlet 受限

    传递给 Invoke-Expression cmdlet 的所有脚本块都在 ConstrainedLanguage 模式下运行,以防止任意代码执行。

  • New-Object cmdlet 受限

    cmdlet New-Object 只能使用允许的 .NET 和 COM 类型,以防止访问不受信任的类型。

  • ForEach-Object cmdlet 限制

    未在批准列表中的任何 .NET 类型不允许对传递给 ForeEach-Object 的变量进行类型方法调用。 一般来说,ConstrainedLanguage 模式会禁止除已批准 .NET 类型之外的所有对象方法调用,以防止对不受信任 .NET 类型的访问。

  • Export-ModuleMember cmdlet 限制

    使用 Export-ModuleMember cmdlet 导出子模块不受信任但父模块受信任的嵌套模块脚本文件中的函数,会导致错误。 阻止此情形可防止恶意不受信任的模块从受信任的模块导出危险函数。

  • New-Module cmdlet 限制

    在受信任的脚本中运行 New-Module 时,ScriptBlock 参数提供所有脚本块都会标记为在 ConstrainedLanguage 模式下运行,以防止将任意代码注入受信任的执行上下文。

  • 不允许使用 Configuration 关键字

    ConstrainedLanguage 模式下不允许使用 Configuration 语言关键字,以防止代码注入攻击。

  • 不允许使用 class 关键字

    ConstrainedLanguage 模式下不允许使用 class 语言关键字,以防止注入任意代码。

  • 脚本块处理作用域限制

    如果脚本块具有不同的信任级别,则不允许子脚本块在父脚本块作用域中运行。 例如,你可以在将一个脚本点源引用到另一个脚本时,创建子关系。 阻止此情形可防止不受信任的脚本访问受信任脚本作用域内的危险函数。

  • 防止命令发现不受信任的脚本函数

    PowerShell 命令发现不会将不受信任的源(如不受信任的脚本或模块)中的函数返回到受信任的函数。 阻止发现不受信任的命令可以防止通过命令植入来注入代码。

  • 不允许将哈希表转换为对象

    ConstrainedLanguage 模式会阻止在 PowerShell 数据 (Data) 文件的 .psd1 部分将哈希表转换为对象,以防止潜在的代码注入攻击。

  • 自动类型转换受限

    ConstrainedLanguage 模式会阻止自动类型转换(一小组经批准的安全类型除外),以防止潜在的代码注入攻击。

  • 隐式模块函数导出限制

    如果模块不显式导出函数,PowerShell 会自动将所有定义的模块函数隐式导出为便利功能。 在 ConstrainedLanguage 模式下,如果模块跨信任边界加载,就不会出现隐式导出。 阻止隐式导出可防止意外泄露不适用于公开使用的危险模块函数。

  • 脚本文件无法作为模块导入

    PowerShell 允许将脚本文件 (.ps1) 作为模块导入。 所有定义的函数都变成可供公开访问。 ConstrainedLanguage 模式会阻止导入脚本文件,以防止意外泄露危险的脚本函数。

  • 设置变量 AllScope 限制

    ConstrainedLanguage 模式会禁止对变量进行 AllScope 设置。 限制变量的作用域可防止变量干扰受信任命令的会话状态。

  • 不允许调用类型方法

    ConstrainedLanguage 模式不允许对未经批准的类型进行方法调用。 阻止对未经批准类型的方法调用可防止调用可能很危险或是允许代码注入的 .NET 类型方法。

  • 不允许使用类型属性资源库

    ConstrainedLanguage 模式会限制对未经批准的类型调用属性资源库。 阻止对未经批准类型的属性资源库调用可以阻止代码注入攻击。

  • 不允许创建类型

    ConstrainedLanguage 模式会阻止对未经批准的类型创建类型,以阻止允许代码注入的不受信任构造函数。

  • 不允许使用模块作用域运算符

    ConstrainedLanguage 模式不允许使用模块作用域运算符。 例如:& (Get-Module MyModule) MyFunction。 阻止模块作用域运算符可防止对模块专用函数和变量的访问。

其他阅读材料