2015년 6월 24일 수요일

Snort 분석(WEB-MISC weblogic/tomcat .jsp view source attempt - 3rd)

지난 글에서 기존 룰의 문제점을 찾고, URL 인코딩된 패턴만을 검사할 수 있도록 다음과 같은 룰 수정을 시도했었다.

(수정 전)
alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS 
(msg:"WEB-MISC weblogic/tomcat .jsp view source attempt"; 
flow:to_server,established; 
content:".jsp"; nocase; http_uri; 
pcre:!"/^\w+\s+[^\n\s\?]*\.jsp/smi"; 
metadata:service http; reference:bugtraq,2527; classtype:web-application-attack; sid:1054; rev:10;)

(수정 후)
alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS 
(msg:"WEB-MISC weblogic/tomcat .jsp view source attempt"; 
flow:to_server,established; 
content:".jsp"; nocase; http_uri; 
pcre:!"/\.jsp/i"; 
metadata:service http; reference:bugtraq,2527; classtype:web-application-attack; sid:1054; rev:10;)

그런데 해당 룰은 인코딩된 패턴이 HTTP 메시지 바디에 위치하는 POST 메소드 트래픽은 검사하지 못한다는 옛 동료의 제보(?)를 받게 되었다.


기껏 룰을 수정했더니 또 구멍이 발견됐다. 그런데 사실 이 문제는 현장에서 매우 자주 발생한다. 이유는 웹 요청 메소드 GET과 POST의 데이터 전송 방식 차이 때문. RFC 규약이 정의하고 있는 웹 요청 트래픽 구조는 다음과 같다.


GET 메소드는 변수값 등의 데이터를 'Request-Line', 즉 URI에 실어서 보내는데 POST는 'message-body'도 이용한다. 그래서 '변수값' 등의 검사가 URI 영역으로 제한된 룰은 POST 메소드를 이용한 공격을 검사하지 못할 수 있다.

대부분의 공격이 웹을 통해 발생하는 상황에서 POST 메소드를 이용한 웹 요청을 검사하지 못한다는 것은 감시체계에 매우 큰 구멍이 뚫렸다는 것을 의미한다. 어떻게 하면 좋을까?

답은 생각보다 간단하다. 메소드별로 룰을 따로 만들면 됨. 하나의 룰로 모든 공격을 다 검사하겠다는 욕심만 버리면 된다는 것.

(GET 메소드 검사 룰)
content:"GET"; nocase; http_method;
content:".jsp"; nocase; http_uri;
pcre:!"/\.jsp/i";

(POST 메소드 검사 룰)
content:"POST"; nocase; http_method;
content:".jsp"; nocase; http_uri;
pcre:!"/\.jsp/iP";
  • 수정자 'P를 이용해서 '.jsp패턴의 검사 위치를 POST 메소드로 전송되는 데이터 영역인 'message-body'로 제한

참고로 Snort가 아닌 보안장비는 pcre 옵션의 수정자 'P' 를 지원하지 않을 수도 있는데(사실 그런 경우가 많음), 모로 가도 서울만 가면 된다고, 그럴 때는 다음과 같은 변칙? 응용이 가능하다.

'message-header'와 'message-body'는 2개의 CRLF(0D0A)로 구분되기 때문에 POST 메소드일 경우 2개의 CRLF(0D0A) 이후에 나타나는 '.jsp' 패턴을 검사하면 'message-body'로 검사 영역을 제한할 수 있다.

(POST 메소드 검사 룰)
content:"POST"; nocase; http_method;
content:".jsp"; nocase; http_uri;
content:"|0d 0a 0d 0a|"; distance:0;
  • 'distance:0' 옵션을 이용해서 이전 패턴 검사가 끝난 지점부터, 즉 URI 영역에서 '.jsp' 검사가 끝난 지점부터 '0d 0a 0d 0a패턴을 찾는다.
pcre:!"/\.jsp/iR";
  • 'R옵션을 이용해서 이전 패턴 검사가 끝난 지점부터, 즉 '0d 0a 0d 0a패턴 검사가 끝난 지점부터 '.jsp패턴을 찾은 후, 부정(!) 옵션을 이용해서 탐지에서 제외.

그럼 이만 즐.. 하고 싶었으나(..)


조금만 더 따져보자. GET, POST 패턴은 공격 특징이 전무할 뿐더러, 가장 많이 사용되는 웹 요청 메소드. 한마디로 변별력이 없어도 너무 없다. 빈번한 패턴매칭으로 인해 전체적인 룰 성능에 악영향을 줄 가능성도 있다.

다음 룰을 보자. 첫번째 룰은 'message-body' 영역을 검사하기 때문에 ('message-body' 영역이 없는) GET 메소드를 이용한 JSP 파일 요청을 검사하지 않으며, POST 메소드를 이용한 JSP 파일 요청만을 검사한다.

그리고 첫번째 룰에서 검사되지 않은, GET 메소드를 이용한 JSP 파일 요청은 두번째 룰에서 검사한다. GET, POST 패턴에 의한 불필요한 패턴매칭을 줄일 수 있는 것이다.

(첫번째 룰)
content:".jsp"; nocase; http_client_body;
  • 'http_client_body' 는 pcre 수정자 'P와 같은 의미의 content 수정자
pcre:!"/\.jsp/iP";

(두번째 룰)
content:".jsp"; nocase; http_uri;
pcre:!"/\.jsp/i";

그런데 이게 제대로 동작할지는 잘 모르겠다.-_- Snort는 가장 긴 패턴을 기준으로 1차 필터링 후, 필터링된 트래픽에 대해 프로토콜, IP, 포트 정보 등이 일치하면, 그리고 추가 패턴이 있으면 2차 패턴 필터링을 하는데, 이때 수정자 필터링을 순서대로 해주나?

룰 성능을 따지는 건 좋은데, 자칫 뒤죽박죽 안드로메다로 가고 있는 룰 관리 체계를 구경하게 될 수도 있다. (그래서 룰 관리 체계가 중요) 한마디로 너무 복잡해진다. 잘 관리한다고 누가 알아주는 것도 아니고.

개 꼬였네

패킷 누수 등의 성능 저하 문제가 발생하지 않는다면, 정신건강을 위해서라도 GET, POST 패턴 구분을 해주는 게 낫지 않나 싶다. 상황에 맞는 최선의 선택을 하기 바란다. 이제 진짜로 끝.


관련 글

댓글 없음:

댓글 쓰기

크리에이티브 커먼즈 라이선스