NetExec/cme/connection.py

341 lines
17 KiB
Python
Executable File

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import logging
import socket
from os.path import isfile
from threading import BoundedSemaphore
from socket import gethostbyname
from functools import wraps
from cme.logger import CMEAdapter
from cme.context import Context
from cme.helpers.logger import write_log
sem = BoundedSemaphore(1)
global_failed_logins = 0
user_failed_logins = {}
def gethost_addrinfo(hostname):
try:
for res in socket.getaddrinfo(hostname, None, socket.AF_INET6,
socket.SOCK_DGRAM, socket.IPPROTO_IP, socket.AI_CANONNAME):
af, socktype, proto, canonname, sa = res
except socket.gaierror:
for res in socket.getaddrinfo(hostname, None, socket.AF_INET,
socket.SOCK_DGRAM, socket.IPPROTO_IP, socket.AI_CANONNAME):
af, socktype, proto, canonname, sa = res
if canonname == '':
return sa[0]
return canonname
def requires_admin(func):
def _decorator(self, *args, **kwargs):
if self.admin_privs is False:
return
return func(self, *args, **kwargs)
return wraps(func)(_decorator)
class connection(object):
def __init__(self, args, db, host):
self.domain = None
self.args = args
self.db = db
self.hostname = host
self.conn = None
self.admin_privs = False
self.logger = None
self.password = ''
self.username = ''
self.kerberos = True if self.args.kerberos or self.args.use_kcache else False
self.aesKey = None if not self.args.aesKey else self.args.aesKey
self.kdcHost = None if not self.args.kdcHost else self.args.kdcHost
self.log = None if not self.args.log else self.args.log
self.use_kcache = None if not self.args.use_kcache else self.args.use_kcache
self.failed_logins = 0
self.local_ip = None
if self.log:
CMEAdapter().setup_logfile(self.log[0])
try:
self.host = gethost_addrinfo(self.hostname)
if self.args.kerberos:
self.host = self.hostname
except Exception as e:
logging.debug('Error resolving hostname {}: {}'.format(self.hostname, e))
return
self.proto_flow()
@staticmethod
def proto_args(std_parser, module_parser):
return
def proto_logger(self):
pass
def enum_host_info(self):
return
def print_host_info(info):
return
def create_conn_obj(self):
return
def check_if_admin(self):
return
def kerberos_login(self):
return
def plaintext_login(self, domain, username, password):
return
def hash_login(self, domain, username, ntlm_hash):
return
def proto_flow(self):
if self.create_conn_obj():
self.enum_host_info()
self.proto_logger()
if self.print_host_info():
# because of null session
if self.login() or (self.username == '' and self.password == ''):
if hasattr(self.args, 'module') and self.args.module:
self.call_modules()
else:
self.call_cmd_args()
def call_cmd_args(self):
for k, v in vars(self.args).items():
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))
r = getattr(self, k)()
def call_modules(self):
module_logger = CMEAdapter(extra={
'module': self.module.name.upper(),
'host': self.host,
'port': self.args.port,
'hostname': self.hostname
})
context = Context(self.db, module_logger, self.args)
context.localip = self.local_ip
if hasattr(self.module, 'on_request') or hasattr(self.module, 'has_response'):
self.server.connection = self
self.server.context.localip = self.local_ip
if hasattr(self.module, 'on_login'):
self.module.on_login(context, self)
if self.admin_privs and hasattr(self.module, 'on_admin_login'):
self.module.on_admin_login(context, self)
if (not hasattr(self.module, 'on_request') and not hasattr(self.module, 'has_response')) and hasattr(self.module, 'on_shutdown'):
self.module.on_shutdown(context, self)
def inc_failed_login(self, username):
global global_failed_logins
global user_failed_logins
if username not in user_failed_logins.keys():
user_failed_logins[username] = 0
user_failed_logins[username] += 1
global_failed_logins += 1
self.failed_logins += 1
def over_fail_limit(self, username):
global global_failed_logins
global user_failed_logins
if global_failed_logins == self.args.gfail_limit:
return True
if self.failed_logins == self.args.fail_limit:
return True
if username in user_failed_logins.keys():
if self.args.ufail_limit == user_failed_logins[username]:
return True
return False
def login(self):
for cred_id in self.args.cred_id:
with sem:
if cred_id.lower() == 'all':
creds = self.db.get_credentials()
else:
creds = self.db.get_credentials(filter_term=int(cred_id))
for cred in creds:
logging.debug(cred)
try:
c_id, domain, username, password, cred_type, pillaged_from = cred
if cred_type and password:
if not domain:
domain = self.domain
if self.args.local_auth:
domain = self.domain
elif self.args.domain:
domain = self.args.domain
if cred_type == 'hash' and not self.over_fail_limit(username):
if self.args.kerberos:
if self.kerberos_login(domain, username, '', password, '', self.kdcHost, False):
return True
elif self.hash_login(domain, username, password): return True
elif cred_type == 'plaintext' and not self.over_fail_limit(username):
if self.args.kerberos:
if self.kerberos_login(domain, username, password, '' , '', self.kdcHost, False):
return True
elif self.plaintext_login(domain, username, password): return True
except IndexError:
self.logger.error("Invalid database credential ID!")
if self.args.use_kcache:
with sem:
username = self.args.username[0] if len(self.args.username) else ''
password = self.args.password[0] if len(self.args.password) else ''
self.kerberos_login(self.domain, username, password, '', '', self.kdcHost, True)
return True
for user in self.args.username:
if isfile(user):
with open(user, 'r') as user_file:
for usr in user_file:
if "\\" in usr:
tmp = usr
usr = tmp.split('\\')[1].strip()
self.domain = tmp.split('\\')[0]
if hasattr(self.args, 'hash') and self.args.hash:
with sem:
for ntlm_hash in self.args.hash:
if isfile(ntlm_hash):
with open(ntlm_hash, 'r') as ntlm_hash_file:
if not self.args.no_bruteforce:
for f_hash in ntlm_hash_file:
if not self.over_fail_limit(usr.strip()):
if self.args.kerberos:
if self.kerberos_login(self.domain, usr.strip(), '', f_hash.strip(), '', self.kdcHost, False):
return True
elif self.hash_login(self.domain, usr.strip(), f_hash.strip()):
return True
elif self.args.no_bruteforce:
user_file.seek(0) # HACK: this should really not be in the usr for loop
for usr, f_hash in zip(user_file, ntlm_hash_file):
if not self.over_fail_limit(usr.strip()):
if self.args.kerberos:
if self.kerberos_login(self.domain, usr.strip(), '', f_hash.strip(), '', self.kdcHost, False): return True
elif self.hash_login(self.domain, usr.strip(), f_hash.strip()):
return True
else: # ntlm_hash is a string
if not self.over_fail_limit(usr.strip()):
if self.args.kerberos:
if self.kerberos_login(self.domain, usr.strip(), '', ntlm_hash.strip(), '', self.kdcHost, False):
return True
elif self.hash_login(self.domain, usr.strip(), ntlm_hash.strip()):
return True
elif self.args.password:
with sem:
for password in self.args.password:
if isfile(password):
with open(password, 'r') as password_file:
if not self.args.no_bruteforce:
for f_pass in password_file:
if not self.over_fail_limit(usr.strip()):
if hasattr(self.args, 'domain'):
if self.args.kerberos:
if self.kerberos_login(self.domain, usr.strip(), f_pass.strip(), '', '', self.kdcHost, False):
return True
elif self.plaintext_login(self.domain, usr.strip(), f_pass.strip()):
return True
else:
if self.plaintext_login(usr.strip(), f_pass.strip()):
return True
elif self.args.no_bruteforce:
user_file.seek(0) # HACK: this should really not be in the usr for loop
for usr, f_pass in zip(user_file, password_file):
if not self.over_fail_limit(usr.strip()):
if hasattr(self.args, 'domain'):
if self.args.kerberos:
if self.kerberos_login(self.domain, usr.strip(), f_pass.strip(), '', '', self.kdcHost, False):
return True
elif self.plaintext_login(self.domain, usr.strip(), f_pass.strip()):
return True
else:
if self.plaintext_login(usr.strip(), f_pass.strip()):
return True
else: # password is a string
if not self.over_fail_limit(usr.strip()):
if hasattr(self.args, 'domain'):
if self.args.kerberos:
if self.kerberos_login(self.domain, usr.strip(), password, '', '', self.kdcHost, False):
return True
elif self.plaintext_login(self.domain, usr.strip(), password):
return True
else:
if self.plaintext_login(usr.strip(), password):
return True
else: # user is a string
if hasattr(self.args, 'hash') and self.args.hash:
with sem:
for ntlm_hash in self.args.hash:
if isfile(ntlm_hash):
with open(ntlm_hash, 'r') as ntlm_hash_file:
for f_hash in ntlm_hash_file:
if not self.over_fail_limit(user):
if self.args.kerberos:
if self.kerberos_login(self.domain, user, '', ntlm_hash.strip(), '', self.kdcHost, False):
return True
elif self.hash_login(self.domain, user, f_hash.strip()):
return True
else: # ntlm_hash is a string
if not self.over_fail_limit(user):
if self.args.kerberos:
if self.kerberos_login(self.domain, user, '', ntlm_hash.strip(), '', self.kdcHost, False): return True
elif self.hash_login(self.domain, user, ntlm_hash.strip()):
return True
elif self.args.password:
with sem:
for password in self.args.password:
if isfile(password):
with open(password, 'r') as password_file:
for f_pass in password_file:
if not self.over_fail_limit(user):
if hasattr(self.args, 'domain'):
if self.args.kerberos:
if self.kerberos_login(self.domain, user, f_pass.strip(), '', '', self.kdcHost, False):
return True
elif self.plaintext_login(self.domain, user, f_pass.strip()):
return True
else:
if self.plaintext_login(user, f_pass.strip()):
return True
else: # password is a string
if not self.over_fail_limit(user):
if hasattr(self.args, 'domain'):
if self.args.kerberos:
if self.kerberos_login(self.domain, user, password, '', '', self.kdcHost, False):
return True
elif self.plaintext_login(self.domain, user, password):
return True
else:
if self.plaintext_login(user, password):
return True
elif self.args.aesKey:
with sem:
for aesKey in self.args.aesKey:
if not self.over_fail_limit(user):
if self.kerberos_login(self.domain, user, '', '', aesKey.strip(), self.kdcHost, False):
return True