1#!/usr/bin/env python 2# Copyright (c) 2003-2016 CORE Security Technologies 3# 4# This software is provided under under a slightly modified version 5# of the Apache Software License. See the accompanying LICENSE file 6# for more information. 7# 8# [MS-SCMR] services common functions for manipulating services 9# 10# Author: 11# Alberto Solino (@agsolino) 12# 13# Reference for: 14# DCE/RPC. 15# TODO: 16# [ ] Check errors 17 18import sys 19import argparse 20import logging 21import codecs 22 23from impacket.examples import logger 24from impacket import version 25from impacket.dcerpc.v5 import transport, scmr 26from impacket.dcerpc.v5.ndr import NULL 27from impacket.crypto import * 28 29 30class SVCCTL: 31 32 def __init__(self, username, password, domain, options, port=445): 33 self.__username = username 34 self.__password = password 35 self.__options = options 36 self.__port = port 37 self.__action = options.action.upper() 38 self.__domain = domain 39 self.__lmhash = '' 40 self.__nthash = '' 41 self.__aesKey = options.aesKey 42 self.__doKerberos = options.k 43 self.__kdcHost = options.dc_ip 44 45 if options.hashes is not None: 46 self.__lmhash, self.__nthash = options.hashes.split(':') 47 48 def run(self, remoteName, remoteHost): 49 50 stringbinding = 'ncacn_np:%s[\pipe\svcctl]' % remoteName 51 logging.debug('StringBinding %s'%stringbinding) 52 rpctransport = transport.DCERPCTransportFactory(stringbinding) 53 rpctransport.set_dport(self.__port) 54 rpctransport.setRemoteHost(remoteHost) 55 if hasattr(rpctransport, 'set_credentials'): 56 # This method exists only for selected protocol sequences. 57 rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey) 58 59 rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost) 60 self.doStuff(rpctransport) 61 62 def doStuff(self, rpctransport): 63 dce = rpctransport.get_dce_rpc() 64 #dce.set_credentials(self.__username, self.__password) 65 dce.connect() 66 #dce.set_max_fragment_size(1) 67 #dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY) 68 #dce.set_auth_level(ntlm.NTLM_AUTH_PKT_INTEGRITY) 69 dce.bind(scmr.MSRPC_UUID_SCMR) 70 #rpc = svcctl.DCERPCSvcCtl(dce) 71 rpc = dce 72 ans = scmr.hROpenSCManagerW(rpc) 73 scManagerHandle = ans['lpScHandle'] 74 if self.__action != 'LIST' and self.__action != 'CREATE': 75 ans = scmr.hROpenServiceW(rpc, scManagerHandle, self.__options.name+'\x00') 76 serviceHandle = ans['lpServiceHandle'] 77 78 if self.__action == 'START': 79 logging.info("Starting service %s" % self.__options.name) 80 scmr.hRStartServiceW(rpc, serviceHandle) 81 scmr.hRCloseServiceHandle(rpc, serviceHandle) 82 elif self.__action == 'STOP': 83 logging.info("Stopping service %s" % self.__options.name) 84 scmr.hRControlService(rpc, serviceHandle, scmr.SERVICE_CONTROL_STOP) 85 scmr.hRCloseServiceHandle(rpc, serviceHandle) 86 elif self.__action == 'DELETE': 87 logging.info("Deleting service %s" % self.__options.name) 88 scmr.hRDeleteService(rpc, serviceHandle) 89 scmr.hRCloseServiceHandle(rpc, serviceHandle) 90 elif self.__action == 'CONFIG': 91 logging.info("Querying service config for %s" % self.__options.name) 92 resp = scmr.hRQueryServiceConfigW(rpc, serviceHandle) 93 print "TYPE : %2d - " % resp['lpServiceConfig']['dwServiceType'], 94 if resp['lpServiceConfig']['dwServiceType'] & 0x1: 95 print "SERVICE_KERNEL_DRIVER ", 96 if resp['lpServiceConfig']['dwServiceType'] & 0x2: 97 print "SERVICE_FILE_SYSTEM_DRIVER ", 98 if resp['lpServiceConfig']['dwServiceType'] & 0x10: 99 print "SERVICE_WIN32_OWN_PROCESS ", 100 if resp['lpServiceConfig']['dwServiceType'] & 0x20: 101 print "SERVICE_WIN32_SHARE_PROCESS ", 102 if resp['lpServiceConfig']['dwServiceType'] & 0x100: 103 print "SERVICE_INTERACTIVE_PROCESS ", 104 print "" 105 print "START_TYPE : %2d - " % resp['lpServiceConfig']['dwStartType'], 106 if resp['lpServiceConfig']['dwStartType'] == 0x0: 107 print "BOOT START" 108 elif resp['lpServiceConfig']['dwStartType'] == 0x1: 109 print "SYSTEM START" 110 elif resp['lpServiceConfig']['dwStartType'] == 0x2: 111 print "AUTO START" 112 elif resp['lpServiceConfig']['dwStartType'] == 0x3: 113 print "DEMAND START" 114 elif resp['lpServiceConfig']['dwStartType'] == 0x4: 115 print "DISABLED" 116 else: 117 print "UNKNOWN" 118 119 print "ERROR_CONTROL : %2d - " % resp['lpServiceConfig']['dwErrorControl'], 120 if resp['lpServiceConfig']['dwErrorControl'] == 0x0: 121 print "IGNORE" 122 elif resp['lpServiceConfig']['dwErrorControl'] == 0x1: 123 print "NORMAL" 124 elif resp['lpServiceConfig']['dwErrorControl'] == 0x2: 125 print "SEVERE" 126 elif resp['lpServiceConfig']['dwErrorControl'] == 0x3: 127 print "CRITICAL" 128 else: 129 print "UNKNOWN" 130 print "BINARY_PATH_NAME : %s" % resp['lpServiceConfig']['lpBinaryPathName'][:-1] 131 print "LOAD_ORDER_GROUP : %s" % resp['lpServiceConfig']['lpLoadOrderGroup'][:-1] 132 print "TAG : %d" % resp['lpServiceConfig']['dwTagId'] 133 print "DISPLAY_NAME : %s" % resp['lpServiceConfig']['lpDisplayName'][:-1] 134 print "DEPENDENCIES : %s" % resp['lpServiceConfig']['lpDependencies'][:-1] 135 print "SERVICE_START_NAME: %s" % resp['lpServiceConfig']['lpServiceStartName'][:-1] 136 elif self.__action == 'STATUS': 137 print "Querying status for %s" % self.__options.name 138 resp = scmr.hRQueryServiceStatus(rpc, serviceHandle) 139 print "%30s - " % self.__options.name, 140 state = resp['lpServiceStatus']['dwCurrentState'] 141 if state == scmr.SERVICE_CONTINUE_PENDING: 142 print "CONTINUE PENDING" 143 elif state == scmr.SERVICE_PAUSE_PENDING: 144 print "PAUSE PENDING" 145 elif state == scmr.SERVICE_PAUSED: 146 print "PAUSED" 147 elif state == scmr.SERVICE_RUNNING: 148 print "RUNNING" 149 elif state == scmr.SERVICE_START_PENDING: 150 print "START PENDING" 151 elif state == scmr.SERVICE_STOP_PENDING: 152 print "STOP PENDING" 153 elif state == scmr.SERVICE_STOPPED: 154 print "STOPPED" 155 else: 156 print "UNKNOWN" 157 elif self.__action == 'LIST': 158 logging.info("Listing services available on target") 159 #resp = rpc.EnumServicesStatusW(scManagerHandle, svcctl.SERVICE_WIN32_SHARE_PROCESS ) 160 #resp = rpc.EnumServicesStatusW(scManagerHandle, svcctl.SERVICE_WIN32_OWN_PROCESS ) 161 #resp = rpc.EnumServicesStatusW(scManagerHandle, serviceType = svcctl.SERVICE_FILE_SYSTEM_DRIVER, serviceState = svcctl.SERVICE_STATE_ALL ) 162 resp = scmr.hREnumServicesStatusW(rpc, scManagerHandle) 163 for i in range(len(resp)): 164 print "%30s - %70s - " % (resp[i]['lpServiceName'][:-1], resp[i]['lpDisplayName'][:-1]), 165 state = resp[i]['ServiceStatus']['dwCurrentState'] 166 if state == scmr.SERVICE_CONTINUE_PENDING: 167 print "CONTINUE PENDING" 168 elif state == scmr.SERVICE_PAUSE_PENDING: 169 print "PAUSE PENDING" 170 elif state == scmr.SERVICE_PAUSED: 171 print "PAUSED" 172 elif state == scmr.SERVICE_RUNNING: 173 print "RUNNING" 174 elif state == scmr.SERVICE_START_PENDING: 175 print "START PENDING" 176 elif state == scmr.SERVICE_STOP_PENDING: 177 print "STOP PENDING" 178 elif state == scmr.SERVICE_STOPPED: 179 print "STOPPED" 180 else: 181 print "UNKNOWN" 182 print "Total Services: %d" % len(resp) 183 elif self.__action == 'CREATE': 184 logging.info("Creating service %s" % self.__options.name) 185 scmr.hRCreateServiceW(rpc, scManagerHandle, self.__options.name + '\x00', self.__options.display + '\x00', 186 lpBinaryPathName=self.__options.path + '\x00') 187 elif self.__action == 'CHANGE': 188 logging.info("Changing service config for %s" % self.__options.name) 189 if self.__options.start_type is not None: 190 start_type = int(self.__options.start_type) 191 else: 192 start_type = scmr.SERVICE_NO_CHANGE 193 if self.__options.service_type is not None: 194 service_type = int(self.__options.service_type) 195 else: 196 service_type = scmr.SERVICE_NO_CHANGE 197 198 if self.__options.display is not None: 199 display = self.__options.display + '\x00' 200 else: 201 display = NULL 202 203 if self.__options.path is not None: 204 path = self.__options.path + '\x00' 205 else: 206 path = NULL 207 208 if self.__options.start_name is not None: 209 start_name = self.__options.start_name + '\x00' 210 else: 211 start_name = NULL 212 213 if self.__options.password is not None: 214 s = rpctransport.get_smb_connection() 215 key = s.getSessionKey() 216 try: 217 password = (self.__options.password+'\x00').encode('utf-16le') 218 except UnicodeDecodeError: 219 import sys 220 password = (self.__options.password+'\x00').decode(sys.getfilesystemencoding()).encode('utf-16le') 221 password = encryptSecret(key, password) 222 else: 223 password = NULL 224 225 226 #resp = scmr.hRChangeServiceConfigW(rpc, serviceHandle, display, path, service_type, start_type, start_name, password) 227 scmr.hRChangeServiceConfigW(rpc, serviceHandle, service_type, start_type, scmr.SERVICE_ERROR_IGNORE, path, 228 NULL, NULL, NULL, 0, start_name, password, 0, display) 229 scmr.hRCloseServiceHandle(rpc, serviceHandle) 230 else: 231 logging.error("Unknown action %s" % self.__action) 232 233 scmr.hRCloseServiceHandle(rpc, scManagerHandle) 234 235 dce.disconnect() 236 237 return 238 239 240# Process command-line arguments. 241if __name__ == '__main__': 242 243 # Init the example's logger theme 244 logger.init() 245 # Explicitly changing the stdout encoding format 246 if sys.stdout.encoding is None: 247 # Output is redirected to a file 248 sys.stdout = codecs.getwriter('utf8')(sys.stdout) 249 print version.BANNER 250 251 parser = argparse.ArgumentParser(add_help = True, description = "Windows Service manipulation script.") 252 253 parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>') 254 parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') 255 subparsers = parser.add_subparsers(help='actions', dest='action') 256 257 # A start command 258 start_parser = subparsers.add_parser('start', help='starts the service') 259 start_parser.add_argument('-name', action='store', required=True, help='service name') 260 261 # A stop command 262 stop_parser = subparsers.add_parser('stop', help='stops the service') 263 stop_parser.add_argument('-name', action='store', required=True, help='service name') 264 265 # A delete command 266 delete_parser = subparsers.add_parser('delete', help='deletes the service') 267 delete_parser.add_argument('-name', action='store', required=True, help='service name') 268 269 # A status command 270 status_parser = subparsers.add_parser('status', help='returns service status') 271 status_parser.add_argument('-name', action='store', required=True, help='service name') 272 273 # A config command 274 config_parser = subparsers.add_parser('config', help='returns service configuration') 275 config_parser.add_argument('-name', action='store', required=True, help='service name') 276 277 # A list command 278 list_parser = subparsers.add_parser('list', help='list available services') 279 280 # A create command 281 create_parser = subparsers.add_parser('create', help='create a service') 282 create_parser.add_argument('-name', action='store', required=True, help='service name') 283 create_parser.add_argument('-display', action='store', required=True, help='display name') 284 create_parser.add_argument('-path', action='store', required=True, help='binary path') 285 286 # A change command 287 create_parser = subparsers.add_parser('change', help='change a service configuration') 288 create_parser.add_argument('-name', action='store', required=True, help='service name') 289 create_parser.add_argument('-display', action='store', required=False, help='display name') 290 create_parser.add_argument('-path', action='store', required=False, help='binary path') 291 create_parser.add_argument('-service_type', action='store', required=False, help='service type') 292 create_parser.add_argument('-start_type', action='store', required=False, help='service start type') 293 create_parser.add_argument('-start_name', action='store', required=False, help='string that specifies the name of ' 294 'the account under which the service should run') 295 create_parser.add_argument('-password', action='store', required=False, help='string that contains the password of ' 296 'the account whose name was specified by the start_name parameter') 297 298 group = parser.add_argument_group('authentication') 299 300 group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH') 301 group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)') 302 group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file ' 303 '(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the ' 304 'ones specified in the command line') 305 group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication ' 306 '(128 or 256 bits)') 307 308 group = parser.add_argument_group('connection') 309 310 group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If ' 311 'ommited it use the domain part (FQDN) specified in the target parameter') 312 group.add_argument('-target-ip', action='store', metavar="ip address", help='IP Address of the target machine. If ' 313 'ommited it will use whatever was specified as target. This is useful when target is the NetBIOS ' 314 'name and you cannot resolve it') 315 group.add_argument('-port', choices=['139', '445'], nargs='?', default='445', metavar="destination port", 316 help='Destination port to connect to SMB Server') 317 318 if len(sys.argv)==1: 319 parser.print_help() 320 sys.exit(1) 321 322 options = parser.parse_args() 323 324 if options.debug is True: 325 logging.getLogger().setLevel(logging.DEBUG) 326 else: 327 logging.getLogger().setLevel(logging.INFO) 328 329 import re 330 331 domain, username, password, remoteName = re.compile('(?:(?:([^/@:]*)/)?([^@:]*)(?::([^@]*))?@)?(.*)').match( 332 options.target).groups('') 333 334 #In case the password contains '@' 335 if '@' in remoteName: 336 password = password + '@' + remoteName.rpartition('@')[0] 337 remoteName = remoteName.rpartition('@')[2] 338 339 if domain is None: 340 domain = '' 341 342 if options.target_ip is None: 343 options.target_ip = remoteName 344 345 if options.aesKey is not None: 346 options.k = True 347 348 if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None: 349 from getpass import getpass 350 password = getpass("Password:") 351 352 services = SVCCTL(username, password, domain, options, int(options.port)) 353 try: 354 services.run(remoteName, options.target_ip) 355 except Exception, e: 356 if logging.getLogger().level == logging.DEBUG: 357 import traceback 358 traceback.print_exc() 359 logging.error(str(e)) 360