In che modo curl protegge una password dalla visualizzazione nell'output ps?


68

Ho notato qualche tempo fa che nomi utente e password dati curlcome argomenti della riga di comando non compaiono psnell'output (anche se ovviamente possono apparire nella tua cronologia di bash).

Allo stesso modo non compaiono /proc/PID/cmdline.

(Tuttavia, la lunghezza dell'argomento combinato nome utente / password può essere derivata).

Dimostrazione di seguito:

[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 ~]# 

Come si ottiene questo effetto? È da qualche parte nel codice sorgente di curl? (Suppongo sia una curlfunzionalità, non una psfunzionalità? O è una funzionalità del kernel di qualche tipo?)


Inoltre: questo può essere ottenuto dall'esterno del codice sorgente di un eseguibile binario? Ad esempio usando i comandi della shell, probabilmente combinati con i permessi di root?

In altre parole, potrei in qualche modo mascherare un argomento dall'apparire /proco psdall'output (stessa cosa, penso) che ho passato a qualche comando di shell arbitrario ? (Immagino che la risposta sia "no", ma sembra che valga la pena includere questa mezza domanda extra.)



16
Non una risposta, ma nota che questo approccio non è sicuro . C'è una finestra di gara tra l'avvio del programma e la cancellazione delle stringhe di argomenti durante le quali qualsiasi utente può leggere la password. Non accettare password sensibili nella riga di comando.
R ..

1
Liberamente correlato: a chi appartengono le variabili d'ambiente? e qualcuno in pratica usa environdirettamente per accedere alle variabili d'ambiente? - la linea di fondo: l'elenco degli argomenti, come l'elenco delle variabili di ambiente, si trova nella memoria del processo utente di lettura / scrittura e può essere modificato dal processo utente.
Scott,

1
@ JPhi1618, trasforma il primo personaggio del tuo grepmodello in una classe di caratteri. Ad esempiops -ef | grep '[c]url'
Wildcard l'

1
@mpy, non è molto complicato. Alcune regex corrispondono a se stesse e altre no. curlcorrisponde curlma [c]urlnon corrisponde [c]url. Se hai bisogno di maggiori dettagli, fai una nuova domanda e sarei felice di risponderti.
Wildcard il

Risposte:


78

Quando il kernel esegue un processo, copia gli argomenti della riga di comando nella memoria di lettura-scrittura appartenente al processo (nello stack, almeno su Linux). Il processo può scrivere su quella memoria come qualsiasi altra memoria. Quando psvisualizza l'argomento, legge tutto ciò che è memorizzato a quel particolare indirizzo nella memoria del processo. La maggior parte dei programmi mantiene gli argomenti originali, ma è possibile modificarli. La descrizione POSIXps afferma che

Non è specificato se la stringa rappresentata sia una versione dell'elenco degli argomenti in quanto è stata passata al comando all'avvio o se è una versione degli argomenti in quanto potrebbero essere stati modificati dall'applicazione. Le applicazioni non possono dipendere dalla possibilità di modificare il proprio elenco di argomenti e di far sì che tale modifica si rifletta nell'output di ps.

La ragione per cui viene menzionata è che la maggior parte delle varianti unix riflette il cambiamento, ma le implementazioni POSIX su altri tipi di sistemi operativi potrebbero non esserlo.

Questa funzione è di utilità limitata poiché il processo non può apportare modifiche arbitrarie. Per lo meno, la lunghezza totale degli argomenti non può essere aumentata, perché il programma non può cambiare la posizione in cui psrecuperare gli argomenti e non può estendere l'area oltre la sua dimensione originale. La lunghezza può essere effettivamente ridotta inserendo byte null alla fine, perché gli argomenti sono stringhe con terminazione null in stile C (ciò non è distinguibile dall'avere un mucchio di argomenti vuoti alla fine).

Se vuoi davvero scavare, puoi guardare l'origine di un'implementazione open source. Su Linux, la fonte di psnon è interessante, tutto ciò che vedrai è che legge gli argomenti della riga di comando dal filesystem proc , in . Il codice che genera il contenuto di questo file è nel kernel, in in . La parte della memoria del processo (accessibile con ) passa dall'indirizzo a ; questi indirizzi sono registrati nel kernel all'avvio del processo e non possono essere modificati in seguito./proc/PID/cmdlineproc_pid_cmdline_readfs/proc/base.caccess_remote_vmmm->arg_startmm->arg_end

Alcuni demoni usano questa capacità per riflettere il loro stato, ad esempio cambiano il loro argv[1]in una stringa come startingo availableo exiting. Molte varianti di unix hanno una setproctitlefunzione per fare questo. Alcuni programmi usano questa capacità per nascondere dati riservati. Si noti che ciò è di utilità limitata poiché gli argomenti della riga di comando sono visibili all'avvio del processo.

