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.

Reply via email to