Linktarythe notary for links

Anatomy of a malicious redirect chain

2026-09-25 · 8 min read · Linktary team

Anatomy of a malicious redirect chain

About the illustrations. Linktary is live in production

(https://linktary.com). The chain diagrams below are rendered from the

verification engine's own test fixtures — the same canned chains the

test suite scores every run — and are labeled illustrative wherever

they appear. A live trust-report specimen follows at the end.

The most instructive way to understand verification is to watch it catch something. Below is a hostile chain, walked hop by hop, exactly as the engine sees it.

The setup

A user submits https://example.com/t for shortening. It looks harmless — one clean domain, HTTPS. The engine fetches it with its bounded budget (5 hops max, 8 seconds total) and records every hop:

Illustrative chain, rendered from engine test fixtures:

hop 1   GET https://example.com/t
        → 302 Location: https://ex%D0%B0mple.com/landing

hop 2   GET https://exаmple.com/landing        (Cyrillic "а", U+0430)
        → 302 Location: http://exаmple.com/verify

hop 3   GET http://exаmple.com/verify
        → 200  content-type: application/x-msdownload
               body starts with: MZ...

Three hops, three separate attacks stacked in one chain. Let's take them in order.

Hop 1 → 2: the lookalike domain

%D0%B0 is the percent-encoding of Cyrillic а (U+0430). Decoded, the hostname reads "exаmple.com" — visually identical to "example.com" in most fonts, a different domain in reality. Servers percent-encode Location headers (a raw-Unicode Location is invalid HTTP), so the lookalike arrives encoded and only reveals itself after decoding.

The engine checks the pre-normalization hostname here deliberately: WHATWG URL parsing punycode-encodes IDNs, so by the time the request goes out, the hostname is xn--exmple-... and the mixed-script evidence is gone. Checking the raw form catches what the normalized form hides.

Signal: mixed_script_host — +40, warn.

"The domain mixes character scripts (e.g. Latin and Cyrillic) to mimic

a trusted name."

Note the precision rule: the engine emits the precise diagnosis, not both. A mixed-script host gets mixed_script_host, never the generic idn_punycode_host on top of it. Scores are reproducible because double-counting is designed out.

Hop 2 → 3: the HTTPS downgrade

The lookalike page redirects again — this time from HTTPS to plain HTTP. On an encrypted hop the attacker can't easily tamper with content in transit; on the downgraded hop, everything is interceptable and modifiable.

Signal: https_downgrade — +40, warn.

"The chain downgrades from HTTPS to HTTP — traffic can be intercepted."

Hop 3: the payload

The final hop returns 200 with content-type: application/x-msdownload and a body starting with MZ — the magic bytes of a Windows executable. The content-type alone is enough, and the magic-byte sniff confirms it independently: even if the server had claimed text/html, the bytes would betray it (that's the content_mismatch signal, a cloaking pattern).

Signal: executable_content — +100, block.

"The destination serves an executable program — the highest-risk

content."

The verdict

The engine also records the domain crossing (cross_domain_hop, +30: example.com → the lookalike is a different registrable domain) and notes that the registration-age lookup was unavailable (rdap_unavailable, +0, informational only).

Illustrative scorecard:

mixed_script_host    +40   warn
https_downgrade      +40   warn
cross_domain_hop     +30   warn
executable_content  +100   block
rdap_unavailable      +0   info
─────────────────────────────
total                210   → BLOCKED (threshold: ≥100)

At 210, this isn't close. The link is BLOCKED: the destination never loads, and the visitor gets a block page naming the top signals in plain language. There is no bypass, no "proceed anyway" — some chains are beyond caution.

What the trust report shows

The same data is published on the link's trust report at linktary.com/v/<code>: the red BLOCKED badge, the three hops with their status codes, the plain-language signal explanations, the score, and the check timestamp. The person who received the link can see exactly why it was refused — without an account, without asking us.

That's the whole philosophy in one chain: fetch it yourself, score it openly, publish the reasoning. The attacker stacked three evasions — lookalike, downgrade, cloaked payload — and each one tripped a signal designed for exactly that evasion. For the full signal taxonomy and the thresholds, see How Linktary verifies a link.