pythonmsn-messenger

Implementing MSNP15 in Python - 911 error but all tickets are correct


I am working on implementing the MSNP15 protocol in Python. I have verified that my hashing and encryption functions are working (or at least, they match up with examples I found online), however I am always receiving a 911 error back from the server.

Thanks for any help.

Python code is as follows;

msntest.py:

import socket
import httplib
from encryption import GetSSOTicket

username = "login@hotmail.com"
password = "password"

def ReadAll(sock):
    data = ""
    while 1:
        try:
            r = sock.recv(4096)
            data += r
        except socket.timeout:
            break
    return data


def SOAPRequest(xml):
    url = "login.live.com"
    con = httplib.HTTPSConnection(url)

    headers = {"Host": "login.live.com",
                "Accept": "text/plain"}

    con.request("POST", "/RST.srf", xml, headers)

    response = con.getresponse().read()

    con.close()

    return response


def GetTicket(policy, nonce):
    xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
    xml += "<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\"><Header>";
    xml += "<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">";
    xml += "<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>";
    xml += "<ps:BinaryVersion>4</ps:BinaryVersion>";
    xml += "<ps:UIVersion>1</ps:UIVersion>";
    xml += "<ps:Cookies></ps:Cookies>";
    xml += "<ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>";
    xml += "</ps:AuthInfo>";
    xml += "<wsse:Security><wsse:UsernameToken Id=\"user\">";
    xml += "<wsse:Username>" + username + "</wsse:Username>";
    xml += "<wsse:Password>" + password + "</wsse:Password>";
    xml += "</wsse:UsernameToken></wsse:Security></Header><Body>";
    xml += "<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">";
    xml += "<wst:RequestSecurityToken Id=\"RST0\">";
    xml += "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>";
    xml += "<wsp:AppliesTo><wsa:EndpointReference><wsa:Address>http://Passport.NET/tb";
    xml += "</wsa:Address></wsa:EndpointReference></wsp:AppliesTo></wst:RequestSecurityToken>";
    xml += "<wst:RequestSecurityToken Id=\"RST1\">";
    xml += "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType><wsp:AppliesTo><wsa:EndpointReference>";
    xml += "<wsa:Address>messengerclear.live.com</wsa:Address></wsa:EndpointReference></wsp:AppliesTo>";
    xml += "<wsse:PolicyReference URI=\"" + policy + "\"></wsse:PolicyReference></wst:RequestSecurityToken>";
    xml += "<wst:RequestSecurityToken Id=\"RST2\">";
    xml += "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>";
    xml += "<wsp:AppliesTo>";
    xml += "<wsa:EndpointReference>";
    xml += "<wsa:Address>contacts.msn.com</wsa:Address>";
    xml += "</wsa:EndpointReference>";
    xml += "</wsp:AppliesTo>";
    xml += "<wsse:PolicyReference URI=\"MBI\">";
    xml += "</wsse:PolicyReference>";
    xml += "</wst:RequestSecurityToken>";
    xml += "</ps:RequestMultipleSecurityTokens></Body></Envelope>";

    xmlDoc = SOAPRequest(xml)

    start = xmlDoc.find("<wst:BinarySecret>")+len("<wst:BinarySecret>")
    end = xmlDoc.find("</wst:BinarySecret>")
    secret = xmlDoc[start:end]

    print "secret:"
    print secret

    start = xmlDoc.find('<wsse:BinarySecurityToken Id="Compact1">')+len('<wsse:BinarySecurityToken Id="Compact1">')
    end = xmlDoc.find("</wsse:BinarySecurityToken>")
    ticket = xmlDoc[start:end].replace("&amp;", "&")

    SSOTicket = GetSSOTicket(secret, nonce)

    return ticket + " " + SSOTicket



