Symptômes · 10 min · publié le 23 juillet 2024

Code en base64 dans des fichiers PHP : démêler légitime et porte

Certains plugins encodent pour se protéger. Un `eval(base64_decode(...))` isolé dans un fichier anodin, presque jamais. Le contexte décide : header de plugin connu, ou une ligne collée en bas de `wp-blog-header.php`. On ne « décode pas tout Internet » ; on tranche fichier par fichier.

Réponse directe

Certains plugins encodent pour se protéger. Un eval(base64_decode(...)) isolé dans un fichier anodin ne l'est presque jamais. Le contexte décide.

base64 php malware eval base64_decode code encodé wordpress

Ce que base64 veut dire ici

Base64 n'est pas un malware. C'est un alphabet pour porter des octets dans du texte. Une image dans un CSS, une clé, un chunk Elementor : plein de base64 honnêtes. Le problème, c'est PHP qui décode puis `eval` / `assert` / `preg_replace` /e / `create_function` / `include` d'une chaîne.

Les kits l'utilisent pour passer les scans à mots-clés (`eval` seul se voit ; une chaîne de 4 Ko moins). Ce n'est pas sophistiqué. C'est fréquent.

Un fichier « tout en une ligne » de 80 Ko de caractères A-Za-z0-9+/= dans un `radio.php` : vous n'avez pas besoin de tout lire. Classez porte, copiez, sortez. Le critère n'est pas « il y a du base64 quelque part » (Gutenberg, CSS, e-mails en ont). C'est PHP qui décode puis exécute. Un `base64_decode` qui alimente un `echo` d'image n'est pas le même dossier qu'un `eval`.

Légitime : licences, builders, ionCube

Plugins payants : chargeur ionCube / sourceguardian, ou un `eval` de licence (discutable, mais connu, header Plugin Name, auteur). Thèmes ThemeForest : parfois un `class` encodé. Comparez au zip que vous avez acheté. S'il est identique, ce n'est pas l'incident — c'est une hygiène douteuse, un autre débat.

Elementor, Divi : du base64 dans le CSS et les data. Ce n'est pas dans un `index.php` cœur. Le chemin du fichier oriente autant que la fonction.

WordPress core n'embarque pas d'`eval(base64_decode` dans `wp-load.php`. Si vous le voyez là, ce n'est pas « une MAJ ». C'est une greffe.

  • Zip d'origine du plugin / thème = même blob ?
  • Fichier cœur CMS = jamais légitime.
  • Fichier anodin à la racine = presque jamais.

Le motif `eval` + decode qui tranche

`eval(base64_decode(`, `eval(gzinflate(base64_decode(`, `eval(str_rot13`, doubles et triples enveloppes. Variantes : `$a='base'.'64_decode'; $a(...)`. Si vous le voyez dans `footer.php` après le HTML, ce n'est pas une licence Divi.

`assert`, `system`, `passthru`, `shell_exec` à côté : webshell, pas encodeur de thème. Voir C99 WSO.

Un `base64_decode` qui alimente `mail()` ou `file_put_contents` : même gravité, autre symptôme (spam, réécriture).

Où chercher sans noyer le diff

Fichiers modifiés récemment, racine, `mu-plugins`, drop-ins, thème enfant, `uploads` PHP, `.htaccess` prepend. Pas un grep de tout `wp-includes` d'abord : trop de bruit si vous cherchez juste `base64` (le core en a pour de vraies raisons, rares).

Grep ciblé : `eval(base64` et `gzinflate` et `str_rot13` dans `wp-content` et la racine. Puis les cœurs si le poids des fichiers a bougé. Voir index lourd.

La base : `wp_options` et `post_content` peuvent contenir le même motif. Un grep SQL sur `base64_decode` dans `wp_posts` sort des faux positifs (tutos) et de vrais payloads. Lisez le contexte de la ligne. Un article de 2019 qui cite le mot en clair n'est pas une injection. Un `wp_options` `widget_text` d'une ligne encodée, créé cette nuit, si. La date `option_id` / `post_modified` tranche plus vite qu'un décodeur.

