Wie schützt curl ein Passwort vor dem Erscheinen in der ps-Ausgabe?

68

Vor einiger Zeit ist mir aufgefallen, dass Benutzernamen und Kennwörter, die curlals Befehlszeilenargumente angegeben wurden, nicht in der psAusgabe erscheinen (obwohl sie natürlich in Ihrem Bash-Verlauf erscheinen können).

Sie erscheinen ebenfalls nicht in /proc/PID/cmdline.

(Die Länge des kombinierten Benutzernamen / Passwort-Arguments kann jedoch abgeleitet werden.)

Demonstration unten:

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

Wie wird dieser Effekt erzielt? Liegt es irgendwo im Quellcode von curl? (Ich nehme an, es ist eine curlFunktion, keine psFunktion? Oder ist es eine Art Kernel-Funktion?)


Außerdem: Kann dies von außerhalb des Quellcodes einer ausführbaren Binärdatei erreicht werden? ZB durch die Verwendung von Shell-Befehlen, wahrscheinlich kombiniert mit Root-Berechtigungen?

Mit anderen Worten, könnte ich ein Argument , das ich an einen beliebigen Shell-Befehl weitergegeben habe, irgendwie davor verbergen, in /procoder in der psAusgabe zu erscheinen (ich glaube dasselbe) ? (Ich denke, die Antwort auf diese Frage ist "Nein", aber es scheint sich zu lohnen, diese zusätzliche halbe Frage einzuschließen.)

Platzhalter
quelle
16
Keine Antwort, aber beachten Sie, dass dieser Ansatz nicht sicher ist . Zwischen dem Start des Programms und dem Löschen der Argumente besteht ein Wettlaufsfenster, in dem jeder Benutzer das Kennwort lesen kann. Akzeptieren Sie keine sensiblen Passwörter in der Befehlszeile.
R ..
1
Locker verwandt: Wem gehören Umgebungsvariablen? und Verwendet jemand in der Praxis environdirekt, um auf Umgebungsvariablen zuzugreifen? - Fazit: Die Argumentliste befindet sich wie die Liste der Umgebungsvariablen im Lese- / Schreibspeicher des Benutzerprozesses und kann vom Benutzerprozess geändert werden.
Scott
1
@ JPhi1618, mache einfach das erste Zeichen deines grepMusters zu einer Zeichenklasse. ZBps -ef | grep '[c]url'
Wildcard
1
@mpy, das ist nicht sehr kompliziert. Einige reguläre Ausdrücke passen zu sich selbst, andere nicht. curlpasst curlaber [c]urlnicht übereinstimmt [c]url. Wenn Sie weitere Informationen benötigen, stellen Sie eine neue Frage und ich würde gerne antworten.
Wildcard

Antworten:

78

Wenn der Kernel einen Prozess ausführt, kopiert er die Befehlszeilenargumente in den zum Prozess gehörenden Lese- / Schreibspeicher (auf dem Stack, zumindest unter Linux). Der Prozess kann wie jeder andere Speicher in diesen Speicher schreiben. Wenn psdas Argument angezeigt wird, wird alles zurückgelesen, was an dieser bestimmten Adresse im Speicher des Prozesses gespeichert ist. Die meisten Programme behalten die ursprünglichen Argumente bei, aber es ist möglich, sie zu ändern. Die POSIX-Beschreibung vonps gibt an, dass

Es ist nicht festgelegt, ob die dargestellte Zeichenfolge eine Version der Argumentliste ist, wie sie beim Start an den Befehl übergeben wurde, oder eine Version der Argumente, wie sie möglicherweise von der Anwendung geändert wurden. Anwendungen können nicht davon abhängen, dass sie ihre Argumentliste ändern können und dass sich diese Änderung in der Ausgabe von ps widerspiegelt.

Der Grund dafür ist, dass die meisten Unix-Varianten die Änderung widerspiegeln, POSIX-Implementierungen auf anderen Betriebssystemen jedoch möglicherweise nicht.

Diese Funktion ist von begrenztem Nutzen, da der Prozess keine willkürlichen Änderungen vornehmen kann. Zumindest kann die Gesamtlänge der Argumente nicht erhöht werden, da das Programm die Position, an der die Argumente abgerufen werden, nicht ändern psund den Bereich nicht über seine ursprüngliche Größe hinaus erweitern kann. Die Länge kann effektiv durch Setzen von Null-Bytes am Ende verringert werden, da Argumente C-terminierte Zeichenfolgen sind (dies ist nicht zu unterscheiden, wenn am Ende eine Reihe leerer Argumente stehen).

Wenn Sie wirklich graben möchten, können Sie sich die Quelle einer Open-Source-Implementierung ansehen. Unter Linux ist die Quelle von psnicht interessant. Sie sehen lediglich , dass sie die Befehlszeilenargumente aus dem proc-Dateisystem in liest . Der Code, der den Inhalt dieser Datei generiert, befindet sich im Kernel in in . Der Teil des Speichers des Prozesses (auf den mit zugegriffen wird ) geht von der Adresse zu ; Diese Adressen werden beim Start des Prozesses im Kernel aufgezeichnet und können danach nicht mehr geändert werden./proc/PID/cmdlineproc_pid_cmdline_readfs/proc/base.caccess_remote_vmmm->arg_startmm->arg_end

Einige Daemons verwenden diese Fähigkeit, um ihren Status widerzuspiegeln, z. B. ändern sie ihren Status argv[1]in einen String wie startingoder availableoder exiting. Viele Unix-Varianten haben dazu eine setproctitleFunktion. Einige Programme verwenden diese Funktion, um vertrauliche Daten zu verbergen. Beachten Sie, dass dies von begrenztem Nutzen ist, da die Befehlszeilenargumente sichtbar sind, während der Prozess gestartet wird.

