Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
cakephp-window-sqli — CakePHP 5.4.2 - High - Remote - SQLi - WindowExpression range/frame offsets spliced into SQL | Kitploit
Tools/GitHubGitHub/abraxas/cakephp-window-sqli
Vulnerability ScannersVulnerability AnalysisCode AnalysisExploitationWeb Application ExploitationWeb SecurityDatabase Security
GitHubabraxas/cakephp-window-sqli

cakephp-window-sqli

CakePHP 5.4.2 - High - Remote - SQLi - WindowExpression range/frame offsets spliced into SQL

View Repository
4 days agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

Abraxas Labs - cakephp-window-sqli

abraxaslabs.tech  ·  github.com/abraxas  ·  @abraxas_null  ·  [email protected]  ·  cakephp-window-sqli

cakephp-window-sqli

Class: SQLi Reach: Remote

CakePHP 5.4.2 (887b641) - Cake Software Foundation

CVE-2026-79752 closed FunctionsBuilder cast / extract / dateAdd with ensureSimpleString(). WindowExpression::range() and frame() were not in that fix. rows() and groups() take ?int. range() still takes a string. buildOffsetSql concatenates the offset, frame type, and PRECEDING / FOLLOWING into OVER. The binder is unused unless the offset is already an ExpressionInterface.

An app that forwards request data into func()->count('*')->over()->range($start, $end) puts the caller's string into RANGE BETWEEN unbound and unquoted.

IDno CVE yet
ClassSQLi
ReachRemote (app that forwards request data into range() / frame())
CWECWE-89
CVSSHigh: 8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
ProductCakePHP (cakephp/database)
Affected5.4.2 (887b641de9f7aba06071f98f17edcbc573cbcf63)
Authapplication that forwards query/form data into ->over()->range($start, $end) / frame()
LicenseGNU Affero GPL v3.0
Lab127.0.0.1 only

What an attacker can do

A user who can set the window bound the app forwards (analytics "last N days", a report builder) changes the OVER clause into extra SQL.

PrivilegeMethod / sinkKindLab-proven or inferred
Splice a SQL fragment into OVER (RANGE BETWEEN ...)GET /?mode=range&start= into AggregateExpression::range($start, $end) / WindowExpression::frame()Integrity of compiled SQLLab-proven. Compiled SQL is RANGE BETWEEN 1) /* CAKEPHP-WINDOW-SQLI-WITNESS */ -- PRECEDING with an empty binder
Read or modify rows as the app's DB userSame query, executedIntegrity / recon of DBInferred from the splice. This lab is compile-only
Change frame type or direction the same wayframe($type, $offset, $direction, ...) concatenates those strings tooIntegrity of compiled SQLInferred from sql() / buildOffsetSql(). Lab used range()
Break rows() / groups() the same wayTyped ?intClosedLab-proven closed. rows(3) is ROWS BETWEEN 3 PRECEDING, not quoted, not bound

Closed on this pin: FunctionsBuilder cast / extract / dateAdd / jsonValue (CVE-2026-79752 / CVE-2026-77635); Query::limit / offset (typed int). Default cakephp/app does not call range() with query-string data. The leftover is the documented window API. Not unauthenticated RCE on a skeleton app. Not a shell.

Same pin leftovers: collate SQLi, attachment CRLF, cache unserialize.

How I found it

I pinned cakephp/cakephp 5.4.2 after the 2026 FunctionsBuilder SQLi GHSAs. Ten hunts on that tag. CSRF / FormProtection, View LFI, BodyParser XXE, mass assignment, helper XSS, and clientIp() without trustProxy came back empty in this bar. WindowExpressionTest::testRange still asserts:

$w = (new WindowExpression())->range("'1 day'", "'10 days'");

buildOffsetSql never binds a string offset:

protected function buildOffsetSql(
    ValueBinder $binder,
    ExpressionInterface|string|int|null $offset,
    string $direction,
): string {
    if ($offset === 0) {
        return 'CURRENT ROW';
    }
    if ($offset instanceof ExpressionInterface) {
        $offset = $offset->sql($binder);
    }
    return sprintf('%s %s', $offset ?? 'UNBOUNDED', $direction);
}

$binder is unused unless the offset is already an ExpressionInterface.

Wrong turns already recorded: treating rows() as the hole (it is typed int); claiming stock cakephp/app exposes this on a public form; executing the injected SQL in SQLite as the oracle (POC-READY is compile-only); a reverse shell. Theatre.

Lab

python3 cakephp-window-sqli-Abraxas-Labs.py

or

cd lab
./run.sh

Target only http://127.0.0.1:19440. Tiny PHP app, cakephp/database 5.4.2, SQLite :memory:. GET /?mode=range&start= is passed as a string into range(). GET /?mode=rows&n=3 is the int control.

SUCCESS cakephp-window-sqli pin=5.4.2 range-raw=yes rows-numeric=yes bound=no quoted=no CAKEPHP-WINDOW-SQLI-WITNESS

IOC compiled SQL:

SELECT (COUNT(*) OVER (RANGE BETWEEN 1) /* CAKEPHP-WINDOW-SQLI-WITNESS */ -- PRECEDING AND CURRENT ROW)) AS n FROM sqlite_master

Control:

SELECT (COUNT(*) OVER (ROWS BETWEEN 3 PRECEDING AND CURRENT ROW)) AS n FROM sqlite_master

The fix

Bind numeric offsets. Allowlist frame type (RANGE / ROWS / GROUPS) and direction (PRECEDING / FOLLOWING). For interval strings, quote them or require ExpressionInterface. Add a test that a non-int range() offset becomes a placeholder or a quoted literal, not raw SQL.

References

  • github.com/cakephp/cakephp tag 5.4.2 (887b641de9f7aba06071f98f17edcbc573cbcf63)
  • WindowExpression.php · AggregateExpression.php
  • Nearby patched: CVE-2026-79752 · CVE-2026-77635
  • Same pin: cakephp-collate-sqli · cakephp-attachment-crlf · cakephp-cache-unserialize
  • CWE-89

License

GNU Affero GPL v3.0. See LICENSE. Loopback lab only. No warranty.

Download Tool