web-dev-qa-db-fra.com

Comment curl protège-t-il un mot de passe d'apparaître dans la sortie ps?

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.)

71
Wildcard

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.

14
Digital Trauma

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:

https://stackoverflow.com/questions/205064/is-there-a-way-to-change-another-processs-environment-variables

3
Hauke Laging