#!/usr/bin/env python3 # -*- coding: utf-8 -*- import argparse import logging import os import sys import threading import traceback from datetime import datetime from time import sleep import requests requests.packages.urllib3.disable_warnings() __author__ = 'moony' __version__ = '1.0.0' DIR = os.path.abspath(os.path.dirname(__file__)) logging.captureWarnings(True) FORMAT = '%(asctime)s (%(levelname)s): %(message)s' logging.basicConfig(filename=DIR + '/logs/aftermath_' + str(datetime.today().date()) + '_' + str(datetime.today().time().strftime('%H:%M:%S')) + '.log', format=FORMAT, level=logging.INFO) class ui: END = '\33[0m' BOLD = '\33[1m' RED = '\33[31m' GREEN = '\33[32m' BLUE = '\33[34m' def getTime(): now = datetime.now().strftime('%H:%M:%S') return now def getDate(): today = datetime.today().date() return today def prompt(): now = datetime.today().time().strftime('%H:%M:%S') prompt = f' {ui.BLUE}[{ui.GREEN}{now}{ui.BLUE}]{ui.END}' return prompt def scan_target(target, args): try: target = target.replace('\n', '') headers = { 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.5', 'Connection': 'keep-alive' } r = requests.get(target, headers=headers, timeout=args.timeout, allow_redirects=False, verify=False) if r.status_code == 200 and 'SCRIPTS MUST BE ENABLED FOR SHELL TO WORK' in r.text: print( f'{ui.prompt()} operational webshell FOUND: {ui.GREEN}{target}{ui.END}' ) logging.info(f'operational webshell FOUND: {target}') with open(args.output_file, 'a+') as f: f.write(f'{target}\n') f.close() else: print(f'{ui.prompt()} no shell found: {target}') logging.info(f'no shell found: {target}') except Exception as e: logging.error(e) pass def main(): try: parser = argparse.ArgumentParser() parser.add_argument('-v', dest='verbose', action='count', default=0, help='enable verbose output. (ex: -v, -vv, -vvv)') parser.add_argument('-f', '--file', dest='input_file', required=True, metavar='', help='input file of urls to scan for shell') parser.add_argument( '-o', '--output', dest='output_file', default= f'{DIR}/output/{datetime.today().time()}_{datetime.today().date()}.txt', metavar='', help='output file to save valid urls') parser.add_argument('-t', '--threads', type=int, dest='threads', default=5, metavar='', help='number of threads to use') parser.add_argument('-T', '--timeout', type=int, dest='timeout', default=10, metavar='', help='the request timeout in seconds') args = parser.parse_args() print( f'{ui.prompt()} {ui.BLUE}aftermath{ui.END} v{ui.BLUE}{__version__}{ui.END} by {ui.BLUE}{__author__}{ui.END} - scanning initiated' ) logging.info( f'aftermath v{__version__} by {__author__} - scanning initiated') f = open(args.input_file, 'r') targets = f.readlines() for target in targets: thread = threading.Thread(target=scan_target, args=(target, args)) thread.daemon = True thread.start() while threading.active_count() > args.threads: sleep(0.001) while threading.active_count() > 1: sleep(0.001) valid = len(open(args.output_file).readlines()) print( f'{ui.prompt()} scanning completed. valid webshells: {ui.GREEN}{valid:,}{ui.END}' ) logging.info(f'scanning completed. valid webshells: {valid}') sys.exit(-1) except KeyboardInterrupt: print(f'{ui.prompt()} user interrupt detected - exiting') logging.info('user interrupt detected. exiting.') sys.exit(-1) except Exception as e: print( f'{ui.prompt()} fatal exception occurred - check the log for detauls' ) logging.error(e, exc_info=traceback) sys.exit(-1) if __name__ == '__main__': main()