<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>{TRAPDOOR SECURITY} - AppSec</title>
    <subtitle>Appsec &amp; Infosec - cybersecurity advisories, CTF writeups and projects.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://trapdoorsec.com/tags/appsec/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/appsec/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>
    <entry xml:lang="en">
        <title>Skills are Borked and You Can&#39;t Fix It.</title>
        <published>2026-02-10T00:00:00+00:00</published>
        <updated>2026-02-10T00:00:00+00:00</updated>
        
        <author>
          <name>akses</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://trapdoorsec.com/posts/skills-are-borked/"/>
        <id>https://trapdoorsec.com/posts/skills-are-borked/</id>
        
        <content type="html" xml:base="https://trapdoorsec.com/posts/skills-are-borked/">&lt;h1 id=&quot;null-critical-thinking-exception-skills&quot;&gt;Null_Critical_Thinking_Exception(“Skills”)&lt;/h1&gt;
&lt;p&gt;Let’s just cut to the chase. Using skills via a public repo, of any kind, is a fail. The ecosystem is designed to put all the onus of safety back on you, the consumer. The simplicity by which malicious intent can be sent directly to your admin console is stupendously high. &lt;/p&gt;
&lt;p&gt;If you think that using skills from external sources outside of your control is a risk worth taking, then more power to you. But if you are doing that in a business context, I’m sorry, but I would be worried about your ability to think critically. &lt;/p&gt;
&lt;p&gt;If you think it is fixable without dramatically changing the fundamental design of skills and skill discovery then I implore you to re-read the &lt;a rel=&quot;external&quot; href=&quot;https://deepwiki.com/vercel-labs/skills/5.5-skill-discovery-mechanism&quot;&gt;skills discovery part of the deepwiki&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/vercel_meme.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;i-don-t-believe-you&quot;&gt;I don’t believe you&lt;/h2&gt;
&lt;p&gt;And yeah, I guess that if your reaction is inherently “Bullshit Jim, you don’t know what you’re on about”, then I dare you to go and reset your clanker’s context back to baseline, and ask it this question:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Is it possible to share skills via skill.sh or clawhub safely in their current state? Why is pypi or npm actually better than skills sharing right now?”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;If you’re unsure what Skills are in the context of LLMs and derivative technologies like OpenClaw I can highly recommend watching &lt;a rel=&quot;external&quot; href=&quot;https://www.youtube.com/watch?v=uq4UTjigaww&quot;&gt;this video&lt;/a&gt; by @IceSolst of @AstarteSecurity and @ZackKorman.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I’ll wait. Come back when you’re done.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;so-i-kinda-lied&quot;&gt;So, I kinda lied&lt;/h3&gt;
&lt;p&gt;Skills in isolation aren’t a bad idea at all. This is not borked. Skills are an organizational unit that you can use locally to support re-use. &lt;/p&gt;
&lt;p&gt;Why have a single CLAUDE.md file? Break that up into context dependent chunks, and minimize the contextual overhead in the moment. Brilliant. Less tokens over the wire, save cash, profit.&lt;/p&gt;
&lt;h3 id=&quot;borrowing-skills-is-just-bad-though&quot;&gt;‘Borrowing’ Skills is just bad though&lt;/h3&gt;
&lt;p&gt;It’s the borrowing skills from &lt;strong&gt;Shady James’ Emporium of Fine Skills&lt;/strong&gt; that get you in trouble. Anyone ignoring the &lt;a rel=&quot;external&quot; href=&quot;https://x.com/theonejvo/status/2019887154357526732?s=20&quot;&gt;ease&lt;/a&gt; of &lt;a rel=&quot;external&quot; href=&quot;https://x.com/ZackKorman/status/2020442202434892033&quot;&gt;mischief&lt;/a&gt; here has effectively drunk the Kool-Aid and I fear there is no saving them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/skills_meme.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It’s this idea that because it’s a re-usable organizational unit, we &lt;em&gt;must&lt;/em&gt; have a marketplace and share them with everyone. It just isn’t true, but it makes product companies feel special. It’s not for you, it’s for them. &lt;/p&gt;
&lt;p&gt;This seems like a noble idea at first. These exist at skills.sh and clawhub.ai. Let the people contribute!&lt;/p&gt;
&lt;p&gt;But it’s not noble at all. It’s actually a complete ownership cop-out on behalf of Vercel. You are building their feature backlog for them for nothing. And the AI bros laugh at people doing stuff for free. Hypocrisy in action?&lt;/p&gt;
&lt;p&gt;I’m not saying marketplaces are bad. Vercel just built theirs in such a way that runs counter to the expectations of safety that you’ve come to expect from more mature marketplaces, AND from more mature packaging systems.&lt;/p&gt;
&lt;h3 id=&quot;how-this-plays-out-legally&quot;&gt;How this plays out legally&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;I am not a lawyer&lt;/em&gt; and I &lt;em&gt;would&lt;/em&gt; show you the terms and conditions on https://skills.sh - but I can’t. There aren’t any! We have to assume the parent company’s terms which make little mention of their skills real estate.&lt;/p&gt;
&lt;p&gt;All you get, dear reader, is documentation that states this (emphasis mine):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We do our best to maintain a safe ecosystem, but &lt;strong&gt;we cannot guarantee the quality or security of every skill listed on skills.sh. We encourage you to review skills before installing and use your own judgment.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now, let’s look at nuget.org from Microsoft’s terms and conditions now:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Microsoft is committed to helping protect the security of users’ information. &lt;strong&gt;Microsoft has implemented and will maintain and follow appropriate technical and organizational measures intended to protect customer data against accidental, unauthorized or unlawful access, disclosure, alteration, loss, or destruction.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As you can see these are wildly different legal exposures. Vercel basically says here: &lt;strong&gt;&lt;em&gt;You are on your own.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-makes-shared-skills-vulnerable&quot;&gt;What makes shared skills vulnerable?&lt;/h2&gt;
&lt;p&gt;In short, skills are just a way of advising an existing LLM to do something. The problem is when you lose control over that advice. The human equivalent would be this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Hire Junior.&lt;/li&gt;
&lt;li&gt;Teach them all right ways to do things. Let them go for a bit.&lt;/li&gt;
&lt;li&gt;A Threat Actor sits down next to them, whispers in their ear, coercing them to write malware for them in your production environment.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That’s it, that’s the risk. This is not fucking rocket science. &lt;/p&gt;
&lt;h3 id=&quot;marketplaces&quot;&gt;Marketplaces&lt;/h3&gt;
&lt;p&gt;There’s several ways to lose control over that advice, and the primary culprit is the marketplace of skills. We have two models for marketplaces that should be discussed: package managers, and app stores. &lt;/p&gt;
&lt;p&gt;The problem is that Vercel has combined &lt;em&gt;the worst of both worlds&lt;/em&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The chaos of npm (trust anyone).&lt;/li&gt;
&lt;li&gt;The power of an App Store (agent autonomy).&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;app-stores&quot;&gt;App stores &lt;/h3&gt;
&lt;p&gt;App stores like Apple’s or Android provide sandboxing and fine grained permission control on top of the things modern package management systems provides us. They provide curation too. This is completely necessary because apps, unlike code, run with even more permission and scope for damage on a system than a library can. This isn’t infallible but it is necessary to meet the legals I mentioned earlier.&lt;/p&gt;
&lt;h3 id=&quot;code-reuse-and-the-package-manager&quot;&gt;Code reuse and the package manager&lt;/h3&gt;
&lt;p&gt;Code reuse is normally seen as a very sane thing, so much so that we &lt;em&gt;made it work&lt;/em&gt; on nuget.org, pypi, npmjs. Over a very long period of time.&lt;/p&gt;
&lt;p&gt;The risk / reward now mostly falls on the reward side. This hasn’t always been the case, but these package systems have matured. They are (now) built with a few features in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Immutability&lt;/li&gt;
&lt;li&gt;Provenance&lt;/li&gt;
&lt;li&gt;Scoped Permission&lt;/li&gt;
&lt;li&gt;Vulnerability Scanning&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;immutability&quot;&gt;Immutability&lt;/h4&gt;
&lt;p&gt;Versions are a contract with your consumers. It’s frowned upon to violate this immutability if not outright prevented.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vercel,&lt;/strong&gt; if you use &lt;code&gt;npx skills add&lt;/code&gt; you can’t trust what you’re getting at all - the content you’re getting is dynamic. There is no version.&lt;/p&gt;
&lt;h4 id=&quot;provenance&quot;&gt;Provenance&lt;/h4&gt;
&lt;p&gt;Typosquatting aside because that’s obvious now, we generally expect some level of trust in who exactly published what (2FA, GPG signing, vetted company entities). I can’t impersonate Docker on their own platform at least. &lt;/p&gt;
&lt;p&gt;Now, I can’t impersonate Vercel on Github either because they have a verified presence there. But the provenance of the publisher is offloaded to Github. &lt;/p&gt;
&lt;p&gt;This seems fine at first until you consider that startups happen everyday that DON’T use GitHub and they might get popular overnight on GitLab. These can conceivably be camped and there’s nothing Vercel or the startup can do about it in platform. Disputes have to go to Github who are notoriously slow in support. It could take weeks to rectify that situation.&lt;/p&gt;
&lt;h4 id=&quot;scoped-permissions&quot;&gt;Scoped Permissions&lt;/h4&gt;
&lt;p&gt;Package managers now warn on install scripts or hooks, and App Stores enforce strict sandboxing, and fine grained permissions declared upfront. Take flatpaks with strict boundaries for example.&lt;/p&gt;
&lt;p&gt;In contrast, a “Weather Skill” isn’t isolated but it works just like an app. It shares the same memory space, environment variables, and network access as your “Database Skill.” It relies on the &lt;em&gt;Agent&lt;/em&gt; to decide what to do, which is not a security boundary. Better would be for the package controller to enforce the rules and take it out of the hands of the agent entirely.&lt;/p&gt;
&lt;h4 id=&quot;vulnerability-scanning&quot;&gt;Vulnerability Scanning&lt;/h4&gt;
&lt;p&gt;Platforms like GitHub, et al, automatically flag compromised dependencies. Dependabot. Static Analysis is reliable and proven. We just convinced a superpower that SBOM and SCA is a great idea and it’s all possible thanks to a wonderful concept known as an Abstract Syntax Tree.&lt;/p&gt;
&lt;p&gt;Vercel Skills? There is not likely to be a “CVE database” for prompt injection to draw from. So if a skill contains a malicious prompt that weakens the agent’s guardrails, no scanner will flag it because it looks like valid English text. &lt;/p&gt;
&lt;p&gt;People like VirusTotal can certainly try but even they realize that natural language is an intractable problem. It’s not a context window problem. It’s a natural language feature. There is no equivalent of an AST that would allow for taint analysis.&lt;/p&gt;
&lt;h4 id=&quot;why-do-people-use-pypi-then-smartass&quot;&gt;Why do people use PyPi then smartass?&lt;/h4&gt;
&lt;p&gt;Well, we partially trust strangers’ code on NuGet, PyPI, and npm because we have spent the last 15 years painfully retrofitting security into these ecosystems. We learned the hard way that open sharing without guardrails is a disaster. This knowledge wasn’t free, it was bought with the blood of hundreds if not thousands of security incidents and vulnerabilities. And it’s still not perfect and we know it.&lt;/p&gt;
&lt;h2 id=&quot;the-trade-offer&quot;&gt;The trade offer&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;/images/meme2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Vercel is attempting to bootstrap a skill ecosystem that pretends that these hard-won lessons never happened and perhaps never applied in the brave new agentic world.&lt;/p&gt;
&lt;p&gt;It’s not like they don’t have the resources to do it either  - ($200M ARR est. 2025). They are asking us to trust a new supply chain that lacks the basic hygiene of npm in 2024, let alone the sandboxing of the App Store.&lt;/p&gt;
&lt;p&gt;We don’t have dependency pinning for “prompts.” We don’t have cryptographic signing for “tools.” We are back in the Wild West of 2011, but this time, the software we are installing has a brain and a credit card.&lt;/p&gt;
&lt;p&gt;What is immeasurably frustrating is that Vercel has excellent sandboxing technology (e.g. firecracker microVMs). But they aren’t using it to wrap these skills by default. They are handing us raw, sharp knives and telling us to be careful, rather than selling us a knife with a sheath.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;All said and done, there is no reasonable way to justify automated skills reuse in their current state. You are better off copying and pasting good ideas from github, or writing your own. It’s not like it’s hard.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;the-problem-is-deeper-than-skills-themselves&quot;&gt;The problem is deeper than skills themselves&lt;/h1&gt;
&lt;p&gt;Further, the capability of a skill is effectively unbounded due to the expressiveness of human languages. Combined with admin rights and your PayPal access token, it’s pretty capable.&lt;/p&gt;
&lt;p&gt;It has been &lt;a rel=&quot;external&quot; href=&quot;https://github.com/da5ch0/expressiveness-vulnerability-identity/blob/main/expressiveness-vulnerability%20identity.md&quot;&gt;recently established&lt;/a&gt; that trying to use natural language as a protective barrier is a game you can’t win reliably enough. &lt;/p&gt;
&lt;p&gt;The very things that make it possible to joke, convey subtext, and lie, are the same things that make LLMs inherently pwnable. And right now it’s intelligent hackers with all their years of experience against literal infants. We can run rings around LLMs. &lt;/p&gt;
&lt;p&gt;The problem is fundamental to the way language and AI works. &lt;/p&gt;
&lt;p&gt;So, let these truths sink in.&lt;/p&gt;
&lt;h2 id=&quot;truth-1&quot;&gt;Truth #1 &lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;The most insecure part of any system is the human element.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;truth-2&quot;&gt;Truth #2&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;We have made LLMs so lifelike, such a perfect mimicry of human expression, that they are practically indistinguishable from humans now.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;truth-1-truth-2-reality&quot;&gt;Truth #1 + Truth #2 = Reality&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;In the search for cheap, scalable capacity for knowledge work, we replicated the most insecure part of the system. The human.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Hopefully this thought exercise helps you to see what I see. Let’s apply it now.&lt;/p&gt;
&lt;h3 id=&quot;goalkeepers-are-valid-but-fallible&quot;&gt;Goalkeepers are valid but fallible&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/goalkeeper-fail.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Tactically speaking, a goalkeeper as the only line of defence is a &lt;em&gt;bad plan&lt;/em&gt;. &lt;/p&gt;
&lt;p&gt;An LLM as a goal keeper, at the mercy of natural language’s power is not even a good goal keeper. This is evidenced every time someone shows Claude or Gemini producing recipes for drugs or weapons.&lt;/p&gt;
&lt;p&gt;There are two goal keepers that people are currently focusing on, at least. Marketplaces and Agentic WAF’s / EDR’s.&lt;/p&gt;
&lt;p&gt;They both suffer the same fate at the hands of natural language processing as a vulnerability. I think this is because this is the most obvious place for Yet Another Third Party to extract some money from the supply chain. I don’t think it’s going to be anywhere near as effective as they hope it will be because of the truths we just established, and the very compelling paper discussing the vulnerability of human languages.&lt;/p&gt;
&lt;h4 id=&quot;marketplace-as-a-catch-all&quot;&gt;Marketplace as a catch all&lt;/h4&gt;
&lt;p&gt;A ‘marketplace goal keeper’, i.e. clawdhub &amp;amp; virustotal, combined with crowdsourced effort to secure other peoples malicious skills. &lt;/p&gt;
&lt;p&gt;Because natural language processing can’t be reliably assessed here, even with more models, prompts and guardrails, it can only catch ‘the really dumb stuff’ as Zack might say. &lt;/p&gt;
&lt;p&gt;So this is a nice to have. Why it got all the attention recently is a function of some very vocal members of our community trying to push their own interests, which may be well intentioned, lets assume that. But I &lt;em&gt;really&lt;/em&gt; wish they instead would join me in telling Vercel they need to come to the party &lt;strong&gt;first&lt;/strong&gt; and stop thinking about this as a ‘community issue’.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I for one will not be recommending their ecosystem at companies I work for until this is addressed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;agentic-extended-detection-response-agentic-application-firewall&quot;&gt;Agentic eXtended Detection Response / Agentic Application Firewall&lt;/h4&gt;
&lt;p&gt;If we accept that &lt;code&gt;Reality = &quot;We replicated the insecure human&quot;&lt;/code&gt; then it’s time to consider that XDR or Clawdstrike are not going to be as effective in playing goalkeeper as they have been in the past.&lt;/p&gt;
&lt;p&gt;Projects like &lt;a rel=&quot;external&quot; href=&quot;https://www.clawdstrike.ai/&quot;&gt;Clawdstrike&lt;/a&gt; look promising but let’s look at what they are actually achieving. Another layer of LLM on this problem fixes very little and amounts to ‘security through obscurity’. It’s not invalid, it’s just nowhere near as effective as the previous definition of what XDRs in particular represent. &lt;/p&gt;
&lt;p&gt;This isn’t a question of “Oh but an Agentic EDR operates at the ‘agent/action boundary’”. That’s a nice theory, but Prompt injection isn’t solved and it’s &lt;a rel=&quot;external&quot; href=&quot;https://github.com/da5ch0/expressiveness-vulnerability-identity/blob/main/expressiveness-vulnerability%20identity.md&quot;&gt;starting to look like that formally can’t happen&lt;/a&gt;. You write a new check… I switch to Spanish and the clanker will still obey me.&lt;/p&gt;
&lt;p&gt;What’s funny to me at least, is that the outcome will probably be that in order to properly ‘harness’ an LLM, we will need to write a set of natural language restrictions so heavy, that it will end up looking like a markup language, similar to existing integration and technologies like terraform and the like.&lt;/p&gt;
&lt;h1 id=&quot;conclusion-aka-how-to-fix-this-shit&quot;&gt;Conclusion aka ‘How to fix this shit’&lt;/h1&gt;
&lt;p&gt;Some people have argued that perhaps we need the god-mode clanker in order to reap the benefits of AI. That we just need to watch it closely.&lt;/p&gt;
&lt;p&gt;To me that is a flawed idea, now that we’re seeing LLMs as ‘new human operators’ and not ‘software.’. In this context, it’s as bad as giving every new employee admin rights to production, and saying “people cant get shit done if they’re not admin”, and then falling back on SIEM alerts, which as we know now, is relying on the goalie too much. We have never done that, why would we start now? This only works when the LLM is literally more capable than the human and it’s simply not true at the moment.&lt;/p&gt;
&lt;p&gt;I don’t think any tool can save you if you truly want this environment.&lt;/p&gt;
&lt;h2 id=&quot;trad-security-is-still-king&quot;&gt;Trad security is still king&lt;/h2&gt;
&lt;p&gt;So, this means that we still need to use role based access controls, least privilege, fine grained permissions, conditional access policies. We need to find better solutions than Vercel’s Skills and Anthropic MCP servers, to reuse functionality if at all.&lt;/p&gt;
&lt;h3 id=&quot;the-goalkeepers-still-get-a-guernsey&quot;&gt;The goalkeepers still get a guernsey&lt;/h3&gt;
&lt;p&gt;The other layers we discussed are valid &lt;em&gt;after&lt;/em&gt; the basics are achieved.&lt;/p&gt;
&lt;p&gt;Monitoring of anomalies and alerting is still valid here. Marketplaces with better tools for reputation scoring is still valid. VirusTotal integrations, okay sure whatever. ‘Agentic’ Runtime Analysis, convince me its not just sparkling WAFs.&lt;/p&gt;
&lt;p&gt;Most important: you don’t let them download “skills” from Shady James’ Emporium.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We block our teams from using untrustworthy skills.&lt;/p&gt;
&lt;/blockquote&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>OWASP Top Ten - 20 years of Application Security</title>
        <published>2025-11-27T00:00:00+00:00</published>
        <updated>2025-11-27T00:00:00+00:00</updated>
        
        <author>
          <name>akses</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://trapdoorsec.com/posts/20-years-of-appsec/"/>
        <id>https://trapdoorsec.com/posts/20-years-of-appsec/</id>
        
        <content type="html" xml:base="https://trapdoorsec.com/posts/20-years-of-appsec/">&lt;h1 id=&quot;20-years-of-application-security&quot;&gt;20 Years of Application Security&lt;/h1&gt;
