Files
Gabriel Gonzalez ae0ec300f8 Techdebt examples bootstrapping v2 (#1928)
* Standardized examples logger init method

* Fixing missing import

* Standardized examples init_ldap_session function #1

* Calling new shared function _ldap3_kerberos_login from example addcomputer

* Removed considering -debug in each example. Now it's handled in utils.

* Standardized EMPTY_LM_HASH in impacket.examples.utils

* Unify parse_identity function (phase 1)

* Standardized parse_identity

* Renamed "_ldap3_kerberos_login" to "ldap3_kerberos_login" (as it is called from outside)

* Standardize "logger" init in regsecrets.py

* Standardized LDAP login into a single function in utils.py:ldap_login
2025-03-26 22:58:39 -03:00

357 lines
16 KiB
Python
Executable File

#!/usr/bin/env python
# Impacket - Collection of Python classes for working with network protocols.
#
# Copyright Fortra, LLC and its affiliated companies
#
# All rights reserved.
#
# This software is provided under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Description:
# [MS-SCMR] services common functions for manipulating services
#
# Author:
# Alberto Solino (@agsolino)
#
# Reference for:
# DCE/RPC.
#
# TODO:
# [ ] Check errors
#
from __future__ import division
from __future__ import print_function
import sys
import argparse
import logging
import codecs
from impacket.examples import logger
from impacket.examples.utils import parse_target
from impacket import version
from impacket.dcerpc.v5 import transport, scmr
from impacket.dcerpc.v5.ndr import NULL
from impacket.crypto import encryptSecret
class SVCCTL:
def __init__(self, username, password, domain, options, port=445):
self.__username = username
self.__password = password
self.__options = options
self.__port = port
self.__action = options.action.upper()
self.__domain = domain
self.__lmhash = ''
self.__nthash = ''
self.__aesKey = options.aesKey
self.__doKerberos = options.k
self.__kdcHost = options.dc_ip
if options.hashes is not None:
self.__lmhash, self.__nthash = options.hashes.split(':')
def run(self, remoteName, remoteHost):
stringbinding = r'ncacn_np:%s[\pipe\svcctl]' % remoteName
logging.debug('StringBinding %s'%stringbinding)
rpctransport = transport.DCERPCTransportFactory(stringbinding)
rpctransport.set_dport(self.__port)
rpctransport.setRemoteHost(remoteHost)
if hasattr(rpctransport, 'set_credentials'):
# This method exists only for selected protocol sequences.
rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey)
rpctransport.set_kerberos(self.__doKerberos, self.__kdcHost)
self.doStuff(rpctransport)
def doStuff(self, rpctransport):
dce = rpctransport.get_dce_rpc()
#dce.set_credentials(self.__username, self.__password)
dce.connect()
#dce.set_max_fragment_size(1)
#dce.set_auth_level(ntlm.NTLM_AUTH_PKT_PRIVACY)
#dce.set_auth_level(ntlm.NTLM_AUTH_PKT_INTEGRITY)
dce.bind(scmr.MSRPC_UUID_SCMR)
#rpc = svcctl.DCERPCSvcCtl(dce)
rpc = dce
ans = scmr.hROpenSCManagerW(rpc)
scManagerHandle = ans['lpScHandle']
if self.__action != 'LIST' and self.__action != 'CREATE':
ans = scmr.hROpenServiceW(rpc, scManagerHandle, self.__options.name+'\x00')
serviceHandle = ans['lpServiceHandle']
if self.__action == 'START':
logging.info("Starting service %s" % self.__options.name)
scmr.hRStartServiceW(rpc, serviceHandle)
scmr.hRCloseServiceHandle(rpc, serviceHandle)
elif self.__action == 'STOP':
logging.info("Stopping service %s" % self.__options.name)
scmr.hRControlService(rpc, serviceHandle, scmr.SERVICE_CONTROL_STOP)
scmr.hRCloseServiceHandle(rpc, serviceHandle)
elif self.__action == 'DELETE':
logging.info("Deleting service %s" % self.__options.name)
scmr.hRDeleteService(rpc, serviceHandle)
scmr.hRCloseServiceHandle(rpc, serviceHandle)
elif self.__action == 'CONFIG':
logging.info("Querying service config for %s" % self.__options.name)
resp = scmr.hRQueryServiceConfigW(rpc, serviceHandle)
print("TYPE : %2d - " % resp['lpServiceConfig']['dwServiceType'], end=' ')
if resp['lpServiceConfig']['dwServiceType'] & 0x1:
print("SERVICE_KERNEL_DRIVER ", end=' ')
if resp['lpServiceConfig']['dwServiceType'] & 0x2:
print("SERVICE_FILE_SYSTEM_DRIVER ", end=' ')
if resp['lpServiceConfig']['dwServiceType'] & 0x10:
print("SERVICE_WIN32_OWN_PROCESS ", end=' ')
if resp['lpServiceConfig']['dwServiceType'] & 0x20:
print("SERVICE_WIN32_SHARE_PROCESS ", end=' ')
if resp['lpServiceConfig']['dwServiceType'] & 0x100:
print("SERVICE_INTERACTIVE_PROCESS ", end=' ')
print("")
print("START_TYPE : %2d - " % resp['lpServiceConfig']['dwStartType'], end=' ')
if resp['lpServiceConfig']['dwStartType'] == 0x0:
print("BOOT START")
elif resp['lpServiceConfig']['dwStartType'] == 0x1:
print("SYSTEM START")
elif resp['lpServiceConfig']['dwStartType'] == 0x2:
print("AUTO START")
elif resp['lpServiceConfig']['dwStartType'] == 0x3:
print("DEMAND START")
elif resp['lpServiceConfig']['dwStartType'] == 0x4:
print("DISABLED")
else:
print("UNKNOWN")
print("ERROR_CONTROL : %2d - " % resp['lpServiceConfig']['dwErrorControl'], end=' ')
if resp['lpServiceConfig']['dwErrorControl'] == 0x0:
print("IGNORE")
elif resp['lpServiceConfig']['dwErrorControl'] == 0x1:
print("NORMAL")
elif resp['lpServiceConfig']['dwErrorControl'] == 0x2:
print("SEVERE")
elif resp['lpServiceConfig']['dwErrorControl'] == 0x3:
print("CRITICAL")
else:
print("UNKNOWN")
print("BINARY_PATH_NAME : %s" % resp['lpServiceConfig']['lpBinaryPathName'][:-1])
print("LOAD_ORDER_GROUP : %s" % resp['lpServiceConfig']['lpLoadOrderGroup'][:-1])
print("TAG : %d" % resp['lpServiceConfig']['dwTagId'])
print("DISPLAY_NAME : %s" % resp['lpServiceConfig']['lpDisplayName'][:-1])
print("DEPENDENCIES : %s" % resp['lpServiceConfig']['lpDependencies'][:-1])
print("SERVICE_START_NAME: %s" % resp['lpServiceConfig']['lpServiceStartName'][:-1])
elif self.__action == 'STATUS':
print("Querying status for %s" % self.__options.name)
resp = scmr.hRQueryServiceStatus(rpc, serviceHandle)
print("%30s - " % self.__options.name, end=' ')
state = resp['lpServiceStatus']['dwCurrentState']
if state == scmr.SERVICE_CONTINUE_PENDING:
print("CONTINUE PENDING")
elif state == scmr.SERVICE_PAUSE_PENDING:
print("PAUSE PENDING")
elif state == scmr.SERVICE_PAUSED:
print("PAUSED")
elif state == scmr.SERVICE_RUNNING:
print("RUNNING")
elif state == scmr.SERVICE_START_PENDING:
print("START PENDING")
elif state == scmr.SERVICE_STOP_PENDING:
print("STOP PENDING")
elif state == scmr.SERVICE_STOPPED:
print("STOPPED")
else:
print("UNKNOWN")
elif self.__action == 'LIST':
logging.info("Listing services available on target")
#resp = rpc.EnumServicesStatusW(scManagerHandle, svcctl.SERVICE_WIN32_SHARE_PROCESS )
#resp = rpc.EnumServicesStatusW(scManagerHandle, svcctl.SERVICE_WIN32_OWN_PROCESS )
#resp = rpc.EnumServicesStatusW(scManagerHandle, serviceType = svcctl.SERVICE_FILE_SYSTEM_DRIVER, serviceState = svcctl.SERVICE_STATE_ALL )
resp = scmr.hREnumServicesStatusW(rpc, scManagerHandle)
for i in range(len(resp)):
print("%30s - %70s - " % (resp[i]['lpServiceName'][:-1], resp[i]['lpDisplayName'][:-1]), end=' ')
state = resp[i]['ServiceStatus']['dwCurrentState']
if state == scmr.SERVICE_CONTINUE_PENDING:
print("CONTINUE PENDING")
elif state == scmr.SERVICE_PAUSE_PENDING:
print("PAUSE PENDING")
elif state == scmr.SERVICE_PAUSED:
print("PAUSED")
elif state == scmr.SERVICE_RUNNING:
print("RUNNING")
elif state == scmr.SERVICE_START_PENDING:
print("START PENDING")
elif state == scmr.SERVICE_STOP_PENDING:
print("STOP PENDING")
elif state == scmr.SERVICE_STOPPED:
print("STOPPED")
else:
print("UNKNOWN")
print("Total Services: %d" % len(resp))
elif self.__action == 'CREATE':
logging.info("Creating service %s" % self.__options.name)
scmr.hRCreateServiceW(rpc, scManagerHandle, self.__options.name + '\x00', self.__options.display + '\x00',
lpBinaryPathName=self.__options.path + '\x00')
elif self.__action == 'CHANGE':
logging.info("Changing service config for %s" % self.__options.name)
if self.__options.start_type is not None:
start_type = int(self.__options.start_type)
else:
start_type = scmr.SERVICE_NO_CHANGE
if self.__options.service_type is not None:
service_type = int(self.__options.service_type)
else:
service_type = scmr.SERVICE_NO_CHANGE
if self.__options.display is not None:
display = self.__options.display + '\x00'
else:
display = NULL
if self.__options.path is not None:
path = self.__options.path + '\x00'
else:
path = NULL
if self.__options.start_name is not None:
start_name = self.__options.start_name + '\x00'
else:
start_name = NULL
if self.__options.password is not None:
s = rpctransport.get_smb_connection()
key = s.getSessionKey()
try:
password = (self.__options.password+'\x00').encode('utf-16le')
except UnicodeDecodeError:
import sys
password = (self.__options.password+'\x00').decode(sys.getfilesystemencoding()).encode('utf-16le')
password = encryptSecret(key, password)
else:
password = NULL
#resp = scmr.hRChangeServiceConfigW(rpc, serviceHandle, display, path, service_type, start_type, start_name, password)
scmr.hRChangeServiceConfigW(rpc, serviceHandle, service_type, start_type, scmr.SERVICE_ERROR_IGNORE, path,
NULL, NULL, NULL, 0, start_name, password, 0, display)
scmr.hRCloseServiceHandle(rpc, serviceHandle)
else:
logging.error("Unknown action %s" % self.__action)
scmr.hRCloseServiceHandle(rpc, scManagerHandle)
dce.disconnect()
return
# Process command-line arguments.
if __name__ == '__main__':
# Explicitly changing the stdout encoding format
if sys.stdout.encoding is None:
# Output is redirected to a file
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
print(version.BANNER)
parser = argparse.ArgumentParser(add_help = True, description = "Windows Service manipulation script.")
parser.add_argument('target', action='store', help='[[domain/]username[:password]@]<targetName or address>')
parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON')
parser.add_argument('-ts', action='store_true', help='Adds timestamp to every logging output')
subparsers = parser.add_subparsers(help='actions', dest='action')
# A start command
start_parser = subparsers.add_parser('start', help='starts the service')
start_parser.add_argument('-name', action='store', required=True, help='service name')
# A stop command
stop_parser = subparsers.add_parser('stop', help='stops the service')
stop_parser.add_argument('-name', action='store', required=True, help='service name')
# A delete command
delete_parser = subparsers.add_parser('delete', help='deletes the service')
delete_parser.add_argument('-name', action='store', required=True, help='service name')
# A status command
status_parser = subparsers.add_parser('status', help='returns service status')
status_parser.add_argument('-name', action='store', required=True, help='service name')
# A config command
config_parser = subparsers.add_parser('config', help='returns service configuration')
config_parser.add_argument('-name', action='store', required=True, help='service name')
# A list command
list_parser = subparsers.add_parser('list', help='list available services')
# A create command
create_parser = subparsers.add_parser('create', help='create a service')
create_parser.add_argument('-name', action='store', required=True, help='service name')
create_parser.add_argument('-display', action='store', required=True, help='display name')
create_parser.add_argument('-path', action='store', required=True, help='binary path')
# A change command
create_parser = subparsers.add_parser('change', help='change a service configuration')
create_parser.add_argument('-name', action='store', required=True, help='service name')
create_parser.add_argument('-display', action='store', required=False, help='display name')
create_parser.add_argument('-path', action='store', required=False, help='binary path')
create_parser.add_argument('-service_type', action='store', required=False, help='service type')
create_parser.add_argument('-start_type', action='store', required=False, help='service start type')
create_parser.add_argument('-start_name', action='store', required=False, help='string that specifies the name of '
'the account under which the service should run')
create_parser.add_argument('-password', action='store', required=False, help='string that contains the password of '
'the account whose name was specified by the start_name parameter')
group = parser.add_argument_group('authentication')
group.add_argument('-hashes', action="store", metavar = "LMHASH:NTHASH", help='NTLM hashes, format is LMHASH:NTHASH')
group.add_argument('-no-pass', action="store_true", help='don\'t ask for password (useful for -k)')
group.add_argument('-k', action="store_true", help='Use Kerberos authentication. Grabs credentials from ccache file '
'(KRB5CCNAME) based on target parameters. If valid credentials cannot be found, it will use the '
'ones specified in the command line')
group.add_argument('-aesKey', action="store", metavar = "hex key", help='AES key to use for Kerberos Authentication '
'(128 or 256 bits)')
group = parser.add_argument_group('connection')
group.add_argument('-dc-ip', action='store',metavar = "ip address", help='IP Address of the domain controller. If '
'ommited it use the domain part (FQDN) specified in the target parameter')
group.add_argument('-target-ip', action='store', metavar="ip address", help='IP Address of the target machine. If '
'ommited it will use whatever was specified as target. This is useful when target is the NetBIOS '
'name and you cannot resolve it')
group.add_argument('-port', choices=['139', '445'], nargs='?', default='445', metavar="destination port",
help='Destination port to connect to SMB Server')
if len(sys.argv)==1:
parser.print_help()
sys.exit(1)
options = parser.parse_args()
# Init the example's logger theme
logger.init(options.ts, options.debug)
domain, username, password, remoteName = parse_target(options.target)
if domain is None:
domain = ''
if options.target_ip is None:
options.target_ip = remoteName
if options.aesKey is not None:
options.k = True
if password == '' and username != '' and options.hashes is None and options.no_pass is False and options.aesKey is None:
from getpass import getpass
password = getpass("Password:")
services = SVCCTL(username, password, domain, options, int(options.port))
try:
services.run(remoteName, options.target_ip)
except Exception as e:
if logging.getLogger().level == logging.DEBUG:
import traceback
traceback.print_exc()
logging.error(str(e))