
Reproduit la RCE non authentifiée de ZendTo via ClamAV ainsi que l'élévation de privilèges root dans un laboratoire autorisé, avec cible Docker épinglée, vérification en mode fail-closed, sortie liée à un nonce et nettoyage.
Ce répertoire est un ensemble autonome de reproduction en laboratoire autorisé pour la chaîne d'exécution de code ZendTo-vers-ClamAV sans compte et sa continuation séparée Smarty/cron root basée sur le profil par défaut. Il comprend :
Le PoC initial exécute une commande choisie par l'appelant sous le compte de service clamav standard. La variante optionnelle poursuit ce point d'appui sans compte jusqu'à une commande choisie par l'appelant exécutée en root. Les deux utilisent id par défaut, rejettent le chemin littéral /root/flag et renvoient une sortie liée à un nonce sur HTTPS.
Les relations vulnérables de groupe, de répertoire, de Smarty et de cron root sont les valeurs par défaut du paquet/installeur Debian ZendTo. L'atteignabilité de root reste néanmoins conditionnée à l'environnement : ce montage positif exact utilise un volume /var/zendto sur ext4, un PHP CLI fonctionnel avec FFI/POSIX/exec, et un processus clamd non confiné. L'application d'AppArmor/SELinux ou de sémantiques de système de fichiers incompatibles peut bloquer la continuation sur une autre installation par ailleurs standard.
Utilisez ceci uniquement sur le montage jetable inclus ou sur un autre système que vous êtes explicitement autorisé à tester.
La cible exacte de référence est un artefact local, volontairement non versionné, situé à :
image/zendto-installer-systemd-debian12.tar.zst
Git ignore les archives d'images Docker. Avant une régression exacte, placez une copie locale autorisée à ce chemin ; scripts/setup.sh exige son SHA-256 et son identifiant d'image enregistrés. source-recipe/README.md documente comment l'image dérivée de l'installeur a été construite et capturée. Une nouvelle construction à partir des dépôts de paquets en direct est utile pour tester la topologie, mais n'est pas présumée identique octet pour octet à la cible épinglée.
Le script d'installation et le point d'entrée du conteneur rejettent toute divergence par rapport au profil suivant :
Les valeurs complètes figurent dans PROFILE.json.
C'est plus fort que d'épingler uniquement la version du paquet ClamAV. La construction de l'allocateur dépend également de glibc, du trafic d'analyse piloté par les CVD, des paramètres des workers et de la bibliothèque cible exacte. FreshClam est désactivé dans l'image capturée, et le démarrage échoue si daily.cvd a dérivé ou si un daily.cld est apparu.
Le conteneur de recherche qui tourne actuellement depuis longtemps dans l'espace de travail parent n'est pas le montage propre : des tests ultérieurs ont désactivé son formulaire d'expéditeur externe et mis à jour sa base de données quotidienne. Ces modifications sont volontairement exclues ici. Cet ensemble utilise la base de référence propre sur laquelle l'exécution native a été démontrée.
/sys/fs/cgroupzstd, Python 3 avec venv, un compilateur C, file et GNU readelfLa cible partage le noyau hôte et l'implémentation ASLR. Le comportement de mappage dépendant du noyau reste donc une variable de portabilité.
Un seul montage systemd doit partager l'espace de noms cgroup de l'hôte à la fois. Sur la machine de recherche d'origine, arrêtez l'ancien montage sans le supprimer :
docker stop zendto-installer-systemd-native
Il peut ensuite être restauré avec docker start zendto-installer-systemd-native.
Depuis ce répertoire :
./scripts/verify-bundle.sh
./scripts/setup.sh
Le script d'installation :
.venv avec les dépendances Python épinglées du PoC ;/var/zendto et démarre systemd ; etrenameat2(RENAME_EXCHANGE) effectuée en tant que clamav.Les points de terminaison par défaut sont limités au loopback :
http://127.0.0.1:18084/
https://127.0.0.1:18447/
Le certificat est auto-signé. Le PoC utilise volontairement verify=False pour chaque requête HTTP. D'autres ports de loopback peuvent être sélectionnés avant l'installation :
export ZENDTO_HTTP_PORT=19084
export ZENDTO_HTTPS_PORT=19447
./scripts/setup.sh
Utilisez les mêmes variables d'environnement pour les commandes Compose/utilitaires ultérieures.
Vérifiez la cible à tout moment :
./scripts/verify-target.sh
docker compose ps
La base de référence propre de l'installeur contient :
allowExternalUploads = TRUE
confirmExternalEmails = TRUE
captcha = google
Ses valeurs CAPTCHA et e-mail sont des espaces réservés de l'installeur, de sorte que l'archive non modifiée ne peut pas réellement délivrer l'e-mail de vérification public. Les tests de régression natifs n'ont représenté que cette étape applicative terminée, via une ligne AuthData équivalente d'expéditeur externe. Le code cible, les paquets, le scanner, les permissions et le chemin d'exploitation n'ont pas été corrigés.
Créez cette unique ligne de laboratoire et un fichier de jeton en mode 0600 avec :
./scripts/mint-lab-auth.py [email protected]
La valeur du jeton est masquée et stockée à :
.lab/upload-auth-token.txt
Cet utilitaire local ne prétend pas qu'un déploiement avec téléversement externe désactivé peut être exploité à distance. Si le formulaire public d'expéditeur externe est désactivé, le PoC s'arrête correctement avant ClamAV, même lorsqu'un fichier de jeton est fourni.
Sur un déploiement autorisé normalement configuré, omettez --external-auth-token-file : ouvrez manuellement la page de vérification, résolvez le CAPTCHA, recevez le message généré par la cible dans une boîte aux lettres contrôlée par l'attaquant, puis collez son URL/jeton à l'invite masquée.
Ce mode n'envoie aucun CAPTCHA, e-mail, capacité, téléversement, POST de connexion ni contenu de scanner. --skip-clamd-tcp-probe supprime également la vérification native TCP/3310 optionnelle en lecture seule :
mkdir -p -m 700 work
.venv/bin/python poc/zendto_6_15_8_unauth_clamav_rce.py \
https://127.0.0.1:18447 \
--enumerate-only \
--skip-clamd-tcp-probe \
--fingerprint-json work/fingerprint.json
L'énumération produit toujours des journaux d'accès web ordinaires et peut initialiser les caches applicatifs normaux. Son résultat de disposition Apache Debian/Ubuntu n'atteste pas le paquet natif, le Build ID, la libc, les CVD, la politique de socket ni l'état MAC. Le script de vérification Docker local fournit cette preuve pour ce montage.
Ceci valide le jeton de laboratoire et la politique de destinataire, mais ne soumet aucun fichier ni contenu de scanner :
.venv/bin/python poc/zendto_6_15_8_unauth_clamav_rce.py \
https://127.0.0.1:18447 \
--sender-email [email protected] \
--recipient [email protected]. \
--external-auth-token-file .lab/upload-auth-token.txt \
--probe-only \
--no-open-browser
[email protected]. correspond à la valeur de domaine interne délibérément inhabituelle test. saisie lors de la création de ce montage d'installeur. Un déploiement réel personnalisé peut exiger un destinataire autorisé réel.
Avertissement : ceci n'est pas une vérification de scanner bénigne. Avant la première tentative, le mode distant envoie EICAR, fait délibérément planter clamd avec le discriminateur PESpin, prouve la récupération déclenchée par socket et exécute deux sondes finales de page de code avec des cycles supplémentaires de plantage/récupération. Les mappages rejetés tuent intentionnellement clamd.
Utilisez un nouveau répertoire de travail et une commande inoffensive :
run_dir="work/run-$(date +%Y%m%d-%H%M%S)"
mkdir -m 700 "$run_dir"
.venv/bin/python poc/zendto_6_15_8_unauth_clamav_rce.py \
https://127.0.0.1:18447 \
--sender-email [email protected] \
--recipient [email protected]. \
--external-auth-token-file .lab/upload-auth-token.txt \
--oracle-conditioned-attempts 72 \
--command 'id; uname -a' \
--workdir "$run_dir" \
--keep-workdir
La voie conditionnée privilégiée teste le mappage utile de nibble-8 avant de soumettre un porteur de corruption. L'agrégat actuel à trois dispositions est une correction ultérieure du cycle de vie ; ses étapes historiques d'oracle à une disposition et de RCE dans le même processus sont documentées séparément dans docs/.
La solution de repli aveugle à nibble-6 fixe est disponible explicitement :
.venv/bin/python poc/zendto_6_15_8_unauth_clamav_rce.py \
https://127.0.0.1:18447 \
--sender-email [email protected] \
--recipient [email protected]. \
--external-auth-token-file .lab/upload-auth-token.txt \
--restart-resample-attempts 72 \
--command 'id; uname -a' \
--workdir work/blind-run \
--keep-workdir
Sous un modèle de nibble uniforme indépendant, 47 mappages frais donnent environ 95 % et 72 environ 99 %. Ce sont des probabilités géométriques de mappage, pas des garanties de fiabilité de bout en bout. Chaque mappage rejeté prend au moins le délai de récupération de 12 secondes plus la période obligatoire de retraite du worker de 40 secondes, hors temps d'analyse. Une exécution de 72 tentatives peut dépasser une heure.
Le résultat n'est pas lu depuis un rappel ni un webshell. Le DSO crée un jeton AuthData séparé de 30 minutes, en sortie uniquement, exécute /bin/sh -c en tant que clamav et renvoie une sortie standard/erreur standard bornée via une page ZendTo anonyme. Le succès exige un résultat encadré où tout ce qui suit est cohérent :
zendto_unauth_clamav_rce_result=1Une preuve manquante, dupliquée, tronquée ou non concordante est une exécution échouée.
Exécutez ceci uniquement après que ./scripts/verify-target.sh rapporte le succès à la fois de [target] et de [privesc-target]. Le script d'orchestration effectue d'abord la même étape ClamAV sans compte, exige sa preuve exacte et bornée d'armement, puis attend le consommateur cron root standard inchangé :
.venv/bin/python poc/zendto_clamav_to_root_smarty_privesc.py chain-unauth \
https://127.0.0.1:18447 \
--sender-email [email protected] \
--recipient [email protected]. \
--external-auth-token-file .lab/upload-auth-token.txt \
--oracle-conditioned-attempts 72 \
--root-command 'id; uname -a' \
--keep-workdir
L'option de fichier de jeton n'est que le substitut d'e-mail terminé du montage. Sur un flux de travail public autorisé, omettez-la et complétez manuellement l'invite CAPTCHA/e-mail. Aucun nom d'utilisateur ni mot de passe ZendTo n'est utilisé par chain-unauth.
L'étape root est délibérément séparée, même si le script d'orchestration automatise sa transition. Elle :
savechunk.php non authentifié ;clamav ;/zendto-root-result-<nonce>.json avec verify=False, en exigeant euid=0, le hachage exact de la commande, le marqueur de restauration et le nonce d'achèvement.Cron peut faire attendre le sondage final près d'une heure ; le délai d'expiration par défaut est de 3900 secondes. La commande root est limitée à 1024 octets et 30 secondes, avec au plus 1 MiB chacun pour la sortie standard et l'erreur standard. Son JSON adressé par nonce est volontairement lisible sur le web dans ce laboratoire jetable ; n'utilisez donc que des sorties de test inoffensives.
Pour une orchestration manuelle, emit affiche la commande exacte à passer au --command du PoC sans compte ; après la preuve d'armement, utilisez poll avec l'identifiant d'exécution affiché et le SHA-256 de la commande root exacte. Voir docs/SMARTY_ROOT_BRIDGE.md pour la construction et le relevé d'audit.
Cette variante est censée s'arrêter en toute sécurité lorsqu'un prérequis manque. Ne chevauchez pas les bras et ne réessayez pas une exécution ambiguë : des étapes échouées peuvent laisser un fichier PHP entrant, un clone caché, un verrou ou un cache échangé. Utilisez un montage neuf pour chaque test root :
./scripts/reset-lab.sh --yes-delete-lab-volume
./scripts/mint-lab-auth.py [email protected]
Une seule corruption réussie est prise en charge par processus clamd. Une seconde invocation n'est défendable qu'après que la sonde de recyclage destructive d'une exécution neuve renvoie la réponse de défaillance du scanner attendue et qu'EICAR prouve la récupération via la socket active. Ne réutilisez pas un identifiant d'exécution, un nom de morceau préparé, un jeton de résultat ni un répertoire de travail client.
Arrêtez-vous immédiatement en cas de redirection, de délai réseau expiré, de corps/statut ambigu, de réponse propre inattendue ou d'échec de récupération EICAR. Ne soumettez jamais manuellement à nouveau la requête de corruption précédente à un démon incertain.
Le service empaqueté a Restart=no. La reproductibilité vient de la clamav-daemon.socket toujours active, pas d'une politique de redémarrage automatique du service. Si la socket est désactivée ou limitée en démarrage, des plantages délibérés peuvent laisser le scanner indisponible.
Les résidus attendus de la cible comprennent les journaux d'accès/erreurs, les enregistrements de plantage de clamd ou les fichiers core, les fichiers de revendication orphelins bornés des gardes d'oracle propres, un DSO /var/zendto/incoming/Bridge<nonce>.1 en ajout seul, et des lignes AuthData de courte durée. Le nettoyage standard ne purge les revendications orphelines ordinaires qu'après plus d'un jour.
La variante root laisse en outre un armeur PHP à nonce, un clone et un verrou cachés, le résultat JSON public appartenant à root, et la sortie standard/erreur standard privée /run. Un script d'orchestration réussi restaure le véritable templates_c avant d'exécuter la commande root, mais ces fichiers de preuve restent jusqu'à la réinitialisation du conteneur et du volume.
Révoquez le jeton de téléversement de laboratoire après les tests :
./scripts/revoke-lab-auth.py
Arrêtez la cible en préservant son volume de preuves :
docker compose down
Pour un montage propre et neuf, supprimez explicitement uniquement le volume nommé de ce projet et redémarrez-le à partir de l'image épinglée :
./scripts/reset-lab.sh --yes-delete-lab-volume
Archivez le répertoire de travail et le JSON d'empreinte avant la réinitialisation s'ils sont nécessaires comme preuves.
docker-compose.yml exact systemd/socket target topology
PROFILE.json exact package/hash/CVD profile
image/*.tar.zst local ignored target artifact
poc/ no-account driver and support modules
third_party/ minimized builder/source/binary closure
scripts/setup.sh image/client/Compose setup
scripts/verify-bundle.sh delivery-manifest verification
scripts/verify-target.sh fail-closed target verification
scripts/verify-privesc-target.sh root-variant semantic preflight
scripts/mint-lab-auth.py local completed-email substitute
scripts/revoke-lab-auth.py remove that lab bearer
scripts/reset-lab.sh explicit clean-volume reset
source-recipe/ installer provenance, not exact rebuild
docs/ technical regression/audit notes
source-recipe/install.ZendTo.tgz et son manuel d'exécution sont inclus pour la provenance et la construction des sources. Ne considérez pas une nouvelle construction à partir du dépôt en direct comme exacte : l'installeur n'épingle pas tous les paquets ni les bases de données ClamAV. La régression exacte exige que l'archive d'image locale ignorée corresponde à PROFILE.json.
MANIFEST.sha256 couvre tous les fichiers livrés sauf lui-même. Les archives d'images Docker, l'utilitaire compilé sur l'hôte third_party/installer-runtime/clamav-pespin/pespin_summit et les arborescences générées .venv, .lab, work et __pycache__ sont ignorés. Le script d'installation reconstruit l'utilitaire à partir de sa source C manifestée et vérifie l'image locale séparément.
Les vérifications statiques au moment de l'empaquetage, la vérification préalable distante, le rééchantillonnage probabiliste et les vérifications instrumentées de bout en bout sont consignées dans docs/BUNDLE_VALIDATION.md. Le relevé distingue les observations distantes de la seule exécution de validation à laquelle une base de module observée localement a été explicitement fournie.
| Composant | Valeur testée exacte |
|---|
| Identifiant d'image Docker | sha256:de6d2f06ca04943362a9bdec5026e808448953728f31bd926343a4ed2ca2ef7c |
| ZendTo | 6.15-8 |
| Paquet ClamAV/libclamav | 1.4.3+dfsg-1~deb12u2 |
| libclamav | libclamav.so.12.0.3, SHA-256 55e3cd94…027c |
| Build ID libclamav | e6427ab62146ee3001fe463d12e797e9d25bf81a |
| glibc | 2.36-9+deb12u14, SHA-256 6b4a4535…421 |
| Base de données principale | v63, SHA-256 0b2182d2…365 |
| Base de données quotidienne | v28082, SHA-256 cddbcccf…906 |
| Base de données bytecode | v339, SHA-256 6d4aa01f…ffb |
| clamd | MaxThreads 12, IdleTimeout 30, Restart=no |
| Cycle de vie | clamav-daemon.socket activée, systemd PID 1 |
| Environnement d'exécution web | Apache 2.4.68, PHP 8.2.32 |
| Posture MAC | montage Docker privilégié non confiné |
| Pont root | clamav standard dans www-data ; /var/zendto root:www-data 0775 |
| Consommateur root | cron de nettoyage root standard, toutes les heures à la minute 25 |
| Cache Smarty | compilation déterministe de zendto.conf par Smarty 4.5.4 |
| Système de fichiers d'échange | volume nommé /var/zendto sur ext4 |