2015년 4월 29일 수요일

Snort 분석(WEB-CLIENT Malformed PNG detected sRGB overflow attempt - 2nd)

지난 글에서 'WEB-CLIENT Malformed PNG detected sRGB overflow attempt' 룰에 의해 발생한 로그의 오탐 근거를 찾아냈었지만, 룰 개선으로 이어지지는 못했다. 룰을 개선해서 오탐을 줄이는 방법을 찾아보도록 하자. 일단 관련 로그 발생 현황.


PNG 처리 취약점을 이용하는 공격을 탐지하는 룰이 총 13개였는데, 그 중 6개의 룰에 의해 로그가 발생하고 있다. 이제 해당 로그 전체를 수집해서 룰 개선의 가능성을 확인해보자. (데이터베이스는 데이터 관리와 활용성을 극대화 시켜준다)


다음은 'Tools > Export Table Data/Result As' 메뉴를 이용하여 추출한 패킷 페이로드 데이터(16진수)를 사람이 읽을 수 있는 문자로 변환한 후, 6개의 필수 탐지패턴을 검색한 결과.

검색 명령어 '/\(sPLT\|sBIT\|iTXt\|cHRM\|sRGB\|iCCP\)'는 6개의 필수 탐지패턴 'sPLTsBITiTXtcHRMsRGBiCCP' 를 OR 조건으로 검색해준다.


그런데 필수 탐지패턴이 검색되지 않는 부분이 있다. 텍스트 데이터가 아닌 파일스트림 패킷 페이로드를 한글 환경에서 인/디코딩을 하다보면 보통 깨졌다고 표현하는 읽을 수 없는 형태로 변환되는 경우를 종종 볼 수 있는데, 'iTXt' 패턴이 깨져서 검색이 되지 않고 있는 상황.


총 450개의 행 중 검색되지 않는 행은 몇 개나 될까? 특정 패턴이 포함된 행만을 골라서 삭제하면 쉽게 확인할 수 있다. 명령어 'g/\(sPLT\|sBIT\|iTXt\|cHRM\|sRGB\|iCCP\)/d' 는 '//'사이에 나열된 패턴이 포함된 행만을 골라서 삭제해준다.


남은 24개의 행 전부 '깨진 iTXt' 패턴을 포함한 행일까? 명령어 'g/\(i\)\@<!txtxml/d'는 'i로 시작하지 않는 txtxml' 패턴을 포함한 행만을 삭제해준다. 참고로 '\(패턴1\)\@<!패턴2' 형식은 '부정형 후방탐색'으로 '패턴1로 시작하지 않는 패턴2'를 찾아준다.


'sPLTsBITiTXtcHRMsRGBiCCP' 을 포함하지 않은 24개의 행 중, '깨진 iTXt' 패턴을 포함한 행이 19개, 나머지가 5개다. 그런데 이 나머지 5개 행은 PNG 처리 취약점 공격 탐지룰과는 전혀 관계가 없는, 악성코드 삽입 테스트 과정에서 발생한 로그이다.

아무래도 Snort가 오동작을 한 것 같다.-_- 일단 정확한 분석을 위해서 '깨진 iTXt' 패턴을 정상 'iTXt' 패턴으로 수정 후, 관련없는 로그는 삭제하도록 하자. VIM에서 'u(ndo)'를 입력하면 최근 명령어가 취소되면서 원하는 시점으로 되돌아갈 수 있다.

로그 원복 후 

치환 명령어 '%s/\(i\)\@<!txtxml/iTXtXML/g' 를 이용해서 'i로 시작하지 않는 txtxml' 패턴, 즉 '깨진 iTXt' 패턴을 'iTXt' 로 치환을 마친 결과는 다음과 같다. '깨진 iTXt' 패턴을 정상으로 수정했으니 이제 관련없는 로그만 삭제하면 됨.


다음은 명령어 'v/\(sPLT\|sBIT\|iTXt\|cHRM\|sRGB\|iCCP\)/d'를 이용해서 'sPLTsBITiTXtcHRMsRGBiCCP' 패턴이 포함되지 않는 행만을 골라서 삭제한 결과. 정확히 445개의 로그만 남았다.


