0

I am patching a tool that works on the NTLM network protocol, I have a structure object where I index a string and pass to a function, inside the scope of the function the variable changes from a <type 'str'> to <type 'instance'>.

function call:

# type(self.challengeMessage['challenge']) == <type 'str'>
self.ParseHTTPHash(self.challengeMessage['challenge'])

inside function:

def ParseHTTPHash(challenge):
    # type(challenge) == <type 'instance'>
    challenge.encode("hex") # causes exception due to being instance type and not string

challengeMessage object:

class NTLMAuthChallenge(Structure):

    structure = (
        ('','"NTLMSSP\x00'),
        ('message_type','<L=2'),
        ('domain_len','<H-domain_name'),
        ('domain_max_len','<H-domain_name'),
        ('domain_offset','<L=40'),
        ('flags','<L=0'),
        ('challenge','8s'),
        ('reserved','8s=""'),
        ('TargetInfoFields_len','<H-TargetInfoFields'),
        ('TargetInfoFields_max_len','<H-TargetInfoFields'),
        ('TargetInfoFields_offset','<L'),
        ('VersionLen','_-Version','self.checkVersion(self["flags"])'), 
        ('Version',':'),
        ('domain_name',':'),
        ('TargetInfoFields',':'))

    @staticmethod
    def checkVersion(flags):
        if flags is not None:
           if flags & NTLMSSP_NEGOTIATE_VERSION == 0:
              return 0
        return 8

    def getData(self):
        if self['TargetInfoFields'] is not None and type(self['TargetInfoFields']) is not bytes:
            raw_av_fields = self['TargetInfoFields'].getData()
            self['TargetInfoFields'] = raw_av_fields
        return Structure.getData(self)

    def fromString(self,data):
        Structure.fromString(self,data)
        self['domain_name'] = data[self['domain_offset']:][:self['domain_len']]
        self['TargetInfoFields'] = data[self['TargetInfoFields_offset']:][:self['TargetInfoFields_len']]
        return self

the weird thing is if I pass self into ParseHTTPHasH(self) and reference it inside the function self.challengeMessage['challenge'] it doesn't change type to instance. What is going on here?

(Edit:)

Apologizes for the vagueness of the original post its hard to show context with this large file

General Layout Context:

class HTTPRelayServer(Thread):
        ...
        # Parse NTLMv1/v2 hash with challengeMessage & token * custom *
        # check out responder ParseSMBHash to implement for smbrelayserver.py
        # replace impacket/impacket/examples/ntlmrelayx/servers/httprelayserver.py
        def ParseHTTPHash(self,client,data):
            LMhashLen    = struct.unpack('<H',data[12:14])[0]
            LMhashOffset = struct.unpack('<H',data[16:18])[0]
            LMHash       = data[LMhashOffset:LMhashOffset+LMhashLen].encode("hex").upper()

            NthashLen    = struct.unpack('<H',data[20:22])[0]
            NthashOffset = struct.unpack('<H',data[24:26])[0]
            NTHash       = data[NthashOffset:NthashOffset+NthashLen].encode("hex").upper()

            UserLen      = struct.unpack('<H',data[36:38])[0]
            UserOffset   = struct.unpack('<H',data[40:42])[0]
            User         = data[UserOffset:UserOffset+UserLen].replace('\x00','')

            # parameter reference
