最近学习了一下tomcat进程注入技术,其原理在于Java在Java SE5后引入了Java Instrumentation,该功能指的是可以用独立于应用程序之外的代理程序来监测和协助包括但不限于获取JVM运行时状态,替换和修改类定义等。这里主要利用GitHub上rebeyond的memShell工程对该技术进行复现。
我们希望通过访问web服务器上的任意一个url,无论该url是静态资源抑或jsp文件,无论是原生servlet还是struts action,甚至无论它是否存在,只要我们传递请求给tomcat,tomcat就能响应我们的指令。要实现这一目的,必须找到一个关键的类,这个类要尽可能在http请求调用栈的上方,又不能与具体的URL有耦合,且还能接受客户端request中的数据。分析后发现,org.apache.catalina.core.ApplicationFilterChain类的internalDoFilter方法最符合要求,internalDoFilter方法的原型如下:
private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {}
该方法有ServletRequest和ServletResponse两个参数,里面封装了用户请求的request和response,同时internalDoFilter方法也是自定义filter的入口:
private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { //Call the next filter if there is one if (pos < n) { ApplicationFilterConfig filterConfig = filters[pos++]; Filter filter = null; try { filter = filterConfig.getFilter(); support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT, filter, request, reponse); ... if ( Global.IS_SECURITY_ENABLED ) { final ServletRequest req = request; final ServletResponse res = response; Principal principal = ((HttpServletRequest) req).getUserPrincipal(); Object[] args = new Object[]{req, res, this}; SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal); } else { filter.doFilter(request, response, this); } ... ...
所以要实现内存注入就需要对该方法插入我们定制的代码段(具体代码可在memShell工程中找到)
首先去官网下载jdk文件,本次复现过程在Ubuntu虚拟机中实现,因此下载Linux版本的jdk文件,然后将下载的压缩包解压:
然后设置环境变量:
vi /etc/profile
这里我将jdk文件夹放到了/usr/local
文件夹中,所以在文件末尾加入以下语句:
export JAVA_HOME=/usr/local/jdk1.8.0_261s export JRE_HOME=${JAVA_HOME}/jre export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib export PATH=.:${JAVA_HOME}/bin:$PATH
然后使配置生效:
source /etc/profile
输入命令java -version
检查配置是否生效:
首先去官网下载tomcat,这里选择了tomcat9,将压缩包放置于/usr/local
文件夹下解压:
tar zxvf apache-tomcat-9.0.38
sudo mv apache-tomcat-9.0.38 tomcat9
然后配置tomcat,并新建用户以非root用户来启动tomcat
# 进入Tomcat安装目录下的bin目录
cd usr/local/tomcat9/bin
# 解压commons-daemon-native.tar.gz
tar zxvf commons-daemon-native.tar.gz
cd commons-daemon-1.2.2-native-src/unix/
# 使java配置生效
source /etc/profile
# 编译
./configure
make
# make后,会在当前文件夹下生成一个名为 jsvc 的文件,将其复制到$CATALINA_HOME/bin目录
cp ../..
# 添加一个用来运行Tomcat的用户
sudo adduser tomcat(根据提示设置密码)
# 回到$CATALINA_HOME/bin目录下
cd ../../
# 设置$CATALINE_HOME文件夹及其所有子文件Owner为tomcat
sudo chown -R tomcat:tomcat /usr/local/tomcat9
# 认证tomcat用户
su tomcat
# 启动tomcat
./daemon.sh start
访问127.0.0.1:8080,页面如下:
将memShell文件上传到tomcat服务器文件夹任意位置,然后执行命令
java -jar inject.jar pppd
执行成功后,在url后加上anyurl?pass_the_word=pppd
,即得到帮助信息:
执行whoami获取用户名:
其他功能参考帮助信息。
本文主要以Java+tomcat为例进行了tomcat进程注入技术复现,该工程仅对内存webshell进行了技术实现,此外还可以通过修改源代码来实现其它功能,同时对于其他容器如JBOSS和WebLogic,只是“定位关键类”那一步稍有不同,其他环节都是一般化的工作,理论上也可以实现类似功能。