이제 로그에서 필수 탐지패턴을 찾고, 그 패턴의 맥락을 파악하는 작업을 445번 반복하면 됨(..) 저 텍스트 덩어리들을 쉽게 분석할 수 있는 방법이 없을까? 텍스트 덩어리를 쪼개서 동일한 규칙이 적용되는 틀에 집어넣어 보면 어떨까?


로그를 탐지패턴을 기준으로 쪼개면 패턴의 맥락 파악이 쉬워진다. 그러기 위해서는 기존 탐지패턴을 구분기호 'ㅋ'이 추가된 탐지패턴으로 치환해야 하는데, 이 때 '%s/치환 전 패턴/치환 후 패턴/' 명령어를 사용한다.

참고로 '/치환 후 패턴/' 영역은 정규표현식을 지원하지 않기 때문에 다음처럼 낭패를 볼 수 있다.


다행히 VIM은 '치환 전 패턴'을 의미하는 '&'라는 특수문자를 제공하기 때문에 다음과 같이 원하는 치환이 가능하다.


다음은 치환이 완료된 로그를 엑셀에 붙여넣은 후, '데이터 > 텍스트 나누기' 메뉴를 이용하여 구분기호 'ㅋ'을 기준으로 텍스트를 나눈 결과.


아직 끝난 게 아니다

해당 로그를 발생시킨 룰은,

1. 필수 탐지패턴 검사 후,
2. 검사가 끝난 지점부터 -8byte를 이동,
3. 이동이 끝난 지점부터 4byte의 값이 3,000보다 크면 탐지한다.

그렇기 때문에 정확한 분석을 위해서는 byte_test 검사 영역인 '탐지패턴 전 4byte'의 데이터도 필요하다.

일단 원본 로그(16진수)에서 명령어 'v/\(73504c54\|73424954\|69545874\|6348524d\|73524742\|69434350\)/d'를 이용해서 'sPLTsBITiTXtcHRMsRGBiCCP'의 헥사코드를 포함하지 않는 행 삭제.


이제 탐지패턴 전 4byte만 추출하면 된다. 명령어 '/.\{8}\(\(73504c54\|73424954\|69545874\|6348524d\|73524742\|69434350\)\)\@='를 이용하면 탐지패턴 전 4byte를 검색할 수 있다.

'패턴1\(패턴2\)\@=' 형식은 '긍정형 전방탐색'으로 '패턴2로 끝나는 패턴1'을 찾아주며, '.\{8}' 형식은 '8개의 문자'를 찾는 정규표현식인데, 16진수는 2개의 문자가 1byte이므로 4byte를 검색하게 된다.


검색된 4byte를 구분기호를 이용하여 엑셀로 불러온 후, 10진수로 변환했더니 byte_test 값이 3,000 미만인 로그가 36개나 된다. 3,000보다 클 때만 탐지해야 하는데, 이것도 Snort 버그인가(..)


일단 탐지패턴 전 4byte 값 십억을 기준으로 전체 로그를 나눴다. 전체 로그 중 십억을 넘는 행은 304개(68%). '패턴 전' 영역에서 PNG 파일스트림을 의미하는 'IHDR(Image Header)' 패턴을 포함한 행은 304개 중 27개(9%).


분할된 이미지 파일의 크기가 1GB? 아무래도 해당 길이값은 뭔가 다른 의미를 가지고 있을 듯 하다. 일단 패킷 구조는 정상.


'패턴 전' 영역에서 'IHDR' 패턴을 포함하지 않는 행만을 검색하면 304개 중 277개(91%)가 나오는데, 이미지 파일의 여러 추가 정보인 메타데이터를 표시하고 있다.


이번에는 전체 로그에서 탐지패턴 전 4byte 값이 십억 미만인 행 141개(32%) 중, '패턴 전' 영역에서 'IHDR' 패턴을 포함하지 않는 행만을 검색해보자. 141개 중 36개(25%)가 검색되며, 역시 메타데이터를 표시하고 있다.

