From 6966e22513c648ac20da4cc30b9d8e9f630d04e8 Mon Sep 17 00:00:00 2001 From: asolino Date: Fri, 30 Nov 2018 14:52:31 -0300 Subject: [PATCH] [MS-RPRN] Interface implementation (Initial) - Just a few methods for now. Needed for @leechristensen RPRN great discovery --- impacket/dcerpc/v5/rprn.py | 341 +++++++++++++++++++++++++++++++++++++ tests/SMB_RPC/rundce.sh | 1 + tests/SMB_RPC/test_rprn.py | 139 +++++++++++++++ 3 files changed, 481 insertions(+) create mode 100644 impacket/dcerpc/v5/rprn.py create mode 100644 tests/SMB_RPC/test_rprn.py diff --git a/impacket/dcerpc/v5/rprn.py b/impacket/dcerpc/v5/rprn.py new file mode 100644 index 000000000..ed36e95bd --- /dev/null +++ b/impacket/dcerpc/v5/rprn.py @@ -0,0 +1,341 @@ +# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved. +# +# This software is provided under under a slightly modified version +# of the Apache Software License. See the accompanying LICENSE file +# for more information. +# +# Author: Alberto Solino (@agsolino) +# +# Description: +# [MS-RPRN] Interface implementation +# +# Best way to learn how to use these calls is to grab the protocol standard +# so you understand what the call does, and then read the test case located +# at https://github.com/SecureAuthCorp/impacket/tree/master/tests/SMB_RPC +# +# Some calls have helper functions, which makes it even easier to use. +# They are located at the end of this file. +# Helper functions start with "h". +# There are test cases for them too. +# + + +from struct import unpack, pack + +from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDRUNION, NDRPOINTER, NDRUniConformantVaryingArray, NDRUniConformantArray +from impacket.dcerpc.v5.dtypes import ULONGLONG, UINT, USHORT, LPWSTR, DWORD, UUID, ULONG, LPULONG, BOOLEAN, SECURITY_INFORMATION, PFILETIME, \ + RPC_UNICODE_STRING, FILETIME, NULL, MAXIMUM_ALLOWED, OWNER_SECURITY_INFORMATION, PWCHAR, PRPC_UNICODE_STRING +from impacket.dcerpc.v5.rpcrt import DCERPCException +from impacket import system_errors, LOG +from impacket.uuid import uuidtup_to_bin + +MSRPC_UUID_RPRN = uuidtup_to_bin(('12345678-1234-ABCD-EF00-0123456789AB', '1.0')) + +class DCERPCSessionError(DCERPCException): + def __init__(self, error_string=None, error_code=None, packet=None): + DCERPCException.__init__(self, error_string, error_code, packet) + + def __str__( self ): + key = self.error_code + if key in system_errors.ERROR_MESSAGES: + error_msg_short = system_errors.ERROR_MESSAGES[key][0] + error_msg_verbose = system_errors.ERROR_MESSAGES[key][1] + return 'RPRN SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose) + else: + return 'RPRN SessionError: unknown error code: 0x%x' % self.error_code + +################################################################################ +# CONSTANTS +################################################################################ +# 2.2.1.1.7 STRING_HANDLE +STRING_HANDLE = LPWSTR +class PSTRING_HANDLE(NDRPOINTER): + referent = ( + ('Data', STRING_HANDLE), + ) + +# 2.2.3.1 Access Values +JOB_ACCESS_ADMINISTER = 0x00000010 +JOB_ACCESS_READ = 0x00000020 +JOB_EXECUTE = 0x00020010 +JOB_READ = 0x00020020 +JOB_WRITE = 0x00020010 +JOB_ALL_ACCESS = 0x000F0030 +PRINTER_ACCESS_ADMINISTER = 0x00000004 +PRINTER_ACCESS_USE = 0x00000008 +PRINTER_ACCESS_MANAGE_LIMITED = 0x00000040 +PRINTER_ALL_ACCESS = 0x000F000C +PRINTER_EXECUTE = 0x00020008 +PRINTER_READ = 0x00020008 +PRINTER_WRITE = 0x00020008 +SERVER_ACCESS_ADMINISTER = 0x00000001 +SERVER_ACCESS_ENUMERATE = 0x00000002 +SERVER_ALL_ACCESS = 0x000F0003 +SERVER_EXECUTE = 0x00020002 +SERVER_READ = 0x00020002 +SERVER_WRITE = 0x00020003 +SPECIFIC_RIGHTS_ALL = 0x0000FFFF +STANDARD_RIGHTS_ALL = 0x001F0000 +STANDARD_RIGHTS_EXECUTE = 0x00020000 +STANDARD_RIGHTS_READ = 0x00020000 +STANDARD_RIGHTS_REQUIRED = 0x000F0000 +STANDARD_RIGHTS_WRITE = 0x00020000 +SYNCHRONIZE = 0x00100000 +DELETE = 0x00010000 +READ_CONTROL = 0x00020000 +WRITE_DAC = 0x00040000 +WRITE_OWNER = 0x00080000 +GENERIC_READ = 0x80000000 +GENERIC_WRITE = 0x40000000 +GENERIC_EXECUTE = 0x20000000 +GENERIC_ALL = 0x10000000 + +# 2.2.3.6.1 Printer Change Flags for Use with a Printer Handle +PRINTER_CHANGE_SET_PRINTER = 0x00000002 +PRINTER_CHANGE_DELETE_PRINTER = 0x00000004 +PRINTER_CHANGE_PRINTER = 0x000000FF +PRINTER_CHANGE_ADD_JOB = 0x00000100 +PRINTER_CHANGE_SET_JOB = 0x00000200 +PRINTER_CHANGE_DELETE_JOB = 0x00000400 +PRINTER_CHANGE_WRITE_JOB = 0x00000800 +PRINTER_CHANGE_JOB = 0x0000FF00 +PRINTER_CHANGE_SET_PRINTER_DRIVER = 0x20000000 +PRINTER_CHANGE_TIMEOUT = 0x80000000 +PRINTER_CHANGE_ALL = 0x7777FFFF +PRINTER_CHANGE_ALL_2 = 0x7F77FFFF + +# 2.2.3.6.2 Printer Change Flags for Use with a Server Handle +PRINTER_CHANGE_ADD_PRINTER_DRIVER = 0x10000000 +PRINTER_CHANGE_DELETE_PRINTER_DRIVER = 0x40000000 +PRINTER_CHANGE_PRINTER_DRIVER = 0x70000000 +PRINTER_CHANGE_ADD_FORM = 0x00010000 +PRINTER_CHANGE_DELETE_FORM = 0x00040000 +PRINTER_CHANGE_SET_FORM = 0x00020000 +PRINTER_CHANGE_FORM = 0x00070000 +PRINTER_CHANGE_ADD_PORT = 0x00100000 +PRINTER_CHANGE_CONFIGURE_PORT = 0x00200000 +PRINTER_CHANGE_DELETE_PORT = 0x00400000 +PRINTER_CHANGE_PORT = 0x00700000 +PRINTER_CHANGE_ADD_PRINT_PROCESSOR = 0x01000000 +PRINTER_CHANGE_DELETE_PRINT_PROCESSOR = 0x04000000 +PRINTER_CHANGE_PRINT_PROCESSOR = 0x07000000 +PRINTER_CHANGE_ADD_PRINTER = 0x00000001 +PRINTER_CHANGE_FAILED_CONNECTION_PRINTER = 0x00000008 +PRINTER_CHANGE_SERVER = 0x08000000 + +# 2.2.3.8 Printer Notification Values +PRINTER_NOTIFY_CATEGORY_2D = 0x00000000 +PRINTER_NOTIFY_CATEGORY_ALL = 0x00010000 +PRINTER_NOTIFY_CATEGORY_3D = 0x00020000 + + +################################################################################ +# STRUCTURES +################################################################################ +# 2.2.1.1.4 PRINTER_HANDLE +class PRINTER_HANDLE(NDRSTRUCT): + structure = ( + ('Data','20s=b""'), + ) + def getAlignment(self): + if self._isNDR64 is True: + return 8 + else: + return 4 + +# 2.2.1.2.1 DEVMODE_CONTAINER +class BYTE_ARRAY(NDRUniConformantArray): + item = 'c' + +class PBYTE_ARRAY(NDRPOINTER): + referent = ( + ('Data', BYTE_ARRAY), + ) + +class DEVMODE_CONTAINER(NDRSTRUCT): + structure = ( + ('cbBuf',DWORD), + ('pDevMode',PBYTE_ARRAY), + ) + +# 2.2.1.11.1 SPLCLIENT_INFO_1 +class SPLCLIENT_INFO_1(NDRSTRUCT): + structure = ( + ('dwSize',DWORD), + ('pMachineName',LPWSTR), + ('pUserName',LPWSTR), + ('dwBuildNum',DWORD), + ('dwMajorVersion',DWORD), + ('dwMinorVersion',DWORD), + ('wProcessorArchitecture',USHORT), + ) + +class PSPLCLIENT_INFO_1(NDRPOINTER): + referent = ( + ('Data', SPLCLIENT_INFO_1), + ) + +# 2.2.1.11.2 SPLCLIENT_INFO_2 +class SPLCLIENT_INFO_2(NDRSTRUCT): + structure = ( + ('notUsed',ULONGLONG), + ) + +class PSPLCLIENT_INFO_2(NDRPOINTER): + referent = ( + ('Data', SPLCLIENT_INFO_2), + ) +# 2.2.1.11.3 SPLCLIENT_INFO_3 +class SPLCLIENT_INFO_3(NDRSTRUCT): + structure = ( + ('cbSize',UINT), + ('dwFlags',DWORD), + ('dwFlags',DWORD), + ('pMachineName',LPWSTR), + ('pUserName',LPWSTR), + ('dwBuildNum',DWORD), + ('dwMajorVersion',DWORD), + ('dwMinorVersion',DWORD), + ('wProcessorArchitecture',USHORT), + ('hSplPrinter',ULONGLONG), + ) + +class PSPLCLIENT_INFO_3(NDRPOINTER): + referent = ( + ('Data', SPLCLIENT_INFO_3), + ) +# 2.2.1.2.14 SPLCLIENT_CONTAINER +class CLIENT_INFO_UNION(NDRUNION): + commonHdr = ( + ('tag', ULONG), + ) + union = { + 1 : ('pClientInfo1', PSPLCLIENT_INFO_1), + 2 : ('pNotUsed1', PSPLCLIENT_INFO_2), + 3 : ('pNotUsed2', PSPLCLIENT_INFO_3), + } + +class SPLCLIENT_CONTAINER(NDRSTRUCT): + structure = ( + ('Level',DWORD), + ('ClientInfo',CLIENT_INFO_UNION), + ) + + +# 2.2.1.13.2 RPC_V2_NOTIFY_OPTIONS_TYPE +class USHORT_ARRAY(NDRUniConformantArray): + item = ' 0: + lmhash, nthash = self.hashes.split(':') + else: + lmhash = '' + nthash = '' + if hasattr(rpctransport, 'set_credentials'): + # This method exists only for selected protocol sequences. + rpctransport.set_credentials(self.username,self.password, self.domain, lmhash, nthash) + dce = rpctransport.get_dce_rpc() + #dce.set_auth_level(RPC_C_AUTHN_LEVEL_PKT_INTEGRITY) + dce.connect() + dce.bind(rprn.MSRPC_UUID_RPRN, transfer_syntax = self.ts) + #resp = rrp.hOpenLocalMachine(dce, MAXIMUM_ALLOWED | rrp.KEY_WOW64_32KEY | rrp.KEY_ENUMERATE_SUB_KEYS) + + return dce, rpctransport#, resp['phKey'] + + def test_RpcOpenPrinter(self): + dce, rpctransport = self.connect() + request = rprn.RpcOpenPrinter() + request['pPrinterName'] = '\\\\%s\x00' % self.machine + request['pDatatype'] = NULL + request['pDevModeContainer']['pDevMode'] = NULL + request['AccessRequired'] = rprn.SERVER_READ + request.dump() + resp = dce.request(request) + resp.dump() + + def test_RpcOpenPrinterEx(self): + dce, rpctransport = self.connect() + request = rprn.RpcOpenPrinterEx() + request['pPrinterName'] = '\\\\%s\x00' % self.machine + request['pDatatype'] = NULL + request['AccessRequired'] = rprn.SERVER_READ + request['pDevModeContainer']['pDevMode'] = NULL + request['pClientInfo']['Level'] = 1 + request['pClientInfo']['ClientInfo']['tag'] = 1 + request['pClientInfo']['ClientInfo']['pClientInfo1']['dwSize'] = 28 + request['pClientInfo']['ClientInfo']['pClientInfo1']['pMachineName'] = '%s\x00' % self.machine + request['pClientInfo']['ClientInfo']['pClientInfo1']['pUserName'] = '%s\\%s\x00' % (self.domain, self.username) + request['pClientInfo']['ClientInfo']['pClientInfo1']['dwBuildNum'] = 0x0 + request['pClientInfo']['ClientInfo']['pClientInfo1']['dwMajorVersion'] = 0x00000000 + request['pClientInfo']['ClientInfo']['pClientInfo1']['dwMinorVersion'] = 0x00000000 + request['pClientInfo']['ClientInfo']['pClientInfo1']['wProcessorArchitecture'] = 0x0009 + request.dump() + resp = dce.request(request) + resp.dump() + + def test_RpcRemoteFindFirstPrinterChangeNotificationEx(self): + dce, rpctransport = self.connect() + + request = rprn.RpcOpenPrinter() + request['pPrinterName'] = '\\\\%s\x00' % self.machine + request['pDatatype'] = NULL + request['pDevModeContainer']['pDevMode'] = NULL + request['AccessRequired'] = rprn.SERVER_READ | rprn.SERVER_ALL_ACCESS | rprn.SERVER_ACCESS_ADMINISTER + request.dump() + resp = dce.request(request) + resp.dump() + + request = rprn.RpcRemoteFindFirstPrinterChangeNotificationEx() + request['hPrinter'] = resp['pHandle'] + request['fdwFlags'] = rprn.PRINTER_CHANGE_ADD_JOB + request['pszLocalMachine'] = '\\\\%s\x00' % self.machine + request['pOptions'] = NULL + request.dump() + try: + resp = dce.request(request) + resp.dump() + except Exception as e: + if str(e).find('ERROR_INVALID_HANDLE') < 0: + raise + + + +class SMBTransport(RPRNTests): + def setUp(self): + RPRNTests.setUp(self) + configFile = configparser.ConfigParser() + configFile.read('dcetests.cfg') + self.username = configFile.get('SMBTransport', 'username') + self.domain = configFile.get('SMBTransport', 'domain') + self.serverName = configFile.get('SMBTransport', 'servername') + self.password = configFile.get('SMBTransport', 'password') + self.machine = configFile.get('SMBTransport', 'machine') + self.hashes = configFile.get('SMBTransport', 'hashes') + self.stringBinding = r'ncacn_np:%s[\PIPE\spoolss]' % self.machine + self.ts = ('8a885d04-1ceb-11c9-9fe8-08002b104860', '2.0') + self.rrpStarted = False + +class SMBTransport64(RPRNTests): + def setUp(self): + RPRNTests.setUp(self) + configFile = configparser.ConfigParser() + configFile.read('dcetests.cfg') + self.username = configFile.get('SMBTransport', 'username') + self.domain = configFile.get('SMBTransport', 'domain') + self.serverName = configFile.get('SMBTransport', 'servername') + self.password = configFile.get('SMBTransport', 'password') + self.machine = configFile.get('SMBTransport', 'machine') + self.hashes = configFile.get('SMBTransport', 'hashes') + self.stringBinding = r'ncacn_np:%s[\PIPE\spoolss]' % self.machine + self.ts = ('71710533-BEBA-4937-8319-B5DBEF9CCC36', '1.0') + +# Process command-line arguments. +if __name__ == '__main__': + import sys + if len(sys.argv) > 1: + testcase = sys.argv[1] + suite = unittest.TestLoader().loadTestsFromTestCase(globals()[testcase]) + else: + suite = unittest.TestLoader().loadTestsFromTestCase(SMBTransport) + suite.addTests(unittest.TestLoader().loadTestsFromTestCase(SMBTransport64)) + unittest.TextTestRunner(verbosity=1).run(suite)