def LogIntoNS():
    address = ("64.4.61.219", 1863)

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(address)
    s.settimeout(3)

    s.send("VER 1 MSNP15 CVR0\r\n")

    ReadAll(s)

    s.send("CVR 2 0x0407 win 6.0 i386 MSNMSGR 8.5 MSMSGS " + username + "\r\n")

    ReadAll(s)

    s.send("USR 3 SSO I " + username + "\r\n")

    recvbuf = ReadAll(s)

    recvbuf = recvbuf[recvbuf.find("USR"):]

    policy = recvbuf.split()[4]

    print "policy:"
    print policy

    nonce = recvbuf.split()[5]
    print "nonce:"
    print nonce

    ticket = GetTicket(policy, nonce)

    print "ticket:"
    print ticket

    s.send("USR 4 SSO S " + ticket + "\r\n")

    print "Sent"

    print s.recv(4096)


LogIntoNS()

encryption.py:

import hashlib, hmac
import base64
import pyDes


def Combine(a, b):
    aBytes = bytes(a)
    bBytes = bytes(b)
    combinedBytes = aBytes + bBytes
    return combinedBytes


def DeriveKey(key, magic):
    hash1 = hmac.new(key, magic, hashlib.sha1).digest()

    combined = str(Combine(hash1, magic))
    hash2 = hmac.new(key, combined, hashlib.sha1).digest()

    hash3 = hmac.new(key, hash1, hashlib.sha1).digest()

    combined = str(Combine(hash3, magic))
    hash4 = hmac.new(key, combined, hashlib.sha1).digest()

    retKey = str(Combine(hash2, hash4[:4]))

    return retKey

def GetBeginning():
    Beginning = [chr(0)]*28

    # StructHeaderSize = 28
    Beginning[0] = 0x1c
    Beginning[1] = 0x00
    Beginning[2] = 0x00
    Beginning[3] = 0x00

    # CryptMode = 1
    Beginning[4] = 0x01
    Beginning[5] = 0x00
    Beginning[6] = 0x00
    Beginning[7] = 0x00

    # CipherType = 0x6603
    Beginning[8] = 0x03
    Beginning[9] = 0x66
    Beginning[10] = 0x00
    Beginning[11] = 0x00

    # HashType = 0x8004
    Beginning[12] = 0x04
    Beginning[13] = 0x80
    Beginning[14] = 0x00
    Beginning[15] = 0x00

    # IV length = 8
    Beginning[16] = 0x08
    Beginning[17] = 0x00
    Beginning[18] = 0x00
    Beginning[19] = 0x00

    # hash length = 20
    Beginning[20] = 0x14
    Beginning[21] = 0x00
    Beginning[22] = 0x00
    Beginning[23] = 0x00

    # cipher length = 72
    Beginning[24] = 0x48
    Beginning[25] = 0x00
    Beginning[26] = 0x00
    Beginning[27] = 0x00

    asd = "".join([chr(x) for x in Beginning])

    return asd


def GetSSOTicket(key, nonce):
    key1 = base64.b64decode(key)

    key2 = DeriveKey(key1, "WS-SecureConversationSESSION KEY HASH")

    key3 = DeriveKey(key1, "WS-SecureConversationSESSION KEY ENCRYPTION")

    nonceHash = hmac.new(key2, nonce, hashlib.sha1).digest()

    iv = [chr(0)]*8
    iv[0] = 0x75
    iv[1] = 0x95
    iv[2] = 0x85
    iv[3] = 0x61
    iv[4] = 0x38
    iv[5] = 0x85
    iv[6] = 0xEF
    iv[7] = 0x5C

    iv = "".join([chr(x) for x in iv])

    encryptor = pyDes.triple_des(key3, pyDes.CBC, iv)

    restOfNonce = chr(0x08) * 8
    fullNonce = Combine(nonce, restOfNonce)


    output = encryptor.encrypt(fullNonce)

    beginning = GetBeginning()

    struc = beginning + iv + nonceHash + output

    value = base64.b64encode(struc)

    return value

