Plugin updates, bug fix, and feature update.

A few manual plugin updates (command formatting etc.)
Fixed bug where processes were left running after AutoRecon is cancelled and/or times out.
Status messages now include PIDs of running processes if verbosity >= 2.

Closes #183
Fixes #184
This commit is contained in:
Tib3rius 2023-01-10 16:24:14 -05:00
parent 65fc104b2d
commit 851ffbd316
6 changed files with 101 additions and 27 deletions

View File

@ -0,0 +1,17 @@
from autorecon.plugins import ServiceScan
class BruteforceSMB(ServiceScan):
def __init__(self):
super().__init__()
self.name = 'Bruteforce SMB'
self.tags = ['default', 'safe', 'active-directory']
def configure(self):
self.match_service('tcp', 445, '^microsoft\-ds')
self.match_service('tcp', 139, '^netbios')
def manual(self, service, plugin_was_run):
service.add_manual_command('Bruteforce SMB', [
'crackmapexec smb {address} --port={port} -u "' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '" -p "' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '"'
])

View File

@ -12,5 +12,5 @@ class LookupSID(ServiceScan):
def manual(self, service, plugin_was_run): def manual(self, service, plugin_was_run):
service.add_manual_command('Lookup SIDs', [ service.add_manual_command('Lookup SIDs', [
'lookupsid.py [username]:[password]@{address}' 'impacket-lookupsid \'[username]:[password]@{address}\''
]) ])

View File

@ -19,14 +19,14 @@ class WinRMDetection(ServiceScan):
def manual(self, service, plugin_was_run): def manual(self, service, plugin_was_run):
service.add_manual_commands('Bruteforce logins:', [ service.add_manual_commands('Bruteforce logins:', [
'crackmapexec winrm {address} -d ' + self.get_global('domain', default='<domain>') + ' -u ' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + ' -p ' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') 'crackmapexec winrm {address} -d \'' + self.get_global('domain', default='<domain>') + '\' -u \'' + self.get_global('username_wordlist', default='/usr/share/seclists/Usernames/top-usernames-shortlist.txt') + '\' -p \'' + self.get_global('password_wordlist', default='/usr/share/seclists/Passwords/darkweb2017-top100.txt') + '\''
]) ])
service.add_manual_commands('Check login (requires credentials):', [ service.add_manual_commands('Check login (requires credentials):', [
'crackmapexec winrm {address} -d ' + self.get_global('domain', default='<domain>') + ' -u <username> -p <password> -x "whoami"' 'crackmapexec winrm {address} -d \'' + self.get_global('domain', default='<domain>') + '\' -u \'<username>\' -p \'<password>\''
]) ])
service.add_manual_commands('Evil WinRM (gem install evil-winrm):', [ service.add_manual_commands('Evil WinRM (gem install evil-winrm):', [
'evil-winrm -u <user> -p <password> -i {address}', 'evil-winrm -u \'<user>\' -p \'<password>\' -i {address}',
'evil-winrm -u <user> -H <hash> -i {address}' 'evil-winrm -u \'<user>\' -H \'<hash>\' -i {address}'
]) ])

View File

