일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- chrome 탐지
- slowloris
- stream_size
- IDS
- Reassembly
- isdataat
- idps
- SSL
- chromium 탐지
- sinature
- stream buffer
- 시그니처
- snort
- header dos
- 암호화
- 재조합
- signatrue
- TLS
- IPS
- rule
- 오탐
- flowbits
- 탐지
- 크롬 탐지
- 수리카타
- Suricata
- 미탐
- HTTP2
- DDOS
- dsize
- Today
- Total
linefilt
raw stream based Slowloris (Inline mode) 본문
raw stream based Slowloris
Slowloris는 서버의 가용 자원을 최대한 낭비하여 DoS를 유발하는 공격으로 일반적인 Flooding 공격과 달리 단순한 임계 값으로는 확인할 수 없으며, 문자열 식별 방식 또한 한계를 가지고 있다. 리버스 프록시나 HTTP 서버 등 최종 Termination 역할을 하지 않는 대부분의 보안장비에서, Slowloris 공격을 탐지하는 방식으로 HTTP 메시지에서 0x0d0a0d0a가 없는 세그먼트를 탐지한다. 이 방법은 대부분 간단한 테스트를 진행하는 방식에 초점이 맞춰져 있기 때문에 실제 트래픽에 이러한 탐지 방법을 적용하는 것은 무리가 있다. 이 문서에서는 문자열 탐지 방식에 적합한 Suricata를 사용하여 Slowloris 공격의 오탐을 최소화하는 관점으로 접근한다.
HTTP 메시지는 메시지의 길이, TCP의 상태 및 구성된 네트워크 환경에 따라 TCP Segmentation이 발생할 수 있다. 예를 들어 1400의 길이를 가지는 정상적인 HTTP GET 요청이 있을 때, Rule 1에 매치되지 않는다.
alert tcp any any → any any (msg:"HTTP DoS Slowloris"; content:"|20|HTTP/1.1|20|"; content:!"|0d 0a 0d 0a|"; distance 0; sid:1; )
GET 요청이 만약 어떠한 상황으로 다수의 TCP Segment로 분리되어 서버로 전송된다면, Rule 1이 매치되는 오탐이 발생한다. 즉 단편적인 룰만으로는 전송되는 페이로드가 HTTP 메시지인지 비 이상적인 동작인지를 중간의 탐지 장비에서는 단편적인 탐지 방식으로는 메시지를 구별하기 어렵다는 문제가 있다.
Slowloris는 세그먼트의 0x0d0a0d0a를 탐지하는 방법 외에도 User-Agent를 탐지하는 방식이 주로 사용된다. 그러나 User-Agent를 특정할 수 있는 상황이 아니라면 적용할 수 없는 문제가 있어, 가급적 공통으로 발생할 수 있는 문자열을 기준하여 탐지 가능성을 최대화 해야 한다.
alert tcp any any → any any (msg:"HTTP DoS Slowloris"; content:"|20|HTTP/1.1|20|"; content:!"|0d 0a 0d 0a|"; http_header; sid:2; )
일부 룰은 Rule 2와 같이 제공되는 경우가 있다. Snort 문법을 지원하는 Suricata 또한 파싱된 HTTP 문자열만을 매치할 수 있는 옵션을 제공한다. 그러나 현재의 Snort와 Suricata에서 HTTP 헤더 영역을 탐지하는 것은 헤더의 끝맺음을 의미하는 0x0d0a0d0a의 존재로 판단하기 때문에 Rule 2의 사용은 어렵다. 이처럼 특정 패턴을 식별하는 패턴 매칭 방식은 주로 공격 행위를 하는 문자열을 탐지하는 방식으로 발전되어 왔기 때문에 문자열의 부정매치(negate)나 프로토콜의 이상징후를 탐지함에 있어서 자유롭지는 못하다는 단점이 존재한다. Suricata 에서는 http 옵션에서 존재하지 않는 값의 식별과 관련한 이슈가 제기된 적은 있으나 크게 진척은 없는 상황이다.
식별 범위를 제한하는 것의 한계가 존재하므로, 모두 식별하거나 분할되는 것을 제외하는 방안도 고려해볼 수 있지만, 이 또한 각 상황별로 문제가 발생된다:
- HTTP 트랜잭션이 완성될 때까지 관련 페이로드를 버퍼에 모두 저장해 두었다가 일괄 탐지
- 페이로드 길이가 긴 HTTP 메시지는 보안장비의 메모리 특성으로 인하여 모든 메시지를 버퍼에 저장할 수 없는 한계를 가진다. (HTTP 메시지는 쿠키 값의 영향으로 8K 정도까지 발생할 수 있으며, 실제 사용에서도 5K 이상으로 빈번하게 발생된다.)
- stream_size 와 같은 옵션을 사용하여 초반부의 세그먼테이션 패킷을 탐지하지 않도록 정의
- 세션 초기의 HTTP 메시지는 탐지할 수 있으나 정확성이 떨어지고 동일 세션에서 지속적으로 발생하는 HTTP 트랜잭션을 처리하기 어려움
결국 특정 상황의 문제를 모면하는 것은 또 다른 문제를 야기할 수 있다. 즉 궁극적인 목표를 기반으로 최대한 다양한 변수를 극복할 수 있도록 설계가 되어야 한다.
TCP Segmentation Slowloris 유형
Slowloris오탐의 근본적인 문제점은 HTTP의 메시지가 TCP MSS(Maximum Segment Size)를 초과하거나 일련의 요건으로TCP Segmentation되어 분할 되었는지에 대한 여부를 정확하게 식별하기 어렵다는 것이다. 만약 HTTP 메시지의 단일 세그먼트에 온전히 포함되어 있다면, HTTP 헤더의 끝을 의미하는 0x0d0a0d0a를 간편히 식별할 수 있으나, 분할된 경우에는 어떤 세그먼트에 0x0d0a0d0a가 적재되어야 하는지를 확인할 수 없기 때문이다. TCP 관점에서 최대한 이를 처리할 수 있는 방안을 고려해보았지만, 아래와 같이 일부 조건과 제약이 발생한다:
- HTTP 메시지는 일반적인 웹 서버가 처리할 수 있는 메시지만 HTTP라고 간주한다.
- TCP Flags에 AP가 명시되면, 응용프로그램 계층에서 하나의 HTTP 메시지를 전부 보냈다고 가정한다.
- 단일 세션에서 연속적으로 발생하는 다수의 HTTP 메시지를 처리할 수 있어야 한다.
- TCP Fast Open에 대하여 지원하여야 한다.
TCP 플래그 PSH는 TCP 계층에서 더 이상 전송할 메시지가 없음을 의미한다. 일반적으로 대부분의 응용프로그램에서 HTTP는 단일 세그먼트 또는 마지막 세그먼트에 PUSH가 포함된다. 그러나 패턴 탐지 우회목적의 프로그램 등에서 간헐적으로 응용프로그램 계층에서 메시지가 나뉘어져 단일 HTTP에서 두 개의 PUSH가 발생하기도 한다.
위 사항을 바탕으로 Slowloris를 탐지하기 위한 흐름도를 구성하면 "그림 2"와 같이 구성할 수 있다.
탐지는 Method, Reqline 그리고 0x0d0a0d0a, 3가지 요소가 사용된다. Method가 식별된 이후 PSH 세그먼트가 발생할 때 까지 이 요소들이 모두 식별되는지 여부를 기준으로 slowloris 공격을 판단한다.
또한 "TCP Fast Open"(TFO)를 지원하도록 구성되어 있다. TFO는 지연시간을 줄이기 위해 HTTP에서 도입되었으나 브라우저 및 서버에서의 호환이 잘 되지 않는 문제 등으로 실제로 지원하는 경우는 거의 존재하지 않는다. 이와 유사한 개념으로 TLS 1.3에서의 0-RTT를 볼 수 있다.
TFO Slowloris case
TFO case는 크게 2가지로 구성된다. 기본 룰 세트에 적용되는 다수의 세그먼트로 구성된 경우와 단일 SYN 세그먼트로 구성된 경우이다.
- 다중 세그먼트 구성: Slowloris 공격의 첫 페이로드는 SYN 세그먼트에 적재되나, 이후의 페이로드는 3WHS 이후 세그먼트에 적재된다. 기본 룰 세트와 동일하게 PSH 플래그를 기준하여 공격 여부를 판단한다.
- 단일 세그먼트 구성: 단일 SYN 세그먼트에 0x0d0a0d0a가 적재되지 않은 경우를 처리해야 한다.
- 공격자는 SYN 세그먼트만 전송하고 더 이상 어떠한 패킷도 전송하지 않는다.
- 공격자는 SYN 세그먼트를 전송하고 3WHS 충족을 위한 페이로드가 적재되지 않은 ACK를 전송한다.
1번은 SYN 세그먼트에 페이로드가 적재된 다는 차이점이 있지만, 최종 판단의 기점이 되는 PSH 세그먼트가 공통으로 발생하기 때문에 기본 룰 세트에서 공통 처리된다.
2번은 두 개의 상황에 맞추어 처리 방법이 나우어진다. 정확히 SYN 세그먼트만 발생된 경우에는 클라이언트(공격자)의 응답이 더 이상 없기 때문에 서버에서 SYN-ACK 재전송이 발생한다. 0x0d0a0d0a 등이 없는 것이 확인된 상태에서 SYN-ACK의 재전송이 발생하면 공격으로 식별하도록 처리한다. SYN-ACK 이후의 첫 ACK의 페이로드가 0이면, SYN-ACK의 재전송은 발생하지 않는다. 그러나 세그먼테이션 된 HTTP 요청이라면 클라이언트의 ACK 세그먼트에 페이로드가 적재되어야 한다.
그림 2의 흐름도를 기반으로 Suricata의 탐지 버퍼 및 flowbits를 사용하여 시그니처를 조합할 수 있다. 시그니처 조합을 이해하기 전에 Suricata의 재조합 버퍼를 기본 동작을 이해할 필요가 있다.
Suricata에서는 페이로드를 탐지함에 있어 단일 패킷과 스트림 기반 재조합 탐지를 병행해서 처리한다. 재조합 버퍼 탐지는 TCP가 세그먼테이션 되거나 악성 행위가 보안 장비를 우회하기 위해 인위적으로 페이로드의 경계를 발생시키는 것을 대응하기 위해 도입되었다. 이때 재조합 버퍼에서 특정 영역을 식별하는 것을 "Sliding Window" (식별 버퍼)라고 한다.
"그림 3"은 Suricata "Sliding Window"의 동작을 표현한다. TCP 세션에서 첫 번째 세그먼트가 확인되면, 엔진은 세그먼트의 페이로드를 재조합 버퍼에 복사한다 . 연속적인 재조합 버퍼에서 첫 번째 세그먼트가 도착한 순간에는 첫 번째 세그먼트와 동일한 페이로드만 존재한다. "Sliding Window"는 자신이 처리할 수 있는 크기만큼 순차적으로 처리한다. Window의 크기가 2500이라고 했을 때, 1460의 사이즈를 가지는 TCP 세그먼트 두 개 의 페이로드는 순차적으로 처리된다. 즉 2500을 초과한 420만큼의 페이로드는 순차적으로 뒤 이어 식별한다. 앞부분 2500만큼의 페이로드는 이미 확인되었기 때문에 잘라내고 식별하더라도 문제되지 않는다. 두 번째 TCP 세그먼트 영역에 해당하는 420의 페이로드는 세그먼트 기준으로는 분할된 페이로드로 생각될 수 있다. 이러한 문제를 방지하고자 Suricata는 단일 패킷을 병행 처리하는 것이며 두 번째 세그먼트가 단일 세그먼트로 온전하게 처리될 수 있도록 한다.
"Sliding Window"는 약 4천 바이트 정도 처리할 수 있기 때문에, 이를 초과하는 범위는 한 번에 식별하지 못한다. flowbits 옵션은 이와 같이 매우 큰 사이즈를 식별하지 못할 때, 세션에 마킹을 함으로 써 chain과 같이 사용할 수 있다. 그러나 flowbits로 지정된 chain은 또 다른 문자열의 경계라고도 볼 수 있다. flowbits로 지정된 chain은 재조합의 개념이 아니기 때문에 경계 처리에 있어서 취약하다. 문자열이 분할되는 것을 제대로 처리하지 못하는 문제는 Slowloris 관점에서는 정상 요청이 공격으로 오탐될 수 있다. 이에 따라 패턴을 확인할 때 마다, 시그니처의 우선순위를 조정해서 재조합 버퍼와 flowbits간의 검사가 모두 이루어질 수 있도록 구성해야 한다.
탐지 룰 세트
시그니처 sid는 반드시 변경될 수 있으나 표현된 것과 같이 오름차순으로 sid가 할당되어야 한다. 이는 위에서 설명한 시그니처 적용 순서를 보장하기 위함이다. 각 시그니처에 대한 설명은 "표 2"에서 설명된다.
Suricata 특화 옵션이 일부 사용되었다:
- tcp-stream: 재조합 버퍼에서만 탐지를 수행한다.
- tcp-pkt: 단일 패킷 버퍼에서만 탐지를 수행한다.
- prefilter: fast_pattern에 대응되는 로직으로 뚜렷한 문자열이 없는 상황에서, 성능 향상을 위해 사용된다.
- flowint: flowbits와 유사하나 카운팅의 적용과 집계에 사용된다.
모든 탐지 룰은 raw 버퍼에 포함되는 페이로드를 기준으로만 식별한다. http* 옵션을 사용하면, http 버퍼 매치로 간소한 룰 세트를 구성할 수 있다. 그러나 http 버퍼에서 페이로드를 식별하는 것과 flags:AP 와 같이 raw 버퍼에서만 처리할 수 있는 룰이 공존하는 경우에는 매치 타이밍이 어긋날 수 있다. 탐지 로직에 있어서 TCP 플래그 PSH의 발생 타이밍의 가장 중요한 만큼, 두 버퍼의 공존 매치는 처리하기 어려운 문제가 있다.
Q. drop이 아닌 reject action을 사용하는 이유
A. drop을 하더라도 3-Way Handshake에 의해서 맺어진 세션은 타임아웃이 되거나 서버의 종료판단이 있을 때 까지 유지된다. 이처럼 Slowloris는 지속적으로 많은 세션을 만들어 서버가 새로운 요청을 받지 못하도록 하기 위함이 목적으로, reject을 사용해서 세션이 바로 종료될 수 있도록 처리해주어야 한다.
1.
alert tcp-pkt any any -> any any (msg:"H1 method part"; flow:to_server; flowbits:isnotset,h1method; flowbits:isnotset,slowloris; content:"P"; nocase; depth:2; flowint:mpart,+,1; flowbits:unset,h1method; noalert; )
alert tcp-pkt any any -> any any (msg:"H1 method part"; flow:to_server; flowbits:isnotset,h1method; flowbits:isnotset,slowloris;content:"O"; nocase; depth:3; flowint:mpart,+,1; flowbits:unset,h1method; noalert;)
alert tcp-pkt any any -> any any (msg:"H1 method part"; flow:to_server; flowbits:isnotset,h1method; flowbits:isnotset,slowloris; content:"G"; nocase; depth:2; flowint:mpart,+,1; flowbits:unset,h1method; noalert;)
alert tcp-pkt any any -> any any (msg:"H1 method part"; flow:to_server; flowbits:isnotset,h1method; flowbits:isnotset,slowloris;content:"E"; nocase; depth:3; flowint:mpart,+,1; flowbits:unset,h1method; noalert; )
alert tcp-pkt any any -> any any (msg:"H1 method part"; flow:to_server; flowbits:isnotset,h1method; flowbits:isnotset,slowloris;content:"T"; nocase; depth:5; flowint:mpart,+,1; flowbits:unset,h1method; noalert; )
alert tcp-pkt any any -> any any (msg:"H1 method part"; flow:to_server; flowbits:isnotset,h1method; flowbits:isnotset,slowloris;content:"|20|"; nocase; depth:6; flowint:mpart,+,1; flowbits:unset,h1method; noalert; )
2.alert tcp-stream any any -> any any (msg:"H1 method"; flow:to_server; flowbits:isnotset,h1method; flowint:mpart,>,3; pcre:"/(GE|POS)T\x20/i"; flowbits:set,h1method; flowint:mpart,=,0; noalert; )
3.alert tcp-stream any any -> any any (msg:"H1 Reqline"; flow:to_server; flowbits:isnotset,h1reqline; flowbits:isset,h1method; flowbits:isnotset,h1rnrn; content:"|20|HTTP/1.1|0d 0a|"; nocase; flowbits:set,h1reqline; noalert; )
4.alert tcp-stream any any -> any any (msg:"H1 HDR Close"; flow:to_server; flowbits:isnotset,h1rnrn; flowbits:isset,h1method; content:"|0d 0a 0d 0a|"; flowbits:set,h1rnrn; noalert; )
5.
alert tcp-pkt any any -> any any (msg:"H1 TFO SSN"; flow:to_server; flowbits:isset,h1method; flowbits:isnotset,h1reqline|h1rnrn; flags:S+; prefilter; flowbits:set,h1tfo; noalert; )
alert tcp-pkt any any -> any any (msg:"H1 TFO SA Check"; flow:to_client; flowbits:isset,h1tfo; flags:SA; flowint:slowloris_sa,+,1; noalert; )
alert tcp-pkt any any -> any any (msg:"H1 TFO Slowloris"; flow:establisehd,to_server; flowbits:isset,h1tfo; stream_size:server,=,1; dsize:0; noalert; )
alert tcp-pkt any any -> any any (msg:"H1 TFO SA Retransmission"; flow:to_client; flags:SA; flowint:slowloris_sa,>,1; flowint:slowloris_limit,+,1; noalert; )
6.alert tcp-pkt any any -> any any (msg:"H1 Slowloris Check"; flow:established,to_server; flowbits:isset,h1method; flowbits:isnotset,h1reqline|h1rnrn; flags:AP; flowbits:set,slowloris; flowbits:unset,h1reqline; flowbits:unset,h1rnrn; noalert; )
7. alert tcp-pkt any any -> any any (msg:"H1 tx Reset"; flow:to_server; flowbits:isnotset,slowloris; flowbits:isset,h1method; flags:AP; flowbits:unset,h1method; flowbits:unset,h1reqline; flowbits:unset,h1rnrn; noalert; )
8.
alert tcp-pkt any any -> any any (msg:"Slowloris"; flow:established,to_client; flowbits:isset,slowloris; flowint:slowloris_limit,+,1; noalert; )
reject tcp-pkt any any -> any any (msg:"HTTP DoS Slowloris Reject"; flow:established,to_client; flowint:slowloris_limit,<,4; )
1. HTTP mehtod로 볼 수 있는 문자를 별한다. 만약 HTTP method가 확인된 상태이고 PSH로 메시지가 종료되지 않은 상태에서는 이런 문자의 추가 식별을 진행하지 않는다
2. 세그먼테이션 되었던 페이로드를 재조합 버퍼에서 식별하였을 때, method 문자열이 확인되면 매치한다. 1번 범주의 룰이 3개 확인된 경우에만 처리된다.
3. HTTP mehtod가 식별되고 HTTP RequestLine으로 볼 수 있는 문자열이 식별되면, flowbits:set,h1reqline을 설정한다.
4. HTTP method가 식별되고 0x0d0a0d0a문자열이 식별되면, flowbits:set,h1rnrn을 설정한다.
5. 단일 SYN 세그먼트로 구성된 Slowloris는 SYN 이후, 페이로드가 적재된 추가 세그먼트가 발생하지 않는다. 이 룰은 HTTP method가 확인되었으나 reqline 또는 0x0d0a0d0a가 없고 SYN-ACK의 재전송이 발생하거나 페이로드의 적재 없이 길이 0의 ACK세그먼트가 발생하면, Slowloris라고 간주하고 이를 처리하기 위해 flowint 옵션을 사용하여 카운팅 한다.
6. 일반 또는 다중 세그먼트 TCP FastOpen의 Slowloris 여부를 확인한다. mehtod가 확인되었으나 reqline이나 0x0d0a0d0a가 PSH 세그먼트가 발생된 순간까지 없는 경우에는 Slowloris로 처리한다.
7. HTTP mehtod가 확인된 상태에서 reqline과 0x0d0a0d0a가 모두 확인되면, HTTP 메시지가 종료되었다고 판단하고 관련 flowbits 및 flowint를 초기화 한다.
8. Slowloris로 처리된 세션의 TCP RST 전송을 처리한다. 불필요하게 과다한 RST 세그먼트를 제한하기 위해 카운팅을 설정한다.
Traffic case
그림 4는 실제 발생할 수 있는 case에서 위 시그니처 세트가 어떻게 동작하는 지를 표현한다.
정의
- AP(TCP ACK, PUSH)가 명시된 경우를 제외하고 모든 세그먼트의 TCP 플래그는 ACK이다.
- 모든 case의 Sliding Window는 2500이며, 각 세그먼트(파란색 박스)의 사이즈는 500이다.
- 하나의 세그먼트(박스)의 내부가 두 개로 분할된 것은 표현을 강조 하고자 하는 페이로드가 두 개인 경우를 의미한다.
용어
- GET(AP): AP플래그가 있는 온전한 GET 요청
- GET, Req line: 세그먼테이션으로 GET 요청이나 Request line(0d0a 포함) 까지만 존재하는 세그먼트
- Req line: 세그먼테이션으로 Request line 필드만 존재하는 세그먼트
- GET(AP) no rnrn: AP플래그가 있는 GET 요청이나 “0d0a0d0a”이 존재하지 않는 세그먼트
- HDR: 세그먼테이션 되어 세그먼테이션 되지 않은 HTTP 헤더를 포함하는 세그먼트
- HDR rnrn: 세그먼테이션 되지 않은 온전한 단일 HTTP 헤더
- HDR no rnrn: “0d0a0d0a”을 포함하고 있지 않는 세그먼트
- URI: 세그먼테이션으로 URI 필드만 가지고 있는 세그먼트
- URI nMethod: URI필드에 GET과 같은 페이로드가 존재하는 경우
- ST /: POST 요청이 세그먼테이션 되어 4바이트인 ST /만 존재하는 세그먼트
- BODY: 세그먼테이션된 BODY 세그먼트
- BODY(AP): AP플래그가 있는 BODY 세그먼트
- BODY nMehtod: GET, POST와 같은 문자열이 BODY 문자열에 포함된 세그먼트
- Multipart: multipart HTTP 헤더 또는 데이터 등이 있는 세그먼트
- Multipart nMethod: GET과 같은 문자열을 가지고 있는 multipart 데이터가 있는 세그먼트
'Rules > behavior' 카테고리의 다른 글
TCP Out-Of-Order (or segment loss) 탐지 (0) | 2019.12.23 |
---|---|
URL Scan과 Crawler (0) | 2019.04.05 |