Die meisten höheren Sprachen kopieren die Argumente in Zeichenfolgenobjekte und bieten keine Möglichkeit, den ursprünglichen Speicher zu ändern. Hier ist ein C-Programm, das diese Fähigkeit demonstriert, indem argvElemente direkt geändert werden.

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

Beispielausgabe:

./a.out hello world
0000000 11111 22222

Sie können argvÄnderungen im Curl-Quellcode sehen. Curl definiert eine Funktion, cleanargmitsrc/tool_paramhlp.c der ein Argument in alle Leerzeichen geändert wird memset. In src/tool_getparam.cdieser Funktion wird durch Schwärzen der einige Male, zum Beispiel verwendet , Benutzer - Passwort . Da die Funktion über das Parameter-Parsing aufgerufen wird, geschieht dies zu Beginn eines Curl-Aufrufs. Wenn Sie jedoch die Befehlszeile davor sichern, werden weiterhin alle Kennwörter angezeigt.

Da die Argumente im eigenen Speicher des Prozesses gespeichert sind, können sie nur mit einem Debugger von außen geändert werden.

Gilles
quelle
Toll! In Bezug auf dieses Spezifikations-Snippet verstehe ich, dass es POSIX-kompatibel wäre, wenn Ihr Kernel die ursprünglichen Befehlszeilenargumente eines Prozesses außerhalb des Lese- / Schreibspeichers des Prozesses speichert ( zusätzlich zu der Kopie im Lese- / Schreibspeicher). ? Haben Sie dann psBerichtsargumente aus diesem Teil des Kernelspeichers und ignorieren Sie alle Änderungen, die im Lese- / Schreibspeicher von Prozessen vorgenommen wurden? Aber (wenn ich es richtig verstanden habe?) Machen die meisten UNIX-Varianten nicht einmal die ersteren, so dass Sie eine psImplementierung nicht ohne Kernel-Modifikationen machen können, da die ursprünglichen Daten nirgendwo aufbewahrt werden?
Wildcard
1
@ Wildcard Richtig. Es mag Unix-Implementierungen geben, die das Original beibehalten, aber ich glaube, keine der gängigen. Mit der Sprache C kann der Inhalt von argvEinträgen geändert werden (Sie können ihn nicht festlegen argv[i], aber Sie können ihn argv[i][0]durchschreiben argv[i][strlen(argv[i])]), sodass sich eine Kopie im Speicher des Prozesses befinden muss.
Gilles
2
Relevante Funktion im Curl-Quellcode: github.com/curl/curl/blob/master/src/tool_paramhlp.c#L139
Uhr
4
@Wildcard, Solaris macht das. Die Befehlszeile von / usr / ucb / ps ist die prozesseigene (veränderbare) Kopie. Die Befehlszeile von / usr / bin / ps ist die (unveränderliche) Kopie des Kernels. Der Kernel behält jedoch nur die ersten 80 Zeichen. Alles andere ist abgeschnitten.
BowlOfRed
1
@Wildcard In der Tat sind die nachfolgenden Nullen leere Argumente. In der psAusgabe sehen viele leere Argumente so aus, als gäbe es nichts, aber ja, es macht einen Unterschied, ob Sie überprüfen, wie viele Leerzeichen vorhanden sind, und von wo aus Sie direkter beobachten können /proc/PID/cmdline.
Gilles
14

Die anderen Antworten beantworten die Frage allgemein. Um konkret zu antworten: " Wie wird dieser Effekt erzielt? Befindet er sich irgendwo im Quellcode von curl? ":

Im Argument-Parsing-Abschnitt des Curl-Quellcodes wird die -uOption wie folgt behandelt:

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

Und die cleanarg()Funktion ist wie folgt definiert :

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
}

Wir können also explizit sehen, dass das Argument Benutzername: Kennwort in argvmit Leerzeichen überschrieben wird, wie in den anderen Antworten beschrieben.

Digitales Trauma
quelle
Ich mag es, dass der Kommentar in cleanargexplizit angibt, dass es das tut, was die Frage stellt!
Floris
3

Ein Prozess kann seine Parameter nicht nur lesen, sondern auch schreiben.

Ich bin kein Entwickler, daher bin ich mit diesen Dingen nicht vertraut, aber es kann von außen mit einem Ansatz möglich sein, der dem Ändern von Umgebungsparametern ähnelt:

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

Hauke ​​Laging
quelle
Okay, aber das Ausführen von z. B. bash -c 'awk 1 /proc/$$/cmdline; set -- something; awk 1 /proc/$$/cmdline'zeigt, dass sich das Festlegen der Parameter zumindest in der Shell davon unterscheidet, das zu ändern, was der Kernel als Prozessparameter sieht.
Wildcard
4
@Wildcard Positional Argumente in einem Shell - Skript sind zunächst Kopien von einigen der Befehlszeilenargumente der Shell - Prozess. Bei den meisten Shells kann das Skript die ursprünglichen Argumente nicht ändern.
Gilles
@ Gilles, ja, das war der Punkt meines Kommentars. :) Dass die allgemeine Aussage, dass ein Prozess das kann (erster Satz dieser Antwort), nicht beantwortet, ob dies durch vorhandene Shell-Features erreicht werden kann. Die Antwort darauf scheint "nein" zu sein, was ich am Ende meiner Frage vermutet habe.
Wildcard