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.
4 steps from start to impact.
Reach the QMS web interface with httpx or a browser
- Target runs Mergen Quality Management System before
v1.2 - Attacker can reach the HTTP/HTTPS service from their position
- 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
Identify an injectable parameter with Burp Suite or sqlmap
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.- At least one unauthenticated endpoint passes user input into SQL
- Application responses are stable enough for boolean/time-based inference
- 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
Dump or tamper with backend data using sqlmap
- Database account used by the app has useful read/write privileges
- Injection point supports stacked, union, error-based, or inferential extraction
- 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
Escalate impact only if the database or host is over-privileged
- Over-privileged DB configuration or weak trust relationships exist
- Attacker can convert application/data compromise into broader access
- 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
The supporting signals.
| In-the-wild status | No 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 availability | No 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. |
| EPSS | 0.00066 (~0.066%) from the prompt intel, which is a very low exploitation-probability signal compared with truly hot internet bugs. |
| KEV status | Not KEV-listed in the current CISA Known Exploited Vulnerabilities catalog review. |
| CVSS vector | CVSS: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 versions | Mergen Quality Management System before v1.2 per the CVE/CNA record. |
| Fixed version | v1.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 reality | Vendor 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. |
| Disclosure | Published 2024-01-18 by TR-CERT; credited finder is Resul Melih MACIT. |
| Scanner coverage | Expect 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. |
noisgate verdict.
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.
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.
What to do — in priority order.
- 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.
- 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.
- 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.
- 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.
- 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.2without vendor confirmation may be insufficient because a later Mergen QMS SQLi,CVE-2024-2865, was published on 2024-03-25.
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.
#!/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)
If you remember one thing.
v1.2 without confirming you are on a vendor-supported build that also covers later fixes.Sources
- OpenCVE record for CVE-2023-5806
- NVD entry for CVE-2023-5806
- CISA Known Exploited Vulnerabilities Catalog
- FIRST EPSS documentation/API reference
- Mergen Smart Factory R&D Project
- MKA Software About Us (Mergen product context)
- OpenCVE record for follow-on Mergen SQLi CVE-2024-2865
- OpenCVE product view for Mergentech Quality Management System
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.