※ 이 포스팅은 아직 조금 더 업데이트 될 내용이 있습니다.


오랜만에 포스팅이다. 작년에는 Heartblead 취약점과 Bash 취약점이 큰 이슈였다. 올해에도 역시 그에 견줄만한 큰 취약점이 나왔다. HTTP.sys 취약점이다. .sys 파일은 .exe, .dll 파일처럼 PE파일의 일종이다. 이 PE파일에서 Integer Buffer Overflow 취약점이 존재한다는 내용이며 이는 메모리 영역의 알정부분 노출(Heartblead 취약점과 비슷함) 더 나아가 Remote Execution까지 가능케 할수도 있다는 것이 이 취약점의 핵심이다. 아래의 링크는 공식적으로 공개된 해당 취약점에 대한 개괄이다.


CVE-2015-1635 : http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-1635

MS15-034 : https://technet.microsoft.com/en-us/library/security/ms15-034


위의 개괄을 보면 아래의 윈도우즈 버전에서 취약하다는 것을 알 수 있다.


취약한 Windows 버전 : 

Microsoft Windows 7 SP1

Windows Server 2008 R2 SP1

Windows 8

Windows 8.1

Windows Server 2012 Gold and R2


그렇다면 HTTP.sys 파일이 언제 사용되는 파일이며 그것을 분석하기 위해서는 어떤 환경이 필요한지를 알아보자. 그것을 알기 위해 우리는 Internet Information Service (IIS) - Windows에서 제공되는 웹서버 -가 어떤 식으로 동작하는지 살펴봐야 한다.


IIS는 Windows에서 제공하는 웹서버이다. 웹서버는 기본적으로 Client의 요청을 처리하여 응답을 돌려주는 것이 가장 중요한 역할이다. 이 때 IIS와 Client 사이에 존재하는 것이 HTTP.sys라는 HTTP Listener이다. 아래의 그림을 보자.

Client가 웹서버에 HTTP Request를 보내면, 웹서버에서 HTTP Listener인 HTTP.sys가 그 요청을 먼저 받아서 처리를 한다. 처리한 내용을 w3wp.exe라는 IIS worker process에게 전달을 한다. 그 요청을 w3wp.exe가 처리를 하여 HTTP Response를 HTTP.sys에게 주면 그것을 Client에게 전달하는 방식이다. 좀 더 자세한 설명은 link를 참고하기 바란다.


여기서 중요한 사실은 HTTP.sys는 Kernel 모드에서 동작한다는 것이다. w3wp.exe는 User 모드 프로세스이다. 즉, Kernel 모드에서 User 모드 프로세스로 Context Switching이 일어난다는 것을 알고 분석을 시작해야 한다. 위의 그림에서 취약점이 일어나는 과정은 HTTP.sys가 Client의 HTTP Request를 w3wp.exe로 전달할 때 그것을 처리하는 부분에 있다.


이것을 분석하기 위해서는 HTTP.sys를 디버깅하는 과정이 필요하다. 그러기 위해서는 당연히 Debugger가 필요한데, 여기서 Windbg라는 Debugger를 이용해보려고 한다. Windbg는 Microsoft 사에서 제공하는 Tool로 User 모드와 Kernel 모드 프로세스를 모두 디버깅할 수 있는 Tool이며(Kernel 모드 디버깅을 지원한다) Microsoft 홈페이지에서 무료로 다운받을 수 있다. 사실 Kernel 모드 디버깅은 한번도 해보지 않은 사람에게는 환경 구성하는데도 조금 시간이 걸릴 수도 있다. 인터넷에 관련된 무수히 많은 정보가 있으니 잘 찾아서 구성해보시길 바란다.


Kernel 모드 디버깅을 하는 준비과정은 전부 마친 상태에서, 취약점을 다시 한번 되세긴 후 자세한 분석에 들어가보자. HTTP.sys 취약점은 Client로부터의 HTTP Request를 HTTP.sys가 처리하는 과정에서 발생하는 Integer Overflow 취약점으로 DoS(Blue Screen)나 RCE(Remote Command Execution) 등을 야기시킬 수 있다.


취약점을 분석할 때 가장 먼저 할 일은 정상 패킷을 통해 정도(定道)를 분석하는 것이다. 정상적인 GET Method를 요청할 경우 HTTP.sys는 어떤 작업을 하는지 대략 중요한 포인트를 집어서 설명해보자. Windbg를 통해 아래의 함수에 Breakpoint를 걸어보자. 아래의 함수들은 HTTP.sys내에 존재하는 함수들이다.


