Skip to content

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.

6 min readCoeus — Maintainersentitiesworkflow

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 formBecomesNote
hxxpshttpsApplied before hxxp, so the s survives
hxxphttp
fxpftp
[.] ( . ) {.} [dot] (dot).Whitespace inside the brackets is allowed
a . b (dot with spaces)a.bOnly 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.

Detection order: defanged forms first, then the plain patterns

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.

TypeDefanged as
URLhxxps://evil[.]com/a?b=1
Domain, IPv4evil[.]com, 185[.]220[.]101[.]4
Emailuser[@]evil[.]com
Everything elseunchanged

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.