$ k=v p &
[1] 3028
existe-t-il un moyen p
de modifier le contenu de /proc/3028/environ
ne pas le mentionner k=v
pendant qu'il p
est toujours en cours d'exécution?
$ k=v p &
[1] 3028
existe-t-il un moyen p
de modifier le contenu de /proc/3028/environ
ne pas le mentionner k=v
pendant qu'il p
est toujours en cours d'exécution?
Réponses:
Sous Linux, vous pouvez remplacer la valeur des chaînes d'environnement sur la pile.
Vous pouvez donc masquer l'entrée en la remplaçant par des zéros ou toute autre chose:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[], char* envp[]) {
char cmd[100];
while (*envp) {
if (strncmp(*envp, "k=", 2) == 0)
memset(*envp, 0, strlen(*envp));
envp++;
}
sprintf(cmd, "cat /proc/%u/environ", getpid());
system(cmd);
return 0;
}
Courir comme:
$ env -i a=foo k=v b=bar ./wipe-env | hd
00000000 61 3d 66 6f 6f 00 00 00 00 00 62 3d 62 61 72 00 |a=foo.....b=bar.|
00000010
le k=v
a été remplacé par \0\0\0
.
Notez que setenv("k", "", 1)
pour écraser la valeur ne fonctionnera pas comme dans ce cas, une nouvelle "k="
chaîne est allouée.
Si vous n'avez pas autrement modifié la k
variable d'environnement avec setenv()
/ putenv()
, vous devriez également pouvoir faire quelque chose comme ça pour obtenir l'adresse de la k=v
chaîne sur la pile (enfin, l'une d'entre elles):
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[]) {
char cmd[100];
char *e = getenv("k");
if (e) {
e -= strlen("k=");
memset(e, 0, strlen(e));
}
sprintf(cmd, "cat /proc/%u/environ", getpid());
system(cmd);
return 0;
}
Notez cependant qu'il supprime une seule des k=v
entrées reçues dans l'environnement. Habituellement, il n'y en a qu'un, mais rien n'empêche quiconque de passer les deux k=v1
et k=v2
(ou k=v
deux) dans la liste env passée à execve()
. Cela a été la cause de failles de sécurité dans le passé, comme CVE-2016-2381 . Cela peut réellement se produire bash
avant le shellshock lors de l'exportation à la fois d'une variable et d'une fonction du même nom.
Dans tous les cas, il y aura toujours une petite fenêtre pendant laquelle la chaîne env var n'a pas encore été surchargée, donc vous voudrez peut-être trouver un autre moyen de transmettre les informations secrètes à la commande (comme un tube par exemple) si vous l'exposez via /proc/pid/environ
est une préoccupation.
Notez également que contrairement à /proc/pid/cmdline
, /proc/pid/environment
n'est accessible que par des processus avec le même euid ou racine (ou root uniquement si l'euid et le ruid du processus ne sont pas les mêmes).
Vous pouvez leur cacher cette valeur /proc/pid/environ
, mais ils peuvent toujours obtenir toute autre copie que vous avez faite de la chaîne en mémoire, par exemple en y attachant un débogueur.
Voir https://www.kernel.org/doc/Documentation/security/Yama.txt pour savoir comment empêcher au moins les utilisateurs non root de le faire.
Il n'a pas été nécessaire d'écraser les chaînes ci-dessus (pas vraiment sur ) la pile du thread principal sous Linux depuis 2010.
Les deux /proc/self/cmdline
et /proc/self/environ
sont modifiables par le processus lui-même à l'exécution, à force d'appeler la prctl()
fonction avec respectivement PR_SET_MM_ARG_START
+ PR_SET_MM_ARG_END
ou PR_SET_MM_ENV_START
+ PR_SET_MM_ENV_END
. Ceux-ci placent directement les pointeurs de mémoire dans l'espace mémoire d'application du processus, détenu par le noyau pour chaque processus, qui sont utilisés pour récupérer le contenu de /proc/${PID}/cmdline
et /proc/${PID}/environ
, et donc la ligne de commande et l'environnement signalés par la ps
commande.
Il suffit donc de construire un nouvel argument ou une nouvelle chaîne d'environnement (pas de vecteur, remarquez - la mémoire pointée doit être les données de chaîne réelles, concaténées et ␀
délimitées) et d'indiquer au noyau où il se trouve.
Ceci est documenté dans la page de manuel Linux pour la prctl(2)
fonction ainsi que dans la environ(7)
page de manuel. Ce qui n'est pas documenté, c'est que le noyau rejette toute tentative de définir l'adresse de début au-dessus de l'adresse de fin, ou l'adresse de fin sous l'adresse de début; ou pour (re) remettre à zéro l'une ou l'autre des adresses. De plus, ce n'est pas le mécanisme original proposé par Bryan Donlan en 2009, qui permettait de régler atomiquement le début et la fin en une seule opération. De plus, le noyau ne fournit aucun moyen d' obtenir les valeurs actuelles de ces pointeurs.
Il est donc difficile de modifier l'environnement et les zones de ligne de commande avec prctl()
. Il faut appeler la prctl()
fonction jusqu'à quatre fois car les premières tentatives peuvent entraîner des tentatives pour définir le pointeur de début plus haut que le pointeur de fin, selon l'endroit où les anciennes et les nouvelles données sont en mémoire. Il faut appeler un autre quatre fois si l' on veut faire en sorte que cela ne se traduit pas par une fenêtre d'opportunité pour d' autres processus sur le système pour inspecter une gamme arbitraire du processus espace mémoire dans la période où le nouveau début / fin a été définie mais la nouvelle fin / début ne l'a pas été.
Un appel système atomique unique qui définit la gamme entière en une seule fois aurait été beaucoup plus facile à utiliser en toute sécurité pour les programmes d'applications.
Une autre ride est que, sans vraiment bonne raison (compte tenu des contrôles dans le noyau, le overwritability des zones de données originales de toute façon , et le fait que les équivalents ne sont pas des opérations privilégiées sur l' un des BSDs), sur Linux , cela nécessite super - utilisateur privilèges.
J'ai écrit assez simple setprocargv()
et des setprocenvv()
fonctions pour mes boîtes à outils, qui emploient cela. Les programmes de chargement en chaîne à partir des jeux d'outils qui sont intégrés, comme setenv
et foreground
, reflètent donc les arguments de commande enchaînés et l'environnement, où Linux le permet.
# / package / admin / nosh / command / clearenv setenv WIBBLE wobble premier plan pause \; vrai & [1] 1057 # hexdump -C / proc / 1057 / cmdline 00000000 66 6f 72 65 67 72 6f 75 6e 64 00 70 61 75 73 65 | pause au premier plan | 00000010 00 3b 00 74 72 75 65 00 |.;. True. | 00000018 # hexdump -C / proc / 1057 / environ 00000000 57 49 42 42 4c 45 3d 77 6f 62 62 6c 65 00 | WIBBLE = wobble. | 0000000e # hexdump -C / proc / 1058 / cmdline 00000000 70 61 75 73 65 00 | pause. | 00000006 # hexdump -C / proc / 1058 / environ 00000000 57 49 42 42 4c 45 3d 77 6f 62 62 6c 65 00 | WIBBLE = wobble. | 0000000e #
Notez que cela ne milite pas contre les choses qui tracent le processus et accèdent directement à sa mémoire par d'autres moyens (plutôt que via ces deux pseudo-fichiers), et laisse bien sûr une fenêtre avant que les chaînes ne soient modifiées où ces informations peuvent être vues, juste comme l'écrasement des données au-dessus de la pile du thread principal. Et tout comme pour l'écrasement des données, cela ne tient pas compte des bibliothèques d'exécution de langage qui font des copies de l'environnement (sur le tas) dans diverses circonstances. En général, ne considérez pas cela comme un aussi bon mécanisme pour transmettre des "secrets" à un programme que (par exemple) le fait d'hériter d'un descripteur de fichier ouvert à la fin de lecture d'un canal sans nom, lu dans un tampon d'entrée entièrement sous votre contrôle que vous essuyez ensuite.
/proc/$pid/stat
( en plus d' autres valeurs que vous pourriez avoir besoin struct prctl_mm_map
). Voir aussi mon exemple filter_env.c pour une petite démo. JdeBP, pouvez-vous ajouter des liens vers vos setprocargv()
/ setprocenvv()
fonctions?