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 cleanup
main
byt3bl33d3r 2017-04-30 12:54:35 -06:00
parent 3e27f30cb1
commit f0752f61b7
13 changed files with 116 additions and 51 deletions

View File

@ -1,3 +1,6 @@
from gevent import monkey
monkey.patch_all()
import sys
import os
import cme

View File

@ -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)')

View File

@ -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

View File

@ -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

View File

@ -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!'))

21
cme/helpers/http.py Normal file
View File

@ -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]

View File

@ -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',

View File

@ -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))

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 = {