■ ■ ■ ■ ■ ■
aardwolf/authentication/ntlm/structures/serverinfo.py
| 1 | + | |
| 2 | + | from aardwolf.authentication.ntlm.structures.avpair import AVPAIRType |
| 3 | + | import datetime |
| 4 | + | import json |
| 5 | + | |
| 6 | + | NTLMSERVERINFO_TSV_HDR = ['domainname', 'computername', 'dnsforestname', 'dnscomputername', 'dnsdomainname', 'local_time', 'os_major_version', 'os_minor_version', 'os_build', 'os_guess' ] |
| 7 | + | |
| 8 | + | |
| 9 | + | import datetime |
| 10 | + | import io |
| 11 | + | |
| 12 | + | # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/2c57429b-fdd4-488f-b5fc-9e4cf020fcdf |
| 13 | + | class FILETIME: |
| 14 | + | def __init__(self): |
| 15 | + | self.dwLowDateTime = None |
| 16 | + | self.dwHighDateTime = None |
| 17 | + | |
| 18 | + | self.datetime = None |
| 19 | + | @staticmethod |
| 20 | + | def from_bytes(data): |
| 21 | + | return FILETIME.from_buffer(io.BytesIO(data)) |
| 22 | + | |
| 23 | + | def calc_dt(self): |
| 24 | + | if self.dwHighDateTime == 4294967295 and self.dwLowDateTime == 4294967295: |
| 25 | + | self.datetime = self.datetime = datetime.datetime(3000, 1, 1, 0, 0) |
| 26 | + | else: |
| 27 | + | ft = (self.dwHighDateTime << 32) + self.dwLowDateTime |
| 28 | + | if ft == 0: |
| 29 | + | self.datetime = datetime.datetime(1970, 1, 1, 0, 0) |
| 30 | + | else: |
| 31 | + | self.datetime = datetime.datetime.utcfromtimestamp((ft - 116444736000000000) / 10000000) |
| 32 | + | |
| 33 | + | @staticmethod |
| 34 | + | def from_dict(d): |
| 35 | + | t = FILETIME() |
| 36 | + | t.dwLowDateTime = d['dwLowDateTime'] |
| 37 | + | t.dwHighDateTime = d['dwHighDateTime'] |
| 38 | + | t.calc_dt() |
| 39 | + | return t |
| 40 | + | |
| 41 | + | @staticmethod |
| 42 | + | def from_buffer(buff): |
| 43 | + | t = FILETIME() |
| 44 | + | t.dwLowDateTime = int.from_bytes(buff.read(4), byteorder='little', signed = False) |
| 45 | + | t.dwHighDateTime = int.from_bytes(buff.read(4), byteorder='little', signed = False) |
| 46 | + | t.calc_dt() |
| 47 | + | return t |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | class NTLMServerInfo: |
| 52 | + | def __init__(self): |
| 53 | + | self.domainname = None |
| 54 | + | self.computername = None |
| 55 | + | self.dnscomputername = None |
| 56 | + | self.dnsdomainname = None |
| 57 | + | self.local_time = None |
| 58 | + | self.dnsforestname = None |
| 59 | + | self.os_major_version = None |
| 60 | + | self.os_minor_version = None |
| 61 | + | self.os_build = None |
| 62 | + | self.os_guess = None |
| 63 | + | |
| 64 | + | @staticmethod |
| 65 | + | def from_challenge(challenge): |
| 66 | + | si = NTLMServerInfo() |
| 67 | + | ti = challenge.TargetInfo |
| 68 | + | for k in ti: |
| 69 | + | if k == AVPAIRType.MsvAvNbDomainName: |
| 70 | + | si.domainname = ti[k] |
| 71 | + | elif k == AVPAIRType.MsvAvNbComputerName: |
| 72 | + | si.computername = ti[k] |
| 73 | + | elif k == AVPAIRType.MsvAvDnsDomainName: |
| 74 | + | si.dnsdomainname = ti[k] |
| 75 | + | elif k == AVPAIRType.MsvAvDnsComputerName: |
| 76 | + | si.dnscomputername = ti[k] |
| 77 | + | elif k == AVPAIRType.MsvAvDnsTreeName: |
| 78 | + | si.dnsforestname = ti[k] |
| 79 | + | elif k == AVPAIRType.MsvAvTimestamp: |
| 80 | + | if isinstance(ti[k], bytes): |
| 81 | + | si.local_time = FILETIME.from_bytes(ti[k]).datetime |
| 82 | + | elif isinstance(ti[k], datetime): |
| 83 | + | si.local_time = ti[k] |
| 84 | + | |
| 85 | + | if challenge.Version is not None: |
| 86 | + | if challenge.Version.ProductMajorVersion is not None: |
| 87 | + | si.os_major_version = challenge.Version.ProductMajorVersion |
| 88 | + | if challenge.Version.ProductMinorVersion is not None: |
| 89 | + | si.os_minor_version = challenge.Version.ProductMinorVersion |
| 90 | + | if challenge.Version.ProductBuild is not None: |
| 91 | + | si.os_build = challenge.Version.ProductBuild |
| 92 | + | if challenge.Version.WindowsProduct is not None: |
| 93 | + | si.os_guess = challenge.Version.WindowsProduct |
| 94 | + | |
| 95 | + | return si |
| 96 | + | |
| 97 | + | def to_dict(self): |
| 98 | + | t = { |
| 99 | + | 'domainname' : self.domainname, |
| 100 | + | 'computername' : self.computername, |
| 101 | + | 'dnscomputername' : self.dnscomputername, |
| 102 | + | 'dnsdomainname' : self.dnsdomainname, |
| 103 | + | 'local_time' : self.local_time, |
| 104 | + | 'dnsforestname' : self.dnsforestname, |
| 105 | + | 'os_build' : self.os_build, |
| 106 | + | 'os_guess' : self.os_guess, |
| 107 | + | 'os_major_version' : None, |
| 108 | + | 'os_minor_version' : None, |
| 109 | + | } |
| 110 | + | if self.os_major_version is not None: |
| 111 | + | t['os_major_version'] = self.os_major_version.name |
| 112 | + | if self.os_minor_version is not None: |
| 113 | + | t['os_minor_version'] = self.os_minor_version.name |
| 114 | + | return t |
| 115 | + | |
| 116 | + | def to_tsv(self, separator = '\t'): |
| 117 | + | def vn(x): |
| 118 | + | if x is None: |
| 119 | + | return '' |
| 120 | + | return str(x) |
| 121 | + | |
| 122 | + | d = self.to_dict() |
| 123 | + | return separator.join([ vn(d[x]) for x in NTLMSERVERINFO_TSV_HDR]) |
| 124 | + | |
| 125 | + | def __str__(self): |
| 126 | + | t = '=== Server Info ====\r\n' |
| 127 | + | for k in self.__dict__: |
| 128 | + | t += '%s: %s\r\n' % (k, self.__dict__[k]) |
| 129 | + | |
| 130 | + | return t |
| 131 | + | |
| 132 | + | def to_json(self): |
| 133 | + | return json.dumps(self.to_dict()) |
| 134 | + | |
| 135 | + | def to_grep(self): |
| 136 | + | t = '' |
| 137 | + | t += '[domainname,%s]' % self.domainname |
| 138 | + | t += '[computername,%s]' % self.computername |
| 139 | + | t += '[dnscomputername,%s]' % self.dnscomputername |
| 140 | + | t += '[dnsdomainname,%s]' % self.dnsdomainname |
| 141 | + | t += '[dnsforestname,%s]' % self.dnsforestname |
| 142 | + | t += '[os_build,%s]' % self.os_build |
| 143 | + | t += '[os_guess,%s]' % self.os_guess |
| 144 | + | if self.local_time is not None: |
| 145 | + | t += '[local_time,%s]' % self.local_time.isoformat() |
| 146 | + | if self.os_major_version is not None: |
| 147 | + | t += '[os_major,%s]' % self.os_major_version.value |
| 148 | + | if self.os_minor_version is not None: |
| 149 | + | t += '[os_minor,%s]' % self.os_minor_version.value |
| 150 | + | |
| 151 | + | return t |