← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2023-5806 · CWE-89 · Disclosed 2024-01-18

Improper Neutralization of Special Elements used in an SQL Command

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

This is a loaded nail gun, but usually kept inside a locked workshop

CVE-2023-5806 is an unauthenticated SQL injection in Mergen Software Quality Management System affecting versions before v1.2. If an attacker can reach the vulnerable web interface, they can likely manipulate backend queries to read or alter QMS data, and in the worst case damage application availability depending on database privileges and how the app handles injected statements.

The vendor-style 9.8/CRITICAL score is technically understandable for a reachable unauthenticated web SQLi, but it overstates the fleet-wide urgency for most enterprises. Mergen is a niche steel-structure manufacturing/factory management platform, the public record shows no KEV listing, no exploitation evidence, and very low EPSS, and the likely exposed population is far smaller than mass-market edge software; that combination pulls this down from internet-emergency to HIGH.

"Bad if exposed, but this looks like a niche internal app, not an Internet-wide fire drill."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Reach the QMS web interface with httpx or a browser

The attacker first needs network reachability to the Mergen QMS web application. In a lab model this is trivial because CVSS assumes a routable web target; in real enterprise deployments, this product is more likely to live on plant, quality, or internal business networks than on a public edge.
Conditions required:
  • Target runs Mergen Quality Management System before v1.2
  • Attacker can reach the HTTP/HTTPS service from their position
Where this breaks in practice:
  • Many deployments are likely internal-only or VPN-gated rather than internet-exposed
  • This is a niche manufacturing/QMS platform, so the exposed population is likely much smaller than common edge appliances
  • NGFW, reverse proxies, or IP allowlists may block direct reachability
Detection/coverage: External attack-surface tools can find the host if exposed, but product fingerprinting appears sparse; expect asset inventory gaps unless you already track this app.
STEP 02

Identify an injectable parameter with Burp Suite or sqlmap

Once the attacker has HTTP access, they need a request parameter that actually hits a vulnerable SQL query. A generic tool like sqlmap is enough if the endpoint is predictable, but niche applications usually require some manual mapping of forms, IDs, filters, or search features before automation works cleanly.
Conditions required:
  • At least one unauthenticated endpoint passes user input into SQL
  • Application responses are stable enough for boolean/time-based inference
Where this breaks in practice:
  • No product-specific public PoC was found in the reviewed sources
  • Custom workflows and nonstandard parameter names slow blind automation
  • WAF normalization, parameter validation, or error suppression can reduce reliability
Detection/coverage: DAST/manual web testing should catch this more reliably than commodity vuln scanners; signature coverage for this product is likely thin.
STEP 03

Dump or tamper with backend data using sqlmap

If injection succeeds, the practical impact is usually database disclosure or modification inside the QMS data store. For this product that may include quality records, audit artifacts, production-related workflows, or safety/compliance tracking data rather than domain admin on day one.
Conditions required:
  • Database account used by the app has useful read/write privileges
  • Injection point supports stacked, union, error-based, or inferential extraction
Where this breaks in practice:
  • Least-privileged DB accounts can sharply reduce blast radius
  • Segmented app tiers may limit access to only the application database
  • Blind SQLi against noisy apps can be slow and operationally obvious
Detection/coverage: Database query logging, WAF telemetry, and application logs may show timing probes, malformed predicates, or abnormal record enumeration if they are retained and reviewed.
STEP 04

Escalate impact only if the database or host is over-privileged

The headline CVSS assumes full confidentiality, integrity, and availability impact, but host-level takeover is not guaranteed from the public record alone. Real damage depends on whether the database user can write dangerous files, execute extended procedures, or whether stolen app data enables follow-on abuse in adjacent systems.
Conditions required:
  • Over-privileged DB configuration or weak trust relationships exist
  • Attacker can convert application/data compromise into broader access
Where this breaks in practice:
  • Nothing in the published advisory proves direct RCE
  • Modern DB hardening often blocks file writes, command execution, and unsafe extensions
  • Lateral movement still requires separate weaknesses or credential reuse
Detection/coverage: EDR will help only if the attack leaves the database/application layer and touches the host; otherwise this remains primarily an app/db logging problem.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo known active exploitation in reviewed sources. CISA ADP enrichment for the CVE shows Exploitation: none, and the CVE is not in the CISA KEV catalog.
Proof-of-concept availabilityNo dedicated public PoC found in reviewed sources. A generic SQLi tool such as sqlmap would still be the obvious weapon once a reachable parameter is found.
EPSS0.00066 (~0.066%) from the prompt intel, which is a very low exploitation-probability signal compared with truly hot internet bugs.
KEV statusNot KEV-listed in the current CISA Known Exploited Vulnerabilities catalog review.
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H — this is the classic *internet-reachable unauthenticated web injection* model, but it does not account for niche deployment, internal-only exposure, or exploit scarcity.
Affected versionsMergen Quality Management System before v1.2 per the CVE/CNA record.
Fixed versionv1.2 is the stated fix boundary for this CVE. Caveat: a second Mergen QMS SQLi, CVE-2024-2865, was published on 2024-03-25 for versions through 25032024, so do not assume that landing exactly on v1.2 means you are done forever.
Exposure realityVendor materials describe Mergen as a steel-structure manufacturing / plant / quality management platform with role-based departmental modules. That strongly suggests a niche and often internal deployment pattern, and I found no reliable internet census/fingerprint data showing broad public exposure.
DisclosurePublished 2024-01-18 by TR-CERT; credited finder is Resul Melih MACIT.
Scanner coverageExpect uneven scanner coverage because this is a niche product with limited public fingerprinting. Manual DAST and internal app inventory will outperform generic network vuln scans here.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to HIGH (7.1/10)

