[Anti-Exploit] ‘갓모드(GodMode)’ 공격의 시작, 제로데이 익스플로잇
OLE 자동화 배열 원격코드 실행 취약점 CVE-2014-6332
최근 인터넷 익스플로러(Internet Explorer) 환경에서 발생하는 ‘OLE 자동화 배열 원격코드 실행 취약점(CVE-2014-6332)’을 이용한 공격 사례가 급증하고 있다. 지난 11월에는 다수의 국내 주요 사이트가 해당 취약점을 이용한 ‘갓모드(GodMode)’ 공격으로 침해 당해 악성코드를 유포하는데 악용된 바 있다. 해당 취약점은 윈도(Windows) 95 이상의 OS와 IE 3 이상의 웹 브라우저에 영향을 주기 때문에 피해가 발생할 수 있는 범위도 넓어 지속적인 주의가 요구된다. 이 글에서는 CVE-2014-6332 취약점의 상세 분석을 통해 익스플로잇 동작 원리와 안랩 MDS(AhnLab MDS)의 익스플로잇 진단 방법을 알아본다.
CVE-2014-6332 취약점은 윈도 OLE(Object Linking and Embedding) 자동화 배열 원격코드 실행 취약점으로, 인터넷 익스플로러(Internet Explorer, 이하 IE)가 OLE 객체를 처리하는 과정에서 원격으로 코드를 실행하는 취약점이다. 이 취약점은 IE의 VBScript 엔진에서 배열의 크기를 재설정하는 과정에서 충분한 에러 처리를 수행하지 않아 발생한다. 배열의 크기를 재설정할 때 배열의 크기를 저장하고 있는 변수를 새로운 값으로 갱신하는데, 배열을 할당하는 과정에서 에러가 발생한 경우 이전 값으로 되돌리지 않는다. 따라서 초기 배열의 영역 이외의 메모리를 읽고, 쓰고, 접근하는 것이 가능하게 된다.
취약점 발생 모듈, 영향 받는 프로그램 등 CVE-2014-6332 취약점의 주요 사항은 다음과 같다.
취약점 발생 원인
1. VBScript에서의 배열
VBScript에서의 배열은 다음과 같은 ‘SAFEARRAY’라는 구조체로 표현된다.
[그림 1] SAFEARRAY 구조체
위의 구조체에서 중요한 변수는 pvData와 rgsabound[0].cElements이다. rgsabound[0].cElements는 배열을 구성하는 엘리먼트(Element)의 개수를 의미한다. pvData는 실제로 엘리먼트의 배열이 위치해 있는 메모리 주소로, 기본적으로 각각의 엘리멘트는 다음과 같이 16바이트 구조로 이루어져 있다. SAFEARRAY 구조체의 cbElements 값이 엘리먼트 한 개의 사이즈를 의미하기 때문에 이 값은 16으로 저장된다. 단, 이 값 또한 변경 가능하다.
[그림 2] 엘리먼트(Element) 구조체
예를 들어, [그림 3]과 같이 배열이 선언되어 있다고 가정해보자.
[그림 3] 배열 테스트 코드
[그림 3]의 배열 코드에는 배열의 크기가 15로 지정되어 있는데, VBScript에서는 0부터 15까지 총 16개의 엘리먼트로 생성된다. 또한 각 엘리먼트는 타입에 상관없이 저장 가능하다. [그림 3]에서 선언된 배열 arr에 대한 SAFEARRAY 구조체는 [그림 4]와 같이 생성된다.
[그림 4] 배열 테스트 코드에 대한 SAFEARRAY 구조체 메모리 내용
[그림 4]는 16개의 엘리먼트로 구성된 1차원 배열이고 실제 데이터는 0x035CDC88에 위치하며, [그림 5]와 같이 저장되어 있다.
[그림 5] 배열 테스트 코드에 대한 엘리먼트 메모리 내용
[그림 5]의 arr(0), arr(3), arr(6), arr(9)에는 문자열이 저장되어 있고, varType은 문자열을 의미하는 0x08로 되어 있다. dataHigh가 저장되어 있는 0x001EC874, 0x00210F7C, 0x001FaF7C, 0x035CFF44 주소를 확인해보면 문자열이 유니코드로 저장되어 있음을 알 수 있다.
[그림 6] 배열 테스트 코드에 지정한 문자열 메모리 내용
arr(1), arr(4), arr(7)에는 정수형 데이터가 저장되어 있고, varType은 정수를 의미하는 0x02로 되어 있다. 정수형 변수는 2 바이트로 저장되기 때문에 해당되는 값은 dataHigh의 상위 2 바이트이며, 각각의 값은 0x01, 0x0A, 0x07인 것을 확인할 수 있다.
arr(2), arr(5), arr(8)에는 부동소수점 데이터가 저장되어 있고, varType은 double형 부동소수점을 의미하는 0x05로 되어 있다. double형 부동소수점은 8 바이트로 저장되기 때문에 이에 해당되는 값은 dataHigh와 dataLow를 모두 확인해야 하며 각각의 값은 0x4000000000000000, 0x3FF1F9ADD3746F66, 0x4020666666666666인 것을 알 수 있다. 이러한 부동소수점 값은 [그림 7]과 같은 방법을 통해서도 확인할 수 있다.
[그림 7] 부동소수점 메모리 확인 코드
2. OLEAUT32!SafeArrayRedim()에서의 인티저 오버플로우(Integer Overflow)
VBScript에서는 “dim arr(x)”과 같은 형식으로 (x+1)개의 엘리먼트를 가진 배열을 생성할 수 있으며, 앞서 살펴본 바와 같이 메모리 상에 저장된다. 그러나 이 배열은 “ReDim arr(y)” 또는 “ReDim Preserve arr(y)”와 같은 방법을 통해 크기를 (y+1)로 변경할 수 있다. 이때 배열의 크기를 변경하는 함수가 OLEAUT32!SafeArrayRedim() 함수이다.
[그림 8] SafeArrayRedim() 함수 프로토타입
SafeArrayRedim() 함수는 배열 정보가 저장되어 있는 SAFEARRAY 구조체 주소와 새롭게 변경할 배열의 크기 정보를 가지고 있는 SAFEARRYBOUND 구조체의 주소를 인자로 받는다. [그림 5]의 예시에 나타난 배열 arr(15)를 arr(20)으로 변경하는 경우(“ReDim Preserve arr(20)”), 다음과 같이 두 번째 인자가 배열의 크기인 21로 전달되는 것을 알 수 있다.
[그림 9] SafeArrayRedim() 함수 인자 확인
이후 [그림 10]과 같이 SAFEARRAY 구조체에서 배열의 크기를 새로운 크기로 변경하고, 기존의 배열 크기와 새로운 배열 크기를 비교하여 메모리를 재할당한다.
[그림 10] SafeArrayRedim() 함수 내 인티저 오버플로우(Integer Overflow) 취약점
바로 이 과정에 취약점이 존재한다. 메모리 할당이 실패할 경우 SAFEARRAY 구조체에 이미 갱신해 둔 배열의 크기를 원래대로 복원해야 하는데, 이 과정이 누락되어 있다. 따라서 배열은 할당하지 않은 영역까지 접근하게 된다.
배열 크기는 엘리먼트 개수에 엘리먼트의 크기인 16바이트를 곱하여 구한다. [그림 10]의 경우0x770DAE00에서 새로운 배열의 크기와 기존 배열의 크기를 비교하는데, JGE opcode로 처리하게 되어 있다. JGE는 signed형 점프 명령이기 때문에 새로운 배열의 크기가 기본의 배열 크기보다 0x80000000 이상 큰 경우에는 오히려 음수로 인식하게 된다. 즉, 새로운 배열의 크기가 더 작은 것으로 인지하고 점프하지 않게 된다. 배열의 크기 차이가 0x80000000 보다 큰 경우는 두 배열의 엘리먼트 개수 차이가 0x08000000 보다 큰 경우이다. 이 경우에는 메모리를 새로 할당하게 되는데, 이 과정에서 메모리 할당에 실패하게 된다.
익스플로잇 동작 원리 상세 분석
[그림 11] 취약 모듈 OLEAUT32.dll
1. 2개 배열의 메모리 상의 배치 확인
앞서 알아본 바와 같이 VBScript의 배열 구조와 OLEAUT32.dll의 취약점을 이용하여 배열의 엘리먼트 타입을 임의로 수정할 수 있다. 이를 위해 2개의 배열을 선언하고, 그 중 하나는 배열 크기를 0x080000000 보다 크게 변경한다. 예를 들어 a0 값이 2라고 가정할 경우, 최종적으로 공격자가 원하는 두 배열의 배치는 [그림 12]와 같다.
[그림 12] Type Confusion을 위한 두 배열의 배치
위와 같은 배치를 얻기 위해 [그림 13]과 같은 코드를 반복적으로 수행한다.
[그림 13] Type Confusion을 위한 코드
위의 코드에서는 aa 배열과 ab 배열을 동일한 ‘a0’ 크기로 선언한 후, aa 배열의 크기를 0x08000000+a0으로 변경한다. 이로써 비록 aa 배열의 크기 변경은 실패하지만 배열의 크기 값이 커진 상태로 남아 있게 되어 임의의 메모리에 접근이 가능하게 된다. 이를 이용해 [그림 12]와 같이 aa 배열과 ab 배열이 8바이트 간격으로 떨어져 있을 경우 ab(0)의 값이 aa(a1)의 타입(Type)이 되는 것이다.
실제 해당 악성코드는 원하는 배치가 나올 때까지 위 함수를 총 400번 수행하도록 되어 있다. 원하는 배치가 나오면 이후 메모리에 원하는 값을 쓰며, 그 값의 타입 또한 원하는 대로 변경할 수 있게 된다.
이처럼 2개의 배열을 선언하고 두 배열의 간격이 8 바이트의 차이가 나는 경우, 한쪽 배열의 타입이 다른 쪽 배열의 값이 됨을 이용하여 타입을 마음대로 조정하는 방법을 이 악성코드에서는 ‘Type Confusion’이라고 지칭하고 있다.
2. 갓모드(GodMode) 진입
일반적으로 웹 브라우저가 VBScript를 사용할 경우 보안 상의 이유로 행위를 제한한다. 이러한 제한은 내부에 갖고 있는 safemode 플래그를 통해 구현되어 있다. 역으로 safemode 플래그의 값이 수정될 경우 웹 브라우저의 VBScript가 어떠한 행위도 할 수 있음을 짐작할 수 있다. CVE-2014-6332 취약점은 이러한 방법을 이용해 VBScript로 어떠한 행위도 수행할 수 있게 한다. 이를 갓모드(GodMode)라고 지칭한다.
safemode 플래그는 [그림 14]와 같이 COleScript 내의 0x168만큼 떨어진 곳에 존재한다.
[그림 14] safemode 플래그 확인 코드
IE 브라우저를 통해서 VBScript를 띄운 경우, 이 플래그 값은 항상 0x0E이다. [그림 14]의 코드를 보면 0x0B와 AND 연산하여 ‘참’이 될 경우 ‘1’을 리턴하게 된다. 1이 리턴되면 safeMode로 간주하고 이로 인해 행위가 제한된다. 반면, 0x0B와 AND 연산하여 ‘거짓’이 되면 ‘0’을 리턴한다. 0이 리턴되면 Not safeMode로 간주하며, 이로 인해 행위에 제한을 받지 않게 된다.
또한 [그림 14]의 코드를 보면 0x168 만큼 떨어진 곳에 플래그가 위치한다(단, 윈도 버전에 따라 다소의 차이가 있다). 따라서 공격 코드에서는 IE 브라우저에서 기본값으로 사용하는 0x0E를 통해 플래그의 위치를 찾는다. 해당 플래그를 찾는 코드는 [그림 15]와 같다.
[그림 15] safemode 플래그 검색 코드
다음 [그림 16]은 safemode 플래그의 값을 ‘0’으로 수정하는 코드이다.
[그림 16] safemode 플래그 변경 코드
[그림 16]의 코드에서는 ‘myarray’라는 변수를 선언하고 aa(a+2)의 위치에 myarray의 주소를 저장하고 있다. 그리고 Type Confusion을 통해 aa(a+2)의 타입을 배열로 변경하였다. myarray의 값은 chrw 함수를 이용해 알아보기 어렵게 지정하고 있지만, 해당 값을 확인해보면 SAFEARRAY 구조체의 형태를 갖추고 있다. 해당 값은 [그림 17]과 같이 지정된다.
[그림 17] 메모리 전체 접근을 위한 배열 선언
위와 같이 새롭게 지정된 배열은 각 엘리먼트의 사이즈가 1 바이트(cbElements)이고, 엘리먼트의 개수는 0x7FFF0000(rgabound[0].cElements)이 된다. 이렇게 배열을 잡는 이유는 1 바이트 단위로 모든 메모리에 접근하기 위함이다. 결국 2차원 배열에 접근하는 방식과 같이 “aa(a1+2)(i+&h11c+k)=ab(4)”처럼 하여 해당 메모리를 ‘0’으로 채운다. ab(4)의 값은 지정한 바 없기 때문에 ‘0’으로 채워져 있고, ab(4) 값의 사이즈가 16이기 때문에 safemode 플래그 주소 보다 4바이트 작은 곳으로 지정하여도 safemode 플래그 값은 0으로 변경된다. 이로써 공격자는 CVE-2014-6332 취약점을 이용해 갓모드에 진입하여 악의적인 행위를 수행할 수 있게 된다.
DICA 엔진을 통한 CVE-2014-6332 취약점 진단 방법
CVE-2014-6332 취약점을 이용한 악성코드는 ‘악의적인 행위’만 변경하여 다양한 형태의 변종을 생성할 수 있다. 따라서 다수의 변종까지 효과적으로 대응하기 위해서는 공격 시도 시점 이전, 즉 익스플로잇이 발생하는 시점에 이를 탐지할 수 있는 방안이 요구된다. 안랩 MDS의 DICA 엔진에서는 CVE-2014-6332 취약점이 발생하는 원인이 되는 부분을 어셈블리 단계에서 진단하기 때문에 공격 시도 부분의 변경 여부와 상관없이 관련된 모든 변형 악성코드를 진단 및 방어할 수 있다. @
- AhnLabMDS 개발팀 김희승 선임