grocy 4.6.0 concatenates the authenticated product-group query parameter into the stock spending report SQL when group-by=product is used. A logged-in user can inject SQL into the report query path.
StockReportsController::Spendings handles /stockreports/spendings. When group-by=product and product-group is neither all nor ungrouped, the controller appends the submitted product-group value into the SQL WHERE clause instead of validating it as a product group identifier or binding it as a parameter.
The resulting SQL reaches Grocy\Services\DatabaseService::ExecuteDbQuery(). In the audited path, the raw SQL is executed before query results are fetched, so attacker-controlled SQL can affect the report query.
A previously explored custom include code-execution chain was not confirmed on the current target. Exact custom_js.html and custom_css.html attachment attempts hit file is not a database errors, so this advisory is limited to the confirmed SQL injection boundary.
- Log in to grocy 4.6.0 with a normal authenticated account.
- Request
/stockreports/spendings?group-by=productand note the normal report behavior for a validproduct-groupvalue. - Repeat the request with a crafted
product-groupvalue that terminates thepg.id = ...expression and adds a SQLite read probe such assqlite_version()orpragma_database_listin the report query. - Observe the report output or database error change according to the injected SQL, including returned SQLite version or database path data in the validated probe.
- Confirm that the proof stops at SQL injection on the current target because the custom include write path returns
file is not a database.
The observed result is authenticated SQL injection in the stock spending report. A logged-in attacker can read or alter data reachable by the application's SQLite connection, subject to database permissions and the exact injected statement behavior. Code execution through the custom include path was not confirmed on the current target.