Um aplicativo do governo saudita expôs a chave privada de um banco. Para denunciar, era preciso ser saudita.

@iam_zachi
INGLÊS08 de set. de 2026
235K
1.5K
95
28
1.1K

TL;DR

Um pesquisador de segurança descobriu que o aplicativo oficial saudita Nusuk vazou uma chave privada RSA e credenciais OAuth do Saudi National Bank, protegidas por uma senha de um único dígito. A denúncia da falha exigiu um tweet viral devido a portais de vulnerabilidade com restrição geográfica.

Um arquivo de 2.835 bytes dentro de um aplicativo governamental com mais de 10 milhões de instalações continha um certificado de cliente ativo para o Banco Nacional Saudita. Foi preciso um tweet viral para que alguém desse atenção a isso.

Resumo

O aplicativo oficial Nusuk (com.moh.nusukapp, Ministério do Hajj e Umrah, mais de 10 milhões de instalações, com o selo "Governamental" do Google Play) distribuía um arquivo PKCS#12 contendo uma chave privada RSA e um certificado de cliente emitido pelo Banco Nacional Saudita. A senha para esse arquivo estava codificada a poucas linhas de distância no próprio código do aplicativo. Era um único caractere: 2.

Ao lado, em texto simples, estavam o ID do cliente OAuth2 e o segredo do cliente para a API de Banking-as-a-Service do banco, solicitando os escopos identity accounts cards verification kyc cardpay transfers.

Qualquer pessoa que baixasse o aplicativo da Google Play tinha tudo isso.

Tentei denunciar. Disseram-me que o portal de vulnerabilidades está disponível apenas para usuários dentro da Arábia Saudita. Então, tuitei sobre o assunto. O tweet teve 1,5 milhão de visualizações e, de repente, o mesmo portal pediu detalhes. Um dia depois, as credenciais foram removidas do aplicativo.

https://x.com/iam_zachi/status/2094445016194207745

Este artigo cobre apenas a descoberta bancária. Tudo o que está abaixo foi corrigido no aplicativo em produção, e o lado do fornecedor afirma que as credenciais foram rotacionadas.

O que é o Nusuk

O Nusuk é a plataforma oficial do governo saudita para o Hajj e a Umrah. Ele gerencia autorizações de peregrinação, e-visas, reservas e o Cartão Nusuk. É operado pelo Ministério do Hajj e Umrah, é identificado como um aplicativo governamental verificado no Google Play e tem mais de dez milhões de instalações. Ele também contém um recurso de carteira, a Nusuk Wallet, construída em conjunto com o Banco Nacional Saudita e aprovada pelo SAMA, o banco central saudita. A carteira é a parte sobre a qual este artigo trata.

A descoberta

Baixei o conjunto APK diretamente do Google Play (versão 17.4.9, versionCode 131215) e o descompactei. Nada de exótico: Kotlin/Compose padrão, sem empacotador, sem ofuscação significativa.

Dentro dos recursos do aplicativo, em res/raw/nusuk.pfx, havia um contêiner PKCS#12 de 2.835 bytes. Um arquivo .pfx é o formato de pacote padrão para um certificado mais sua chave privada, criptografado com uma senha.

A senha estava no código do aplicativo, a poucas linhas de onde o arquivo é carregado:

text
1const-string v3, "2"

Um caractere, presente no bytecode descompilado como um literal. Uma chamada openssl depois:

text
1RSA private key, 2048 bit, 2 prime factors
2Subject: C=SA, ST=Jeddah, L=Jeddah, O=Nusuk.sa, CN=eshbeata@staq.io
3Issuer: C=SA, ST=Riyadh, L=Riyadh, O=The Saudi National Bank, OU=Finto,
4 CN=Application Issuer
5Serial: 0x24 (36)
6Valid: 2026-04-27 -> 2027-04-27
7Extended Key Usage (critical): TLS Web Client Authentication