La maggior parte dei linguaggi di alto livello copia gli argomenti negli oggetti stringa e non consente di modificare la memoria originale. Ecco un programma C che dimostra questa capacità cambiando argvdirettamente gli elementi.

#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;
}

Uscita campione:

./a.out hello world
0000000 11111 22222

Puoi vedere le argvmodifiche nel codice sorgente del ricciolo. Curl definisce una funzione cleanarginsrc/tool_paramhlp.c cui viene utilizzato per modificare un argomento in tutti gli spazi usando memset. In src/tool_getparam.cquesta funzione viene utilizzata più volte, ad es. Ridimensionando la password dell'utente . Poiché la funzione viene chiamata dall'analisi dei parametri, si verifica all'inizio di una chiamata di arricciatura, ma il dumping della riga di comando prima che ciò accada mostrerà comunque eventuali password.

Poiché gli argomenti sono archiviati nella memoria del processo, non possono essere modificati dall'esterno se non utilizzando un debugger.


Grande! Quindi, per quanto riguarda lo snippet di specifiche, capisco che significherebbe che sarebbe conforme a POSIX rendere il kernel archiviare gli argomenti della riga di comando originali del processo al di fuori della memoria di lettura-scrittura del processo ( oltre alla copia nella memoria di lettura-scrittura) ? E poi psriportare argomenti da quella parte della memoria del kernel, ignorando le modifiche apportate alla memoria di lettura-scrittura dei processi? Ma (se ho capito bene?) La maggior parte delle varianti di UNIX non fanno nemmeno la prima, quindi non puoi fare psun'implementazione facendo la seconda senza modifiche del kernel, dato che i dati originali non sono conservati da nessuna parte?
Wildcard l'

1
@Wildcard Correct. Potrebbero esserci implementazioni Unix che mantengono l'originale, ma non credo che nessuna di quelle comuni lo faccia. Il linguaggio C consente di modificare il contenuto delle argvvoci (non è possibile impostarle argv[i], ma è possibile scriverle argv[i][0]attraverso argv[i][strlen(argv[i])]), quindi deve esserci una copia nella memoria del processo.
Gilles 'SO- smetti di essere malvagio' l'

2
Funzione rilevante nel codice sorgente di arricciatura: github.com/curl/curl/blob/master/src/tool_paramhlp.c#L139
sebasth

4
@Wildcard, Solaris lo fa. La riga di comando visualizzata da / usr / ucb / ps è la copia (mutabile) di proprietà del processo. La riga di comando vista da / usr / bin / ps è la copia (immutabile) di proprietà del kernel. Il kernel mantiene solo i primi 80 caratteri. Qualcos'altro viene troncato.
BowlOfRed

1
@Wildcard In effetti i null finali sono argomenti vuoti. In psuscita, un sacco di argomenti vuoti sembra che non c'è niente, ma sì, fa la differenza se si controlla quanti spazi ci sono, e si può osservare più direttamente da /proc/PID/cmdline.
Gilles 'SO- smetti di essere malvagio' l'

14

Le altre risposte rispondono bene alla domanda in modo generale. Per rispondere in modo specifico " Come si ottiene questo effetto? È da qualche parte nel codice sorgente del ricciolo? ":

Nella sezione di analisi degli argomenti del codice sorgente di arricciatura , l' -uopzione viene gestita come segue:

    case 'u':
      /* user:password  */
      GetStr(&config->userpwd, nextarg);
      cleanarg(nextarg);
      break;

E la cleanarg()funzione è definita come segue:

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
}

Quindi possiamo vedere esplicitamente che l'argomento username: password in argvviene sovrascritto con spazi, come descritto dalle altre risposte.


Mi piace il fatto che il commento cleanargaffermi esplicitamente che sta facendo ciò che la domanda sta ponendo!
Floris,

3

Un processo può non solo leggere i suoi parametri ma anche scriverli.

Non sono uno sviluppatore quindi non ho familiarità con queste cose, ma potrebbe essere possibile dall'esterno con un approccio simile alla modifica dei parametri ambientali:

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


Va bene, ma eseguire ad esempio bash -c 'awk 1 /proc/$$/cmdline; set -- something; awk 1 /proc/$$/cmdline'mostra che almeno nella shell, l'impostazione dei parametri è distinta dalla modifica di ciò che il kernel vede come parametri di processo.
Carattere jolly

4
@Wildcard Gli argomenti posizionali in uno script di shell sono inizialmente copie di alcuni degli argomenti della riga di comando del processo shell. La maggior parte delle shell non consente allo script di modificare gli argomenti originali.
Gilles 'SO- smetti di essere malvagio' il

@Gilles, sì, quello era il punto del mio commento. :) Che l'affermazione generale secondo cui un processo può farlo (prima frase di questa risposta) non risponde se ciò può essere ottenuto dalle funzionalità di shell esistenti. La risposta a questa sembra essere "no", che è quello che ho indovinato in fondo alla mia domanda.
Wildcard l'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.