Décoder pour classer, pas pour exécuter

Un décodeur en ligne ou `base64 -d` sur un extrait : vous lisez des noms de domaines, des `mail(`, des `wp_insert_post`. Ça classe. N'en faites pas un `php -r eval`.

Les triples enveloppes : deux, trois passes. Si au bout vous avez encore du bruit, arrêtez-vous : c'est une porte, le détail est pour le constat, pas pour la curiosité.

Ne collez pas un payload entier dans un chatbot public. Extraire le domaine de sortie suffit au ticket.

Faux positifs : Gutenberg, CSS, e-mails

Les articles qui expliquent la sécurité citent `base64_decode` en clair. Vos propres tutos. Les newsletters HTML. Un grep sans lire classe « infecté » un blog légitime. Le fichier et le `eval` manquant tranchent.

Les SVG et les data-uri. Les exports ACF. Bruit.

Un plugin de sécurité qui « encode » ses règles : lisez le header. En doute, isolez, testez, comparez au zip wordpress.org.

Une ligne dans un cœur CMS

Remplacez le fichier par le zip officiel après copie. Ne « nettoyez » pas la ligne à la main en laissant un caractère minifié : vous cassez le parse et vous n'avez plus de preuve propre.

Si dix fichiers cœur ont une ligne ajoutée, quelqu'un a un process (cron, shell). Un seul fichier : parfois un edit manuel (éditeur WP). Cherchez quand même l'entrée.

Les checksums WordPress (un outil qui compare au repo) accélèrent. Ils ne voient pas `wp-content`.

Après retrait : la copie encodée ailleurs

Le même blob dans un mu-plugin et en fin de `functions.php` : deux amorces. Grep le début de la chaîne (20 caractères) sur tout le compte.

Un cron recolle le blob. J+1. Panel.

Search Console : le HTML spam peut venir de ce PHP. Inspection live après retrait. Safe Browsing seulement si ressource dangereuse ; le délai 24-72 h n'attend pas que vous ayez « tout décodé ». Grep les vingt premiers caractères du blob sur tout le compte : la même enveloppe dans un mu-plugin et en fin de `functions.php`, deux amorces. En retirer une seule, le title japonais revient.

Scanners et ce qu'ils ratent

Wordfence flag beaucoup d'`eval`. Utile comme liste. « Réparer » un plugin payant encodé le casse. « Ignorer » un `index.php` cœur, erreur. Traitez la liste à la main, fichier par fichier.

Un one-liner `eval($x)` avec `$x` construit sans le mot base64 : faux négatif. Les dates et les poids restent vos filets.

Vous pouvez déclarer le site. Une liste de chemins `eval(base64` suffit à démarrer ; pas besoin d'avoir tout décodé.

  • Contexte (cœur vs plugin payant vs racine).
  • Copie puis retrait / overlay zip.
  • Grep du blob sur le compte.
  • J+1 contre réécriture.

Questions fréquentes

Je décode et je vois du japonais. C'est du SEO spam ?

+
Très probablement un générateur ou du cloaking. Traitez aussi spam SEO japonais : le PHP n'est qu'une amorce.

Un plugin « encodeur » est-il une bonne idée après incident ?

+
Non. Vous ajoutez de l'opacité. Après incident, on veut du lisible et des zips officiels, pas plus d'eval.

base64 dans un `.htaccess`, ça existe ?

+
Rare. Plus souvent un prepend vers un PHP qui, lui, décode. Suivez le chemin cité.

Puis-je tout passer dans un décodeur automatique ?

+
Les outils aident. Ils se trompent sur les licences. Le jugement reste humain sur les faux positifs coûteux (plugin e-commerce cassé le samedi).

WordPress a du base64 dans le core. Mon grep est inutilisable ?

+
Ciblez `eval(base64` et `wp-content` + racine d'abord. Le core a des usages ponctuels sans eval de payload. Le couple eval+decode dans un cœur, lui, est une greffe.
À lire ensuite
Webshell C99 WSO b374k index.php trop lourd Fichiers PHP à la racine Must-use plugin Déclarer mon site