Search

Win RPC 1-day

태그
Windows
Property
그림112.png

취약점 정보

2022년 MS Patch Tuesday에서 RPC관련 CVSS 9.8 스코어를 가진 CVE-2022-26809 취약점이 공개 되었습니다.
이 취약점은 별도의 인증이나 사용자의 상호작용이 필요 없는 wormable 취약점입니다.
이로 인해 노출되어 있는 1,329,075개의 windows 서버 및 desktop 대상으로 악의적인 행위에 이용될 수 있습니다.

취약점 발생 함수 진입 방법

취약점 패치 이전 버전의 바이너리와 취약점 패치 이후 버전의 바이너리를 대상으로 디핑 수행 결과, 하기의 script를 사용해 취약점이 발생한 OSF_SCALL::ProcessReceivedPDU 함수에 도달할 수 있었습니다.

바이너리 디핑 및 취약점 발생 함수 접근

바이너리 디핑 도구인 Diaphora(https://github.com/joxeankoret/diaphora)를 사용해 취약점 패치 이전 버전의 바이너리 rpcrt4.dll(10.0.19041.1466)과 취약점 패치 이후 버전의 바이너리 rpcrt4.dll(10.0.19041.1645)을 대상으로 바이너리 디핑을 수행했습니다.
OSF_SCALL::ProcessReceivedPDU 함수 내에 패치 된 부분을 대조해 보았을 때, integer overflow 검사함수가 추가되었음을 확인할 수 있었습니다.
rpcrt4!OSF_SCALL::ProcessReceivedPDU (10.0.19041.1466) / rpcrt4!OSF_SCALL::ProcessReceivedPDU(10.0.19041.1645)
취약점이 발생한 위치에 접근하기 위해, [MS-RPCE] 문서를 참조하였고, python3 impacket 라이브러리를 이용하여 script를 작성했습니다.
하기의 script를 이용해 OSF_SCALL::ProcessReceivedPDU 함수에 접근 할 수 있었습니다.
from impacket.dcerpc.v5 import transport from impacket.structure import Structure from impacket.uuid import * rpctransport = transport.DCERPCTransportFactory(r"ncacn_ip_tcp:192.168.137.128") rpctransport.set_dport(135) rpctransport.setRemoteHost('192.168.137.128') dce = rpctransport.DCERPC_class(rpctransport) dce = rpctransport.get_dce_rpc() dce.connect() dce.bind(uuidtup_to_bin(('e1af8308-5d1f-11c9-91a4-08002b14a0fa','3.0'))) dce.call(0, b"")
Python
복사

상세분석

OSF_SCALL::ProcessReceivedPDU 함수 내 break point를 설정한 뒤, stack trace를 출력 시켰을 때, 하기와 같은 내용이 출력 됩니다.
OSF_SCALL::ProcessReceivedPDU 함수 내에서 취약점을 trigger하기 위해서 *(this+145)변수의 값이 초기화되어 있어야 합니다.
__int64 __fastcall OSF_SCALL::ProcessReceivedPDU(OSF_SCALL *this, struct rpcconn_common *a2, int a3, int a4) { ... ... ... if ( !*((_DWORD *)this + 145) || *((_DWORD *)this + 115) ) { ... ... ... } else { ... ... ... if ( !(unsigned int)QUEUE::PutOnQueue((OSF_SCALL *)((char *)this + 600), (char *)a2 + 24, Size) ) { *((_DWORD *)this + 147) += v43; if ( (v57 & 2) != 0 ) { v15 = (*((_BYTE *)this + 736) & 4) == 0; *((_DWORD *)this + 135) = 3; if ( !v15 ) { _InterlockedAnd((volatile signed __int32 *)(*((_QWORD *)this + 38) + 428i64), 0xFFFFFFFD); *((_DWORD *)this + 184) &= ~4u; } } ... ... } ... ... }
C++
복사
OSF_SCALL::BeginRpcCall 함수에서 OSF_SCONNECTION::LookupBinding 함수를 호출하여 binding 된 v8 변수를 가져옵니다.
(v8 + 0x38) & 2 변수의 값이 초기화되어 있을 때, *(this + 145)변수의 값을 초기화합니다.
binding된 객체는 RpcServerRegisterIf2 함수에서 할당되고, RPC_INTERFACE::RPC_INTERFACE 함수에서 처음 initialize 됩니다.
RPC_INTERFACE *__fastcall RPC_INTERFACE::RPC_INTERFACE( RPC_INTERFACE *this, struct _RPC_SERVER_INTERFACE *a2, struct RPC_SERVER *a3, int a4, unsigned int a5, unsigned int a6, int (__stdcall *a7)(void *, void *), void *a8, int *a9, struct RPCP_INTERFACE_GROUP *a10) { int updated; // eax bool v15; // zf *((_DWORD *)this + 2) = 0; RtlInitializeCriticalSectionAndSpinCount((PRTL_CRITICAL_SECTION)((char *)this + 16), 0); *((_DWORD *)this + 0xE) = 0; *((_DWORD *)this + 50) = 0; *((_QWORD *)this + 27) = (char *)this + 0xE8; *((_QWORD *)this + 0x1C) = 4i64; *(_OWORD *)((char *)this + 0xE8) = 0i64; *(_OWORD *)((char *)this + 0xF8) = 0i64; *((_DWORD *)this + 0x53) = 0; *((_DWORD *)this + 84) = 0; *((_DWORD *)this + 85) = 0; *((_DWORD *)this + 86) = 0; *((_DWORD *)this + 87) = 0; *((_DWORD *)this + 88) = 0; *((_DWORD *)this + 89) = 0; *((_DWORD *)this + 90) = 0; *((_DWORD *)this + 91) = 0; RtlInitializeSRWLock((char *)this + 480); *((_QWORD *)this + 47) = (char *)this + 368; *((_QWORD *)this + 46) = (char *)this + 368; *((_DWORD *)this + 96) = 0; *((_DWORD *)this + 122) = 0; *((_QWORD *)this + 62) = (char *)this + 512; *((_QWORD *)this + 63) = 4i64; *((_OWORD *)this + 32) = 0i64; *((_OWORD *)this + 33) = 0i64; *((_DWORD *)this + 136) = 0; *((_QWORD *)this + 69) = 0i64; *(_QWORD *)this = a3; *((_QWORD *)this + 0x1A) = 0i64; *((_QWORD *)this + 0x17) = 0i64; *((_QWORD *)this + 0x46) = 0i64; *((_QWORD *)this + 0x47) = 0i64; updated = RPC_INTERFACE::UpdateRpcInterfaceInformation(this, a2, a4, a5, a6, a7, a8, a10); v15 = a2->Length == 0x60; *a9 = updated; if ( v15 && (a2->Flags & 1) != 0 ) *((_DWORD *)this + 14) |= 2u; return this; }
C++
복사
a2->Flags & 1 변수의 값이 초기화 되어있을 때, (_DWORD *)this + 14)(==(v8 + 0x38))변수의 값을 초기화합니다.
따라서 RPC를 등록하는 함수 RpcServerRegisterIf 함수의 첫 번째 인자로부터 +0x38의 상대 주소의 위치한 변수가 초기화 되어 있을 때 취약점을 trigger할 수 있을 것으로 의심됩니다.

TODO

Antonio Cocomazzi (https://twitter.com/splinter_code)가 custom RPC 서버를 이용해 취약점이 발생한 함수에 접근한 사실을 확인할 수 있었습니다.
custom RPC 서버를 이용해 취약점이 발생한 basic block에 접근할 수 있는 condition 을 구성 해 취약점을 trigger 할 수 있을 것으로 생각됩니다.

결론

분석을 수행 한 몇몇 연구원들은 default 상태의 RPC 서버를 통해 취약점이 발생한 basic block 에 접근하기 어렵다는 의견입니다.