# Breakpoint List

UlpParseRange

UlPrepareCacheMissRangeResponse

UlAdjustRangesToContentSize

UlpBuildCacheEntry

UlBuildFastRangeCacheMdlChain

UlBuildFastRangeCacheMdlChain+0x311


# Normal Request

GET /welcome.png HTTP/1.1

Host: 192.168.0.100

Range: 18-1365


# welcome.png FileSize : 184946


처음에 아래와 같이 정상적인 요청을 할 경우 아래와 같은 순서로 Break가 걸린다.

1. UlpParseRange

2. UlPrepareCacheMissRangeResponse

3. UlAdjustRangesToContentSize


그런데 동일한 요청을 두번째 요청할 경우에는 아래와 같은 순서로 Break가 걸린다.

1. UlpParseRange

2. UlpBuildCacheEntry

3. UlAdjustRangesToContentSize


UlpBuildCacheEntry를 호출하는 이유는 웹서버의 성능을 높이기 위함이다. 동일한 요청에 대해서는 웹서버에서 보통 Cache된 Content를 사용하여 속도를 높이곤 한다. HTTP Cache에 대한 자세한 설명은 link를 참고하길 바란다. 이번 취약점에서 또 중요한 점 중에 하나가 HTTP Cache를 이용할 때 발생한다는 점이다. 따라서 두번째 요청할 경우에 (UlpParseRange → UlpBuildCacheEntry → UlAdjustRangesToContentSize) 초점을 맞추어서 분석해보자.


중요한 곳은 UlAdjustRangesToContentSize이다. 이 함수는 Range-Low + (Range-High - Range-Low  + 1) 즉, Range-High + 1을 계산하여 그것이 실제 파일 사이즈보다 작은지를 검증하는 Security Check 함수이다. (상식적으로 생각해봤을 때 Range-High + 1이 실제 파일사이즈보다 크다면 뭔가 이상한 일이 발생하겠지?)



Range: 18-1365로 요청을 했을 때, Range-High + 1 = 1366(0x556)이다. 1366 < 184946이므로 Security Check OK이다. 그리고 이 함수의 Return 값은 범위의 길이이다. 즉, Return 값은 1365 - 18 + 1 = 1348이다.



이후에 UlBuildFastRangeCacheMdlChain함수를 호출하게 되는데 이 함수 내에서 _imp__IoBuildPartialMdl 함수를 호출한다. 이 때 1348를 파라미터로 사용하여 1348 byte만 메모리를 할당하게 된다. 그렇게 만들어진 메모리 영역에 Contents를 넣고 Response로 보내는 것이다.

위의 그림은 _imp__IoBuildPartialMdl 함수에 들어가기 바로 직전에 esp 스택 값을 프린트해본 것이다. 0x544(1348)이 파라미터로 쓰인다는 것을 알 수 있다. 이 함수는 요청한 파일에 대하여 메모리를 할당하는 역할을 한다. (1348 만큼의 메모리를 할당할 것이다.)


정도(定道)를 이해했으면 취약점이 발생하는 요청을 분석해보자.


# Exploit

GET /welcome.png HTTP/1.1

Host: 192.168.0.100

Range: 18-18446744073709551615


Range에서 18은 임의의 0보다 큰 값을 택한 것이다. 18446744073709551615는 16진수로 0xFFFFFFFFFFFFFFFF이다. 우선 처음 요청을 하고, 그 다음 같은 요청을 또 해서 UlpBuildCacheEntry함수를 호출해보도록 하자. 이 함수에서의 Security Check를 해보면 0xFFFFFFFFFFFFFFFF + 1 = 0x10000000000000000 이어야 하는데, 64bit 이상 표현할 수 없는 한계 때문에 0x0000000000000000이 되버린다. 0x0은 실제 FileSize보다는 작을 것이다. 반면에 Return 값은 0xFFFFFFFFFFFFFFFF - 0x18 + 1 = 0xFFFFFFFFFFFFFFEE가 된다. 이 값을 Unsigned Integer로 취급하므로 이 값은 어마어마하게 큰 값이 된다. 이 다음 UlBuildFastRangeCacheMdlChain 함수를 호출하면 어떻게 될까? 

