ARTIGO COMPLETO:
Este repo documenta e gera um catálogo de técnicas de ofuscação de URL
baseadas no componente userinfo da authority (o trecho antes do @), com
foco em uma variação: usar caracteres Unicode que parecem a barra / para
simular um caminho de pastas inteiramente dentro do userinfo, fazendo a URL
aparentar apontar para um site conhecido enquanto resolve para outro host.
O gerador é o probe.py: ele constrói cada caso, imprime no console
e escreve uma página urls.html com os links. Todos os domínios são
placeholders (exemplo-malicioso.com), nunca hosts reais.
Referência: https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1
userinfo host port
┌──┴───┐ ┌────────┴───────┐ ┌┴┐
https://john.doe@www.example.com:123/forum/questions/
└─┬─┘ └────────────┬───────────────┘└──────┬───────┘
scheme authority path
Gramática (ABNF) relevante:
; authority: parte entre "//" e o proximo "/", "?" ou "#".
; [ ... ] = opcional; so ha userinfo se existir um "@" antes do host.
authority = [ userinfo "@" ] host [ ":" port ]
; userinfo: o que vem ANTES do "@". *( ... ) = zero ou mais dos itens abaixo.
; Aceita apenas: unreserved, pct-encoded, sub-delims e ":". Repare: "/" NAO entra.
userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
; unreserved: caracteres "seguros" (letras, digitos e - . _ ~).
; Nunca sao delimitadores e nunca precisam de escape.
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
; pct-encoded: um byte escrito como "%" + 2 digitos hexadecimais.
; Ex.: %2F representa "/" como DADO, sem ele agir como delimitador.
pct-encoded = "%" HEXDIG HEXDIG
; sub-delims (sub-delimitadores): pontuacao PERMITIDA dentro de um componente
; (userinfo, host, path...). Tem sentido especifico de cada esquema; nao
; separa um componente do outro.
sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
/ "*" / "+" / "," / ";" / "="
; gen-delims (delimitadores gerais): separam os GRANDES componentes da URI
; (scheme, authority, path, query, fragment). Sao eles que "cortam" a URL:
; o "@" encerra o userinfo e o "/" inicia o path.
gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
; reserved: o conjunto completo de caracteres com papel estrutural
; (gen-delims + sub-delims). Para virarem dado literal, precisam de pct-encoded.
reserved = gen-delims / sub-delims
; ALPHA (A-Z a-z), DIGIT (0-9) e HEXDIG (0-9 A-F) sao regras-base do ABNF (RFC 5234).Três fatos da especificação sustentam as técnicas:
- O
@delimita. Havendo um@na authority, tudo antes dele éuserinfoe o host é o que vem depois. Um humano lê da esquerda para a direita e para no primeiro domínio que reconhece; o parser lê o host depois do@. user:passwordé obsoleto, mas ainda é parseado (§3.2.1: "Use of the format 'user:password' in the userinfo field is deprecated."). Deprecado ≠ rejeitado; navegadores continuam aceitando.- A barra
/(U+002F,SOLIDUS) é umgen-delime não faz parte douserinfo. Um/real encerra a authority e inicia opath.
A §7.6 (Semantic Attacks) da própria RFC descreve o abuso: como o userinfo
aparece antes do host, dá para construir uma URI que engana o humano, parecendo
apontar para uma autoridade confiável enquanto aponta para outra. Exemplo da
RFC: ftp://cnn.example.com&story=breaking_news@10.0.0.1/top_story.htm.
http://www.google.com@exemplo-malicioso.com
O olho lê google.com; o parser resolve host = exemplo-malicioso.com
(www.google.com é apenas um "usuário"). Limitação: assim que aparece um /,
? ou # real, a authority termina e o disfarce quebra. Por exemplo,
http://www.google.com/@exemplo-malicioso.com resolve para www.google.com.
A ideia é substituir cada / por um caractere Unicode diferente que se parece
com uma barra. Para o parser, esse caractere é "mais uma letra" do userinfo
(não é o gen-delim U+002F, logo não encerra a authority); para o olho, é
uma barra de caminho. Assim dá para montar um caminho falso plausível,
inteiro dentro do userinfo, imitando uma URL real:
http://www.google.com╱mail╱u╱0╱inbox@exemplo-malicioso.com
╲__________________╱
tudo isto é userinfo (as "barras" são U+2571, não U+002F)
O usuário lê "google.com/mail/u/0/inbox" (cara de caixa de entrada) e confia,
mas o host verdadeiro continua depois do @.
Variações no mesmo componente userinfo, cobertas para comparação:
- Bytes de controle C0 + DEL, tanto percent-encoded (
%00..%1F,%7F) quanto crus. - Invisíveis / zero-width (NBSP, ZWSP, BOM, RLO
U+202E, etc.). - Sinais ASCII no userinfo (`~ ' ! $ & * + , ; = ( ) [ ] { } | ^ < > " ``).
- Ambiguidade estrutural (
\@,/@,%2F@,%5C@, múltiplos@,%40,?@,#@). - Letras de outros idiomas (incl. o cirílico
аU+0430, homoglyph doalatino). - "Primas": host em IP hex/decimal, fragmento e path como isca.
O motivo é direto e decorre da regra 3 acima:
- A barra ASCII
/(U+002F) derruba o ataque. Sendo umgen-delim, ela encerra a authority: tudo após ela virapath, e o navegador vai para o host legítimo (www.google.com). Ou seja, um caminho "de verdade" com/não serve para o disfarce. - Os homoglyphs não são U+002F.
╱(U+2571),⁄(U+2044),∕(U+2215),/(U+FF0F) e dezenas de outros são caracteres comuns, não delimitadores. O parser os mantém dentro douserinfo: a authority continua até o@. - Resultado: o "caminho" inteiro (
╱mail╱u╱0╱inbox) fica nouserinfo, o host real permanece o que está depois do@, e visualmente a URL parece um deep link para um serviço confiável. - Alguns são letras que gente digita. Katakana
ノ, o traço CJK丿e afins se misturam a textos legítimos e não chamam atenção como um "símbolo estranho", o que os torna os mais perigosos do conjunto.
URL visivel : http://www.google.com╱mail╱u╱0╱inbox@exemplo-malicioso.com
host real : exemplo-malicioso.com
userinfo : www.google.com╱mail╱u╱0╱inbox
path : ''
Contraste trocando só ╱ (U+2571) pela barra ASCII / (U+002F):
URL visivel : http://www.google.com/mail/u/0/inbox@exemplo-malicioso.com
host real : www.google.com ← resolve para o host legítimo
userinfo : None
path : '/mail/u/0/inbox@exemplo-malicioso.com'
Visualmente quase idênticos; para o parser, opostos.
Esses homoglyphs também não são caracteres válidos de userinfo pela ABNF
estrita, mas os parsers reais (WHATWG URL) são permissivos: em vez de
rejeitar, percent-encodam o caractere dentro do userinfo e seguem. A
navegação vai para o host depois do @ do mesmo jeito.
| Code point | Char | Escrita | Nome Unicode |
|---|---|---|---|
U+30CE |
ノ |
Katakana (japonês) | KATAKANA LETTER NO |
U+4E3F |
丿 |
Han (CJK) | CJK IDEOGRAPH-4E3F (traço piě) |
U+2F03 |
⼃ |
Han (radical Kangxi) | KANGXI RADICAL SLASH |
U+2CC6 |
Ⳇ |
Copta | COPTIC CAPITAL LETTER OLD COPTIC ESH |
U+1735 |
᜵ |
Filipina (Baybayin) | PHILIPPINE SINGLE PUNCTUATION |
U+3033 |
〳 |
Kana (japonês) | VERTICAL KANA REPEAT MARK UPPER HALF |
U+1D23A |
𝈺 |
Notação musical grega | GREEK INSTRUMENTAL NOTATION SYMBOL-47 |
| Code point | Char | Nome Unicode |
|---|---|---|
U+2044 |
⁄ |
FRACTION SLASH |
U+2215 |
∕ |
DIVISION SLASH |
U+27CB |
⟋ |
MATHEMATICAL RISING DIAGONAL |
U+29F8 |
⧸ |
BIG SOLIDUS |
U+2571 |
╱ |
BOX DRAWINGS LIGHT DIAGONAL (usado no exemplo) |
U+31D3 |
㇓ |
CJK STROKE SP |
U+2041 |
⁁ |
CARET INSERTION POINT |
U+FF0F |
/ |
FULLWIDTH SOLIDUS |
U+1F67C |
🙼 |
VERY HEAVY SOLIDUS |
U+29F6 |
⧶ |
SOLIDUS WITH OVERBAR |
U+2E4A |
⹊ |
DOTTED SOLIDUS |
U+233F |
⌿ |
APL FUNCTIONAL SYMBOL SLASH BAR |
U+2AFD |
⫽ |
DOUBLE SOLIDUS OPERATOR |
U+2AFB |
⫻ |
TRIPLE SOLIDUS BINARY RELATION |
U+2CFD |
⳽ |
COPTIC FRACTION ONE HALF |
Só depende da biblioteca padrão (os, html). A estrutura é uma função por
família de técnica; cada uma gera seus casos e chama o helper emitir(valor, url), que imprime a linha e acumula um link para o HTML.
| Função | Conteúdo |
|---|---|
baseline |
@ e user:pass@ falsos |
hexa |
bytes de controle percent-encoded |
raw |
os mesmos bytes, crus |
invisiveis |
invisíveis / zero-width |
estruturais |
ambiguidade em torno do delimitador |
sinais |
sinais ASCII no userinfo |
barras_escritas |
homoglyphs de / que são letras de escritas reais |
barras_simbolos |
homoglyphs de / do script COMMON |
letras_idiomas |
uma letra representativa de várias escritas |
cousins |
IP hex/decimal, fragmento e path como isca |
Constantes no topo: ISCA (prefixo-isca) e ALVO (host real após o @);
mudá-las reaponta toda a matriz.
Os caracteres perigosos nunca são colados literais no fonte: são construídos
em runtime via chr(codepoint). Um invisível ou um U+202E (RLO) literal no
código poderia reordenar ou esconder o próprio fonte.
python3 probe.pySem dependências externas. Diretório de saída: a pasta do script por padrão;
sobrescreva com OUTDIR=/caminho python3 probe.py.
- Mostrar o host de forma inequívoca: destacar o domínio registrável e
esconder/realçar o
userinfo. - Normalizar e detectar confusables (Unicode UTS #39); aplicar IDNA/Punycode; sinalizar rótulos com mistura de scripts e caracteres fora do esperado.
- Tratar
userinfocom desconfiança: muitas libs permitem desabilitar credenciais embutidas na URL. - Não confiar no prefixo visual: o host real é o trecho logo depois do
último
@e imediatamente antes do primeiro/,?,#real ou fim. Como a vítima não distingue um/real de um homoglyph, a defesa não pode depender só do olho.
- RFC 3986, URI: Generic Syntax, §3.2 / §3.2.1 / §7.6: https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1
- WHATWG URL Standard: https://url.spec.whatwg.org/
- RFC 3987, Internationalized Resource Identifiers (IRIs)
- Unicode UTS #39, Security Mechanisms (confusables): https://www.unicode.org/reports/tr39/
MrCl0wn
- 🌐 Blog: http://blog.mrcl0wn.com
- 🐙 GitHub: @MrCl0wnLab
- 🐦 Twitter: @MrCl0wnLab
- 📧 Email: mrcl0wnlab@\gmail.com
⭐ Se este projeto foi útil, considere dar uma estrela!
💡 Sugestões e feedbacks são sempre bem-vindos!
💀 Hacker Hackeia!