Solution

  • Modified your msntest.py a little and now it should work:

    import socket
    import httplib
    from encryption import GetSSOTicket
    import re
    from xml.sax import saxutils
    
    username = "login@hotmail.com"
    password = "password"
    
    def ReadAll(sock):
        data = ""
        while 1:
            try:
                r = sock.recv(4096)
                data += r
            except socket.timeout:
                break
        return data
    
    
    def SOAPRequest(xml):
        url = "login.live.com"
        con = httplib.HTTPSConnection(url)
    
        headers = {"Host": "login.live.com",
                    "Accept": "text/plain"}
    
        con.request("POST", "/RST.srf", xml, headers)
    
        response = con.getresponse().read()
    
        con.close()
    
        return response
    
    
    def GetTicket(policy, nonce):
        xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
        xml += "<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\"><Header>";
        xml += "<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">";
        xml += "<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>";
        xml += "<ps:BinaryVersion>4</ps:BinaryVersion>";
        xml += "<ps:UIVersion>1</ps:UIVersion>";
        xml += "<ps:Cookies></ps:Cookies>";
        xml += "<ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>";
        xml += "</ps:AuthInfo>";
        xml += "<wsse:Security><wsse:UsernameToken Id=\"user\">";
        xml += "<wsse:Username>" + username + "</wsse:Username>";
        xml += "<wsse:Password>" + password + "</wsse:Password>";
        xml += "</wsse:UsernameToken></wsse:Security></Header><Body>";
        xml += "<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">";
        xml += "<wst:RequestSecurityToken Id=\"RST0\">";
        xml += "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>";
        xml += "<wsp:AppliesTo><wsa:EndpointReference><wsa:Address>http://Passport.NET/tb";
        xml += "</wsa:Address></wsa:EndpointReference></wsp:AppliesTo></wst:RequestSecurityToken>";
        xml += "<wst:RequestSecurityToken Id=\"RST1\">";
        xml += "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType><wsp:AppliesTo><wsa:EndpointReference>";
        xml += "<wsa:Address>messengerclear.live.com</wsa:Address></wsa:EndpointReference></wsp:AppliesTo>";
        xml += "<wsse:PolicyReference URI=\"" + policy + "\"></wsse:PolicyReference></wst:RequestSecurityToken>";
        xml += "<wst:RequestSecurityToken Id=\"RST2\">";
        xml += "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>";
        xml += "<wsp:AppliesTo>";
        xml += "<wsa:EndpointReference>";
        xml += "<wsa:Address>contacts.msn.com</wsa:Address>";
        xml += "</wsa:EndpointReference>";
        xml += "</wsp:AppliesTo>";
        xml += "<wsse:PolicyReference URI=\"MBI\">";
        xml += "</wsse:PolicyReference>";
        xml += "</wst:RequestSecurityToken>";
        xml += "</ps:RequestMultipleSecurityTokens></Body></Envelope>";
    
        xmlDoc = SOAPRequest(xml)
    
        m = re.search(r'<wsse:BinarySecurityToken Id="Compact1">(.*?)</wst:BinarySecret>', xmlDoc)
        secret = saxutils.unescape(m.group(1))
        secret = secret.split("<wst:BinarySecret>")[1]
    
        print "secret:"
        print secret
    
        m = re.search(r'<wsse:BinarySecurityToken Id="Compact1">(.*?)</wsse:BinarySecurityToken>',xmlDoc)
        ticket = saxutils.unescape(m.group(1))
    
        SSOTicket = GetSSOTicket(secret, nonce)
    
        return ticket + " " + SSOTicket
    
    
    
    def LogIntoNS():
        address = ("64.4.61.219", 1863)
    
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect(address)
        s.settimeout(3)
    
        s.send("VER 1 MSNP15 CVR0\r\n")
    
        ReadAll(s)
    
        s.send("CVR 2 0x0407 win 6.0 i386 MSNMSGR 8.5 MSMSGS " + username + "\r\n")
    
        ReadAll(s)
    
        s.send("USR 3 SSO I " + username + "\r\n")
    
        recvbuf = ReadAll(s)
    
        recvbuf = recvbuf[recvbuf.find("USR"):]
    
        policy = recvbuf.split()[4]
    
        print "policy:"
        print policy
    
        nonce = recvbuf.split()[5]
        print "nonce:"
        print nonce
    
        ticket = GetTicket(policy, nonce)
    
        print "ticket:"
        print ticket
    
        s.send("USR 4 SSO S " + ticket + "\r\n")
    
        print "Sent"
    
        print s.recv(4096)
    
    
    LogIntoNS()