Um certificado de cliente ativo emitido por um banco, válido por mais um ano, destinado a autenticar um cliente para um servidor via TLS.

Não estava sozinho. O mesmo caminho de código é o componente da carteira, empacotado como com.walletstaq e integrado ao Trustless SDK da Staq Technologies, a empresa que opera a plataforma Finto BaaS do SNB. Mais três valores estavam lá em texto claro:

com o escopo OAuth solicitado:

text
1identity accounts cards verification kyc cardpay transfers

Ambas as credenciais também foram concatenadas em uma única string e passadas para um logger de depuração, junto com a URL base.

(Não estou publicando o material da chave ou os valores completos do segredo. O que importa aqui é a forma do problema.)

Por que isso é grave, em termos simples

Pense na API do banco como uma porta com duas fechaduras.

A primeira fechadura é o TLS mútuo (mTLS). Normalmente, um servidor prova sua identidade para você com um certificado. Com o mTLS, você também precisa provar sua identidade para o servidor com um certificado. Esse é o arquivo .pfx: o certificado e a chave privada que provam que você é o dono dele. Supõe-se que seja algo que apenas o sistema cliente legítimo possui.

A segunda fechadura é o segredo do cliente OAuth, a senha que o aplicativo usa para solicitar um token de acesso à API do banco.

Ambas as fechaduras foram enviadas dentro de um aplicativo gratuito no Google Play, e a chave para a caixa que guardava a primeira era o dígito 2.

Confirmei que o host de destino realmente impõe mTLS: o handshake TLS com api.baas.alahli.com solicita certificados de cliente (ele envia Acceptable client certificate CA names), e o certificado do servidor lê CN=*.baas.alahli.com, O=The Saudi National Bank. Portanto, não era um certificado decorativo para um ambiente de teste. Era a credencial para a porta da frente de uma API bancária de produção, com escopos que cobrem identidade, contas, cartões, KYC, pagamentos com cartão e transferências.

Separadamente do vazamento, há um problema de design subjacente. Um certificado de cliente que é enviado de forma idêntica para dez milhões de dispositivos não pode distinguir uma instalação de outra. Cada cópia apresenta a mesma credencial, então o certificado diz ao banco qual aplicativo está chamando e nada sobre quem está chamando. Credenciais para uma API como essa pertencem ao seu próprio backend: o aplicativo fala com seu servidor, seu servidor fala com o banco.

O que eu não fiz

Executei exatamente uma verificação no endpoint de token (POST /api/tppa/token) para ver se as credenciais estavam ativas. Ele retornou HTTP 403 do nginx. O mesmo aconteceu com uma solicitação sem certificado algum e com a URL raiz nua. Isso é um bloqueio em nível de rede na frente da API, quase certamente geográfico, e não diz nada sobre se as credenciais funcionam.

De fora da Arábia Saudita, não pude determinar se essas credenciais estavam ativas. Parei por aí. Qualquer coisa além disso teria sido uma tentativa de contornar o controle de acesso de um banco, e a descoberta não depende disso: uma chave privada e um segredo OAuth bancário com escopo de transferência em um artefato publicamente baixável é a descoberta, independentemente de eu conseguir ou não alcançar o endpoint pessoalmente.

Tentando denunciar

Esta é a parte que fez o tweet viralizar, e é a metade mais interessante.

Procurei uma maneira de denunciar isso de forma responsável. O que existe:

Canal

Resultado

security.txt em nusuk.sa, haj.gov.sa, hajj.nusuk.sa

Não existe

Página de denúncia de vulnerabilidades do Saudi CERT (cert.gov.sa)

Redireciona para NCA; própria página de denúncia desativada

Formulário de vulnerabilidades do NCA (haseen.gov.sa)

Inacessível da Alemanha: timeout, bloqueio geográfico

bugbounty.sa

Programa fechado, HTTP 403 de fora

HackerOne / Bugcrowd

Nenhum programa para Nusuk, Ministério ou Elm

Contatos na listagem da loja de aplicativos