UlBuildFastRangeCacheMdlChain 함수 내에 있는 _imp__IoBuildPartialMdl 함수에서 0xFFFFFFFFFFFFFFEE만큼의 메모리를 할당하게 된다. 그런데 실제로 FileSize는 이 값보다는 훨씬 적을 것이다. 이때 FileSize 이상의 메모리를 긁어오는 과정에서 Blue Screen Of Death(BSOD)가 발생하게 된다.


조금 복잡한 내용이므로 간단하게 요약을 아래의 그림과 같이 해봤다.


핵심은 두번째 패킷을 보내는 과정에서 Integer Overflow로 인해 BSOD취약점이 발생한다는 것에 있다. 그런데 이 취약점을 잘 보면 2014년에 발표된 HeartBleed 취약점과 굉장히 비슷하다. 이 취약점은 임의의 Memory Contents를 노출시킨다는 취약점을 가지고 있었던 반면, 이번 HTTP.sys취약점은 BSOD를 야기시킨다. 결과는 다르나 취약점이 발생하는 원인은 비슷하다는 것이다. HeartBleed는 link에 자세하게 설명되어있다.


일반적으로 취약점이 발표될 때 보안 전문가가 확인해야 될 Check List는 아래와 같다.

1. 취약점의 원인과 탐지 규칙 생성

2. 관제 대상 서버에 대하여 취약점 점검 (PoC코드 사용 등등)

3. 조치 방법


이번 취약점 같은 경우, 취약점의 영향력이 BSOD이기 때문에 함부로 PoC코드를 실행했다가 서버가 다운되면 어떻하는지 걱정에 앞서서 PoC코드를 돌리지 않는 경우도 있다. BSOD가 발생하는 취약한 시스템에서 아래의 PoC코드를 무수히 많이 실행시켜 보았으나 한번도 Blue Screen이 발생하지 않았다. 그 이유는 위에 설명한 것을 바탕으로 생각해보면 자연스럽게 이해될 수 있다.

PoC 코드 : https://www.exploit-db.com/exploits/36773/


그 이유를 설명해보면 이렇다. Range: 0-18446744073709551615과 같이 요청을 한번 하고 두번째 했을 때, UlAdjustRangesToContentSize는 Range가 0xFFFFFFFFFFFFFFFF + 1 = 0이므로 FileSize보다 작다고 인식할 것이며, 0xFFFFFFFFFFFFFFFF - 0 + 1 = 0을 Return할 것이다. 아래의 그림은 Range: 0-18446744073709551615과 같이 요청했을 때의 UlAdjustRangesToContentSize의 Return값이다. 


실제 FileSize는 184946인데 할당한 메모리는 0밖에 안 된다. 이럴 경우 당연히 BSOD는 발생하지 않을 것이며 웹서버는 Requested Range not satisfiable를 의미하는 416에러를 응답값으로 준다.


416에러를 받았다는 것은 결국 정상적으로 에러를 처리했다는 뜻이 된다. 에러를 정상적으로 처리했을 때 BSOD는 당연히 일어나지 않는다. 따라서, Range를 0부터 0xFFFFFFFFFFFFFFFF로 했을 경우에는 BSOD가 일어나지 않는다.


이 취약점은 WIndows 최신 업데이트를 통해 간단히 해결가능하다. Patch 된 버전의 UlpParseRange 함수 내부를 들여다보면 RtlULongLongAdd 함수를 호출하여 Overflow 여부를 확인하는 부분이 있다. Integer Overflow가 발생할 경우 RtlULongLongAdd 함수는 STATUS_INTEGER_OVERFLOW 에러를 리턴하고, BSOD를 야기시켰던 함수인 UlAdjustRangesToContentSize, UlBuildFastRangeCacheMdlChain, _imp__IoBuildPartialMdl 함수에 접근하지 않게 된다. 즉, 직접적으로 취약점을 고쳤다기 보다는 취약점이 존재하는 코드를 우회시키는 방식으로 패치를 한 것이다.


Reference : 

https://technet.microsoft.com/en-us/library/cc735084(v=ws.10).aspx

http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-1635

http://www.iis.net/learn/get-started/introduction-to-iis/introduction-to-iis-architecture#Protocol

http://www.securitysift.com/an-analysis-of-ms15-034/

https://community.qualys.com/blogs/securitylabs/2015/04/20/ms15-034-analyze-and-remote-detection

https://www.exploit-db.com/exploits/36773/ 

Posted by 빛나유
,