The decisive factor is reachability at scale: this is a serious unauthenticated web SQLi, but it appears tied to a niche manufacturing/QMS product that is far less likely to be broadly internet-exposed than mainstream edge software. The absence of KEV status, public exploitation evidence, and any obvious public PoC keeps it out of CRITICAL for enterprise patch scheduling.

HIGH Affected-version boundary before `v1.2` and base technical impact as a SQLi
MEDIUM Assessment that real-world exposure population is narrow
MEDIUM Assessment that exploitation prevalence is currently low

Why this verdict

  • Downgraded for exposure reality: vendor materials position Mergen as a factory/quality platform for steel-structure manufacturers, not a ubiquitous edge service; that sharply reduces the likely reachable population.
  • Downgraded for threat intel scarcity: no KEV listing, CISA ADP marks exploitation as none, and the supplied EPSS is extremely low.
  • Still HIGH because attacker position is strong: if the web app is reachable, this is unauthenticated remote SQLi with potentially full application-database compromise and no user interaction.

Why not higher?

I am not calling this CRITICAL because the public record does not show active exploitation, broad internet exposure, or a mature public exploit ecosystem. This is exactly the kind of bug that looks catastrophic in CVSS math but lands in a much smaller real target set once you ask how many enterprises actually expose this product to untrusted networks.

Why not lower?

I am not pushing this down to MEDIUM because the attacker does not need credentials or user interaction, and SQLi remains one of the cleanest paths to immediate data compromise when the app is reachable. If you do run this product and it is externally reachable, the blast radius inside the application/database tier is substantial enough to justify a HIGH.

05 · Compensating Control

What to do — in priority order.

  1. Put it behind VPN or allowlists — Remove direct internet reachability to the QMS interface and restrict access to trusted admin, plant, or vendor support networks. For a HIGH verdict, deploy this containment within 30 days; if the system is currently public-facing, treat that as the first risk reducer while patch planning runs.
  2. Enable SQLi-focused WAF rules — If the application must stay reachable, place it behind a reverse proxy or WAF with SQL injection detection, request normalization, and aggressive logging. This is not a substitute for patching, but it meaningfully raises attacker friction and should be in place within 30 days for exposed deployments.
  3. Constrain database privileges — Ensure the application service account cannot execute dangerous DB-side features, write files, or access unrelated schemas. This does not fix the injection, but it can turn a full-compromise headline into a narrower data-layer incident; complete the privilege review within 30 days.
  4. Turn on high-fidelity web and DB logging — Preserve reverse-proxy, application, and database query/error telemetry long enough to spot probing, time-based SQLi, and mass enumeration. Logging will not prevent exploitation, but it shortens dwell time and should be operationalized within 30 days.
What doesn't work
  • MFA on the front door does not help if the injectable endpoint is unauthenticated or reachable before login.
  • EDR alone will miss purely application/database-layer abuse; it only helps once activity breaks out onto the host.
  • Relying on generic network vuln scans is weak coverage for a niche web app like this; manual validation and app inventory matter more.
  • Patching to exactly v1.2 without vendor confirmation may be insufficient because a later Mergen QMS SQLi, CVE-2024-2865, was published on 2024-03-25.
06 · Verification

Crowdsourced verification payload.

Run this from an auditor workstation that can reach the target web app, or locally on the application server if you have filesystem access. Invoke it as python3 mergen_qms_check.py --url https://qms.example.com or python3 mergen_qms_check.py --root /opt/mergen; no admin rights are required for URL mode, while local file inspection needs read access to the application directory.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/usr/bin/env python3
# Mergen QMS CVE-2023-5806 version triage helper
# Outputs: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=determined, 2=unknown, 3=usage/runtime error

import argparse
import os
import re
import ssl
import sys
import urllib.request
from urllib.parse import urljoin

FIX_VERSION = (1, 2)
TIMEOUT = 8
MAX_READ = 1024 * 1024  # 1 MB per file/response

VERSION_PATTERNS = [
    re.compile(r'(?i)\bversion\b[^0-9]{0,16}v?([0-9]+(?:\.[0-9]+){1,3})'),
    re.compile(r'(?i)\bv?([0-9]+\.[0-9]+(?:\.[0-9]+)?)\b'),
]

CANDIDATE_FILES = {
    'version', 'version.txt', 'about', 'about.txt', 'readme', 'readme.txt',
    'changelog', 'manifest.json', 'package.json', 'composer.json',
    'appsettings.json', 'config.php', 'config.json', 'web.config'
}

