Re-wrote the HTTP protocol to use splinter and phantomjs
- All http connections are now concurrent - Added a flag to take screenshots of webpages - Minor Code cleanupmain
parent
3e27f30cb1
commit
f0752f61b7
|
@ -1,3 +1,6 @@
|
|||
from gevent import monkey
|
||||
monkey.patch_all()
|
||||
|
||||
import sys
|
||||
import os
|
||||
import cme
|
||||
|
|
|
@ -54,9 +54,9 @@ def gen_cli_args():
|
|||
|
||||
module_parser = argparse.ArgumentParser(add_help=False)
|
||||
mgroup = module_parser.add_mutually_exclusive_group()
|
||||
mgroup.add_argument("-M", "--module", metavar='MODULE', help='payload module to use')
|
||||
mgroup.add_argument("-M", "--module", metavar='MODULE', help='module to use')
|
||||
#mgroup.add_argument('-MC','--module-chain', metavar='CHAIN_COMMAND', help='Payload module chain command string to run')
|
||||
module_parser.add_argument('-o', metavar='MODULE_OPTION', nargs='+', default=[], dest='module_options', help='payload module options')
|
||||
module_parser.add_argument('-o', metavar='MODULE_OPTION', nargs='+', default=[], dest='module_options', help='module options')
|
||||
module_parser.add_argument('-L', '--list-modules', action='store_true', help='list available modules')
|
||||
module_parser.add_argument('--options', dest='show_module_options', action='store_true', help='display module options')
|
||||
module_parser.add_argument("--server", choices={'http', 'https'}, default='https', help='use the selected server (default: https)')
|
||||
|
|
|
@ -21,9 +21,9 @@ class connection(object):
|
|||
def __init__(self, args, db, host):
|
||||
self.args = args
|
||||
self.db = db
|
||||
self.hostname = host
|
||||
self.conn = None
|
||||
self.admin_privs = False
|
||||
self.hostname = None
|
||||
self.logger = None
|
||||
self.password = None
|
||||
self.username = None
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
#!/usr/bin/env python2
|
||||
|
||||
#This must be one of the first imports or else we get threading error on completion
|
||||
from gevent import monkey
|
||||
monkey.patch_all()
|
||||
|
||||
from gevent.pool import Pool
|
||||
from gevent import sleep
|
||||
from cme.logger import setup_logger, setup_debug_logger, CMEAdapter
|
||||
|
|
|
@ -3,7 +3,6 @@ import sqlite3
|
|||
import shutil
|
||||
import cme
|
||||
from cme.helpers.logger import highlight
|
||||
#from cme.helpers.powershell import is_powershell_installed
|
||||
from cme.loaders.protocol_loader import protocol_loader
|
||||
from subprocess import check_output, PIPE
|
||||
from sys import exit
|
||||
|
@ -76,5 +75,3 @@ def first_run_setup(logger):
|
|||
exit(1)
|
||||
|
||||
os.system('openssl req -new -x509 -keyout {path} -out {path} -days 365 -nodes -subj "/C=US" > /dev/null 2>&1'.format(path=CERT_PATH))
|
||||
|
||||
#if not is_powershell_installed(): logger.error(highlight('[!] PowerShell not found and/or not installed, advanced PowerShell script obfuscation will be disabled!'))
|
|
@ -0,0 +1,21 @@
|
|||
import random
|
||||
|
||||
def get_desktop_uagent(uagent=None):
|
||||
|
||||
desktop_uagents = {
|
||||
"MSIE9.0" : "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)",
|
||||
"MSIE8.0" : "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0)",
|
||||
"MSIE7.0" : "Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; en-US)",
|
||||
"MSIE6.0" : "Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)",
|
||||
"Chrome32" : "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36",
|
||||
"Chrome31" : "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.16 Safari/537.36",
|
||||
"Firefox25": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0",
|
||||
"Firefox24": "Mozilla/5.0 (Windows NT 6.0; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0,",
|
||||
"Safari5.1": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13+ (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2",
|
||||
"Safari5.0": "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0 Safari/533.16"
|
||||
}
|
||||
|
||||
if not uagent:
|
||||
return desktop_uagents[random.choice(desktop_uagents.keys())]
|
||||
elif uagent:
|
||||
return desktop_uagents[uagent]
|
|
@ -49,7 +49,7 @@ class CMEAdapter(logging.LoggerAdapter):
|
|||
else:
|
||||
module_name = colored(self.extra['protocol'], 'blue', attrs=['bold'])
|
||||
|
||||
return u'{:<24} {:<15} {} {:<16} {}'.format(module_name,
|
||||
return u'{:<24} {:<15} {:<6} {:<16} {}'.format(module_name,
|
||||
self.extra['host'],
|
||||
self.extra['port'],
|
||||
self.extra['hostname'].decode('utf-8') if self.extra['hostname'] else 'NONE',
|
||||
|
|
|
@ -1,51 +1,98 @@
|
|||
import requests
|
||||
import bs4
|
||||
from socket import gethostbyname
|
||||
from requests import ConnectionError, ConnectTimeout, ReadTimeout
|
||||
import os
|
||||
from gevent.pool import Pool
|
||||
from datetime import datetime
|
||||
from sys import exit
|
||||
from cme.helpers.logger import highlight
|
||||
from cme.logger import CMEAdapter
|
||||
from cme.connection import *
|
||||
from cme.helpers.http import *
|
||||
from requests import ConnectionError, ConnectTimeout, ReadTimeout
|
||||
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
||||
|
||||
# The following disables the warning on an invalid cert and allows any SSL/TLS cipher to be used
|
||||
# I'm basically guessing this is the way to specify to allow all ciphers since I can't find any docs about it, if it don't worky holla at me
|
||||
requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS += ':ANY:ALL'
|
||||
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||
|
||||
try:
|
||||
from splinter import Browser
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
except ImportError:
|
||||
print highlight('[!] HTTP protocol requires splinter and phantomjs', 'red')
|
||||
exit(1)
|
||||
|
||||
class http(connection):
|
||||
|
||||
def __init__(self, args, db, host):
|
||||
self.page_title = None
|
||||
|
||||
connection.__init__(self, args, db, host)
|
||||
|
||||
@staticmethod
|
||||
def proto_args(parser, std_parser, module_parser):
|
||||
http_parser = parser.add_parser('http', help="own stuff using HTTP(S)", parents=[std_parser])
|
||||
http_parser.add_argument('--ports', nargs='*', default=[80, 443, 8443, 8080, 8008, 8081], help='HTTP(S) ports')
|
||||
http_parser.add_argument('--transports', nargs='+', choices=['http', 'https'], default=['http', 'https'], help='force connection')
|
||||
http_parser = parser.add_parser('http', help="own stuff using HTTP", parents=[std_parser, module_parser])
|
||||
http_parser.add_argument('--ports', nargs='*', default=[80, 443, 8443, 8008, 8080, 8081], help='http ports to connect to (default: 80, 443, 8443, 8008, 8080, 8081)')
|
||||
http_parser.add_argument('--transports', nargs='+', choices=['http', 'https'], default=['http', 'https'], help='force connection over http or https (default: all)')
|
||||
http_parser.add_argument('--screenshot', action='store_true', help='take a screenshot of the loaded webpage')
|
||||
|
||||
return parser
|
||||
|
||||
def proto_flow(self):
|
||||
|
||||
def start(self, transport, port):
|
||||
conn = self.create_conn_obj(transport, port)
|
||||
if conn:
|
||||
self.proto_logger(transport, port)
|
||||
self.print_host_info(conn)
|
||||
self.call_cmd_args(conn, transport, port)
|
||||
|
||||
pool = Pool(len(self.args.transports) * len(self.args.ports))
|
||||
jobs = []
|
||||
for transport in self.args.transports:
|
||||
for port in self.args.ports:
|
||||
self.enum_host_info(transport, port)
|
||||
self.proto_logger(port)
|
||||
self.print_host_info()
|
||||
#if self.login():
|
||||
#elif self.module is None and self.chain_list is None:
|
||||
self.call_cmd_args()
|
||||
jobs.append(pool.spawn(start, self, transport, port))
|
||||
|
||||
def proto_logger(self, port):
|
||||
for job in jobs:
|
||||
job.join()
|
||||
|
||||
def call_cmd_args(self, conn, transport, port):
|
||||
for k, v in vars(self.args).iteritems():
|
||||
if hasattr(self, k) and hasattr(getattr(self, k), '__call__'):
|
||||
if v is not False and v is not None:
|
||||
logging.debug('Calling {}()'.format(k))
|
||||
getattr(self, k)(conn, transport, port)
|
||||
|
||||
def proto_logger(self, transport, port):
|
||||
self.logger = CMEAdapter(extra={'protocol': 'HTTP',
|
||||
'host': gethostbyname(self.host),
|
||||
'host': self.host,
|
||||
'port': port,
|
||||
'hostname': None})
|
||||
'hostname': self.hostname})
|
||||
|
||||
def enum_host_info(self, transport, port):
|
||||
def print_host_info(self, conn):
|
||||
self.logger.info('{} (Title: {})'.format(conn.url, conn.title.strip()))
|
||||
|
||||
def create_conn_obj(self, transport, port):
|
||||
user_agent = get_desktop_uagent()
|
||||
url = '{}://{}:{}/'.format(transport, self.hostname, port)
|
||||
try:
|
||||
self.conn = requests.get('{}://{}:{}/'.format(transport, self.host, port), timeout=10)
|
||||
html = bs4.BeautifulSoup(self.conn.text, "html.parser")
|
||||
self.page_title = html.title.text
|
||||
r = requests.get(url, timeout=10, headers={'User-Agent': user_agent})
|
||||
except ConnectTimeout, ReadTimeout:
|
||||
pass
|
||||
return False
|
||||
except Exception as e:
|
||||
if str(e).find('Read timed out') == -1:
|
||||
logging.debug('Error connecting to {}://{}:{} :{}'.format(transport, self.host, port, e))
|
||||
logging.debug('Error connecting to {}://{}:{} :{}'.format(transport, self.hostname, port, e))
|
||||
return False
|
||||
|
||||
def print_host_info(self):
|
||||
self.logger.info("Title: '{}'".format(self.page_title.strip()))
|
||||
capabilities = DesiredCapabilities.PHANTOMJS
|
||||
capabilities['phantomjs.page.settings.userAgent'] = user_agent
|
||||
#capabilities['phantomjs.page.settings.resourceTimeout'] = 10 * 1000
|
||||
capabilities['phantomjs.page.settings.userName'] = 'none'
|
||||
capabilities['phantomjs.page.settings.password'] = 'none'
|
||||
|
||||
conn = Browser('phantomjs', service_args=['--ignore-ssl-errors=true', '--web-security=no', '--ssl-protocol=any'],
|
||||
service_log_path=os.path.expanduser('~/.cme/logs/ghostdriver.log'), desired_capabilities=capabilities)
|
||||
|
||||
conn.driver.set_window_size(1200, 675)
|
||||
conn.visit(url)
|
||||
return conn
|
||||
|
||||
def screenshot(self, conn, transport, port):
|
||||
screen_output = os.path.join(os.path.expanduser('~/.cme/logs/'), '{}:{}_{}'.format(self.hostname, port, datetime.now().strftime("%Y-%m-%d_%H%M%S")))
|
||||
conn.screenshot(name=screen_output)
|
||||
self.logger.success('Screenshot stored at {}.png'.format(screen_output))
|
||||
|
|
|
@ -29,7 +29,7 @@ class mssql(connection):
|
|||
mssql_parser.add_argument("-q", "--query", metavar='QUERY', type=str, help='execute the specified query against the MSSQL DB')
|
||||
mssql_parser.add_argument("-a", "--auth-type", dest='mssql_auth', choices={'windows', 'normal'}, default='windows', help='MSSQL authentication type to use (default: windows)')
|
||||
|
||||
cgroup = mssql_parser.add_argument_group("Command Execution", "Options for executing commands")
|
||||
cgroup = mssql_parser.add_argument_group("Command Execution", "options for executing commands")
|
||||
cgroup.add_argument('--force-ps32', action='store_true', help='force the PowerShell command to run in a 32-bit process')
|
||||
cgroup.add_argument('--no-output', action='store_true', help='do not retrieve command output')
|
||||
xgroup = cgroup.add_mutually_exclusive_group()
|
||||
|
@ -43,7 +43,7 @@ class mssql(connection):
|
|||
'protocol': 'MSSQL',
|
||||
'host': self.host,
|
||||
'port': self.args.mssql_port,
|
||||
'hostname': u'{}'.format(self.hostname)
|
||||
'hostname': self.hostname
|
||||
})
|
||||
|
||||
def enum_host_info(self):
|
||||
|
@ -228,4 +228,4 @@ def printRepliesCME(self):
|
|||
_type = "%d" % key['Type']
|
||||
self._MSSQL__rowsPrinter.info("ENVCHANGE(%s): Old Value: %s, New Value: %s" % (_type,record['OldValue'].decode('utf-16le'), record['NewValue'].decode('utf-16le')))
|
||||
|
||||
#tds.MSSQL.printReplies = printRepliesCME
|
||||
tds.MSSQL.printReplies = printRepliesCME
|
||||
|
|
|
@ -25,6 +25,7 @@ from cme.protocols.smb.mmcexec import MMCEXEC
|
|||
from cme.protocols.smb.smbspider import SMBSpider
|
||||
from cme.protocols.smb.passpol import PassPolDump
|
||||
from cme.helpers.logger import highlight
|
||||
#from cme.helpers.powershell import is_powershell_installed
|
||||
from cme.helpers.misc import *
|
||||
from cme.helpers.powershell import create_ps_command
|
||||
from pywerview.cli.helpers import *
|
||||
|
@ -36,6 +37,9 @@ from functools import wraps
|
|||
smb_share_name = gen_random_string(5).upper()
|
||||
smb_server = None
|
||||
|
||||
#if not is_powershell_installed():
|
||||
# logger.error(highlight('[!] PowerShell not found and/or not installed, advanced PowerShell script obfuscation will be disabled!'))
|
||||
|
||||
def requires_smb_server(func):
|
||||
def _decorator(self, *args, **kwargs):
|
||||
global smb_server
|
||||
|
@ -164,7 +168,7 @@ class smb(connection):
|
|||
'protocol': 'SMB',
|
||||
'host': self.host,
|
||||
'port': self.args.smb_port,
|
||||
'hostname': u'{}'.format(self.hostname)
|
||||
'hostname': self.hostname
|
||||
})
|
||||
|
||||
def get_os_arch(self):
|
||||
|
@ -190,9 +194,7 @@ class smb(connection):
|
|||
return 0
|
||||
|
||||
def enum_host_info(self):
|
||||
#Get the remote ip address (in case the target is a hostname)
|
||||
self.local_ip = self.conn.getSMBServer().get_socket().getsockname()[0]
|
||||
remote_ip = self.conn.getRemoteHost()
|
||||
|
||||
try:
|
||||
self.conn.login('' , '')
|
||||
|
@ -200,12 +202,11 @@ class smb(connection):
|
|||
if "STATUS_ACCESS_DENIED" in e.message:
|
||||
pass
|
||||
|
||||
self.host = remote_ip
|
||||
self.domain = self.conn.getServerDomain()
|
||||
self.hostname = self.conn.getServerName()
|
||||
self.server_os = self.conn.getServerOS()
|
||||
self.os_arch = self.get_os_arch()
|
||||
self.signing = self.conn.isSigningRequired()
|
||||
self.os_arch = self.get_os_arch()
|
||||
|
||||
self.output_filename = os.path.expanduser('~/.cme/logs/{}_{}_{}'.format(self.hostname, self.host, datetime.now().strftime("%Y-%m-%d_%H%M%S")))
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 8ca4065ed8f29f4b68e67bd7d1da73cc940d5755
|
||||
Subproject commit 0cc150e599d1826573505e8ba110bf95bd05e2d7
|
|
@ -1,9 +1,9 @@
|
|||
pycrypto>=2.6
|
||||
pyasn1>=0.1.8
|
||||
gevent>=1.2.0
|
||||
requests>=2.3.0
|
||||
bs4
|
||||
netaddr
|
||||
pyOpenSSL
|
||||
termcolor
|
||||
requests>=2.3.0
|
||||
msgpack-python
|
4
setup.py
4
setup.py
|
@ -9,7 +9,7 @@ setup(name='crackmapexec',
|
|||
'Programming Language :: Python :: 2.7',
|
||||
'Topic :: Security',
|
||||
],
|
||||
keywords='pentesting security windows smb active-directory networks',
|
||||
keywords='pentesting security windows active-directory networks',
|
||||
url='http://github.com/byt3bl33d3r/CrackMapExec',
|
||||
author='byt3bl33d3r',
|
||||
author_email='byt3bl33d3r@protonmail.com',
|
||||
|
@ -21,11 +21,11 @@ setup(name='crackmapexec',
|
|||
'pycrypto>=2.6',
|
||||
'pyasn1>=0.1.8',
|
||||
'gevent>=1.2.0',
|
||||
'requests>=2.3.0',
|
||||
'bs4',
|
||||
'netaddr',
|
||||
'pyOpenSSL',
|
||||
'termcolor',
|
||||
'requests>=2.3.0',
|
||||
'msgpack-python'
|
||||
],
|
||||
entry_points = {
|
||||
|
|
Loading…
Reference in New Issue