@ -4,7 +4,7 @@ import argparse, asyncio, importlib.util, inspect, ipaddress, math, os, re, sele
from datetime import datetime from datetime import datetime
try: try:
import appdirs, colorama, impacket, requests, toml, unidecode import appdirs, colorama, impacket, psutil, requests, toml, unidecode
from colorama import Fore, Style from colorama import Fore, Style
except ModuleNotFoundError: except ModuleNotFoundError:
print('One or more required modules was not installed. Please run or re-run: ' + ('sudo ' if os.getuid() == 0 else '') + 'python3 -m pip install -r requirements.txt') print('One or more required modules was not installed. Please run or re-run: ' + ('sudo ' if os.getuid() == 0 else '') + 'python3 -m pip install -r requirements.txt')
@ -17,7 +17,7 @@ from autorecon.io import slugify, e, fformat, cprint, debug, info, warn, error,
from autorecon.plugins import Pattern, PortScan, ServiceScan, Report, AutoRecon from autorecon.plugins import Pattern, PortScan, ServiceScan, Report, AutoRecon
from autorecon.targets import Target, Service from autorecon.targets import Target, Service
VERSION = "2.0.30" VERSION = "2.0.31"
if not os.path.exists(config['config_dir']): if not os.path.exists(config['config_dir']):
shutil.rmtree(config['config_dir'], ignore_errors=True, onerror=None) shutil.rmtree(config['config_dir'], ignore_errors=True, onerror=None)
@ -92,18 +92,36 @@ def calculate_elapsed_time(start_time, short=False):
else: else:
return ', '.join(elapsed_time) return ', '.join(elapsed_time)
def cancel_all_tasks(signal, frame): # sig and frame args are only present so the function
# works with signal.signal() and handles Ctrl-C.
# They are not used for any other purpose.
def cancel_all_tasks(sig, frame):
for task in asyncio.all_tasks(): for task in asyncio.all_tasks():
task.cancel() task.cancel()
processes = []
for target in autorecon.scanning_targets: for target in autorecon.scanning_targets:
for process_list in target.running_tasks.values(): for process_list in target.running_tasks.values():
for process_dict in process_list['processes']: for process_dict in process_list['processes']:
try: try:
process_dict['process'].kill() parent = psutil.Process(process_dict['process'].pid)
except ProcessLookupError: # Will get raised if the process finishes before we get to killing it. processes.extend(parent.children(recursive=True))
processes.append(parent)
except psutil.NoSuchProcess:
pass pass
for process in processes:
try:
process.send_signal(signal.SIGKILL)
except psutil.NoSuchProcess: # Will get raised if the process finishes before we get to killing it.
pass
_, alive = psutil.wait_procs(processes, timeout=10)
if len(alive) > 0:
error('The following process IDs could not be killed: ' + ', '.join([str(x.pid) for x in sorted(alive, key=lambda x: x.pid)]))
if not config['disable_keyboard_control']: if not config['disable_keyboard_control']:
# Restore original terminal settings. # Restore original terminal settings.
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, terminal_settings) termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, terminal_settings)
@ -114,9 +132,28 @@ async def start_heartbeat(target, period=60):
async with target.lock: async with target.lock:
count = len(target.running_tasks) count = len(target.running_tasks)
tasks_list = '' tasks_list = []
if config['verbose'] >= 1: if config['verbose'] >= 1:
tasks_list = ': {bblue}' + ', '.join(target.running_tasks.keys()) + '{rst}' for tag, task in target.running_tasks.items():
task_str = tag
if config['verbose'] >= 2:
processes = []
for process_dict in task['processes']:
if process_dict['process'].returncode is None:
processes.append(str(process_dict['process'].pid))
try:
for child in psutil.Process(process_dict['process'].pid).children(recursive=True):
processes.append(str(child.pid))
except psutil.NoSuchProcess:
pass
if processes:
task_str += ' (PID' + ('s' if len(processes) > 1 else '') + ': ' + ', '.join(processes) + ')'
tasks_list.append(task_str)
tasks_list = ': {bblue}' + ', '.join(tasks_list) + '{rst}'
current_time = datetime.now().strftime('%H:%M:%S') current_time = datetime.now().strftime('%H:%M:%S')
@ -153,24 +190,42 @@ async def keyboard():
if len(input) > 0 and input[0] == 's': if len(input) > 0 and input[0] == 's':
input = input[1:] input = input[1:]
for target in autorecon.scanning_targets: for target in autorecon.scanning_targets:
count = len(target.running_tasks) async with target.lock:
count = len(target.running_tasks)
tasks_list = [] tasks_list = []
if config['verbose'] >= 1: if config['verbose'] >= 1:
for key, value in target.running_tasks.items(): for tag, task in target.running_tasks.items():
elapsed_time = calculate_elapsed_time(value['start'], short=True) elapsed_time = calculate_elapsed_time(task['start'], short=True)
tasks_list.append('{bblue}' + key + '{rst}' + ' (elapsed: ' + elapsed_time + ')')
tasks_list = ':\n ' + '\n '.join(tasks_list) task_str = '{bblue}' + tag + '{rst}' + ' (elapsed: ' + elapsed_time + ')'
else:
tasks_list = ''
current_time = datetime.now().strftime('%H:%M:%S') if config['verbose'] >= 2:
processes = []
for process_dict in task['processes']:
if process_dict['process'].returncode is None:
processes.append(str(process_dict['process'].pid))
try:
for child in psutil.Process(process_dict['process'].pid).children(recursive=True):
processes.append(str(child.pid))
except psutil.NoSuchProcess:
pass
if count > 1: if processes:
info('{bgreen}' + current_time + '{rst} - There are {byellow}' + str(count) + '{rst} scans still running against {byellow}' + target.address + '{rst}' + tasks_list) task_str += ' (PID' + ('s' if len(processes) > 1 else '') + ': ' + ', '.join(processes) + ')'
elif count == 1:
info('{bgreen}' + current_time + '{rst} - There is {byellow}1{rst} scan still running against {byellow}' + target.address + '{rst}' + tasks_list) tasks_list.append(task_str)
tasks_list = ':\n ' + '\n '.join(tasks_list)
else:
tasks_list = ''
current_time = datetime.now().strftime('%H:%M:%S')
if count > 1:
info('{bgreen}' + current_time + '{rst} - There are {byellow}' + str(count) + '{rst} scans still running against {byellow}' + target.address + '{rst}' + tasks_list)
elif count == 1:
info('{bgreen}' + current_time + '{rst} - There is {byellow}1{rst} scan still running against {byellow}' + target.address + '{rst}' + tasks_list)
else: else:
input = input[1:] input = input[1:]
await asyncio.sleep(0.1) await asyncio.sleep(0.1)

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "autorecon" name = "autorecon"
version = "2.0.30" version = "2.0.31"
description = "A multi-threaded network reconnaissance tool which performs automated enumeration of services." description = "A multi-threaded network reconnaissance tool which performs automated enumeration of services."
authors = ["Tib3rius"] authors = ["Tib3rius"]
license = "GNU GPL v3" license = "GNU GPL v3"
@ -14,6 +14,7 @@ python = "^3.8"
appdirs = "^1.4.4" appdirs = "^1.4.4"
colorama = "^0.4.5" colorama = "^0.4.5"
impacket = "^0.10.0" impacket = "^0.10.0"
psutil = "^5.9.4"
requests = "^2.28.1" requests = "^2.28.1"
toml = "^0.10.2" toml = "^0.10.2"
Unidecode = "^1.3.1" Unidecode = "^1.3.1"

View File

@ -1,6 +1,7 @@
appdirs appdirs
colorama colorama
impacket impacket
psutil
requests requests
toml toml
unidecode unidecode