From d09e68fd6de7a2eacb22ff5f04a8ecb45ce3da29 Mon Sep 17 00:00:00 2001 From: guervild <11190755+guervild@users.noreply.github.com> Date: Wed, 29 Jun 2022 13:44:41 +0200 Subject: [PATCH] Add upload/download function to mssql --- cme/protocols/mssql.py | 30 +++++++++++++++++++++++ cme/protocols/mssql/mssqlexec.py | 42 +++++++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/cme/protocols/mssql.py b/cme/protocols/mssql.py index 572ba788..c5b626a8 100755 --- a/cme/protocols/mssql.py +++ b/cme/protocols/mssql.py @@ -47,6 +47,10 @@ class mssql(connection): psgroup.add_argument('--obfs', action='store_true', help='Obfuscate PowerShell scripts') psgroup.add_argument('--clear-obfscripts', action='store_true', help='Clear all cached obfuscated PowerShell scripts') + tgroup = mssql_parser.add_argument_group("Files", "Options for put and get remote files") + tgroup.add_argument("--put-file", nargs=2, metavar="FILE", help='Put a local file into remote target, ex: whoami.txt C:\\Windows\\Temp\\whoami.txt') + tgroup.add_argument("--get-file", nargs=2, metavar="FILE", help='Get a remote file, ex: C:\\Windows\\Temp\\whoami.txt whoami.txt') + return parser def proto_flow(self): @@ -279,6 +283,32 @@ class mssql(connection): ps_command = create_ps_command(payload, force_ps32=force_ps32, dont_obfs=dont_obfs) return self.execute(ps_command, get_output) + @requires_admin + def put_file(self): + self.logger.info('Copy {} to {}'.format(self.args.put_file[0], self.args.put_file[1])) + with open(self.args.put_file[0], 'rb') as f: + try: + data = f.read() + self.logger.info('Size is {} bytes'.format(len(data))) + exec_method = MSSQLEXEC(self.conn) + exec_method.put_file(data, self.args.put_file[1]) + if exec_method.file_exists(self.args.put_file[1]): + self.logger.success('File has been uploaded on the remote machine') + else: + self.logger.error('File does not exist on the remote system.. erorr during upload') + except Exception as e: + self.logger.error('Error during upload : {}'.format(e)) + + @requires_admin + def get_file(self): + self.logger.info('Copy {} to {}'.format(self.args.get_file[0], self.args.get_file[1])) + try: + exec_method = MSSQLEXEC(self.conn) + exec_method.get_file(self.args.get_file[0], self.args.get_file[1]) + self.logger.success('File {} was transferred to {}'.format(self.args.get_file[0], self.args.get_file[1])) + except Exception as e: + self.logger.error('Error reading file {}: {}'.format(self.args.get_file[0], e)) + # We hook these functions in the tds library to use CME's logger instead of printing the output to stdout # The whole tds library in impacket needs a good overhaul to preserve my sanity diff --git a/cme/protocols/mssql/mssqlexec.py b/cme/protocols/mssql/mssqlexec.py index bdadad14..0e8e26b9 100755 --- a/cme/protocols/mssql/mssqlexec.py +++ b/cme/protocols/mssql/mssqlexec.py @@ -1,5 +1,5 @@ import logging - +import binascii class MSSQLEXEC: @@ -31,3 +31,43 @@ class MSSQLEXEC: def disable_xp_cmdshell(self): self.mssql_conn.sql_query("exec sp_configure 'xp_cmdshell', 0 ;RECONFIGURE;exec sp_configure 'show advanced options', 0 ;RECONFIGURE;") + + def enable_ole(self): + self.mssql_conn.sql_query("exec master.dbo.sp_configure 'show advanced options',1;RECONFIGURE;exec master.dbo.sp_configure 'Ole Automation Procedures', 1;RECONFIGURE;") + + def disable_ole(self): + self.mssql_conn.sql_query("exec master.dbo.sp_configure 'show advanced options',1;RECONFIGURE;exec master.dbo.sp_configure 'Ole Automation Procedures', 0;RECONFIGURE;") + + def put_file(self, data, remote): + try: + self.enable_ole() + hexdata = data.hex() + self.mssql_conn.sql_query("DECLARE @ob INT;" + "EXEC sp_OACreate 'ADODB.Stream', @ob OUTPUT;" + "EXEC sp_OASetProperty @ob, 'Type', 1;" + "EXEC sp_OAMethod @ob, 'Open';" + "EXEC sp_OAMethod @ob, 'Write', NULL, 0x{};" + "EXEC sp_OAMethod @ob, 'SaveToFile', NULL, '{}', 2;" + "EXEC sp_OAMethod @ob, 'Close';" + "EXEC sp_OADestroy @ob;".format(hexdata, remote)) + self.disable_ole() + except Exception as e: + logging.debug('Error uploading via mssqlexec: {}'.format(e)) + + def file_exists(self, remote): + try: + res = self.mssql_conn.batch("DECLARE @r INT; EXEC master.dbo.xp_fileexist '{}', @r OUTPUT; SELECT @r as n".format(remote))[0]['n'] + return res == 1 + except: + return False + + def get_file(self, remote, local): + try: + self.mssql_conn.sql_query("SELECT * FROM OPENROWSET(BULK N'{}', SINGLE_BLOB) rs".format(remote)) + data = self.mssql_conn.rows[0]['BulkColumn'] + + with open(local, 'wb+') as f: + f.write(binascii.unhexlify(data)) + + except Exception as e: + logging.debug('Error downloading via mssqlexec: {}'.format(e)) \ No newline at end of file