'패턴 전' 영역에서 메타데이터를 포함하고 있는 행들은 탐지패턴 역시 메타데이터의 일부일 가능성이 높다. 그 얘기는 byte_test 옵션 역시 원래 목적인 chunck 길이가 아닌 메타데이터 영역을 검사했을 가능성이 높다는 뜻이다.


PNG Signature(89504E470D0A1A0A), Image Header(IHDR) 순으로 표시되고 있는 파일스트림의 구조를 보면 chunk 길이 byte는 IHDR이 표시된 위치에서 그리 멀지 않다는 사실을 알 수 있다.


길이를 재보자. 다음은 445개의 패킷 페이로드 중 'PNG Signature(89504E470D0A1A0A)' 패턴을 포함하고 있는 131개(29%)를 추출한 후, PNG Signature부터 탐지패턴까지의 byte만 추출한 결과.


엑셀의 'LEN()' 함수로 길이를 잰 후, 2로 나눴다. 최대값은 82byte. PNG Signature(8byte)와 chunck 패턴(4byte) 사이의 길이는 최대 70byte. chunck 길이 byte는 넉넉하게 잡아도 PNG Signature 이후, 100byte 이내에 위치하고 있다.


결국 해당 룰은 chunck 길이 영역에 대한 검사 위치 정보의 부족으로, PNG Signature 추적 후, 전송되는 이미지 데이터 영역 전체, 동일 세션에서 분할된 TCP 세그먼트 전체를 검사하고 있다.

결과적으로 445개의 페이로드 중 131개(29%)에 대해서만 룰 의도대로 chunk 길이 영역 검사 성공(..) 룰을 수정해보자. 굳이 'flowbit' 옵션을 사용할 필요는 없을 듯 하다.

alert tcp any any -> any any 
(msg:"WEB-CLIENT Malformed PNG detected chunck_패턴 overflow attempt"; 
content:"|89|PNG|0D 0A 1A 0A|"; 
  • 'PNG' Signature 검사
content:"chunck_패턴"; distance:0; within:100; 
  • 이전 문자열 검사가 끝난 지점(distance:0)부터 100byte 이내에서(within:100) 'chunck_패턴' 문자열 검사
byte_test:4,>,3000,-8,relative; 
  • 이전 문자열 검사가 끝난 지점(relative)부터 특정 위치의 byte를 찾아서 값이 3,000보다 큰지 검사
sid:6692;)

수집된 데이터의 분석 결과, 룰 수정 근거를 마련할 수 있었다. 수집된 데이터의 양이 많아질수록 분석 결과가 정확해질 가능성은 높아지며, 결국 룰 정확도 역시 높아질 것이다.

물론 로그 샘플링을 통해서도 비슷한 결과를 가져올 수 있다. 하지만 데이터 전수조사는 샘플링의 오차 한계를 극복하고, 측정된 정량적 근거 자료는 샘플링과는 비교할 수 없는 설득력을 제공해준다. 빅데이터가 각광받는 이유가 아닐까?

추가로 3,000보다 큰 chunck 길이값이 오버플로우 공격의 절대 기준은 아니기 때문에 정확한 위치의 길이값을 검사한 후, 쉘코드 등 추가 검사가 이루어져야 한다. 룰 조건과 일치한다고 해서 무조건 공격은 아니라는 얘기.

특정 룰에 의해 발생한 전체 로그의 분석을 통해 룰의 문제점 및 개발 의도와 일치 여부를 확인하고 개선 근거를 확보하는 사례를 살펴봤다.

컴퓨터 환경에서 'Garbage in Garbage out' 법칙은 정말 진리인 것 같다. 룰이 엉터리면 엉터리 로그인 오탐이 나올 수 밖에 없는 것. 반면 Garbage output을 잘 분석하면 input의 품질은 얼마든지 높일 수 있다.

처음부터 완벽한 룰을 만들기는 어렵지만, 그 룰의 입력 결과인 로그를 잘 정리하면 더 좋은 룰을 만들 수 있다. 사고 단위가 아닌 데이터 단위 분석이 필요한 이유가 될 것이다.

관련글

댓글 없음:

댓글 쓰기

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