Endereços de suporte, sem mandato de segurança

Nada disso me deixou um canal com mandato de segurança que eu pudesse realmente alcançar. Enviei e-mails mesmo assim. A resposta do Suporte Haseen:

"O acesso ao portal Haseen é restrito a usuários dentro do Reino da Arábia Saudita. Para quaisquer outras dúvidas, você pode entrar em contato conosco através do serviço 'Nós nos importamos' disponível no Portal Haseen oficial."

O Portal Haseen, que é exatamente a coisa que não consigo acessar. Respondi explicando que não sou saudita, que se trata de um certificado bancário privado exposto em um aplicativo governamental e que só queria entregá-lo. A resposta, novamente, foi que o formulário só funciona para cidadãos da Arábia Saudita.

Então, registrei um relatório no CERT/CC através da plataforma VINCE deles como intermediário coordenador (VRF#26-08-DXMKL), com escopo apenas para esta descoberta. Essa é a rota que você toma quando a parte afetada não tem um canal próprio acessível.

E então tuitei sobre o assunto, principalmente por frustração.

O tweet teve 1,5 milhão de visualizações. Em poucas horas, o Suporte Haseen me enviou um e-mail, sem ser solicitado, na mesma thread que duas vezes me disse que o portal não era para mim:

"De acordo com nossa equipe relevante, por favor, forneça mais detalhes sobre a vulnerabilidade de segurança."

Enviei todos os detalhes. Prefiro que a coisa seja corrigida do que estar certo sobre o processo.

A correção

A próxima atualização do aplicativo chegou tanto no Android quanto no iOS. Baixei a nova versão do Android (17.5.0, versionCode 156635) diretamente da Play e a diferenciei do que havia analisado. Três verificações:

  1. Nenhum contêiner de certificado em nenhum lugar do novo conjunto APK: nenhum arquivo com extensão .pfx, .p12, .pkcs12, .jks, .bks, .pem ou .key. Também fiz o hash do antigo nusuk.pfx e o comparei byte a byte com cada arquivo do mesmo tamanho na nova versão, caso ele tivesse sido simplesmente renomeado. Nenhuma correspondência.
  2. Nenhuma das credenciais conhecidas. Pesquisei os valores antigos exatos para URL base, escopo, ID do cliente e segredo do cliente em todos os arquivos DEX, bibliotecas nativas, assets, XML, JSON e recursos brutos. Zero resultados para todos os quatro.
  3. Nenhum caminho de código. A versão 17.4.9 continha 4.571 arquivos sob os pacotes com.walletstaq e com.trustless; a 17.5.0 contém zero. Os marcadores baas.alahli, tppa/token, nusuk.pfx, o assunto do certificado e o nome do emissor retornam zero resultados na versão decodificada.

Toda a integração da carteira e BaaS foi removida. A verificação de renomeação na etapa 1 é o que descarta a possibilidade de os valores terem simplesmente se movido para outro lugar no pacote.

A parte que ninguém de fora pode verificar

Remover um segredo de um aplicativo não invalida o segredo. Cópias antigas do APK permanecem disponíveis para sempre, e o certificado era válido até abril de 2027. Portanto, a questão em aberto é se ele foi revogado e se o segredo OAuth foi rotacionado.

Procurei uma maneira de verificar isso de forma independente. Não existe, e a razão é, por si só, uma descoberta.

O certificado não carrega nenhuma extensão crlDistributionPoints, portanto nenhuma lista de revogação é referenciada. Seu único endpoint de revogação é:

text
1OCSP - URI: http://finto-ocsp-responder.prod.svc.cluster.local:8080/api/v1/ocsp

.cluster.local é o sufixo DNS interno de um cluster Kubernetes. Por definição, não é roteável na internet pública e resolve para NXDOMAIN de qualquer lugar fora desse cluster, sobre HTTP simples na porta 8080.

Portanto, o status de revogação deste certificado não pode ser verificado de fora da infraestrutura do banco, porque não há nada aqui fora para consultar. Para qualquer parte confiável fora desse cluster, os certificados deste emissor são efetivamente irrevogáveis, o que merece um parágrafo na revisão de arquitetura de alguém.

Esse mesmo campo também publicou o nome do cluster, namespace, nome do serviço e porta da PKI de produção da plataforma BaaS de um banco, em um aplicativo distribuído para dez milhões de pessoas.

Apenas SNB, Finto ou Staq podem confirmar a rotação. O lado do fornecedor afirma que as credenciais foram rotacionadas. Não tenho como verificar isso de forma independente.

Linha do tempo

Data (2026)

Evento

29 de agosto

APK analisado, descoberta confirmada localmente

29 de agosto

Relatório enviado por e-mail; Haseen responde que o portal é restrito a usuários dentro da Arábia Saudita

31 de agosto

Tentativas repetidas, mesma resposta. Tuítei sobre o assunto; ~1,5 milhão de visualizações

1º de setembro

Haseen reabre a thread sem ser solicitado, pede detalhes. Detalhes enviados

1º de setembro

Relatório também registrado no CERT/CC VINCE (VRF#26-08-DXMKL) como intermediário

1º de setembro

Versão 17.5.0 publicada no Google Play

3 de setembro

Reteste no conjunto APK 17.5.0 não modificado confirma a remoção completa

8 de setembro

Este artigo

Não posso provar que a atualização foi causada pelo meu relatório. A versão 17.5.0 pode muito bem já estar em desenvolvimento. O que posso mostrar é que o material estava na 17.4.9 e não está na 17.5.0.

O que eu aprenderia com isso

  1. Esconder algo no aplicativo não é um limite de segurança. Não em recursos, não em um .so nativo, não atrás de ofuscação ou de uma senha armazenada no mesmo binário. Se o aplicativo pode ler, todos que instalarem o aplicativo também podem. Várias equipes aprendem isso publicamente todos os anos.
  2. Um certificado de cliente compartilhado não é autenticação. Se dez milhões de dispositivos apresentam o mesmo certificado, ele diz qual aplicativo está chamando e nada sobre quem está chamando, e o aplicativo é um arquivo que qualquer um pode baixar. Credenciais para uma API de terceiros, especialmente de um banco, pertencem a um servidor que você controla.
  3. Colocar uma cerca geográfica no seu canal de divulgação de vulnerabilidades é, por si só, uma vulnerabilidade. Atacantes não preenchem formulários. Se a única maneira de relatar uma falha em um aplicativo publicado globalmente para dez milhões de pessoas é estar fisicamente dentro de um país, então as pessoas que não conseguem acessar o formulário são exatamente aquelas de quem você mais precisa ouvir. Foi preciso um tweet viral para abrir um canal que um arquivo security.txt teria aberto de graça.

Analisei o conjunto APK disponível publicamente na Play Store em modo convidado, sem conta e sem dados pessoais reais. Não fiz nenhuma tentativa de contornar o bloqueio em nível de rede na frente da API bancária. Cada valor neste artigo é estrutural (caminhos, nomes de classes, metadados de certificado) ou redigido; nenhum material de chave privada e nenhum segredo completo são publicados.

Salvar com um clique

Faça leitura profunda de artigos virais com IA no YouMind

Salve a fonte, faça perguntas específicas, resuma o argumento e transforme um artigo viral em notas reutilizáveis em um único espaço de trabalho com IA.

Explorar o YouMind
Para criadores

Transforme seu Markdown em um artigo 𝕏 impecável

Quando você publica seus próprios textos longos, formatar imagens, tabelas e blocos de código para o 𝕏 é uma dor de cabeça. O YouMind transforma um rascunho completo em Markdown em um artigo 𝕏 impecável e pronto para publicar.

Experimente Markdown para 𝕏

Mais padrões para decifrar

Artigos virais recentes

Explorar mais artigos virais