URL_PATHS = ['/', '/login', '/about', '/version', '/api/version']


def parse_version_tuple(s):
    parts = s.strip().lower().lstrip('v').split('.')
    nums = []
    for p in parts:
        m = re.match(r'^(\d+)', p)
        if not m:
            return None
        nums.append(int(m.group(1)))
    while len(nums) < 3:
        nums.append(0)
    return tuple(nums[:3])


def compare_versions(found, fixed=FIX_VERSION + (0,)):
    if found is None:
        return None
    if found < fixed:
        return -1
    if found > fixed:
        return 1
    return 0


def extract_versions(text):
    results = []
    for pat in VERSION_PATTERNS:
        for m in pat.finditer(text):
            vt = parse_version_tuple(m.group(1))
            if vt:
                results.append((m.group(1), vt))
    return results


def fetch_url(url):
    ctx = ssl.create_default_context()
    req = urllib.request.Request(url, headers={'User-Agent': 'mergen-qms-check/1.0'})
    with urllib.request.urlopen(req, timeout=TIMEOUT, context=ctx) as resp:
        raw = resp.read(MAX_READ)
        headers = '\n'.join(f'{k}: {v}' for k, v in resp.headers.items())
        body = raw.decode('utf-8', errors='ignore')
        return headers + '\n' + body


def inspect_url(base_url):
    hits = []
    for path in URL_PATHS:
        target = urljoin(base_url.rstrip('/') + '/', path.lstrip('/'))
        try:
            text = fetch_url(target)
            versions = extract_versions(text)
            for original, vt in versions:
                hits.append((f'url:{target}', original, vt))
        except Exception:
            continue
    return hits


def inspect_root(root):
    hits = []
    for dirpath, _, filenames in os.walk(root):
        for name in filenames:
            lower = name.lower()
            if lower in CANDIDATE_FILES or 'version' in lower or 'about' in lower:
                full = os.path.join(dirpath, name)
                try:
                    if os.path.getsize(full) > MAX_READ:
                        continue
                    with open(full, 'r', encoding='utf-8', errors='ignore') as f:
                        text = f.read(MAX_READ)
                    versions = extract_versions(text)
                    for original, vt in versions:
                        hits.append((f'file:{full}', original, vt))
                except Exception:
                    continue
    return hits


def choose_best_hit(hits):
    if not hits:
        return None
    # Prefer explicit version labels, then shortest path/source string
    def score(hit):
        source, original, vt = hit
        explicit = 0 if 'version' in source.lower() else 1
        return (explicit, len(source))
    return sorted(hits, key=score)[0]


def main():
    ap = argparse.ArgumentParser(description='Check Mergen QMS version exposure for CVE-2023-5806')
    ap.add_argument('--url', help='Base URL of the Mergen QMS application, e.g. https://qms.example.com')
    ap.add_argument('--root', help='Local application root to inspect, e.g. /opt/mergen')
    args = ap.parse_args()

    if not args.url and not args.root:
        print('UNKNOWN - provide --url or --root')
        sys.exit(2)

    hits = []
    if args.url:
        hits.extend(inspect_url(args.url))
    if args.root:
        if not os.path.isdir(args.root):
            print(f'UNKNOWN - root path not found: {args.root}')
            sys.exit(2)
        hits.extend(inspect_root(args.root))

    best = choose_best_hit(hits)
    if not best:
        print('UNKNOWN - could not determine Mergen QMS version from reachable content or local files')
        sys.exit(2)

    source, original, vt = best
    cmp_result = compare_versions(vt)
    normalized = '.'.join(map(str, vt))

    if cmp_result == -1:
        print(f'VULNERABLE - discovered version {original} ({normalized}) from {source}; fixed boundary is v1.2')
        sys.exit(0)
    elif cmp_result in (0, 1):
        print(f'PATCHED - discovered version {original} ({normalized}) from {source}; meets/exceeds v1.2 for CVE-2023-5806')
        sys.exit(0)
    else:
        print('UNKNOWN - unable to compare discovered version')
        sys.exit(2)


if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print('UNKNOWN - interrupted')
        sys.exit(2)
    except Exception as e:
        print(f'UNKNOWN - runtime error: {e}')
        sys.exit(3)
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning: find every Mergen QMS instance in your environment, verify whether any are reachable from untrusted networks, and remove public exposure or place compensating controls within 30 days per the noisgate mitigation SLA for a HIGH issue. Then move the actual software update into a tracked remediation plan and finish vendor patching within 180 days per the noisgate remediation SLA; because a later Mergen SQLi (CVE-2024-2865, disclosed 2024-03-25) exists, do not stop at a box-check of v1.2 without confirming you are on a vendor-supported build that also covers later fixes.

Sources

  1. OpenCVE record for CVE-2023-5806
  2. NVD entry for CVE-2023-5806
  3. CISA Known Exploited Vulnerabilities Catalog
  4. FIRST EPSS documentation/API reference
  5. Mergen Smart Factory R&D Project
  6. MKA Software About Us (Mergen product context)
  7. OpenCVE record for follow-on Mergen SQLi CVE-2024-2865
  8. OpenCVE product view for Mergentech Quality Management System
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.