2019-11-11 10:06:39 +00:00
#!/usr/bin/env python3
2022-07-18 23:59:14 +00:00
# -*- coding: utf-8 -*-
2016-06-04 05:42:26 +00:00
from cme . logger import setup_logger , setup_debug_logger , CMEAdapter
2016-12-20 07:23:40 +00:00
from cme . helpers . logger import highlight
2017-10-25 02:08:19 +00:00
from cme . helpers . misc import identify_target_file
2017-10-21 23:24:09 +00:00
from cme . parsers . ip import parse_targets
2017-10-25 02:08:19 +00:00
from cme . parsers . nmap import parse_nmap_xml
from cme . parsers . nessus import parse_nessus_file
2016-12-15 07:28:00 +00:00
from cme . cli import gen_cli_args
from cme . loaders . protocol_loader import protocol_loader
from cme . loaders . module_loader import module_loader
from cme . servers . http import CMEServer
2016-06-04 05:42:26 +00:00
from cme . first_run import first_run_setup
2016-12-15 07:28:00 +00:00
from cme . context import Context
2020-11-15 23:42:28 +00:00
from concurrent . futures import ThreadPoolExecutor
2016-09-12 06:52:50 +00:00
from pprint import pformat
2020-11-15 23:42:28 +00:00
from decimal import Decimal
import time
import asyncio
import aioconsole
import functools
2019-11-10 21:42:04 +00:00
import configparser
2017-06-26 09:49:04 +00:00
import cme . helpers . powershell as powershell
2016-12-15 07:28:00 +00:00
import cme
2017-06-26 09:49:04 +00:00
import shutil
2016-12-15 07:28:00 +00:00
import webbrowser
2016-06-04 05:42:26 +00:00
import sqlite3
2016-12-15 07:28:00 +00:00
import random
2016-06-04 05:42:26 +00:00
import os
import sys
2016-08-12 06:36:38 +00:00
import logging
2016-06-04 05:42:26 +00:00
2020-11-15 23:42:28 +00:00
setup_logger ( )
logger = CMEAdapter ( )
2017-10-25 02:08:19 +00:00
2023-01-04 16:48:52 +00:00
try :
import librlers
except :
print ( " Incompatible python version, try with another python version or another binary 3.8 / 3.9 / 3.10 / 3.11 that match your python version (python -V) " )
sys . exit ( )
2020-11-15 23:42:28 +00:00
async def monitor_threadpool ( pool , targets ) :
logging . debug ( ' Started thread poller ' )
while True :
try :
text = await aioconsole . ainput ( " " )
if text == " " :
pool_size = pool . _work_queue . qsize ( )
finished_threads = len ( targets ) - pool_size
percentage = Decimal ( finished_threads ) / Decimal ( len ( targets ) ) * Decimal ( 100 )
logger . info ( f " completed: { percentage : .2f } % ( { finished_threads } / { len ( targets ) } ) " )
except asyncio . CancelledError :
logging . debug ( " Stopped thread poller " )
break
async def run_protocol ( loop , protocol_obj , args , db , target , jitter ) :
try :
if jitter :
value = random . choice ( range ( jitter [ 0 ] , jitter [ 1 ] ) )
logging . debug ( f " Doin ' the jitterbug for { value } second(s) " )
await asyncio . sleep ( value )
thread = loop . run_in_executor (
None ,
functools . partial (
protocol_obj ,
args ,
db ,
str ( target )
)
)
await asyncio . wait_for (
thread ,
timeout = args . timeout
)
except asyncio . TimeoutError :
logging . debug ( " Thread exceeded timeout " )
except asyncio . CancelledError :
logging . debug ( " Stopping thread " )
thread . cancel ( )
2022-04-03 11:38:48 +00:00
except sqlite3 . OperationalError as e :
logging . debug ( " Sqlite error - sqlite3.operationalError - {} " . format ( str ( e ) ) )
2020-11-15 23:42:28 +00:00
async def start_threadpool ( protocol_obj , args , db , targets , jitter ) :
pool = ThreadPoolExecutor ( max_workers = args . threads + 1 )
loop = asyncio . get_running_loop ( )
loop . set_default_executor ( pool )
monitor_task = asyncio . create_task (
monitor_threadpool ( pool , targets )
)
jobs = [
run_protocol (
loop ,
protocol_obj ,
args ,
db ,
target ,
jitter
)
for target in targets
]
2016-06-04 05:42:26 +00:00
2020-11-15 23:42:28 +00:00
try :
logging . debug ( " Running " )
await asyncio . gather ( * jobs )
except asyncio . CancelledError :
print ( ' \n ' )
logger . info ( " Shutting down, please wait... " )
logging . debug ( " Cancelling scan " )
finally :
monitor_task . cancel ( )
pool . shutdown ( wait = True )
def main ( ) :
2016-06-04 05:42:26 +00:00
first_run_setup ( logger )
2016-12-15 07:28:00 +00:00
args = gen_cli_args ( )
if args . darrell :
2017-03-27 21:09:36 +00:00
links = open ( os . path . join ( os . path . dirname ( cme . __file__ ) , ' data ' , ' videos_for_darrell.harambe ' ) ) . read ( ) . splitlines ( )
2016-12-15 07:28:00 +00:00
try :
webbrowser . open ( random . choice ( links ) )
except :
2017-03-27 21:09:36 +00:00
sys . exit ( 1 )
2016-12-15 07:28:00 +00:00
2016-06-04 05:42:26 +00:00
cme_path = os . path . expanduser ( ' ~/.cme ' )
2019-11-10 21:42:04 +00:00
config = configparser . ConfigParser ( )
2016-12-15 07:28:00 +00:00
config . read ( os . path . join ( cme_path , ' cme.conf ' ) )
2016-06-04 05:42:26 +00:00
2018-03-01 19:36:17 +00:00
module = None
2017-03-27 21:09:36 +00:00
module_server = None
targets = [ ]
jitter = None
2016-12-15 07:28:00 +00:00
server_port_dict = { ' http ' : 80 , ' https ' : 443 , ' smb ' : 445 }
current_workspace = config . get ( ' CME ' , ' workspace ' )
2016-06-04 05:42:26 +00:00
if args . verbose :
setup_debug_logger ( )
2016-09-12 06:52:50 +00:00
logging . debug ( ' Passed args: \n ' + pformat ( vars ( args ) ) )
2016-08-12 06:36:38 +00:00
2017-03-27 21:09:36 +00:00
if args . jitter :
if ' - ' in args . jitter :
start , end = args . jitter . split ( ' - ' )
jitter = ( int ( start ) , int ( end ) )
else :
jitter = ( 0 , int ( args . jitter ) )
2016-12-15 07:28:00 +00:00
if hasattr ( args , ' cred_id ' ) and args . cred_id :
2016-09-12 06:52:50 +00:00
for cred_id in args . cred_id :
if ' - ' in str ( cred_id ) :
start_id , end_id = cred_id . split ( ' - ' )
2016-09-21 19:40:59 +00:00
try :
for n in range ( int ( start_id ) , int ( end_id ) + 1 ) :
args . cred_id . append ( n )
args . cred_id . remove ( cred_id )
except Exception as e :
logger . error ( ' Error parsing database credential id: {} ' . format ( e ) )
sys . exit ( 1 )
2016-09-12 06:52:50 +00:00
2016-12-15 07:28:00 +00:00
if hasattr ( args , ' target ' ) and args . target :
for target in args . target :
if os . path . exists ( target ) :
2017-10-25 02:08:19 +00:00
target_file_type = identify_target_file ( target )
if target_file_type == ' nmap ' :
targets . extend ( parse_nmap_xml ( target , args . protocol ) )
elif target_file_type == ' nessus ' :
targets . extend ( parse_nessus_file ( target , args . protocol ) )
else :
with open ( target , ' r ' ) as target_file :
for target_entry in target_file :
2020-04-28 12:42:25 +00:00
targets . extend ( parse_targets ( target_entry . strip ( ) ) )
2016-12-15 07:28:00 +00:00
else :
targets . extend ( parse_targets ( target ) )
2017-06-26 09:49:04 +00:00
# The following is a quick hack for the powershell obfuscation functionality, I know this is yucky
if hasattr ( args , ' clear_obfscripts ' ) and args . clear_obfscripts :
shutil . rmtree ( os . path . expanduser ( ' ~/.cme/obfuscated_scripts/ ' ) )
os . mkdir ( os . path . expanduser ( ' ~/.cme/obfuscated_scripts/ ' ) )
logger . success ( ' Cleared cached obfuscated PowerShell scripts ' )
if hasattr ( args , ' obfs ' ) and args . obfs :
powershell . obfuscate_ps_scripts = True
2016-12-15 07:28:00 +00:00
p_loader = protocol_loader ( )
protocol_path = p_loader . get_protocols ( ) [ args . protocol ] [ ' path ' ]
protocol_db_path = p_loader . get_protocols ( ) [ args . protocol ] [ ' dbpath ' ]
protocol_object = getattr ( p_loader . load_protocol ( protocol_path ) , args . protocol )
protocol_db_object = getattr ( p_loader . load_protocol ( protocol_db_path ) , ' database ' )
db_path = os . path . join ( cme_path , ' workspaces ' , current_workspace , args . protocol + ' .db ' )
# set the database connection to autocommit w/ isolation level
db_connection = sqlite3 . connect ( db_path , check_same_thread = False )
db_connection . text_factory = str
db_connection . isolation_level = None
db = protocol_db_object ( db_connection )
2016-06-05 20:43:51 +00:00
2018-02-23 10:13:46 +00:00
setattr ( protocol_object , ' config ' , config )
2017-03-27 21:09:36 +00:00
if hasattr ( args , ' module ' ) :
2016-06-04 05:42:26 +00:00
2016-12-15 07:28:00 +00:00
loader = module_loader ( args , db , logger )
2016-09-12 06:52:50 +00:00
2016-12-15 07:28:00 +00:00
if args . list_modules :
modules = loader . get_modules ( )
2016-09-21 19:40:59 +00:00
2017-05-08 05:19:04 +00:00
for name , props in sorted ( modules . items ( ) ) :
logger . info ( ' {:<25} {} ' . format ( name , props [ ' description ' ] ) )
2017-03-27 21:09:36 +00:00
sys . exit ( 0 )
2016-09-21 19:40:59 +00:00
2016-12-20 07:23:40 +00:00
elif args . module and args . show_module_options :
2016-09-21 19:40:59 +00:00
2016-12-15 07:28:00 +00:00
modules = loader . get_modules ( )
2017-05-08 05:19:04 +00:00
for name , props in modules . items ( ) :
if args . module . lower ( ) == name . lower ( ) :
logger . info ( ' {} module options: \n {} ' . format ( name , props [ ' options ' ] ) )
2017-03-27 21:09:36 +00:00
sys . exit ( 0 )
2016-09-12 06:52:50 +00:00
2016-12-15 07:28:00 +00:00
elif args . module :
modules = loader . get_modules ( )
2017-05-08 05:19:04 +00:00
for name , props in modules . items ( ) :
if args . module . lower ( ) == name . lower ( ) :
module = loader . init_module ( props [ ' path ' ] )
2016-12-15 07:28:00 +00:00
setattr ( protocol_object , ' module ' , module )
break
2017-04-07 04:34:30 +00:00
if not module :
logger . error ( ' Module not found ' )
exit ( 1 )
2016-12-20 07:23:40 +00:00
if getattr ( module , ' opsec_safe ' ) is False :
2020-01-18 12:20:10 +00:00
ans = input ( highlight ( ' [!] Module is not opsec safe, are you sure you want to run this? [Y/n] ' , ' red ' ) )
2017-03-27 21:09:36 +00:00
if ans . lower ( ) not in [ ' y ' , ' yes ' , ' ' ] :
sys . exit ( 1 )
2016-12-20 07:23:40 +00:00
if getattr ( module , ' multiple_hosts ' ) is False and len ( targets ) > 1 :
2020-01-18 12:20:10 +00:00
ans = input ( highlight ( " [!] Running this module on multiple hosts doesn ' t really make any sense, are you sure you want to continue? [Y/n] " , ' red ' ) )
2017-03-27 21:09:36 +00:00
if ans . lower ( ) not in [ ' y ' , ' yes ' , ' ' ] :
sys . exit ( 1 )
2016-12-20 07:23:40 +00:00
2016-12-15 07:28:00 +00:00
if hasattr ( module , ' on_request ' ) or hasattr ( module , ' has_response ' ) :
if hasattr ( module , ' required_server ' ) :
args . server = getattr ( module , ' required_server ' )
if not args . server_port :
args . server_port = server_port_dict [ args . server ]
context = Context ( db , logger , args )
2017-03-27 21:09:36 +00:00
module_server = CMEServer ( module , context , logger , args . server_host , args . server_port , args . server )
module_server . start ( )
setattr ( protocol_object , ' server ' , module_server . server )
2016-09-21 19:40:59 +00:00
2016-06-04 05:42:26 +00:00
try :
2020-11-15 23:42:28 +00:00
asyncio . run (
start_threadpool ( protocol_object , args , db , targets , jitter )
)
2016-06-04 05:42:26 +00:00
except KeyboardInterrupt :
2020-11-15 23:42:28 +00:00
logging . debug ( " Got keyboard interrupt " )
finally :
if module_server :
module_server . shutdown ( )
2020-05-10 18:06:08 +00:00
if __name__ == ' __main__ ' :
2022-04-03 11:38:48 +00:00
main ( )