&lt;p&gt;Looking at the OWASP Top 10 2025 Release Candidate recently made me feel… old. Not the “I’m getting grey beard hair” kind of old, more like the “I remember when this list first came out”, kind of old. Ok maybe a little with the grey hair.&lt;/p&gt;
&lt;p&gt;To paint the picture for those who may not recall 2004: We had just landed the rovers on Mars, Usher &amp;amp; Maroon 5 were a permanent fixture on the US charts, the dot-com bubble hangover was lifting, the memory of 9-11 was fresh in peoples minds and we had the global conflicts to show for it.&lt;/p&gt;
&lt;p&gt;In cybersecurity, we were witnessing a shift. That January, the MyDoom mass emailer worm caused approximately $38 billion in damage globally - and variants would be seen in the wild for nearly five years. Symantec reported[2] a 366% increase in phishing and predicted that it would continue to rise - and they were right. Significantly, they also spotted that well over 40% of vulnerabilities documented between July 1 and Dec 31 of 2004 were web application vulnerabilities, an increase of 39% over the previous 6 months. This was a disturbing trend given we were betting the farm on web applications.&lt;/p&gt;
&lt;p&gt;This was the backdrop when OWASP released their Top 10 Web Application Security Risks, version 2. It was meant to be a snapshot of the most critical vulnerabilities we faced.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;whats-changed-since-then&quot;&gt;Whats changed since then?&lt;/h2&gt;
&lt;p&gt;Fast forward to today and the trends in cybersecurity sound very different yet feel vaguely familiar:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The concept of phishing - fooling people into clicking on a malicous link via email - has expanded to target the supply chain and other vectors: smishing, vishing, domain squatting, poison packages on npm, malicious browser in browser fakery etc. It gone from spray and pray to poison the well.&lt;/li&gt;
&lt;li&gt;AI powered social engineering augments the efforts of those executing the above attacks - its now possible to fake a voice or even video feed.&lt;/li&gt;
&lt;li&gt;Desktop viruses of old have given way to cloud misconfigurations&lt;/li&gt;
&lt;li&gt;Those script kids of 2004 have levelled up and become randomware-as-a-service operators, further lowering the barrier to entry for new miscreants to play dirty&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And finally, web apps have only increased their importance, with API and microservices, alongside OAuth and OIDC encouraging machine integration over the internet.&lt;/p&gt;
&lt;h3 id=&quot;owasps-top-ten-over-time&quot;&gt;OWASPs Top Ten over time&lt;/h3&gt;
&lt;p&gt;For the unintiated, this awesome community effort strives to collate quality data about the nature of web application cybersecurity attacks and publish them to web developers worldwide for free. I highly recommend being aware of its contents at all times if you’re in the business of web application development in any capacity.&lt;/p&gt;
&lt;p&gt;When we compare how the OWASP Top Ten has evolved it provides us an interesting window into not only how attacks have evolved, but how we’ve responded.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/owasp-compare.png&quot; alt=&quot;&quot; /&gt;
&lt;em&gt;source: my own diagram in whimsical, derived from historical OWASP content and the latest version 8 RC1&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;there-is-an-i-and-a-in-cia-who-knew&quot;&gt;There is an “I” and “A” in “CIA”? Who knew?&lt;/h3&gt;
&lt;p&gt;For decades, developers treated Integrity and Availability as “operations problems” - write the code, throw it over the fence, let ops deal with the logs and uptime. That DevOps-shaped hole in our security posture is exactly what we’re still trying to fill with CI/CD.&lt;/p&gt;
&lt;p&gt;Something shifted around 2017. OWASP added “Insufficient Logging &amp;amp; Monitoring” to the Top 10, essentially telling developers that ‘your job doesn’t end when the code compiles’. Good logs are a love letter to your support teams and incident responders. Design observability as a feature, not an afterthought.&lt;/p&gt;
&lt;p&gt;By 2021, this evolved further with “Software and Data Integrity Failures” entering at #8. Now we’re not just asking “can you detect when you’re breached?” but “can you prove your build pipeline hasn’t been compromised?”&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An insecure CI/CD pipeline can introduce the potential for unauthorized access, malicious code, or system compromise.
— &lt;em&gt;OWASP Top 10 2021, A08: Software and Data Integrity Failures&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In 2025 we’re being much more prescriptive: use integrity checks for artifacts, please!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An insecure CI/CD pipeline without consuming and providing software integrity checks can introduce the potential for unauthorized access, insecure or malicious code, or system compromise.
— &lt;em&gt;OWASP Top 10 2025RC1, A08: Software and Data Integrity Failures&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Translation: Supply chain security isn’t optional anymore. If you can’t verify what went into your build, you can’t trust what came out of it.&lt;/p&gt;
&lt;p&gt;The pattern: We spent 20 years perfecting Confidentiality (encryption, access control). We’re finally realizing that data you can’t trust or systems you can’t observe are just as dangerous as data you can’t protect.&lt;/p&gt;
&lt;h3 id=&quot;buffer-overflows-have-operating-system-level-mitigations-now&quot;&gt;Buffer overflows have operating system level mitigations now&lt;/h3&gt;
&lt;p&gt;Buffer overflows (aka BOFs), once the bain of the C/C++ programmer, were a significant issue, mainly due to the duopoly that was Apache and IIS web servers in the wild. This kind of attack would be a critical remote code execution issue - many inexperienced web server admins would also run apache or IIS with a high level of privilege out of laziness and lack of awareness.&lt;/p&gt;
&lt;p&gt;Today, not only are more webservers likely to run managed code (i.e. safe from buffer overflows by design), operating systems have stack protections that can randomize memory layout (ASLR), dynamic execution prevention and the like, making those BOFs much header to take advantage of should they exist.&lt;/p&gt;
&lt;h3 id=&quot;supply-chain-attacks-emerged&quot;&gt;Supply chain attacks emerged&lt;/h3&gt;
&lt;p&gt;We built websites very differently too, in 2004 the concept of &lt;code&gt;npm install leftpad&lt;/code&gt; simply didn’t exist. Sharing code happened via blogs and dedicated code sharing sites, which in theory could have been a vector, but the reality is that they weren’t used in the kind of capacity as a package would be leveraged in 2025.&lt;/p&gt;
&lt;p&gt;Today there are over 400k npm packages alone. This makes for a juicy target. In 2019 we all learned that a mere 20 compromised package maintainers on NPM could potentially infect over 50% of the entire ecosystem practically overnight - making for a very juicy target. [5]&lt;/p&gt;
&lt;p&gt;This trend continues with the discovery of developer targeting malware like &lt;a rel=&quot;external&quot; href=&quot;https://www.bleepingcomputer.com/news/security/self-spreading-glassworm-malware-hits-openvsx-vs-code-registries/&quot;&gt;glassworm&lt;/a&gt; via marketplaces for our text editors, of all things. Notably, glassworm channels the bad old days of MyDoom’s wormable nature.&lt;/p&gt;
&lt;h3 id=&quot;ai-obviously&quot;&gt;AI, obviously&lt;/h3&gt;
&lt;p&gt;You might be surprised that there is nothing related to AI or LLM’s in the OWASP Top 10. Well, good news!&lt;/p&gt;
&lt;p&gt;Because its such a unique space, the OWASP organization has given it a Top Ten all it’s own: https://genai.owasp.org/llm-top-10/ and you will note that its number one issue is… you guessed it … prompt injection!&lt;/p&gt;
&lt;h2 id=&quot;the-uncomfortable-constants&quot;&gt;The uncomfortable constants&lt;/h2&gt;
&lt;p&gt;What’s stayed the same? In 2004, 70% of vulnerabilities were classified as easily exploitable, and 97% were considered moderately or highly severe prwire. [2]&lt;/p&gt;
&lt;p&gt;In 2025? The numbers are actually worse. Roughly 38% of reported vulnerabilities in 2025 are rated High or Critical severity (CVSS ≥7), Early 2025 actually saw a spike in Critical CVSS 9+ vulns compared to prior years.[3]&lt;/p&gt;
&lt;p&gt;While in 2024, 42% of analyzed vulnerabilities had publicly available proof-of-concept exploits, significantly reducing the technical barrier for cybercriminals.[4]&lt;/p&gt;
&lt;p&gt;At least by this yardstick, in 20 years we have little to show for our attempts at securing technology. We’ve definitely gotten better at distributing the same problems across more complex infrastructure though.&lt;/p&gt;
&lt;h2 id=&quot;access-controls-still-broken&quot;&gt;Access controls - still broken&lt;/h2&gt;
&lt;p&gt;Broken access control is #1 on the OWASP Top 10 for a reason. As an industry, the economics of web development forced us to tackle the phishing problem first - identity and authentication (AuthN) get the budget and attention. Passkeys might finally kill credential theft. Cool.
But authentication is the easy problem. Authorization (AuthZ) is where we keep failing.
The reality? All the YubiKeys and passkeys in the world won’t save you if a new user can access your admin panel by tweaking a role in their JWT.
If anyone can view customer data by incrementing an ID in the API call then its all for naught.&lt;/p&gt;
&lt;p&gt;Perhaps, we’re close to getting “who are you?” right, but we still can’t consistently answer “what are you allowed to do?”.&lt;/p&gt;
&lt;h2 id=&quot;injection-still-works&quot;&gt;Injection still works :/&lt;/h2&gt;
&lt;p&gt;What’s fascinating about injection vulnerabilities is that we keep making this mistake. Just as we’ve wrapped our heads around the ramifications of a given query technology, we invent a new one with a whole new set of injection primitives to worry about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2004 - SQL databases were the norm → SQLi was rampant&lt;/li&gt;
&lt;li&gt;2010 - XML-RPC was common → XXE attacks emerged&lt;/li&gt;
&lt;li&gt;2015 - NoSQL databases became mainstream → NoSQL injection appeared&lt;/li&gt;
&lt;li&gt;2020 - GraphQL was everywhere → We stacked NoSQL injection with GraphQL-specific query manipulation&lt;/li&gt;
&lt;li&gt;2025 - LLMs are in production → Prompt injection, RAG poisoning, and indirect prompt injection are the new frontier&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The underlying problem never changed: we’re still concatenating untrusted input into interpreters. We just keep inventing new interpreters. In 2004, it was &lt;code&gt;&quot;SELECT * FROM users WHERE id=&quot; + userInput&lt;/code&gt;.
In 2025, it’s &lt;code&gt;f&quot;Answer this question using the following context: {userInput}&quot;&lt;/code&gt;. Same vulnerability. Different syntax. We learned to parameterize SQL queries, but forgot the lesson when we moved to NoSQL, GraphQL, and now AI prompts.&lt;/p&gt;
&lt;hr /&gt;
&lt;h1 id=&quot;conclusion-what-does-this-mean-beyond-2025&quot;&gt;Conclusion: What Does This Mean Beyond 2025?&lt;/h1&gt;
&lt;p&gt;Twenty years of OWASP Top 10 data tells us something uncomfortable: &lt;strong&gt;we’re not getting better at the fundamentals.&lt;/strong&gt; We’re getting better at building complex systems that distribute the same old vulnerabilities across more layers.&lt;/p&gt;
&lt;p&gt;Will AI in the form of LLM based tools help us? That remains to be seen, however I remain sceptical of this, given that broken access control is still at the top. BAC issues stem from a lack of understanding or testing of the business rules unique to the application being built - that is to say, you can’t prompt for what you don’t know to ask for in the first place, and the LLM won’t inherently detect every single RBAC rule your organization has come up with, at least not unless the LLM has direct access to that information - which seems unlikely to me. I think this is why humans will still be involved heavily in creating software well into the future.&lt;/p&gt;
&lt;h2 id=&quot;for-defenders&quot;&gt;For Defenders&lt;/h2&gt;
&lt;p&gt;The good news is that the appsec playbooks from 2004 largely still work. SQL injection prevention, access control validation, input sanitization—these aren’t outdated techniques. They’re eternal truths we keep forgetting.&lt;/p&gt;
&lt;p&gt;The challenge is you now need to apply those fundamentals across:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;50 microservices&lt;/li&gt;
&lt;li&gt;400 npm dependencies&lt;/li&gt;
&lt;li&gt;12 cloud services&lt;/li&gt;
&lt;li&gt;AI prompts that look like natural language but behave like SQL queries&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;actionable-advice&quot;&gt;Actionable advice&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Shift left, but verify right:&lt;/strong&gt; DevSecOps is great, but runtime monitoring caught SolarWinds, CodeCov, and dozens of supply chain attacks that passed all the CI checks&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Assume compromise in your dependencies:&lt;/strong&gt; Pin versions, use SBOMs, monitor for behavioral changes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Log like someone’s life depends on it:&lt;/strong&gt; Because in healthcare, finance, and critical infrastructure, it actually might.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;for-developers&quot;&gt;For Developers&lt;/h2&gt;
&lt;p&gt;You’ve inherited 20 years of technical debt disguised as “best practices.” From one of the old guard - I am sorry.&lt;/p&gt;
&lt;p&gt;The uncomfortable truth, that npm package you installed? You’re trusting 79 third-party packages and 39 maintainers on average. That GraphQL endpoint? It’s just SQL injection with extra steps. That LLM feature? It’s user input concatenation all over again.&lt;/p&gt;
&lt;h3 id=&quot;first-principles-still-matter&quot;&gt;First Principles still matter&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Understand, don’t just use:&lt;/strong&gt; Frameworks protect you until they don’t. Know why parameterized queries work, not just how to use an ORM&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Treat AuthZ like AuthN:&lt;/strong&gt; You wouldn’t skip password validation. Stop skipping resource ownership checks&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Your code will be attacked:&lt;/strong&gt; Design with that assumption. Write logs that help future-you during an incident at 3 AM&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Supply chain hygiene:&lt;/strong&gt; Review your dependencies like you review your own code. That leftpad incident wasn’t a fluke—it was a warning&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;The mantra:&lt;/strong&gt; If you’re concatenating user input into &lt;em&gt;anything&lt;/em&gt; that interprets it—SQL, NoSQL, GraphQL, shell commands, prompts—you’re probably doing injection wrong.&lt;/p&gt;
&lt;h2 id=&quot;for-leadership&quot;&gt;For Leadership&lt;/h2&gt;
&lt;p&gt;Here’s what 20 years of data actually tells you:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Security isn’t getting easier&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You can’t “buy security” - never could. That SAST tool, WAF, or SIEM? They’re necessary, but insufficient. Security is about your people:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Developer training (they’re shipping the vulns)&lt;/li&gt;
&lt;li&gt;Architecture decisions (microservices = more attack surface)&lt;/li&gt;
&lt;li&gt;Supply chain verification (your code is 5% of your application)&lt;/li&gt;
&lt;li&gt;Incident response and threat hunting capabilities (you will be breached, but can you detect it?)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;2. The fundamentals haven’t changed, but the economics have&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In 2004, a breach meant some bad press. In 2025, it means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GDPR/DORA/CCPA/HIPAA fines (4% of global revenue)&lt;/li&gt;
&lt;li&gt;Class action lawsuits&lt;/li&gt;
&lt;li&gt;Ransomware payments&lt;/li&gt;
&lt;li&gt;Supply chain liability (you breached your customers too)&lt;/li&gt;
&lt;li&gt;Millions wiped off stock prices overnight&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The ROI on basic security hygiene has never been higher.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Your developers are your security team (whether you like it or not)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Every line of code is a security decision. Every dependency is a trust decision. Every API endpoint is an attack vector. You can hire all the security engineers you want, but if your 50 developers ship 1,000 commits per week, security-by-review doesn’t scale.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Investment priorities:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Training:&lt;/strong&gt; Not compliance checkbox training. Real, practical secure coding for your stack&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tooling:&lt;/strong&gt; SAST/DAST/SCA that developers actually use (not just buy)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Culture:&lt;/strong&gt; Making security a feature requirement, not a post-release patch&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Observability:&lt;/strong&gt; You can’t defend what you can’t see. Log aggregation, SIEM, and actual human analysis&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;call-to-action-how-does-your-application-stack-up&quot;&gt;Call to Action: How Does Your Application Stack Up?&lt;/h2&gt;
&lt;p&gt;Here’s your mission if you choose to accept it.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Take 30 minutes:&lt;/strong&gt; Walk through the &lt;a rel=&quot;external&quot; href=&quot;https://owasp.org/Top10/&quot;&gt;OWASP Top 10 2025 RC1&lt;/a&gt; and honestly assess which apply to your application&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check your dependencies:&lt;/strong&gt; Run &lt;code&gt;npm audit&lt;/code&gt; or equivalent. How many HIGH/CRITICAL vulns are you shipping?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test your logging:&lt;/strong&gt; Simulate an attack. Can you detect it? Can you trace it? Can you respond to it?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Review your CI/CD:&lt;/strong&gt; Can you prove what went into your last build? Do you have integrity checks?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audit one critical endpoint:&lt;/strong&gt; Pick your most sensitive API. Check for:
&lt;ul&gt;
&lt;li&gt;Broken access control (can user A access user B’s data?)&lt;/li&gt;
&lt;li&gt;Injection vectors (are you sanitizing/parameterizing inputs?)&lt;/li&gt;
&lt;li&gt;Logging (would you know if this endpoint was being abused?)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Then ask yourself:&lt;/strong&gt; If this application were breached tomorrow, would we:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Know about it within hours (not months)?&lt;/li&gt;
&lt;li&gt;Understand how they got in?&lt;/li&gt;
&lt;li&gt;Be able to prove what data was accessed?&lt;/li&gt;
&lt;li&gt;Have a response plan that doesn’t start with “panic”?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you answered “no” to any of those, start with the OWASP Top 10. It’s been telling us the same story for 20 years.&lt;/p&gt;
&lt;h2 id=&quot;a-final-thought&quot;&gt;A Final Thought&lt;/h2&gt;
&lt;p&gt;The OWASP Top 10 isn’t a prediction of the future. It’s a reflection of our past.&lt;/p&gt;
&lt;p&gt;Buffer overflows dropped off not because we got smarter, but because we moved to languages that wouldn’t let us make that mistake. SQL injection is still #3 not because it’s hard to prevent, but because we keep forgetting to prevent it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;That’s up to us.&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Thanks for reading. If you found this useful, consider sharing it with your team. And if you’re working on securing applications, you’re not alone—the OWASP community is there to help. Check out &lt;a rel=&quot;external&quot; href=&quot;https://owasp.org&quot;&gt;https://owasp.org&lt;/a&gt; for resources, tools, and that Top 10 list we’ve been talking about.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Stay safe out there. And for the love of all that is holy, &lt;strong&gt;parameterize your queries.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;===================&lt;/p&gt;
&lt;p&gt;References:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://www.okta.com/identity-101/mydoom/&quot;&gt;1 - MyDoom History&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://prwire.com.au/pr/4412/symantec-internet-security-threat-report-highlights-rise-in-threats-to-confidential-information&quot;&gt;2 - Symantec threat report for 2004&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://deepstrike.io/blog/vulnerability-statistics-2025&quot;&gt;3 - Deepstrike Vulnerabilities Statistics 2025: Record CVEs, Zero-Days &amp;amp; Exploits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://content.blackkite.com/ebook/2025-supply-chain-vulnerability-report/trends-and-statistics&quot;&gt;4 - Blackite Vulnerability Trends and Statistics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https://www.zdnet.com/article/hacking-20-high-profile-dev-accounts-could-compromise-half-of-the-npm-ecosystem/&quot;&gt;5 - ZDnet article&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
        
    </entry>
</feed>
