
# CVE-2026-25243 के लिए स्थिर POC (Redis RESTORE double-free -> रिमोट कोड निष्पादन)
Rocky Linux 8.10, aarch64, Redis 8.6.2, jemalloc 5.3.0 के विरुद्ध सत्यापित।
संदर्भ: https://www.zeroday.cloud/blog/redis-cve-2026-25243-deep-dive
TLDR; स्थिर एक्सप्लॉइट, विभिन्न OS डिस्ट्रो और आर्किटेक्चर पर काम करता है।
यह क्या है? Redis में एक मेमोरी भ्रष्टाचार भेद्यता जो एक प्रमाणित
हमलावर को Redis उपयोगकर्ता के रूप में मनमाने कमांड चलाने की अनुमति देती है। हमला
वास्तविक दुनिया में काम करता है और इसके लिए केवल एक RESTORE कमांड की आवश्यकता होती है — यह एक सामान्य Redis
ऑपरेशन है, न कि केवल-व्यवस्थापक। यह एक्सप्लॉइट एक सेकंड से भी कम समय में पूर्ण RCE प्रदर्शित करता है।
प्रभाव? कोई भी प्रमाणित Redis क्लाइंट इसे ट्रिगर कर सकता है, और क्षति पूर्ण है: Redis प्रक्रिया में मनमाना कोड निष्पादन (अक्सर कंटेनरों में root के रूप में चल रहा होता है)। Redis को स्वयं पैच किए बिना इसे कम करने का कोई तरीका नहीं है।
यह एक नज़र में कैसे काम करता है? Redis में एक सीरियलाइज़ेशन सुविधा (RESTORE)
है जो बाइनरी डेटा का एक ब्लॉब लेती है और इसे Redis ऑब्जेक्ट के रूप में पुनर्निर्मित करती है। जो कोड ब्लॉब के प्रारूप को मान्य करता है और जो कोड इसे डिसीरियलाइज़ करता है, वे कुछ अनुक्रमों को पार्स करने के तरीके पर असहमत हैं — यह एक बग है जिसका हमलावर हीप को भ्रष्ट करने के लिए शोषण करता है। एक बार हीप भ्रष्ट हो जाने पर, हमलावर Redis प्रक्रिया में किसी भी मेमोरी पते को पढ़ने और लिखने की क्षमता प्राप्त कर लेता है, और वहाँ से शेल कमांड निष्पादित करने के लिए सर्वर की आंतरिक स्थिति को हाईजैक कर लेता है।
वास्तविक एक्सप्लॉइट तकनीक: यह एक साधारण क्रैश नहीं है। यह एक हीप शोषण श्रृंखला है: भ्रष्ट → ओवरलैप → मनमाना R/W → सूचना रिसाव → सर्वर स्ट्रक्चर खोजें → फंक्शन पॉइंटर्स हाईजैक करें → RCE। एक्सप्लॉइट 9 चरणों में चलता है और रनटाइम पर कई पतों को लीक करने, बाइनरी स्ट्रक्चर पार्स करने और मेमोरी एलियासिंग का पता लगाने की आवश्यकता होती है। इसे विभिन्न आर्किटेक्चरों (x86-64, aarch64, आदि) पर काम करने योग्य बनाने वाली बात यह है कि सभी पते लक्ष्य से ही लीक किए जाते हैं, मान लिए नहीं जाते।
CVE-2026-25243 एकल प्रमाणित RESTORE कमांड से पहुंचने योग्य डबल-फ्री बगों की एक जोड़ी है। RESTORE key ttl <serialized-value> हमलावर-नियंत्रित RDB ब्लॉब को डिसीरियलाइज़ करता है; दोनों बग उस वैलिडेटर के बीच की खाई में रहते हैं जो ब्लॉब की जाँच करता है और उस कन्वर्टर के बीच जो इसे मूर्त रूप देता है।
बग 1 — लीगेसी zipmap रूपांतरण (CWE-415, वह पथ जो यह एक्सप्लॉइट उपयोग करता है)।
zipmap वैलिडेटर (zipmapValidateIntegrity()) और कन्वर्टर
(zipmapNext()) एक अनावश्यक लंबाई एन्कोडिंग के बारे में असहमत हैं। छोटी लंबाई
4 को कानूनी रूप से लंबे पाँच-बाइट रूप FE 04 00 00 00 में लिखा जा सकता है।
वैलिडेटर बाइट्स की एक संख्या उपभोग करता है, कन्वर्टर दूसरी — एक 4-बाइट
पार्सिंग डिसिंक्रोनाइज़ेशन। इसलिए कन्वर्टर उससे भिन्न संरचना पर चलता है
जिसे मान्य किया गया था, फ़ील्ड को डिक्शनरी में डाले जाने के बाद lpSafeToAdd() विफल हो जाता है, और सफाई पथ फ़ील्ड को दो बार मुक्त करता है: एक बार dictRelease() के माध्यम से और फिर से sdsfree() के माध्यम से।
बग 2 — स्ट्रीम उपभोक्ता PEL लोडिंग (CWE-415)।
rdbLoadStreamConsumersGroup() में, एक डुप्लिकेट एंट्री ID वाला उपभोक्ता PEL
दूसरे raxTryInsert() को विफल कर देता है, जो समूह के वैश्विक PEL द्वारा अभी भी स्वामित्व वाले streamNACK पर streamFreeNACK() को कॉल करता है। दो बार मुक्त।
(--vuln-type stream के साथ चुनने योग्य।)
या तो बग हमलावर को एक साथ मुक्त और संदर्भित मेमोरी का एक टुकड़ा देता है — हीप-ओवरलैप एक्सप्लॉइट के लिए क्लासिक प्रारंभिक बिंदु।
प्रभाव: एक प्रमाणित Redis क्लाइंट (कोई व्यवस्थापक अधिकार नहीं, RESTORE एक सामान्य डेटा कमांड है) redis उपयोगकर्ता के रूप में मनमाना कोड निष्पादन प्राप्त करता है — डिफ़ॉल्ट कंटेनर छवि में root।
नौ चरण, जिनमें से प्रत्येक एक कमज़ोर प्रिमिटिव को एक मज़बूत प्रिमिटिव में बदल देता है:
| चरण | प्राप्त प्रिमिटिव | तंत्र |
|---|---|---|
| 0 | लक्ष्य प्रोफ़ाइल | INFO server / INFO memory → संस्करण, आर्किटेक्चर, डिस्ट्रो, pid, निष्पादन योग्य पथ, प्रारंभ समय, एलोकेटर |
| 1 | डबल फ्री | malformed zipmap (या stream) RESTORE |
| 2 | मेमोरी साझा करने वाली दो कुंजियाँ | मुक्त चंक पर मार्कर कुंजियाँ स्प्रे करें, एलियासिंग का पता लगाएं, फिर एक कुंजी के SDS हेडर को उसके जुड़वाँ के माध्यम से अधिलेखित करके उसे 1 MB "मेमव्यू" में फुलाएँ |
| 3 | मनमाना R/W | मेमव्यू के अंदर एक INCRBYFLOAT ऑब्जेक्ट खोजें, उसके ptr फ़ील्ड को हाईजैक करें: उस कुंजी पर GETRANGE/SETRANGE अब किसी भी पते को पढ़/लिख सकते हैं |
| 4 | इमेज पॉइंटर | redis-server इमेज के अंदर किसी मान के लिए हीप को पीछे की ओर स्कैन करें |
| 5 | &server | ELF हेडर तक नीचे जाएँ, प्रोग्राम हेडर पार्स करें, लिखने योग्य सेगमेंट डंप करें, server.pid मिलाएँ |
| 6 | मेमोरी में पेलोड | मेमव्यू में "/bin/sh", "-c", "<cmd>" और एक argv सरणी लिखें |
| 7 | हाईजैक किया गया स्ट्रक्चर | server.executable, server.exec_argv, और server.enable_debug_cmd अधिलेखित करें |
| 8 | RCE | DEBUG CRASH-AND-RECOVER → restartServer() → execve(server.executable, server.exec_argv, environ) |
python3 exploit.py --host 127.0.0.1 --port 6379 \
--password mypassword --cmd 'id > /tmp/pwned123.txt'
सत्यापित करें:
cat /tmp/pwned123.txt
# uid=0(root) gid=0(root) groups=0(root)
प्रारंभिक बिंदु: एक्सप्लॉइट केवल x86-64 था और aarch64 लक्ष्य पर चरण 3 में विफल हो गया। अंतिम स्थिति: aarch64 Rocky Linux 8.10 पर एक सेकंड से भी कम समय में पूर्ण RCE, 116 Redis कमांड।
a) रनटाइम लक्ष्य फ़िंगरप्रिंटिंग (नया, चरण 0)। लक्ष्य के बारे में अब कुछ भी मान नहीं लिया जाता। INFO server + INFO memory Redis संस्करण, CPU आर्किटेक्चर (os: पंक्ति से), डिस्ट्रो परिवार (gcc_version से अनुमानित), एलोकेटर, और — सबसे महत्वपूर्ण — तीन सत्यापन एंकर देते हैं: process_id, executable, और सटीक stat_starttime (server_time_usec/1e6 - uptime_in_seconds)। बाद के चरण अनुमान लगाने के बजाय इनके विरुद्ध तुलना करते हैं।
b) आर्किटेक्चर-स्वतंत्र मेमोरी लेआउट। चार हार्डकोडेड x86-64 स्थिरांक (BINARY_ADDR_MIN/MAX, HEAP_ADDR_MIN/MAX) को प्रति-आर्किटेक्चर तालिका (ARCH_PROFILES) से बदल दिया गया है जो x86_64, aarch64 (39- और 48-बिट VA दोनों), riscv64, ppc64le और s390x को कवर करती है, प्रत्येक के लिए ET_EXEC और ET_DYN दोनों प्लेसमेंट के साथ, साथ ही किसी भी सूचीबद्ध न किए गए के लिए एक व्यापक जेनेरिक फ़ॉलबैक। यह वास्तविक कारण था कि एक्सप्लॉइट इस लक्ष्य पर विफल रहा: लीक किया गया पॉइंटर 0x0000ffff8a5fdf32 एक पूरी तरह से मान्य aarch64 mmap पता है जिसे x86-64 रेंज जाँच ने अस्वीकार कर दिया।
c) सर्वसम्मति-आधारित लीक सत्यापन (चरण 3)। हार्डकोडेड हीप विंडो पर भरोसा करने के बजाय, स्कैन अब मेमव्यू में हर संरचनात्मक रूप से मान्य 1337.NNNNNN ऑब्जेक्ट एकत्र करता है और उनमें से कम से कम दो को समान मेमव्यू आधार पता (ptr - offset_of_value) प्राप्त करने की आवश्यकता होती है। व्यवहार में 502 उम्मीदवार सहमत होते हैं, जो इस बात का प्रमाण है जो कोई रेंज तालिका नहीं दे सकती। पुष्टि किया गया पॉइंटर फिर रनटाइम पर हीप विंडो को कैलिब्रेट करता है। प्रारूप सत्यापन को भी (राउंड-ट्रिप-महंगे) लेखन-नियंत्रण परीक्षण से पहले स्थानांतरित कर दिया गया।
d) चरण 3 स्कैन सीमा (बग फिक्स)। स्कैन एक हार्डकोडेड 10 MB तक चलता था जबकि मेमव्यू 1 MB है, इसलिए यह अंत से आगे पढ़ता था, खाली उत्तर प्राप्त करता था और AssertionError: Empty data from memview के साथ रद्द हो जाता था। अब यह मेमव्यू के वास्तविक STRLEN द्वारा सीमित है, प्रति राउंड-ट्रिप 64 KB के बजाय 256 KB पढ़ता है, और व्यर्थ 6×1s रिट्री-स्लीप लूप हटा दिया गया है।
e) चरण 5 पुनर्लिखित: ELF-निर्देशित, क्रैश-मुक्त (सबसे बड़ा)। पुराना कार्यान्वयन एक इमेज पॉइंटर से आगे की ओर स्कैन करता था, पतों की जाँच करता था और जो भी लंबाई एक कचरा SDS हेडर दावा करता था उसे पढ़ता था। इस लक्ष्य पर यह पठनीय-केवल सेगमेंट के अंत से सीधे 0x715000 पर बिना मैप किए छेद में चला गया और सर्वर को मार डाला (getrangeCommand में SIGSEGV → memcpy)। अंधा स्कैनिंग सुरक्षित नहीं बनाया जा सकता। प्रतिस्थापन निर्धारणात्मक है: