J'ai remarqué il y a quelque temps que les noms d'utilisateur et les mots de passe donnés à curl
comme arguments de ligne de commande n'apparaissent pas dans la sortie ps
(bien qu'ils puissent bien sûr apparaître dans votre historique bash).
Ils n'apparaissent pas non plus dans /proc/PID/cmdline
.
(La longueur de l'argument combiné nom d'utilisateur/mot de passe peut être dérivée, cependant.)
Démonstration ci-dessous:
[root@localhost ~]# nc -l 80 &
[1] 3342
[root@localhost ~]# curl -u iamsam:samiam localhost &
[2] 3343
[root@localhost ~]# GET / HTTP/1.1
Authorization: Basic aWFtc2FtOnNhbWlhbQ==
User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.15.3 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
Host: localhost
Accept: */*
[1]+ Stopped nc -l 80
[root@localhost ~]# jobs
[1]+ Stopped nc -l 80
[2]- Running curl -u iamsam:samiam localhost &
[root@localhost ~]# ps -ef | grep curl
root 3343 3258 0 22:37 pts/1 00:00:00 curl -u localhost
root 3347 3258 0 22:38 pts/1 00:00:00 grep curl
[root@localhost ~]# od -xa /proc/3343/cmdline
0000000 7563 6c72 2d00 0075 2020 2020 2020 2020
c u r l nul - u nul sp sp sp sp sp sp sp sp
0000020 2020 2020 0020 6f6c 6163 686c 736f 0074
sp sp sp sp sp nul l o c a l h o s t nul
0000040
[root@localhost ~]#
Comment cet effet est-il obtenu? Est-ce quelque part dans le code source de curl
? (Je suppose que c'est une fonctionnalité curl
, pas une fonctionnalité ps
? Ou est-ce une fonctionnalité du noyau d'une certaine sorte?)
Aussi: cela peut-il être réalisé depuis l'extérieur du code source d'un exécutable binaire? Par exemple. en utilisant des commandes Shell, probablement combinées avec des autorisations root?
En d'autres termes, pourrais-je en quelque sorte masquer un argument d'apparaître dans /proc
ou dans ps
sortie (même chose, je pense) que j'ai passée à une commande Shell arbitraire ? (Je suppose que la réponse à cette question est "non", mais il semble utile d'inclure cette demi-question supplémentaire.)
Lorsque le noyau exécute un processus, il copie les arguments de la ligne de commande dans la mémoire en lecture-écriture appartenant au processus (sur la pile, au moins sous Linux). Le processus peut écrire dans cette mémoire comme n'importe quelle autre mémoire. Lorsque ps
affiche l'argument, il lit tout ce qui est stocké à cette adresse particulière dans la mémoire du processus. La plupart des programmes conservent les arguments d'origine, mais il est possible de les modifier. la description POSIX de ps
indique que
Il n'est pas spécifié si la chaîne représentée est une version de la liste d'arguments telle qu'elle a été transmise à la commande lors de son démarrage, ou est une version des arguments tels qu'ils peuvent avoir été modifiés par l'application. Les applications ne peuvent pas dépendre de la possibilité de modifier leur liste d'arguments et que cette modification soit reflétée dans la sortie de ps.
La raison pour laquelle cela est mentionné est que la plupart des variantes Unix reflètent le changement, mais les implémentations POSIX sur d'autres types de systèmes d'exploitation peuvent ne pas.
Cette fonctionnalité est d'une utilité limitée car le processus ne peut pas apporter de modifications arbitraires. À tout le moins, la longueur totale des arguments ne peut pas être augmentée, car le programme ne peut pas modifier l'emplacement où ps
récupérera les arguments et ne pourra pas étendre la zone au-delà de sa taille d'origine. La longueur peut effectivement être diminuée en plaçant des octets nuls à la fin, car les arguments sont des chaînes terminées par le style C (cela ne se distingue pas d'avoir un tas d'arguments vides à la fin).
Si vous voulez vraiment creuser, vous pouvez regarder la source d'une implémentation open-source. Sous Linux, la source de ps
n'est pas intéressante, tout ce que vous verrez, c'est qu'elle lit les arguments de la ligne de commande du système de fichiers proc , dans /proc/PID/cmdline
. Le code qui génère le contenu de ce fichier est dans le noyau, en proc_pid_cmdline_read
dans fs/proc/base.c
. La partie de la mémoire du processus (accessible avec access_remote_vm
) va de l'adresse mm->arg_start
à mm->arg_end
; ces adresses sont enregistrées dans le noyau au démarrage du processus et ne peuvent plus être modifiées par la suite.
Certains démons utilisent cette capacité pour refléter leur statut, par exemple ils changent leur argv[1]
à une chaîne comme starting
ou available
ou exiting
. De nombreuses variantes Unix ont une fonction setproctitle
pour ce faire. Certains programmes utilisent cette capacité pour masquer des données confidentielles. Notez que cela est d'une utilité limitée car les arguments de la ligne de commande sont visibles lorsque le processus démarre.
La plupart des langages de haut niveau copient les arguments dans des objets chaîne et ne permettent pas de modifier le stockage d'origine. Voici un programme C qui démontre cette capacité en modifiant directement les éléments argv
.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
int i;
system("ps -p $PPID -o args=");
for (i = 0; i < argc; i++)
{
memset(argv[i], '0' + (i % 10), strlen(argv[i]));
}
system("ps -p $PPID -o args=");
return 0;
}
Exemple de sortie:
./a.out hello world
0000000 11111 22222
Vous pouvez voir argv
modification dans le code source de curl. Curl définit une fonction cleanarg
dans src/tool_paramhlp.c
qui est utilisé pour changer un argument à tous les espaces en utilisant memset
. Dans src/tool_getparam.c
cette fonction est utilisée plusieurs fois, par ex. en supprimant le mot de passe utilisateur . Étant donné que la fonction est appelée à partir de l'analyse des paramètres, elle se produit au début d'une invocation de boucles, mais le vidage de la ligne de commande avant que cela ne se produise affichera toujours les mots de passe.
Étant donné que les arguments sont stockés dans la propre mémoire du processus, ils ne peuvent pas être modifiés de l'extérieur, sauf à l'aide d'un débogueur.
Les autres réponses répondent bien à la question de manière générale. Pour répondre spécifiquement " Comment cet effet est-il obtenu? Est-ce quelque part dans le code source de curl ? ":
Dans la section analyse des arguments du code source curl , l'option -u
Est gérée comme suit:
case 'u':
/* user:password */
GetStr(&config->userpwd, nextarg);
cleanarg(nextarg);
break;
Et la fonction cleanarg()
est définie comme suit:
void cleanarg(char *str)
{
#ifdef HAVE_WRITABLE_ARGV
/* now that GetStr has copied the contents of nextarg, wipe the next
* argument out so that the username:password isn't displayed in the
* system process list */
if(str) {
size_t len = strlen(str);
memset(str, ' ', len);
}
#else
(void)str;
#endif
}
Ainsi, nous pouvons voir explicitement que l'argument nom d'utilisateur: mot de passe dans argv
est remplacé par des espaces, comme décrit par les autres réponses.
Un processus peut non seulement lire ses paramètres mais aussi les écrire.
Je ne suis pas développeur donc je ne connais pas ce truc mais ça peut être possible de l'extérieur avec une approche similaire au changement de paramètres d'environnement: