<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>{TRAPDOOR SECURITY} - CVE Archaeology</title>
    <subtitle>Appsec &amp; Infosec - cybersecurity advisories, CTF writeups and projects.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://trapdoorsec.com/tags/cve-archaeology/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://trapdoorsec.com"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-08-31T00:00:00+00:00</updated>
    <id>https://trapdoorsec.com/tags/cve-archaeology/atom.xml</id>
    <entry xml:lang="en">
        <title>Vulnerability Spelunking #1: SSRF with Golang&#39;s url.Parse</title>
        <published>2026-08-31T00:00:00+00:00</published>
        <updated>2026-08-31T00:00:00+00:00</updated>
        
        <author>
          <name>akses</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://trapdoorsec.com/posts/vulnerability-spelunking-ep-1/"/>
        <id>https://trapdoorsec.com/posts/vulnerability-spelunking-ep-1/</id>
        
        <content type="html" xml:base="https://trapdoorsec.com/posts/vulnerability-spelunking-ep-1/">&lt;h1 id=&quot;let-s-go-on-a-ssrfing-adventure&quot;&gt;Let’s go on a SSRFing adventure&lt;/h1&gt;
&lt;p&gt;I feel that there is a great deal of knowledge locked up in the mistakes of history. Since I am interested in application security, this means I am inherently interested in the history of application security mistakes.&lt;/p&gt;
&lt;p&gt;People like Louis Nyffenegger, (who possibly coined the term &lt;a rel=&quot;external&quot; href=&quot;https://www.amazon.com/CVE-Archeologists-Field-Guide-vulnerability-ebook/dp/B0GL97C567&quot;&gt;‘CVE archaeology’&lt;/a&gt; in the first place) and &lt;a rel=&quot;external&quot; href=&quot;https://x.com/vxunderground&quot;&gt;vx-underground&lt;/a&gt; are an inspiration for those of us who like to explore these digital oddities.&lt;/p&gt;
&lt;p&gt;My intent is to show you this exploration, or ‘spelunking’ if you will. These are not my CVEs, just an analysis of other peoples. So, grab your hard hat and let’s head into the ancient CaVEs of MITRE’s back catalog of &lt;a rel=&quot;external&quot; href=&quot;https://cve.org&quot;&gt;CVEs&lt;/a&gt; and see what treasures we can find from the mistakes of yesterday, so that we are not doomed to repeat them today.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://trapdoorsec.com/posts/vulnerability-spelunking-ep-1/adventure.png&quot; alt=&quot;obligatory lord of the rings meme picture of bilbo running away from the shire&quot; /&gt;&lt;/p&gt;
&lt;h1 id=&quot;cave-2026-25679&quot;&gt;CaVE-2026-25679&lt;/h1&gt;
&lt;p&gt;CVE-2026-25679 itself is a parsing issue that affects a category of golang web services that I’ll demonstrate in a little while. While it is new, it has historical pedigree on account of the fact that it was a mistake made while addressing an older &lt;a rel=&quot;external&quot; href=&quot;https://github.com/advisories/GHSA-447v-2qg4-h8hc&quot;&gt;CVE&lt;/a&gt; from last year.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;CVE-2026-25679 is a high-severity input validation vulnerability in the Go programming language’s standard library net/url package.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It affects the &lt;code&gt;url.Parse&lt;/code&gt; function, which fails to correctly validate the host/authority component of URLs, specifically allowing malformed IPv6 host literals and other invalid characters that should be rejected according to RFC 3986 standards. URL parsing errors in standard libraries provide bug hunters like me a target rich environment.&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;ID&lt;/td&gt;&lt;td&gt;&lt;a rel=&quot;external&quot; href=&quot;https://nvd.nist.gov/vuln/detail/cve-2026-25679&quot;&gt;CVE-2026-25679&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Credit goes to&lt;/td&gt;&lt;td&gt;&lt;a rel=&quot;external&quot; href=&quot;https://sg.wantedly.com/users/28514192&quot;&gt;Masaki Hari&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Report&lt;/td&gt;&lt;td&gt;&lt;a rel=&quot;external&quot; href=&quot;https://sg.wantedly.com/companies/wantedly/post_articles/1041394&quot;&gt;https://sg.wantedly.com/companies/wantedly/post_articles/1041394&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Weakness(es)&lt;/td&gt;&lt;td&gt;&lt;a rel=&quot;external&quot; href=&quot;https://cwe.mitre.org/data/definitions/1286.html&quot;&gt;Improper Validation&lt;/a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https://cwe.mitre.org/data/definitions/425.html&quot;&gt;Forced Browsing&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Severity&lt;/td&gt;&lt;td&gt;&lt;span class=&quot;severity-pill&quot; data-sev=&quot;7.5&quot;&gt;&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CVSS Vector&lt;/td&gt;&lt;td&gt;CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Vendor&lt;/td&gt;&lt;td&gt;&lt;code&gt;golang std lib&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Affected Components &amp;amp; Versions&lt;/td&gt;&lt;td&gt;&lt;code&gt;net/url&lt;/code&gt; before go1.25.8, from go1.26.0-0 before go1.26.1&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;into-the-darkness-we-go&quot;&gt;Into the darkness we go&lt;/h1&gt;
&lt;p&gt;The flaw is simple: normally, an invalid URL would fail validation checks. But in these versions of go, an invalid URL could slip through the cracks. E.g. a non-standard URL like this should fail validation &lt;code&gt;http://trapdoorsec.com[::1]/&lt;/code&gt;, but it passes. Worse, it &lt;em&gt;accepts&lt;/em&gt; the value inside the brackets!&lt;/p&gt;
&lt;p&gt;For this to make sense you need to know that URLs are made up of many components, &lt;code&gt;host&lt;/code&gt; being one of them. Consider this code that breaks a URL apart into its constituents.&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;package&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-8&quot;&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;import&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;	&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;	&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;	&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;net/url&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; main&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;	raw_url&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; :=&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;https://trapdoorsec.com/login?redir=page&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;	url&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; err&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; :=&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; url&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Parse&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;raw_url&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;                             &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;	if&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; err&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; !=&lt;/span&gt;&lt;span class=&quot;z-l-7 z-d-7&quot;&gt; nil&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;		log&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Fatal&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;err&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;	fmt&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;Scheme:  &lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; parsedURL&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Scheme&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;	fmt&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;Host:    &lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; parsedURL&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Host&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;	fmt&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;Hostname:&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; parsedURL&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Hostname&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;	fmt&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;Port:    &lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; parsedURL&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Port&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;	fmt&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;Path:    &lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; parsedURL&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Path&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you were to run this with &lt;code&gt;go run main.go&lt;/code&gt; it would print out the result.&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Scheme:   https&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Host:     trapdoorsec.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Hostname: trapdoorsec.com&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Port:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Path:     /login&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are more components than this, but this is all you need to know. From the above, I trust that you can see that &lt;code&gt;url.Parse&lt;/code&gt; has several very important jobs.&lt;/p&gt;
&lt;p&gt;One of which is to find the part of the received string that corresponds with the &lt;code&gt;host&lt;/code&gt;. The function that does this is called &lt;code&gt;parseHost&lt;/code&gt; and it happens to be where this &lt;a rel=&quot;external&quot; href=&quot;https://github.com/golang/go/blob/d90b98e65320778f3b1f99a6951ab20f04d218b3/src/net/url/url.go#L549-L552&quot;&gt;vulnerability exists&lt;/a&gt;:-&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; parseHost&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scheme&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; host&lt;/span&gt;&lt;span class=&quot;z-l-3 z-l-b z-d-6 z-d-b&quot;&gt; string&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;z-l-3 z-l-b z-d-6 z-d-b&quot;&gt;string&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-3 z-l-b z-d-6 z-d-b&quot;&gt; error&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;	if&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; openBracketIdx&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; :=&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; strings&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;LastIndex&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;host&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; openBracketIdx&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; !=&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; -&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-9&quot;&gt;1&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span class=&quot;z-l-6 z-l-i z-d-3 z-d-i&quot;&gt; //&lt;/span&gt;&lt;span class=&quot;z-l-6 z-l-i z-d-3 z-d-i&quot;&gt; &amp;lt;-- vulnerability here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-l-i z-d-3 z-d-i&quot;&gt;		//&lt;/span&gt;&lt;span class=&quot;z-l-6 z-l-i z-d-3 z-d-i&quot;&gt; Parse an IP-Literal in RFC 3986 and RFC 6874.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-l-i z-d-3 z-d-i&quot;&gt;		//&lt;/span&gt;&lt;span class=&quot;z-l-6 z-l-i z-d-3 z-d-i&quot;&gt; E.g., &amp;quot;[fe80::1]&amp;quot;, &amp;quot;[fe80::1%25en0]&amp;quot;, &amp;quot;[fe80::1]:80&amp;quot;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;		closeBracketIdx&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; :=&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; strings&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;LastIndex&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;host&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;		if&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; closeBracketIdx&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-9&quot;&gt; 0&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;			return&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; errors&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;New&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;missing &amp;#39;]&amp;#39; in host&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-l-i z-d-3 z-d-i&quot;&gt;		//&lt;/span&gt;&lt;span class=&quot;z-l-6 z-l-i z-d-3 z-d-i&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The job of that &lt;code&gt;if&lt;/code&gt; check on the second line, is to hunt down open brackets inside of the host string. This is because, most of the time, hosts in URLs don’t have square brackets in them. If they do, it must mean it is an IPv6 address. An example of a valid IPv6 address in a URL looks like this: &lt;code&gt;https://[::1]/login?redir=home&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The comment from the contributor stated:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Now, the parsing logic should strictly enforce that only IPv6 hostnames can be resolved when in square brackets.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Except that code isn’t strict enough. Go’s &lt;code&gt;strings.LastIndex&lt;/code&gt; will return the &lt;em&gt;last&lt;/em&gt; occurrence of the &lt;code&gt;[&lt;/code&gt; character. URLs with IPv6 addresses in them should only ever have a &lt;code&gt;[&lt;/code&gt; at the &lt;em&gt;beginning&lt;/em&gt; of the string. This mistake trusts that the open bracket will &lt;em&gt;always be at the beginning of the string&lt;/em&gt; but never checks that! To bring this full circle, hopefully now you can see why this validation/parsing issue is possible.&lt;/p&gt;
&lt;p&gt;The patch is also relatively straight forward, all we have to do is fail at that point&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; parseHost&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;host&lt;/span&gt;&lt;span class=&quot;z-l-3 z-l-b z-d-6 z-d-b&quot;&gt; string&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span class=&quot;z-l-3 z-l-b z-d-6 z-d-b&quot;&gt;string&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-3 z-l-b z-d-6 z-d-b&quot;&gt; error&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;	if&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; openBracketIdx&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; :=&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; strings&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;LastIndex&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;host&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; openBracketIdx&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; &amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-9&quot;&gt; 0&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;		return&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; errors&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;New&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;invalid IP-literal&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-l-6 z-l-i z-d-3 z-d-i&quot;&gt; //&lt;/span&gt;&lt;span class=&quot;z-l-6 z-l-i z-d-3 z-d-i&quot;&gt; &amp;lt;-- fix here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; else&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; if&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; openBracketIdx&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; ==&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-9&quot;&gt; 0&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-l-i z-d-3 z-d-i&quot;&gt;		//&lt;/span&gt;&lt;span class=&quot;z-l-6 z-l-i z-d-3 z-d-i&quot;&gt; Parse an IP-Literal in RFC 3986 and RFC 6874.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-l-i z-d-3 z-d-i&quot;&gt;		//&lt;/span&gt;&lt;span class=&quot;z-l-6 z-l-i z-d-3 z-d-i&quot;&gt; E.g., &amp;quot;[fe80::1]&amp;quot;, &amp;quot;[fe80::1%25en0]&amp;quot;, &amp;quot;[fe80::1]:80&amp;quot;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;		closeBracketIdx&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; :=&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; strings&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;LastIndex&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;host&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;		if&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; closeBracketIdx&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-9&quot;&gt; 0&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;			return&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; errors&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;New&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;missing &amp;#39;]&amp;#39; in host&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;		}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-l-i z-d-3 z-d-i&quot;&gt;		//&lt;/span&gt;&lt;span class=&quot;z-l-6 z-l-i z-d-3 z-d-i&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;exploitation&quot;&gt;Exploitation&lt;/h2&gt;
&lt;p&gt;I think perhaps the reason this bug didn’t get loads of attention is that its direct impact is limited to availability, despite its low attack complexity.&lt;/p&gt;
&lt;p&gt;That doesn’t mean it isn’t useful though, because one of the issues with the CVSS (the scoring system we use to rate these bugs) is that it doesn’t really deal with ‘chainability’. Arguably, this is a highly chainable primitive by which to launch other attacks from.&lt;/p&gt;
&lt;p&gt;Let’s return to our contrived example code and see what happens:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;go&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; main&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-6 z-l-i z-d-3 z-d-i&quot;&gt;	//&lt;/span&gt;&lt;span class=&quot;z-l-6 z-l-i z-d-3 z-d-i&quot;&gt; this time pass a &amp;#39;malicious string&amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;	raw_url&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; :=&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;https://trapdoorsec.com[::1]:8080/login?redir=page&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;	url&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; err&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; :=&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; url&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Parse&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;raw_url&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;                            &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;	if&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; err&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; !=&lt;/span&gt;&lt;span class=&quot;z-l-7 z-d-7&quot;&gt; nil&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;		log&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Fatal&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;err&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;	fmt&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;Scheme:  &lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; parsedURL&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Scheme&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;	fmt&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;Host:    &lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; parsedURL&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Host&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;	fmt&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;Hostname:&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; parsedURL&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Hostname&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;	fmt&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;Port:    &lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; parsedURL&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Port&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;	fmt&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Println&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;Path:    &lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; parsedURL&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;Path&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;output:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Scheme:   https&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Host:     trapdoorsec.com[::1]:8080&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Hostname: ::1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Port:     8080&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Path:     /login&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Crazy right? The actual URL is ignored, and the hostname is the malicious IPv6 address, and the port remains intact!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Notice that Host retains the malformed authority exactly as supplied, while Hostname() interprets the destination as ::1. The apparently legitimate trapdoorsec.com portion has disappeared from Go’s interpretation of the network destination.&lt;/p&gt;
&lt;p&gt;Now consider that there are many types of systems that rely on trustworthy URL parsing;-&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Webhook delivery systems (CI/CD, Stripe-style callbacks, monitoring pings):&lt;/strong&gt; imagine an [ab]user registers &lt;code&gt;https://ci.example.com[::1]/hook&lt;/code&gt; as their webhook target&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSO/OIDC redirect &amp;amp; issuer validation:&lt;/strong&gt; an IdP that validates &lt;code&gt;redirect_uri&lt;/code&gt; by checking the parsed host will pass the check while subsequent request flows go to an attacker-chosen internal address&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;URL fetchers in security tooling:&lt;/strong&gt; VirusTotal-style scanners, image/CVE feed fetchers, package proxy validators. A “scan this URL” feature becomes an internal network mapper with a trusted origin&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Proxy/ACL enforcement:&lt;/strong&gt; corporate egress proxies or API gateways that parse the URL to decide allow/deny by domain category.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Microservice internal routing:&lt;/strong&gt; services that accept a URL from a peer and route by parsed host&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Social media style URL ‘unfurlers’:&lt;/strong&gt; these are quite common, a user makes a post with a URL in it, the backend goes and fetches cool stuff about that URL and renders it in the post.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In all instances, the victim becomes a bit like a curl puppet to the attacker, and can be misused to retrieve internal system details to further the attackers goals (cookies, tokens, internal network info). So lets pick on an example and throw it into a lab.&lt;/p&gt;
&lt;h3 id=&quot;example-url-unfurling&quot;&gt;Example: URL unfurling&lt;/h3&gt;
&lt;blockquote class=&quot;markdown-alert-note&quot;&gt;
&lt;p&gt;If you are already familiar with SSRF attacks, none of the following is likely to surprise you, but I encourage you to stick around to the end of the post to go over mitigations beyond ‘patch ur sh**’&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let’s imagine if you are trusting &lt;code&gt;url.Parse&lt;/code&gt; to get you the correct host, but instead, your program is fetching against the local network on all ports &lt;em&gt;as the server&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;What could we do with that? Well for this part I need you to imagine a twitter clone or other kind of social media site. Let’s call it &lt;code&gt;tripper&lt;/code&gt; for funzies. Tripper lets you chat with friends, and when you use a URL in your posts, the backend will ‘unfurl’ it.&lt;/p&gt;
&lt;p&gt;Wait up, what is &lt;em&gt;unfurling&lt;/em&gt;? You’ve probably seen this in many social media sites, where you get a little automatic embed for every link you sprinkle into your post. For example, &lt;a rel=&quot;external&quot; href=&quot;https://docs.slack.dev/messaging/unfurling-links-in-messages/&quot;&gt;slack does this&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So it is very common, and a naive approach to doing it would look something like this:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;mermaid&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;flowchart&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    user &lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;--&amp;gt;|&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;submits message with url inside&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;|&lt;/span&gt;&lt;span&gt; tripper&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    tripper &lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;--&amp;gt;|&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;validate url&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;|&lt;/span&gt;&lt;span&gt; policy&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;check allow list&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    policy &lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;--&amp;gt;&lt;/span&gt;&lt;span&gt; fetcher&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;preview web fetch&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    fetcher &lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;--&amp;gt;&lt;/span&gt;&lt;span&gt; internet&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;external website&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    internet &lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;--&amp;gt;&lt;/span&gt;&lt;span&gt; fetcher&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    fetcher &lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;--&amp;gt;|&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;renders html embed&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;|&lt;/span&gt;&lt;span&gt; tripper&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;p&gt;Typically, once initial url validation passes, destination validation is checked to make sure this address was allowed. &lt;strong&gt;This is precisely where this sneaky little bug has benefit.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;At its heart, it is a potential for bypassing loose validation logic, similar to the way we would dodge badchars in XSS attacks.&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;mermaid&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;flowchart&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; TD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    attacker&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    attacker &lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;--&amp;gt;|&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;sends&amp;lt;br/&amp;gt;http://example.com[::1]:port&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;|&lt;/span&gt;&lt;span&gt; parser&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;vulnerable url.parse&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    parser &lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;--&amp;gt;&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;parsed host becomes&amp;lt;br/&amp;gt;::1&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    result &lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;--&amp;gt;&lt;/span&gt;&lt;span&gt; request&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;fetcher bypasses block list&amp;lt;br/&amp;gt;localhost:port instead of&amp;lt;br/&amp;gt;example.com&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    expected&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;expected behaviour:&amp;lt;br/&amp;gt;reject malformed authority&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    attacker &lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;-.-&amp;gt;&lt;/span&gt;&lt;span&gt; expected&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So now we can see why this bug is narrow in isolation but potentially dangerous under the right conditions and so therefore still deserves a &lt;code&gt;high&lt;/code&gt; risk rating.&lt;/p&gt;
&lt;p&gt;The potential for &lt;a rel=&quot;external&quot; href=&quot;https://owasp.org/www-community/attacks/server_side_request_forgery&quot;&gt;server side request forgery&lt;/a&gt; is present here. The reason why SSRF is not mentioned in the CVE is that this is only a potential second order effect, and highly situational. That said, URL validation followed by server-side fetching is an extremely common pattern. Applications using an affected Go runtime alongside this kind of unsafe validation logic may have been vulnerable, and unpatched applications may remain so.&lt;/p&gt;
&lt;h3 id=&quot;the-lab&quot;&gt;The lab&lt;/h3&gt;
&lt;p&gt;To prove this out for real, I built a small Docker Compose network that mimics the trust boundary you’d find in any real deployment. There are exactly two services on a pinned internal subnet (&lt;code&gt;172.28.0.0/24&lt;/code&gt;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;tripper&lt;/strong&gt; (&lt;code&gt;172.28.0.20&lt;/code&gt;) - our vulnerable unfurler, and the &lt;em&gt;only&lt;/em&gt; thing exposed to the outside world, bound to &lt;code&gt;127.0.0.1:8080&lt;/code&gt; on the host.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;victim&lt;/strong&gt; (&lt;code&gt;172.28.0.10&lt;/code&gt;) - a stand-in for something juicy and internal. It listens &lt;em&gt;internally&lt;/em&gt; on port 80 and dumps whatever it receives to its logs. Crucially, it has no port mapping at all: so it is invisible to our attacker. For now.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The attacker (that’s us, sitting out on the “internet”) has exactly one route into this network: the unfurl endpoint. We can’t reach the victim directly, we can’t sweep the subnet from the host, and the SSRF guard string-matches the hostname precisely to keep it that way. And yet, with a single smuggled URL, the guard waves us straight through and tripper - a &lt;em&gt;trusted internal service&lt;/em&gt; - knocks on the victim’s door on our behalf. The defender’s control and the attacker’s egress are the same socket.&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;mermaid&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;title: Lab topology&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;flowchart&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; LR&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;    subgraph&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; Internet&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        A&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;curl / ffuf&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;    subgraph&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; Docker network 172&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;28&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;24&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        T&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;website&amp;lt;br/&amp;gt;int: 172.28.0.20:8080&amp;lt;br/&amp;gt;ext: vulnerable.site:8080&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        V&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;victim&amp;lt;br/&amp;gt;172.28.0.10:80&amp;lt;br/&amp;gt;no ext. comms&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;    end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    A &lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;--&amp;gt;|&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;http://vulnerable.site:8080&amp;lt;br/&amp;gt;/unfurl?url=&amp;lt;br/&amp;gt;http://amazon.com[::ffff:ac1c:a]:80&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;|&lt;/span&gt;&lt;span&gt; T&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    T &lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;--&amp;gt;|&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;SSRF fetch amazon.com port 80&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;|&lt;/span&gt;&lt;span&gt; V&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    V &lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;--&amp;gt;&lt;/span&gt;&lt;span&gt; T&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    A &lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;-.-&amp;gt;|&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;direct access impossible&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;|&lt;/span&gt;&lt;span&gt; V&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To demonstrate this I’ve written a contrived example of a vulnerable webservice that does server side web fetching, available as a &lt;a rel=&quot;external&quot; href=&quot;https://gist.github.com/akses0/75206bef66ae9889ac26a86a74520308&quot;&gt;gist&lt;/a&gt; it takes a url as a get param, (which is what makes it contrived - use your imagination from here), and a vulnerable network in which to test it from your host. The demo shows that basic tools can do an address sweep to find other attack-able web servers on an internal network by fuzzing for IPv6 addresses.&lt;/p&gt;
&lt;blockquote class=&quot;markdown-alert-note&quot;&gt;
&lt;p&gt;As far as I could tell, IPv6 addresses would need to be written in fully hexadecimal form. For e.g. to sweep a network, you’d want a list like this…&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; ::ffff:ac1c:02&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; ::ffff:ac1c:03&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; ::ffff:ac1c:04&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; ::ffff:ac1c:05&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; ...and so on&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using &lt;code&gt;ffuf&lt;/code&gt; we would take aim at a vulnerable service in the following way, noting that usual issues like encoding might get in the way of a good time:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;seq&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-9&quot;&gt; 2&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-9&quot;&gt; 254&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; awk&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; &amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;{printf &amp;quot;::ffff:ac1c:%x\n&amp;quot;, $1}&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; &amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; ips.txt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;ffuf&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-8&quot;&gt; -&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-8&quot;&gt;u&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; &amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;http://vulnerable.site:8080/unfurl?url=http%3A%2F%2Fdummy.com%5BFUZZ%5D%3A80&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-8&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-8&quot;&gt;     -&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-8&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; ips.txt&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-8&quot;&gt; -&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-8&quot;&gt;fr&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; &amp;#39;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;deadline exceeded&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The sweep above is then just automation on top: ask tripper to unfurl the mapped-hex form for every candidate address on the subnet and watch the timing oracle light up when something answers. Two hosts exist on this network, and from outside, only one of them is supposed to be findable.&lt;/p&gt;
&lt;script src=&quot;https://asciinema.org/a/GthJhGlIL5xjUwAS.js&quot; id=&quot;asciicast-GthJhGlIL5xjUwAS&quot; async=&quot;true&quot;&gt;&lt;/script&gt;
&lt;h1 id=&quot;what-treasures-can-we-extract-from-this-spelunk&quot;&gt;What treasures can we extract from this spelunk?&lt;/h1&gt;
&lt;p&gt;Most teams kind of ‘patch and move on’ from something like this, however the real lesson is not to trust that input at all. &lt;code&gt;url.Parse&lt;/code&gt; was never meant to be a security boundary, it just tells you if the URL is valid. Besides, what if it happens again?&lt;/p&gt;
&lt;p&gt;The truth is that it is up to application logic AND network design to prevent SSRF from becoming a problem in your architecture. So here are some final thoughts on additional mitigations we have at our disposal.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Parse the URL once and use that same parsed representation for both validation and retrieval.&lt;/li&gt;
&lt;li&gt;Permit only the schemes they actually require, usually &lt;code&gt;http&lt;/code&gt; and &lt;code&gt;https&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Apply destination controls consistently to IPv4, IPv6, and IPv4-mapped IPv6 addresses. This isn’t an excuse to turn off IPv6 IMO.&lt;/li&gt;
&lt;li&gt;Reject loopback, private, link-local and otherwise prohibited destinations &lt;em&gt;after&lt;/em&gt; DNS resolution.&lt;/li&gt;
&lt;li&gt;Apply the same checks to every redirect target.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enforce outbound network restrictions so the fetcher cannot reach sensitive internal services even if application validation fails.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Add malformed authorities such as example.com[::1] to regression tests.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;until-next-time&quot;&gt;Until next time!&lt;/h2&gt;
&lt;p&gt;In summary, I hope you can see that the exploitability of a flaw like this depends heavily on the architecture around it. A finding from a dumb scanner could be meaningless noise, or genuine cause for alarm.&lt;/p&gt;
&lt;p&gt;Even LLM-assisted reachability analysis may get this wrong, because source code alone may not reveal the runtime routing, configuration and trust boundaries that determine whether the flaw is exploitable. Sometimes, confirming the real risk requires testing under the representative dynamic conditions of the production environment.&lt;/p&gt;
&lt;p&gt;Thanks for reading, I do hope you like the new theme as well :)&lt;/p&gt;
</content>
        
    </entry>
</feed>