*---------> NumChal = self.challengeMessage['challenge'].encode("hex")

            if NthashLen == 24:
                HostNameLen     = struct.unpack('<H',data[46:48])[0]
                HostNameOffset  = struct.unpack('<H',data[48:50])[0]
                HostName        = data[HostNameOffset:HostNameOffset+HostNameLen].replace('\x00','')
                WriteHash       = '%s::%s:%s:%s:%s' % (User, HostName, LMHash, NTHash, NumChal)

            if NthashLen > 24:
                NthashLen      = 64
                DomainLen      = struct.unpack('<H',data[28:30])[0]
                DomainOffset   = struct.unpack('<H',data[32:34])[0]
                Domain         = data[DomainOffset:DomainOffset+DomainLen].replace('\x00','')
                HostNameLen    = struct.unpack('<H',data[44:46])[0]
                HostNameOffset = struct.unpack('<H',data[48:50])[0]
                HostName       = data[HostNameOffset:HostNameOffset+HostNameLen].replace('\x00','')
                WriteHash      = '%s::%s:%s:%s:%s' % (User, Domain, NumChal, NTHash[:32], NTHash[32:])
            return WriteHash

        def do_PROPFIND(self):
            proxy = False
            if (".jpg" in self.path) or (".JPG" in self.path):
                content = """<?xml version="1.0"?><D:multistatus xmlns:D="DAV:"><D:response><D:href>http://webdavrelay/file/image.JPG/</D:href><D:propstat><D:prop><D:creationdate>2016-11-12T22:00:22Z</D:creationdate><D:displayname>image.JPG</D:displayname><D:getcontentlength>4456</D:getcontentlength><D:getcontenttype>image/jpeg</D:getcontenttype><D:getetag>4ebabfcee4364434dacb043986abfffe</D:getetag><D:getlastmodified>Mon, 20 Mar 2017 00:00:22 GMT</D:getlastmodified><D:resourcetype></D:resourcetype><D:supportedlock></D:supportedlock><D:ishidden>0</D:ishidden></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response></D:multistatus>"""
            else:
                content = """<?xml version="1.0"?><D:multistatus xmlns:D="DAV:"><D:response><D:href>http://webdavrelay/file/</D:href><D:propstat><D:prop><D:creationdate>2016-11-12T22:00:22Z</D:creationdate><D:displayname>a</D:displayname><D:getcontentlength></D:getcontentlength><D:getcontenttype></D:getcontenttype><D:getetag></D:getetag><D:getlastmodified>Mon, 20 Mar 2017 00:00:22 GMT</D:getlastmodified><D:resourcetype><D:collection></D:collection></D:resourcetype><D:supportedlock></D:supportedlock><D:ishidden>0</D:ishidden></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response></D:multistatus>"""

            messageType = 0
            if self.headers.getheader('Authorization') is None:
                self.do_AUTHHEAD(message='NTLM')
                pass
            else:
                typeX = self.headers.getheader('Authorization')
                try:
                    _, blob = typeX.split('NTLM')
                    token = base64.b64decode(blob.strip())
                except:
                    self.do_AUTHHEAD()
                messageType = struct.unpack('<L', token[len('NTLMSSP\x00'):len('NTLMSSP\x00') + 4])[0]

            if messageType == 1:
                if not self.do_ntlm_negotiate(token, proxy=proxy):
                    LOG.info("do negotiate failed, sending redirect")
                    self.do_REDIRECT()
            elif messageType == 3:
                authenticateMessage = ntlm.NTLMAuthChallengeResponse()
                authenticateMessage.fromString(token)
                if authenticateMessage['flags'] & ntlm.NTLMSSP_NEGOTIATE_UNICODE:
                    LOG.info("Authenticating against %s://%s as %s\\%s SUCCEED" % (
                        self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode('utf-16le'),
                        authenticateMessage['user_name'].decode('utf-16le')))
                else:
                    LOG.info("Authenticating against %s://%s as %s\\%s SUCCEED" % (
                        self.target.scheme, self.target.netloc, authenticateMessage['domain_name'].decode('ascii'),
                        authenticateMessage['user_name'].decode('ascii')))
                self.do_ntlm_auth(token, authenticateMessage)
                self.do_attack()

                # Function call
*------------>  print(self.ParseHTTPHash(self,str(token)))

                self.send_response(207, "Multi-Status")
                self.send_header('Content-Type', 'application/xml')
                self.send_header('Content-Length', str(len(content)))
                self.end_headers()
                self.wfile.write(content)
                return
        ...
rooter
  • 99
  • 2
  • 8

3 Answers3

0
def ParseHTTPHash(challenge):

If this is a free function (defined outside class), you shouldn't call it self.ParseHTTPHash(value), but simply ParseHTTPHash(value). If it's not @staticmethod and it's inside the class - add self as first parameter.

RafalS
  • 5,834
  • 1
  • 20
  • 25
  • That was my original solve for this problem but `self.ParseHTTPHash(self)` looked weird, like I was programming something wrong. Is this normal? – rooter Feb 01 '20 at 14:39
  • No. It's not. Could you show me where is the function call, and where is the ParseHTTPHash function defined? – RafalS Feb 01 '20 at 14:41
  • ParseHTTPHash is defined in a HTTPServer class and the function call is in another function of HTTPRelayServer, it may be a little hard to show as the file is huge but I added the general layout as an edit to thist post – rooter Feb 01 '20 at 15:19
0

I guess ParseHTTPHash is a method inside your class. If so please pass self as first parameter.

 def ParseHTTPHash(self,challenge)
Pal
  • 19
  • 2
0

All normal instance methods of a class (not @staticmethod or whatever) take a first parameter, which is the object on which they've been invoked.

So, when you call

obj.foo(param)

on an object obj of class C, it translates to roughly

C.foo(obj, param)

which is usually implemented as

class C:
    def foo(self, param):
        pass # whatever

Otherwise the instance method wouldn't know which instance of class C it was supposed to be working on.

So, if ParseHTTPHash is an instance method, you should call it as self.ParseHTTPHash(param), but you declared it wrong.

If ParseHTTPHash is not an instance method, you should not call it as self.ParseHTTPHash(param) in the first place, because that syntax is specifically for instance methods.

The only way to know which case applies is for you to show the definition of ParseHTTPHash as @RafalS asked.

Useless
  • 64,155
  • 6
  • 88
  • 132