Em um mundo digital cada vez mais dinâmico e globalizado, a segurança das contas online é fundamental. Um dos vetores de ataque mais sofisticados, e muitas vezes negligenciados, envolve o uso de Punycode, uma técnica que permite representar caracteres Unicode em domínios DNS. Essa técnica, embora legítima, pode ser explorada por atacantes para realizar um 0-Click Account Takeover. Nesse cenário, o usuário perde acesso à conta sem sequer interagir com um link malicioso.
Neste artigo, vamos explorar como o Punycode funciona, como ele pode ser utilizado em ataques, e por que esse tipo de exploração é tão perigoso.
O que é Punycode?
O Punycode é um sistema de codificação que permite representar nomes de domínio com caracteres Unicode (como letras com acentos ou alfabetos não latinos) dentro do padrão restrito de caracteres ASCII usado pelo DNS. Ele é utilizado em IDNs (Internationalized Domain Names).
Por exemplo:
xn--googl-r51b.com representa googlẹ.com (observe que o caractere "ẹ", Unicode U+1EB9, é uma letra do alfabeto latino estendido. Ele é visualmente semelhante ao “e” em muitas fontes, embora tecnicamente distinto).
Visualmente, o domínio parece legítimo, mas na prática aponta para um servidor controlado por um atacante.
Transformando caracteres em Punycode
A conversão de domínios Unicode para Punycode é necessária para que nomes contendo caracteres especiais possam ser resolvidos pelo sistema DNS, que aceita apenas caracteres ASCII. Porém, além desse uso legítimo, essa conversão também pode ser explorada para criar domínios visualmente semelhantes a outros, facilitando ataques de spoofing.
Para converter domínios entre Unicode e Punycode, existem ferramentas que auxiliam tanto administradores quanto atacantes, como:
O site punycoder.com, que permite converter domínios entre Unicode e Punycode, facilitando a análise e criação de domínios visualmente enganadores.
O utilitário de linha de comando idn, comum em sistemas Unix, que faz a conversão de forma rápida:
Com essa técnica, um domínio com um único caractere visualmente parecido, mas diferente, pode ser registrado e utilizado para ataques sofisticados que enganam tanto usuários quanto sistemas.
MySQL vs SMTP: a falha invisível na comparação de e-mails
Agora que entendemos como domínios visualmente legítimos podem ser criados com Punycode, é hora de ver como isso pode ser explorado em uma situação real e comum: a recuperação de senha.
Imagine o fluxo típico de “Esqueci minha senha”:
O usuário insere seu e-mail no formulário.
A aplicação verifica no banco de dados se o e-mail está cadastrado.
Se o e-mail existir:
Um token de redefinição é gerado.
O token é armazenado no banco.
Um link de recuperação é enviado por e-mail.
Até aqui, tudo parece normal. Mas a falha crítica está no passo final:
Qual e-mail será usado para enviar o link? O informado pelo usuário ou o que foi recuperado do banco?
Essa diferença, aparentemente sutil, é o que possibilita o ataque.
Onde mora o problema?
O comportamento padrão do MySQL, quando utiliza a collation utf8_general_ci, é tratar muitos caracteres Unicode como equivalentes.
Por exemplo:
"ė" é tratado como “e”.
"à" como “a”.
"ú" como “u”.
Se a vítima utiliza [email protected], o atacante pode registrar victim@gmàil.com, substituindo o “a” por um caractere Unicode visualmente semelhante com acento. O banco de dados pode considerar os dois e-mails como iguais, mas o servidor SMTP não. O link de recuperação acaba sendo enviado para o domínio controlado pelo atacante.
Resultado:
A aplicação verifica o e-mail com MySQL → “válido”.
O e-mail informado é usado para envio → link vai para o atacante.
Encontrando equivalências Unicode no MySQL
Para facilitar esse tipo de ataque, o invasor pode utilizar um script que testa milhares de caracteres Unicode contra um caractere base, como “e“, para descobrir quais são considerados equivalentes pelo banco de dados, de acordo com a collation configurada (por exemplo, utf8_general_ci).
import mysql.connector
import sys
if len(sys.argv) != 2 or len(sys.argv[1]) != 1:
print(f"Usage: {sys.argv[0]} <single_character>")
sys.exit(1)
base = sys.argv[1]
try:
conn = mysql.connector.connect(
host="127.0.0.1",
user="root",
password="root",
database="test"
)
cursor = conn.cursor()
for i in range(0x110000):
if 0xD800 <= i <= 0xDFFF:
continue
char = chr(i)
try:
cursor.execute("SELECT %s = %s", (char, base))
result = cursor.fetchone()
if result and result[0]:
print(f"U+{i:04X} ({char}) == '{base}'")
except:
continue
except KeyboardInterrupt:
print("\n[!] Interrupted by user")
finally:
try: cursor.close()
except: pass
try: conn.close()
except: pass
print("Done.")
Essa análise ajuda a revelar quais caracteres Unicode o banco considera iguais a um caractere base durante comparações, mesmo que sejam visualmente distintos.
Explorando o fluxo de “Esqueci minha senha”
Agora que já entendemos o conceito técnico e o impacto dessa falha, vamos aplicar tudo isso em um cenário prático, com uma aplicação vulnerável criada exatamente para simular esse tipo de ataque.
O cenário
A aplicação simula um sistema comum de autenticação, com a funcionalidade de “Esqueci minha senha”. Quando um usuário informa seu e-mail, o sistema verifica se ele está presente no banco de dados. Se sim, gera um token de recuperação e envia um link para o endereço fornecido.
No banco de dados da aplicação, temos o seguinte usuário legítimo cadastrado:
Como atacante, registramos o domínio nosferatú.site, substituindo a letra “u” pela letra Unicode “ú” (U+00FA). Visualmente, o domínio é quase idêntico ao original, mas tecnicamente é completamente diferente. O caractere alterado transforma o domínio em um IDN, codificado como xn--nosferat-v5a.site via Punycode.
Após o registro, configuramos o domínio malicioso com o serviço ImprovMX para fazer o redirecionamento de e-mails. Toda mensagem enviada para robert@nosferatú.site será encaminhada para a caixa de entrada do atacante:
A aplicação utiliza MySQL com a collation padrão utf8_general_ci, que trata ú e u como equivalentes em comparações. Isso faz com que o sistema interprete robert@nosferatú.site como equivalente a [email protected] durante a verificação.
Contudo, na hora do envio do e-mail, o servidor SMTP não faz essa equivalência. Ele envia exatamente para o endereço informado.
Demonstração
O atacante envia o formulário com robert@nosferatú.site.
Observação: Alguns navegadores ou ferramentas convertem automaticamente domínios com caracteres Unicode para sua forma Punycode. Assim, o endereço robert@nosferatú.site pode ser transformado em [email protected]. Para simular o ataque corretamente, pode ser necessário interceptar e modificar a requisição, substituindo o e-mail convertido pela versão original com Unicode antes do envio.
O MySQL considera esse e-mail como válido, pois o compara com [email protected].
A aplicação gera o token e envia o link de recuperação para robert@nosferatú.site.
O atacante recebe o link e consegue redefinir a senha da vítima sem que ela precise clicar em nada.
Collation permissiva e confiança no input: a combinação perigosa
O ponto-chave é que a aplicação confia cegamente no e-mail informado pelo usuário para enviar o link de recuperação de senha.
Veja o trecho do código:
Esse e-mail é o que o usuário forneceu, não o que foi recuperado do banco. Isso significa que, mesmo que o banco valide o e-mail com base em uma comparação permissiva (como no caso do utf8_general_ci), o envio do link ocorre para o endereço original fornecido, permitindo o sequestro da conta.
Sequestro de Conta via OAuth do GitLab
No segundo cenário, vamos explorar como uma integração legítima com um provedor OAuth, como o GitLab, pode ser explorada por um atacante para comprometer uma conta, sem necessidade de redefinição de senha ou qualquer interação prévia da vítima.
O cenário
A aplicação oferece duas formas de autenticação:
Por e-mail e senha (tradicional)
Através do botão “Continue with GitLab”
No banco de dados da aplicação, existe o seguinte usuário legítimo registrado:
Sabendo da existência desse usuário, o atacante cria uma nova conta no GitLab utilizando um e-mail visualmente idêntico, mas tecnicamente diferente, substituindo o caractere “u” por sua versão acentuada: "ú" (Unicode U+00FA):
robert@nosferatú.site
Apesar da diferença técnica, o endereço ainda parece idêntico visualmente. Como o GitLab aceita e armazena e-mails com caracteres Unicode, o cadastro ocorre normalmente.
A resposta da API do GitLab contém os dados do perfil, incluindo o campo “email“, que reflete exatamente o valor usado durante o registro:
"email": "robert@nosferatú.site"
Esse valor é então utilizado pela aplicação para verificar se o e-mail está registrado no banco de dados. O trecho de código responsável por isso é o seguinte:
A aplicação consulta a base de dados com o e-mail retornado (robert@nosferatú.site). O problema é que o banco está configurado com a collation padrão utf8_general_ci, que trata u e ú como equivalentes em comparações.
Na prática, isso significa que ao executar a seguinte consulta:
O banco considera que “robert@nosferatú.site” é igual a “[email protected]”, e retorna o registro do usuário legítimo.
Resultado: o atacante acessa a conta da vítima como se fosse ela, sem saber a senha, sem redefinir nada e sem que a vítima sequer perceba.
A seguir, um vídeo mostrando o ataque em ação. O atacante autentica-se via GitLab com robert@nosferatú.site e obtém acesso total à conta de [email protected], sem qualquer intervenção da vítima.
Considerações Finais
Ao longo deste artigo, exploramos dois cenários reais e silenciosos de 0-Click Account Takeover, onde o uso de e-mails com caracteres Unicode, como robert@nosferatú.site, permitiu que um atacante assumisse o controle de contas legítimas. Em ambos os casos, o ataque foi possível devido à combinação entre uma collation permissiva no banco de dados (como utf8_general_ci) e a confiança cega no e-mail fornecido, seja em fluxos de redefinição de senha ou em integrações com OAuth.
Mais do que técnicas isoladas, esses cenários revelam uma superfície de ataque sutil e negligenciada: a forma como strings são comparadas, armazenadas e tratadas entre diferentes sistemas (banco de dados, provedores OAuth, e servidores de e-mail).
E isso é só o começo.
Essa técnica pode ser aplicada em diversos pontos de uma aplicação, não apenas no login. Ela também pode afetar formulários de recuperação de senha, validações de permissões e qualquer funcionalidade que dependa da identidade baseada em e-mail.
Além disso, é importante destacar que outras collations também são vulneráveis a esse tipo de comparação permissiva, como utf8mb4_unicode_ci e utf8mb4_unicode_520_ci. Esta última, inclusive, é usada por padrão em plataformas como o WordPress.
No entanto, o WordPress não é explorável nesse cenário específico porque, no fluxo de redefinição de senha, ele não utiliza o e-mail informado pelo usuário para enviar o link, mas sim o endereço recuperado diretamente do banco de dados após a verificação. Essa decisão de implementação evita que o link de recuperação seja enviado para um e-mail visualmente idêntico, porém malicioso.
Mesmo assim, a superfície de ataque continua ampla. Basta um pequeno descuido na lógica de verificação ou envio para tornar uma aplicação vulnerável.
Estudamos o artigo Punycode 0-click Account Takeover, publicado por @Voorivex e Amir, que foi crucial para entendermos essa nova técnica e construirmos um ambiente prático de exploração. Deixamos aqui nossos parabéns a ambos pela excelente pesquisa e contribuição à comunidade.
Recomendação
Para prevenir esse tipo de ataque, recomendamos as seguintes práticas:
Evite collations permissivas (como utf8_general_ci, utf8mb4_unicode_ci, utf8mb4_unicode_520_ci) em colunas sensíveis, especialmente aquelas que armazenam e-mails, logins e chaves de autenticação. Prefira collations binárias como utf8mb4_bin, que realizam comparações exatas, caractere por caractere, e não tratam letras acentuadas ou variações Unicode como equivalentes
Nunca confie diretamente nos dados retornados pelo provedor OAuth, como o campo email, para identificar usuários. Sempre que possível, associe a conta do provedor a um ID interno único e previamente validado (por exemplo, exigindo login tradicional antes de permitir o vínculo com GitLab, Google etc.).
Ao enviar e-mails de recuperação de senha, sempre utilize o endereço armazenado no banco de dados. Nunca use o valor informado pelo usuário na requisição.
Sanitize e normalize as entradas do usuário, especialmente quando utilizadas para identificar registros. Isso inclui e-mails, nomes de usuário e domínios.
Realize auditorias periódicas nos collations utilizados nas tabelas do banco de dados, principalmente em aplicações legadas ou que passaram por migrações de versão/charset ao longo do tempo.