Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2025-47810 — PunkBuster LPI para NT AUTHORITY\SYSTEM | Kitploit
Ferramentas/GitHubGitHub/ptrstr/cve-2025-47810
Escalada de PrivilégiosAnálise de VulnerabilidadesExploraçãoMovimento LateralAnálise de Binários
GitHubptrstr/cve-2025-47810

CVE-2025-47810

PunkBuster LPI para NT AUTHORITY\SYSTEM

Ver Repositório
há 11 mesesAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

PunkBuster LPI (CVE-2025-47810)

Screenshot

Contexto

O PunkBuster instala-se como dois serviços e, opcionalmente?, um driver de kernel.

  • PnkBstrA: Serviço que é executado constantemente em segundo plano e é responsável por gerir o PunkBuster como um todo
  • PnkBstrB: Serviço que inicia quando um jogo protegido é iniciado. Tem mais funcionalidades do que a sua contraparte A.

Ambos os serviços são estruturados de forma semelhante, escutando UDP em localhost numa porta no intervalo [44301, 44400]. Assim que encontra uma porta, esta é escrita no valor Port em HKLM:\SOFTWARE\Even Balance\PnkBstrA ou HKLM:\SOFTWARE\WOW6432Node\Even Balance\PnkBstrA.

Cada pedido é armazenado num buffer global com 1500 bytes de comprimento (no entanto, apenas são recebidos 1499 bytes por causa de um terminador NUL).

Não existe uma estrutura padrão para os dados do pedido, mas aqui está a sua estrutura habitual:

  • primeiro byte (normalmente uma letra) que indica qual funcionalidade invocar
  • argumentos para a funcionalidade, normalmente separados por espaços se forem necessários vários

Os tipos de pedido em PnkBstrA são os seguintes:

  • l: Load (Carregar): Inicia e atualiza o PnkBstrB
    • Aceita um argumento (opcional), o caminho para o executável PnkBstrB atualizado
  • u: Unload (Descarregar): Para o PnkBstrB
  • v: Version (Versão): Simplesmente devolve a versão
  • m: Monitor (Monitorizar): Aceita um PID e verifica se a DLL do cliente PunkBuster (pbcl.dll) está no processo, despejando a sua memória se estiver presente.

Vulnerabilidade

O handler de load (l) contém uma vulnerabilidade do tipo TOCTOU, levando a uma Escalação Local de Privilégios como NT AUTHORITY\SYSTEM.

O fluxo do handler é algo como isto:

  1. Elimina o serviço PnkBstrB
  2. Calcula o MD5 do ficheiro no argumento, se presente
  3. Valida o Authenticode e os certificados do executável no argumento, se presente
    • Entre outras verificações, confirma que o assunto do certificado é Even Balance, Inc.
  4. Dorme entre 750ms e 2.25s sem manter handles para o ficheiro, enquanto tenta copiar o ficheiro do argumento para C:\Windows\SysWOW64\PnkBstrB.exe ou C:\Windows\System32\PnkBstrB.exe, dependendo da plataforma
  5. Calcula o MD5 do ficheiro copiado no diretório de sistema
  6. Valida que ambos os hashes MD5 correspondem
    • Se sim, cria e inicia o serviço PnkBstrB a correr como LocalSystem com o ficheiro recém-copiado.

O problema aqui é que o ficheiro que o PnkBstrA está a manipular é reaberto muitas vezes para cada operação, levando a uma situação em que um atacante poderia modificar o ficheiro entre operações.

Isto pode levar a um problema em que, depois de o ficheiro ser validado quanto ao seu certificado, este é trocado e um ficheiro malicioso acaba como executável do serviço PnkBstrB. Este executável tornaria possível a um utilizador sem privilégios elevar para NT AUTHORITY\SYSTEM.

A descompilação relevante é mostrada aqui:

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);

    ...
}

Uma correção simples seria copiar primeiro o ficheiro para um local seguro mas temporário (ex.: PnkBstrB.exe.tmp) e calcular o MD5 e as verificações a partir daí. Desta forma, o ficheiro não poderia ser editado por um ator malicioso. Isto também garantiria que apenas uma cópia do ficheiro é aberta, o que impede a exploração via SMB.

Exploração

Abordagem 1

Um cenário de ataque poderia ser fornecer um ficheiro malicioso, esperar que o MD5 seja calculado, substituir o ficheiro pelo PnkBstrB.exe original para que as verificações WinVerifyTrust e de certificado passem, e depois substituí-lo novamente pelo ficheiro malicioso, para que o segundo MD5 passe e o ficheiro seja copiado e executado.

No entanto, este cenário é muito difícil de executar, pois substituir o ficheiro é difícil, e modificá-lo enquanto está aberto pelo PnkBstrA não parece aplicar as alterações. Isto acontece porque é difícil agendar o nosso código no INJECTION POINT 1, como visto no código. Provavelmente é possível usando muitas threads críticas em termos de tempo, algumas das quais tentam constantemente substituir o ficheiro, e outras bloqueiam núcleos concorrentes em busy waits.

Abordagem 2

Outra abordagem seria tentar ter uma colisão MD5 entre o PnkBstrB e o ficheiro malicioso. Isto funcionaria usando primeiro o ficheiro legítimo como candidato para o primeiro MD5 e sendo verificado quanto ao certificado. No entanto, depois disso temos uma boa janela de 750 milissegundos ou mais para substituir o ficheiro pelo nosso. Então, o segundo MD5 passaria e o nosso ficheiro seria injetado. No entanto, durante o desenvolvimento, tive preguiça de esperar que uma colisão fosse gerada, por isso optei por outra abordagem.

Abordagem 3

Este código depende do Windows e usa fopen, que mapeia para as funções de I/O do próprio Windows. Por definição, isto significaria que partilhas SMB seriam acessíveis. Além disso, o MSDN menciona que este comportamento é suportado:

fopen aceita caminhos UNC e caminhos que envolvem unidades de rede mapeadas, desde que o sistema que executa o código tenha acesso à partilha ou unidade mapeada no momento da execução

Isto significa que poderíamos usar a nossa primeira abordagem, mas como o código dependeria da nossa partilha SMB, enviamos ficheiros diferentes dependendo do número de vezes que o ficheiro foi pedido.

Ao modificar o smbserver do Impacket, é possível obter este comportamento. O ficheiro .patch completo pode ser encontrado aqui. As principais alterações são:

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'

Como pode ver, se for a primeira vez que abrimos o ficheiro (primeiro MD5) ou pelo menos a 8.ª vez (cópia do ficheiro e seguintes), enviamos ao cliente o ficheiro malicioso, enquanto enviamos o original nos outros casos.

O código do ficheiro malicioso pode ser encontrado aqui, que é um simples serviço hello world com uma reverse shell.

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

Divulgação

A EvenBalance foi contactada múltiplas vezes desde 2025-02-15 através de diferentes métodos, mas não respondeu.

Este problema foi totalmente divulgado em 2025-05-10.

Baixar ferramenta