# Copyright (c) 2003-2006 CORE Security Technologies # # This software is provided under under a slightly modified version # of the Apache Software License. See the accompanying LICENSE file # for more information. # # $Id: samr.py,v 1.7 2006/05/23 21:19:26 gera Exp $ # # Description: # SAMR (Security Account Manager Remote) interface implementation. # import array from time import strftime, gmtime from struct import * from impacket import ImpactPacket import dcerpc import ndrutils MSRPC_UUID_SAMR = '\x78\x57\x34\x12\x34\x12\xcd\xab\xef\x00\x01\x23\x45\x67\x89\xac\x01\x00\x00\x00' OP_NUM_CREATE_USER_IN_DOMAIN = 0xC OP_NUM_ENUM_USERS_IN_DOMAIN = 0xD OP_NUM_CREATE_ALIAS_IN_DOMAIN = 0xE def display_time(filetime_high, filetime_low, minutes_utc=0): d = filetime_high*4.0*1.0*(1<<30) d += filetime_low d *= 1.0e-7 d -= (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60)) if minutes_utc == 0: r = (strftime("%a, %d %b %Y %H:%M:%S",gmtime(d)), minutes_utc/60)[0] else: r = "%s GMT %d " % (strftime("%a, %d %b %Y %H:%M:%S",gmtime(d)), minutes_utc/60) return r class MSRPCUserInfo: ITEMS = {'Account Name':0, 'Full Name':1, 'Home':2, 'Home Drive':3, 'Script':4, 'Profile':5, 'Description':6, 'Workstations':7, 'Comment':8, 'Parameters':9, 'Logon hours':10 } def __init__(self, data = None): self._logon_time_low = 0 self._logon_time_high = 0 self._logoff_time_low = 0 self._logoff_time_high = 0 self._kickoff_time_low = 0 self._kickoff_time_high = 0 self._pwd_last_set_low = 0 self._pwd_last_set_high = 0 self._pwd_can_change_low = 0 self._pwd_can_change_high = 0 self._pwd_must_change_low = 0 self._pwd_must_change_high = 0 self._items = [] self._rid = 0 self._group = 0 self._acct_ctrl = 0 self._bad_pwd_count = 0 self._logon_count = 0 self._country = 0 self._codepage = 0 self._nt_pwd_set = 0 self._lm_pwd_set = 0 if data: self.set_header(data) def set_header(self,data): index = 8 self._logon_time_low, self._logon_time_high, self._logoff_time_low, self._logoff_time_high, self._kickoff_time_low,self._kickoff_time_high, self._pwd_last_set_low,self._pwd_last_set_high, self._pwd_can_change_low,self._pwd_can_change_high, self._pwd_must_change_low, self._pwd_must_change_high = unpack(' 0 return SAMRConnectHeader.__SIZE + var_size class SAMRRespConnectHeader(ImpactPacket.Header): __SIZE = 24 def __init__(self, aBuffer = None): ImpactPacket.Header.__init__(self, SAMRRespConnectHeader.__SIZE) if aBuffer: self.load_header(aBuffer) def get_context_handle(self): return self.get_bytes().tostring()[:20] def set_context_handle(self, handle): assert 20 == len(handle) self.get_bytes()[:20] = array.array('B', handle) def get_return_code(self): return self.get_long(20, '<') def set_return_code(self, code): self.set_long(20, code, '<') def get_header_size(self): return SAMRRespConnectHeader.__SIZE class SAMREnumDomainsHeader(ImpactPacket.Header): OP_NUM = 0x6 __SIZE = 28 def __init__(self, aBuffer = None): ImpactPacket.Header.__init__(self, SAMREnumDomainsHeader.__SIZE) self.set_pref_max_size(8192) if aBuffer: self.load_header(aBuffer) def get_context_handle(self): return self.get_bytes().tolist()[:20] def set_context_handle(self, handle): assert 20 == len(handle) self.get_bytes()[:20] = array.array('B', handle) def get_resume_handle(self): return self.get_long(20, '<') def set_resume_handle(self, handle): self.set_long(20, handle, '<') def get_pref_max_size(self): return self.get_long(24, '<') def set_pref_max_size(self, size): self.set_long(24, size, '<') def get_header_size(self): return SAMREnumDomainsHeader.__SIZE class SAMRRespEnumDomainHeader(ImpactPacket.Header): __SIZE = 12 def __init__(self, aBuffer = None): ImpactPacket.Header.__init__(self, SAMRRespEnumDomainHeader.__SIZE) if aBuffer: self.load_header(aBuffer) def get_resume_handle(self): return self.get_long(0, '<') def set_resume_handle(self, handle): self.set_long(0, handle, '<') def get_domains(self): return dcerpc.MSRPCNameArray(self.get_bytes()[4:-8].tostring()) def set_domains(self, domains): assert isinstance(domains, dcerpc.MSRPCNameArray) self.get_bytes()[4:-8] = array.array('B', domains.rawData()) def get_entries_num(self): return self.get_long(-8, '<') def set_entries_num(self, num): self.set_long(-8, num, '<') def get_return_code(self): return self.get_long(-4, '<') def set_return_code(self, code): self.set_long(-4, code, '<') def get_header_size(self): var_size = len(self.get_bytes()) - SAMRRespEnumDomainHeader.__SIZE assert var_size > 0 return SAMRRespEnumDomainHeader.__SIZE + var_size class SAMRLookupDomainHeader(ImpactPacket.Header): OP_NUM = 0x5 __SIZE = 20 def __init__(self, aBuffer = None): ImpactPacket.Header.__init__(self, SAMRLookupDomainHeader.__SIZE) if aBuffer: self.load_header(aBuffer) def get_context_handle(self): return self.get_bytes().tolist()[:20] def set_context_handle(self, handle): assert 20 == len(handle) self.get_bytes()[:20] = array.array('B', handle) def get_domain(self): return dcerpc.MSRPCArray(self.get_bytes().tolist()[20:]) def set_domain(self, domain): assert isinstance(domain, dcerpc.MSRPCArray) self.get_bytes()[20:] = array.array('B', domain.rawData()) def get_header_size(self): var_size = len(self.get_bytes()) - SAMRLookupDomainHeader.__SIZE assert var_size > 0 return SAMRLookupDomainHeader.__SIZE + var_size class SAMRRespLookupDomainHeader(ImpactPacket.Header): __SIZE = 36 def __init__(self, aBuffer = None): ImpactPacket.Header.__init__(self, SAMRRespLookupDomainHeader.__SIZE) if aBuffer: self.load_header(aBuffer) ## def get_sid_count(self): ## return self.get_long(4, '<') ## def set_sid_count(self, count): ## self.set_long(4, count, '<') ## def get_domain_sid(self): ## return self.get_bytes().tolist()[8:8+24] ## def set_domain_sid(self, sid): ## assert 24 == len(sid) ## self.get_bytes()[8:8+24] = array.array('B', sid) def get_domain_sid(self): return self.get_bytes().tolist()[4:4+28] def set_domain_sid(self, sid): assert 28 == len(sid) self.get_bytes()[4:4+28] = array.array('B', sid) def get_return_code(self): return self.get_long(32, '<') def set_return_code(self, code): self.set_long(32, code, '<') def get_header_size(self): return SAMRRespLookupDomainHeader.__SIZE class SAMROpenDomainHeader(ImpactPacket.Header): OP_NUM = 0x7 __SIZE = 52 def __init__(self, aBuffer = None): ImpactPacket.Header.__init__(self, SAMROpenDomainHeader.__SIZE) self.set_access_mask(0x304) if aBuffer: self.load_header(aBuffer) def get_context_handle(self): return self.get_bytes().tolist()[:20] def set_context_handle(self, handle): assert 20 == len(handle) self.get_bytes()[:20] = array.array('B', handle) def get_access_mask(self): return self.get_long(20, '<') def set_access_mask(self, mask): self.set_long(20, mask, '<') ## def get_sid_count(self): ## return self.get_long(24, '<') ## def set_sid_count(self, count): ## self.set_long(24, count, '<') ## def get_domain_sid(self): ## return self.get_bytes().tolist()[28:28+24] ## def set_domain_sid(self, sid): ## assert 24 == len(sid) ## self.get_bytes()[28:28+24] = array.array('B', sid) def get_domain_sid(self): return self.get_bytes().tolist()[24:24+28] def set_domain_sid(self, sid): assert 28 == len(sid) self.get_bytes()[24:24+28] = array.array('B', sid) def get_header_size(self): return SAMROpenDomainHeader.__SIZE class SAMRRespOpenDomainHeader(ImpactPacket.Header): __SIZE = 24 def __init__(self, aBuffer = None): ImpactPacket.Header.__init__(self, SAMRRespOpenDomainHeader.__SIZE) if aBuffer: self.load_header(aBuffer) def get_context_handle(self): return self.get_bytes().tolist()[:20] def set_context_handle(self, handle): assert 20 == len(handle) self.get_bytes()[:20] = array.array('B', handle) def get_return_code(self): return self.get_long(20, '<') def set_return_code(self, code): self.set_long(20, code, '<') def get_header_size(self): return SAMRRespOpenDomainHeader.__SIZE class SAMREnumDomainUsersHeader(ImpactPacket.Header): OP_NUM = OP_NUM_ENUM_USERS_IN_DOMAIN __SIZE = 32 def __init__(self, aBuffer = None): ImpactPacket.Header.__init__(self, SAMREnumDomainUsersHeader.__SIZE) self.set_pref_max_size(3275) if aBuffer: self.load_header(aBuffer) def get_context_handle(self): return self.get_bytes().tolist()[:20] def set_context_handle(self, handle): assert 20 == len(handle) self.get_bytes()[:20] = array.array('B', handle) def get_resume_handle(self): return self.get_long(20, '<') def set_resume_handle(self, handle): self.set_long(20, handle, '<') def get_account_control(self): return self.get_long(24, '<') def set_account_control(self, mask): self.set_long(24, mask, '<') def get_pref_max_size(self): return self.get_long(28, '<') def set_pref_max_size(self, size): self.set_long(28, size, '<') def get_header_size(self): return SAMREnumDomainUsersHeader.__SIZE class SAMRRespEnumDomainUsersHeader(ImpactPacket.Header): __SIZE = 16 def __init__(self, aBuffer = None): ImpactPacket.Header.__init__(self, SAMRRespEnumDomainUsersHeader.__SIZE) if aBuffer: self.load_header(aBuffer) def get_resume_handle(self): return self.get_long(0, '<') def set_resume_handle(self, handle): self.set_long(0, handle, '<') def get_users(self): return dcerpc.MSRPCNameArray(self.get_bytes()[4:-8].tostring()) def set_users(self, users): assert isinstance(users, dcerpc.MSRPCNameArray) self.get_bytes()[4:-8] = array.array('B', users.rawData()) def get_entries_num(self): return self.get_long(-8, '<') def set_entries_num(self, num): self.set_long(-8, num, '<') def get_return_code(self): return self.get_long(-4, '<') def set_return_code(self, code): self.set_long(-4, code, '<') def get_header_size(self): var_size = len(self.get_bytes()) - SAMRRespEnumDomainUsersHeader.__SIZE assert var_size > 0 return SAMRRespEnumDomainUsersHeader.__SIZE + var_size class SAMROpenUserHeader(ImpactPacket.Header): OP_NUM = 0x22 __SIZE = 28 def __init__(self, aBuffer = None): ImpactPacket.Header.__init__(self, SAMROpenUserHeader.__SIZE) self.set_access_mask(0x2011B) if aBuffer: self.load_header(aBuffer) def get_context_handle(self): return self.get_bytes().tolist()[:20] def set_context_handle(self, handle): assert 20 == len(handle) self.get_bytes()[:20] = array.array('B', handle) def get_access_mask(self): return self.get_long(20, '<') def set_access_mask(self, mask): self.set_long(20, mask, '<') def get_rid(self): return self.get_long(24, '<') def set_rid(self, id): self.set_long(24, id, '<') def get_header_size(self): return SAMROpenUserHeader.__SIZE class SAMRRespOpenUserHeader(ImpactPacket.Header): __SIZE = 24 def __init__(self, aBuffer = None): ImpactPacket.Header.__init__(self, SAMRRespOpenUserHeader.__SIZE) if aBuffer: self.load_header(aBuffer) def get_context_handle(self): return self.get_bytes().tolist()[:20] def set_context_handle(self, handle): assert 20 == len(handle) self.get_bytes()[:20] = array.array('B', handle) def get_return_code(self): return self.get_long(20, '<') def set_return_code(self, code): self.set_long(20, code, '<') def get_header_size(self): return SAMRRespOpenUserHeader.__SIZE class SAMRQueryUserInfoHeader(ImpactPacket.Header): OP_NUM = 0x24 __SIZE = 22 def __init__(self, aBuffer = None): ImpactPacket.Header.__init__(self, SAMRQueryUserInfoHeader.__SIZE) self.set_level(21) if aBuffer: self.load_header(aBuffer) def get_context_handle(self): return self.get_bytes().tolist()[:20] def set_context_handle(self, handle): assert 20 == len(handle) self.get_bytes()[:20] = array.array('B', handle) def get_level(self): return self.get_word(20, '<') def set_level(self, level): self.set_word(20, level, '<') def get_header_size(self): return SAMRQueryUserInfoHeader.__SIZE class SAMRRespQueryUserInfoHeader(ImpactPacket.Header): __SIZE = 4 def __init__(self, aBuffer = None): ImpactPacket.Header.__init__(self, SAMRRespQueryUserInfoHeader.__SIZE) if aBuffer: self.load_header(aBuffer) def get_user_info(self): return MSRPCUserInfo(self.get_bytes()[:-4].tostring()) def set_user_info(self, info): assert isinstance(info, MSRPCUserInfo) self.get_bytes()[:-4] = array.array('B', info.rawData()) def get_return_code(self): return self.get_long(-4, '<') def set_return_code(self, code): self.set_long(-4, code, '<') def get_header_size(self): var_size = len(self.get_bytes()) - SAMRRespQueryUserInfoHeader.__SIZE assert var_size > 0 return SAMRRespQueryUserInfoHeader.__SIZE + var_size class SAMRCloseRequestHeader(ImpactPacket.Header): OP_NUM = 0x1 __SIZE = 20 def __init__(self, aBuffer = None): ImpactPacket.Header.__init__(self, SAMRCloseRequestHeader.__SIZE) if aBuffer: self.load_header(aBuffer) def get_context_handle(self): return self.get_bytes().tolist()[:20] def set_context_handle(self, handle): assert 20 == len(handle) self.get_bytes()[:20] = array.array('B', handle) def get_header_size(self): return SAMRCloseRequestHeader.__SIZE class SAMRRespCloseRequestHeader(ImpactPacket.Header): __SIZE = 24 def __init__(self, aBuffer = None): ImpactPacket.Header.__init__(self, SAMRRespCloseRequestHeader.__SIZE) if aBuffer: self.load_header(aBuffer) def get_context_handle(self): return self.get_bytes().tolist()[:20] def set_context_handle(self, handle): assert 20 == len(handle) self.get_bytes()[:20] = array.array('B', handle) def get_return_code(self): return self.get_long(20, '<') def set_return_code(self, code): self.set_long(20, code, '<') def get_header_size(self): return SAMRRespCloseRequestHeader.__SIZE class DCERPCSamr: def __init__(self, dcerpc): self._dcerpc = dcerpc def connect(self): samrcon = SAMRConnectHeader() samrcon.set_server('*SMBSERVER') self._dcerpc.send(samrcon) data = self._dcerpc.recv() retVal = SAMRRespConnectHeader(data) return retVal def enumdomains(self,context_handle): enumdom = SAMREnumDomainsHeader() enumdom.set_context_handle(context_handle) self._dcerpc.send(enumdom) data = self._dcerpc.recv() retVal = SAMRRespEnumDomainHeader(data) return retVal def lookupdomain(self,context_handle,domain): lookupdom = SAMRLookupDomainHeader() lookupdom.set_context_handle(context_handle) lookupdom.set_domain(domain) self._dcerpc.send(lookupdom) data = self._dcerpc.recv() retVal = SAMRRespLookupDomainHeader(data) return retVal def opendomain(self,context_handle,domain_sid): opendom = SAMROpenDomainHeader() opendom.set_context_handle(context_handle) opendom.set_domain_sid(domain_sid) self._dcerpc.send(opendom) data = self._dcerpc.recv() retVal = SAMRRespOpenDomainHeader(data) return retVal def enumusers(self,context_handle): enumusers = SAMREnumDomainUsersHeader() enumusers.set_context_handle(context_handle) self._dcerpc.send(enumusers) data = self._dcerpc.recv() retVal = SAMRRespEnumDomainUsersHeader(data) return retVal def openuser(self,context_handle, rid): openuser = SAMROpenUserHeader() openuser.set_context_handle(context_handle) openuser.set_rid(rid) self._dcerpc.send(openuser) data = self._dcerpc.recv() retVal = SAMRRespOpenUserHeader(data) return retVal def queryuserinfo(self,context_handle): userinfo = SAMRQueryUserInfoHeader() userinfo.set_context_handle(context_handle) self._dcerpc.send(userinfo) data = self._dcerpc.recv() retVal = SAMRRespQueryUserInfoHeader(data) return retVal def closerequest(self,context_handle): closereq = SAMRCloseRequestHeader() closereq.set_context_handle(context_handle) self._dcerpc.send(closereq) data = self._dcerpc.recv() retVal = SAMRRespCloseRequestHeader(data) return retVal