alert tcp $EXTERNAL_NET any -> $HTTP_SERVERS $HTTP_PORTS
(msg:"WEB-MISC weblogic/tomcat .jsp view source attempt";
flow:to_server,established;
- TCP 세션이 성립된 트래픽 중 Server(Syn을 받은)로 향하는
- HTTP URI 영역에서 '.jsp' 문자열 검사
- '//' 사이의 문자열과 일치하면 탐지 회피(!)
- 행(Line)의 첫번째 문자열부터 검사(^)
- 하나 이상(+)의 문자(\w)와 공백(\s)으로 시작하며, '줄바꿈(\n)', '공백(\s)', '?(\?)' 문자열을 제외한 임의의 문자로 이어지는 .jsp
- '[ab]'는 'a 또는 b', '[^ab]'는 'a, b'를 제외한 나머지 임의의 문자 하나
- '/s'는 정규표현식 메타문자 '.'의 검사범위를 줄바꿈 문자까지 확대('.'이 없는데 왜 사용했는지 모르겠음 -_-)
- '/m'은 앵커(Anchor)문자 '^'의 검사범위를 전체 행으로 확대
- '/i'는 대소문자 구분 해제
reference:bugtraq,2527;
classtype:web-application-attack;
sid:1054;
rev:10;)
해당 룰은 2001년에 발표된 'Weblogic 및 Tomcat과 연동된 JSP 소스코드 노출 취약점(Bugtraq ID : 2527)을 이용한 공격'을 탐지하고자 한다. 웹 요청 시 JSP 파일명을 URL 인코딩해서 보내면 소스코드가 노출된다는 것.
Tomcat and WebLogic's inbuilt webserver will return the source code of JSP files when an HTTP request contains URL encoded replacements for characters in the filename.
공격코드 예시는 다음과 같다. (웹서버가 JSP 파일을 인식하지 못하도록 함)
http://www.example.com/examples/jsp/num/numguess.js%70
http://www.example.com/examples/snp/snoop%252ejsp
해당 룰이 의도대로 동작했는지 확인해보자. 해당 룰에 의해서 중복되지 않은 473개의 로그가 발생했다.
데이터베이스에서 추출한 로그는 다음과 같다. 그런데 뭔가 이상하다.
2개의 패킷이 합쳐진 로그가 89개나 된다. 버그인가?
- 2개의 패킷이 합쳐진 패턴을 찾는 정규표현식은 다음과 같으며, 'v/패턴/d' 명령어는 '패턴'을 포함하지 않는 행만을 골라서 삭제해준다. '패턴'을 포함한 행만 삭제하고 싶을 때는 'g/패턴/d'
pcre : (get|post)\s\/.+?(?=(get|post)\s\/)
pcre 구조 : 'GET(또는 POST) /'로 시작해서 'GET(또는 POST) /'로 끝나는 문자열을 찾되, 두번째 'GET(또는 POST) /'는 검색에서 제외한다.
'-l c:\snort\log' 옵션에 의해 저장된 패킷을 직접 확인해보자. 먼저 'snort.log.일련번호' 형식의 파일들을 모두 합쳐야 해당 룰 관련 패킷만을 추출하는 작업이 쉬워진다.
모두 379개의 패킷이 검색되었다. 'snort.log.jsp_all.pcap'란 이름으로 저장.
- Wireshark도 정규표현식을 지원한다. 다음 정규표현식을 이용해서 URI 영역에서 '.jsp'의 대소문자 및 대소문자와 대응하는 URL 인코딩 패턴을 검색해봤다. 참고로 Wireshark의 정규표현식은 '메타문자 예외처리(단순 문자열로 인식)' 또는 '\메타문자' 사용 시 '\'를 두번 사용해야 인식한다.
pcre 구조 : 소문자 'p'를 URL 인코딩하면 '%70'인데 '%2570'처럼 더블 퍼센트 인코딩되는 경우도 있어서 표현식에 추가 (물론 해커의 보안장비 우회 시도일 수도 있음)
패킷이 합쳐진 현상은 없다. Snort 버그 맞네(..)
해당 룰은 content 옵션을 통해 URI 영역에서 '.jsp' 문자열이 있는지를 1차 검사한 후, pcre 옵션을 이용해서 '특정 형태의 .jsp' 문자열이 없는지를 2차 검사한다.
2차 검사에 사용된 정규표현식대로면 하나 이상(+)의 문자(\w)와 공백(\s)으로 시작하며, '줄바꿈(\n)', '공백(\s)', '?(\?)' 문자열을 제외한 임의의 문자로 이어지는 .jsp 라는 의미인데, 뭐래는 건지 잘 모르겠다(..) pcre 옵션에 적용된 표현식을 실제 사용해보자.
해당 표현식은 임의의 문자와 공백(\w+\s+)으로 시작하는데, URI 영역은 줄바꿈 문자를 포함한 공백이 없도록 설계되었기 때문에 HTTP 영역 전체를 검색 범위로 지정했다. 37개의 패킷이 검색됐다.
그런데 회피 옵션(!)을 사용했기 때문에 검색이 되면 탐지하면 안되는데? 탐지를 안했으면 패킷 저장도 안했을텐데?
검색된 패턴은 모두 JSP 파일에 대한 웹 요청이다. 룰 개발자는 왜 이런 패턴을 힘들게 검사해서 탐지를 회피하려고 했을까? 다음은 해당 pcre 표현식을 데이터베이스에서 추출한 로그에 적용한 결과이다. Wireshark와 동일한 검색 결과.
- JSP 파일에 대한 웹 요청 패턴을 찾는 정규표현식은 다음과 같은데, URI는 줄바꿈 문자를 포함한 공백이 존재할 수도 없기 때문에 '\n'과 '\s'는 사용하지 않았으며, VIM의 메타문자 '[]'는 정규표현식을 인식하지 않기 때문에, 수량자 '?'는 '?'나 '\?' 모두 그냥 문자열로 인식한다.
pcre : ^\w+\s+[^\n\s\?]*\.jsp
pcre 구조 : 하나 이상의 문자와 공백, 그리고 '.jsp' 사이에 '줄바꿈(\n)', '공백(\s)', '?' 문자가 없는 패턴 검색
룰 개발자의 표현식을 사용하면 '?'로 시작하는, 즉 JSP 파일명이 변수값으로 사용된 패턴은 검색이 안된다는 사실을 알 수 있다.
해당 룰은 content 1차 검사 통과 후, pcre 2차 검사를 통과하지 못하면 탐지하는 구조다. 즉 해당 룰은 JSP 파일명이 변수값으로 사용된 패턴만 탐지한다.
변수값은 웹 요청된 파일의 최종 결과에만 영향을 줄 뿐이다. 왜 룰 개발자는 JSP 파일에 대한 웹 요청 패턴의 탐지는 회피하고, JSP 파일명이 변수값으로 사용된 패턴만 탐지하려고 했을까?
아무래도 줄바꿈 문자(\n)와 공백(\s)을 제외해서 pcre 검사 범위를 URI 영역으로 제한하고, ‘\?’ 패턴을 제외해서 변수값으로 사용된 JSP 파일 패턴을 제외하는 식의 복잡한 정규표현식을 만드는 과정에서 머리가 너무 복잡해진 나머지 회피 옵션(!)을 잘못 사용하는 실수를 한 듯하다.
- JSP 파일명이 변수값으로 사용된 패턴을 검색하는 정규표현식은 다음과 같으며 '?'로 시작해서 '.jsp'로 끝나는 문자열을 의미.
pcre : \?.*\.jsp
JSP 파일에 대한 웹 요청 패턴을 삭제하면, JSP 파일명이 변수값으로 사용된 패턴만 남는데 전부 JSP가 아닌 파일을 요청하는 과정에서 JSP 파일명이 변수값으로 사용된 경우. 결국 해당 룰은 버그가 발생하지 않는 한, JSP 파일에 대한 웹 요청을 탐지하지 못한다.
중요한 기술 배경 하나 더
Snort는 룰 검사 전에 'http_inspect' 라는 전처기(preprocessor)를 통해 URL 인코딩된 HTTP 데이터를 미리 디코딩한다. 다음 2개의 룰은 URL 인코딩되지 않은 '.jsp'는 물론, '.js%70', '%252ejsp' 등 인코딩된 패턴까지 탐지한다.
alert tcp any any -> any any (msg:"test1"; content:".jsp"; http_uri; sid:1000000;)
alert tcp any any -> any any (msg:"test2"; uricontent:".jsp"; sid:1000001;)
그러나 다음 2개의 룰은 오로지 인코딩되지 않은 순수한 문자열 '.jsp' 패턴만을 탐지한다. 'http_inspect' 전처리기는 'http_uri(uricontent)' 옵션만을 지원하기 때문.
alert tcp any any -> any any (msg:"test3"; pcre:"/\.jsp/"; sid:1000002;)
alert tcp any any -> any any (msg:"test4"; content:".jsp"; sid:1000003;)
룰 개발자 역시 'content'와 'http_uri' 옵션을 이용하여 인코딩되거나, 되지 않은 모든 형태의 '.jsp' 패턴을 검사한 후, 'pcre' 옵션으로 한번 더 검사하여 인코딩되지 않은 패턴만을 걸러내는 룰 구조를 원했을 것이다.
해당 취약점을 이용한 공격을 탐지하기 위해서는 URI 영역의 ‘.jsp’ 패턴, 즉 파일 확장자 영역의 인코딩 여부만 검사하면 된다. 그리고 content와 pcre 옵션이 동일한 패턴을 검사할 경우, content 검사가 성공하면 pcre는 같은 패턴을 한번 더 검사하게 된다.
즉 content가 URI 영역을 검사했다면 pcre 역시 URI 영역을 검사하게 된다. 룰 수정은 간단하다.
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;)
관련 글
alert tcp any any -> any any (msg:"test1"; content:".jsp"; http_uri; sid:1000000;)
alert tcp any any -> any any (msg:"test2"; uricontent:".jsp"; sid:1000001;)
그러나 다음 2개의 룰은 오로지 인코딩되지 않은 순수한 문자열 '.jsp' 패턴만을 탐지한다. 'http_inspect' 전처리기는 'http_uri(uricontent)' 옵션만을 지원하기 때문.
Five options search normalized fields in HTTP requests, responses, or both, as appropriate
alert tcp any any -> any any (msg:"test3"; pcre:"/\.jsp/"; sid:1000002;)
alert tcp any any -> any any (msg:"test4"; content:".jsp"; sid:1000003;)
룰 개발자 역시 'content'와 'http_uri' 옵션을 이용하여 인코딩되거나, 되지 않은 모든 형태의 '.jsp' 패턴을 검사한 후, 'pcre' 옵션으로 한번 더 검사하여 인코딩되지 않은 패턴만을 걸러내는 룰 구조를 원했을 것이다.
개발자가 원한 룰 구조 |
해당 취약점을 이용한 공격을 탐지하기 위해서는 URI 영역의 ‘.jsp’ 패턴, 즉 파일 확장자 영역의 인코딩 여부만 검사하면 된다. 그리고 content와 pcre 옵션이 동일한 패턴을 검사할 경우, content 검사가 성공하면 pcre는 같은 패턴을 한번 더 검사하게 된다.
즉 content가 URI 영역을 검사했다면 pcre 역시 URI 영역을 검사하게 된다. 룰 수정은 간단하다.
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;)
관련 글
댓글 없음:
댓글 쓰기