https://bz.apache.org/SpamAssassin/show_bug.cgi?id=8471
Bug ID: 8471
Summary: Redirectors: some redirects are not resolved properly
Product: Spamassassin
Version: 4.0.3
Hardware: PC
OS: Mac OS X
Status: NEW
Severity: normal
Priority: P2
Component: Libraries
Assignee: [email protected]
Reporter: [email protected]
Target Milestone: Undefined
Created attachment 6163
--> https://bz.apache.org/SpamAssassin/attachment.cgi?id=6163&action=edit
proposed patch
A real-world spam/phishing evasion works against SpamAssassin while the same
URL resolves normally in a browser, so the true destination is never fetched,
scanned, or available to downstream rules.
SUMMARY
When a shortener/redirector URL contains a userinfo component (the
"user[:pass]@" that appears before the host), the Redirectors plugin did fetch
the URL, but fetched it verbatim -- so LWP turned the userinfo into a
preemptive "Authorization: Basic" header. A browser does not send that header
until challenged, so the shortener redirects for a browser; but a shortener
that refuses requests carrying an Authorization header answers 403 instead of
the redirect. In that case the plugin obtained no Location and never saw the
real target. The fetch itself was not skipped and the host was matched
correctly; the request simply carried a header that made some servers decline
to redirect. A spammer exploits this by prepending a lookalike userinfo to a
shortener URL: it reads like a trusted brand to a human, resolves normally in a
browser, but defeats a scanner that fetches it as-is.
A related weakness in the same code path: the host was requested exactly as it
appeared in the message, so a percent-encoded or IDN-octet spelling of the host
(e.g. b%C3%BCcher.de) was handed to LWP unchanged. LWP does not fold such a
host to its punycode form, so it attempts to resolve the raw octets and fails,
again hiding the destination that a browser would have reached.
ORIGINAL SAMPLE
An email (a spoofed Google Calendar "Zoom meeting" invitation) contained this
link in both the text/calendar and text/html parts:
https://us06web.zoom.us-join-meeting@da[.]gd/4zHT
FIX
A new accessor Mail::SpamAssassin::URI::http_url() returns the http(s) URL to
request from a server -- rebuilt from the URI's parts -- or undef when there is
nothing a client could fetch. Specifically it:
- uses the canonical host (decoded, lowercased, IDN-folded to xn--), so an
obfuscated spelling is requested as the real host, matching the browser and
the host the plugin matched on;
- excludes the userinfo (not sent preemptively by a browser) and the fragment
(client-side only, RFC 3986);
- preserves the original percent-encoding of the path and query;
- returns undef for a non-http(s) scheme or a host that is not a requestable
name (e.g. the encoded-slash authority), which a caller treats as "nothing
to fetch".
http_url() replaces the former without_fragment(), whose only consumer was this
plugin. Redirectors.pm now calls $uri->http_url in _do_http() (returning early
when it is undef) and in the self-redirect loop comparison in _walk_redirects()
(undef-safe: a Location with no requestable form is simply not the same
request).
Files changed:
- lib/Mail/SpamAssassin/URI.pm
- lib/Mail/SpamAssassin/Plugin/Redirectors.pm
- t/uri_class.t
--
You are receiving this mail because:
You are the assignee for the bug.