Defanging, refanging, and why hxxp still matters
Where hxxp came from, exactly which defanged forms Coeus recognises, and the conventions worth using when you paste an entity into a ticket.
Try the standalone Defang / Refang tool to convert pasted text in your browser. The tool page has current instructions and supported behavior.
Defanging is the practice of writing a hostile URL so that nothing between you and the reader
turns it back into a link. http://evil.com/payload becomes hxxp://evil[.]com/payload. It is
a convention, not a standard: nobody ratified it, and every group has a slightly different
dialect.
It survives because the failure it prevents is expensive and silent. A ticketing system autolinks a URL in a comment. A chat client fetches an unfurl preview. A mail gateway rewrites links and, in doing so, fetches them. A new investigator middle-clicks a row in a spreadsheet. None of those are attacks; they are helpful software doing its job, and each one can put a request on a C2 server from inside your network, attributed to your egress address.
Where the forms came from
The [.] bracket came first, because square brackets are the one punctuation almost no
autolinker treats as part of a hostname. hxxp came from mailing lists, where subscribers'
clients were linkifying anything that started with http. fxp followed for FTP. The (at)
spelling for @ is older than either and comes from spam-harvester avoidance on public forums.
Because they grew separately, they compose in ways nobody designed. You will see
hxxps://evil[.]com, hxxps://evil(.)com, evil . com, evil[dot]com, user[@]evil[.]com
and evil\.com — the last from someone who pasted a regular expression instead of a hostname.
All of these mean the same thing, and a tool that only handles two of them is worse than
useless: it silently types the value as "unknown" and you lose the trace.
What Coeus recognises
Detection tries the defanged forms first. If a value carries any marker, it is refanged and then re-typed; only if that fails does the plain path run. The rules are applied in order and are case-insensitive.
| Written form | Becomes | Note |
|---|---|---|
hxxps | https | Applied before hxxp, so the s survives |
hxxp | http | |
fxp | ftp | |
[.] ( . ) {.} [dot] (dot) | . | Whitespace inside the brackets is allowed |
a . b (dot with spaces) | a.b | Only when the dot has space on both sides |
[:] | : | For the scheme separator and IPv6-ish pastes |
[@] (at) [at] | @ | |
[/] | / | |
\. | . | Conditional — see below |
The last rule is the interesting one. Replacing \. with . unconditionally would corrupt
Windows paths and regular expressions, so it is applied as a second pass: the value is
refanged once, and if that does not parse as any known type, the escape rule is applied and
detection is tried again. evil\.com becomes a domain. C:\Windows\System32\cmd.exe never
does, because it already parsed as a file path on the first pass.
Refanging is only half of it. The reverse direction has to be conservative, because a defanged value is something you are about to paste into a permanent record.
| Type | Defanged as |
|---|---|
| URL | hxxps://evil[.]com/a?b=1 |
| Domain, IPv4 | evil[.]com, 185[.]220[.]101[.]4 |
user[@]evil[.]com | |
| Everything else | unchanged |
Only the host part of a URL is touched. Query strings, paths and fragments are left alone, which matters more than it sounds: a URL whose path contains a dotted filename becomes unreadable if you bracket every dot in the string, and a value that cannot be read back is not safer, only harder to work with. Hashes, CVEs, ASNs, MAC addresses, Bitcoin addresses, file paths and registry keys are never defanged, because they were never clickable.
Pitfalls worth knowing
Brackets inside a URL are legal. http://[2001:db8::1]/x is a perfectly valid URL with an
IPv6 literal host, and a naive [.]-to-. substitution will not damage it — but a naive
[ stripper will. Coeus only rewrites the bracketed sequences in the table above, never
brackets in general.
(at) is ambiguous. sales(at)example.com is an email. payload(at)0x400 is a note
somebody wrote in a malware report. The rule only fires as part of type detection, and if
nothing parses afterwards you get null rather than a wrong answer.
Trailing punctuation eats entities. Contact evil.com. in a sentence — the final dot is
the sentence's, not the domain's. Detection normalises a trailing dot away, which is why the
page scan does not turn every sentence-ending hostname into a broken trace.
Spaces are not a defang. 1 . 2 . 3 . 4 refangs to 1.2.3.4 because the dot-with-spaces
rule fires, but 1 2 3 4 is four numbers, and no tool should guess otherwise.
Conventions for tickets
The point of a convention is that the next person can undo it mechanically. Three habits make that true:
Defang once, at the boundary
Defang when the value leaves an analysis tool for a document a human will read: a ticket, an
email, a report. Do not defang inside your own pipeline — a detection rule with [.] in it is
a bug waiting to happen.
Use one dialect consistently
Pick hxxp plus [.] plus [@] and write it down. Coeus reads all the dialects above, but the
grep you run in six months will not.
Put the refanged value beside it
In a case, write the defanged form for the reader and put the exact original in a field nothing renders. Coeus's own copy actions give you both — "Copy defanged" and "Copy refanged" are two separate menu items for exactly this reason.
If you want to see the rules run, the context menu on any selection will show you the type it inferred before you click anything, and the placeholder reference has a live renderer that shows the defanged form alongside every other way a value can be substituted into a URL template.