W jaki sposób curl chroni hasło przed pojawieniem się na wyjściu ps?

68

Jakiś czas temu zauważyłem, że nazwy użytkowników i hasła podane curljako argumenty wiersza poleceń nie pojawiają się w psdanych wyjściowych (chociaż oczywiście mogą pojawiać się w twojej historii bashów).

Podobnie nie pojawiają się w /proc/PID/cmdline.

(Można jednak określić długość połączonego argumentu nazwa użytkownika / hasło.)

Pokaz poniżej:

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

Jak osiąga się ten efekt? Czy jest to gdzieś w kodzie źródłowym curl? (Zakładam, że jest to curlfunkcja, a nie psfunkcja? A może to jakaś funkcja jądra?)


Ponadto: czy można to osiągnąć spoza kodu źródłowego binarnego pliku wykonywalnego? Np. Używając poleceń powłoki, prawdopodobnie w połączeniu z uprawnieniami roota?

Innymi słowy, czy mógłbym w jakiś sposób maskować argument pojawiający się w danych wyjściowych /proclub pswyjściowych (myślę, że to samo), które przekazałem do dowolnego polecenia powłoki? (Sądzę, że odpowiedź na to pytanie brzmi „nie”, ale wydaje się, że warto uwzględnić to dodatkowe pół pytania).

Dzika karta
źródło
16
Nie jest to odpowiedź, ale należy pamiętać, że takie podejście nie jest bezpieczne . Między uruchomieniem programu a usunięciem argumentów jest okno wyścigu, podczas którego każdy użytkownik może odczytać hasło. Nie akceptuj wrażliwych haseł w wierszu poleceń.
R ..
1
Luźno powiązane: Do kogo należą zmienne środowiskowe? oraz Czy ktoś w praktyce korzysta environbezpośrednio z dostępu do zmiennych środowiskowych? - dolny wiersz: lista argumentów, podobnie jak lista zmiennych środowiskowych, znajduje się w pamięci procesu odczytu / zapisu użytkownika i może być modyfikowana przez proces użytkownika.
Scott,
1
@ JPhi1618, po prostu uczyń pierwszą postać grepwzorca klasą postaci. Np.ps -ef | grep '[c]url'
Wildcard,
1
@ mpy, t nie jest bardzo skomplikowane. Niektóre wyrażenia regularne pasują do siebie, a niektóre nie. curlpasuje, curlale [c]urlnie pasuje [c]url. Jeśli potrzebujesz więcej szczegółów, zadaj nowe pytanie, a chętnie odpowiem.
Wildcard,

Odpowiedzi:

78

Kiedy jądro wykonuje proces, kopiuje argumenty wiersza poleceń do pamięci odczytu i zapisu należącej do procesu (na stosie, przynajmniej w Linuksie). Proces może zapisywać do tej pamięci, jak każda inna pamięć. Kiedy pswyświetla argument, odczytuje z powrotem, co jest zapisane w tym konkretnym adresem w pamięci procesu. Większość programów zachowuje oryginalne argumenty, ale można je zmienić. Opis POSIX zps państw, które

Nie jest określone, czy reprezentowany ciąg jest wersją listy argumentów, która została przekazana do komendy podczas jej uruchamiania, czy też jest wersją argumentów, ponieważ mogły one zostać zmodyfikowane przez aplikację. Aplikacje nie mogą polegać na tym, że mogą modyfikować listę argumentów i mieć odzwierciedlenie tej modyfikacji w wynikach ps.

Powodem tego jest fakt, że większość wariantów Uniksa odzwierciedla zmianę, ale implementacje POSIX na innych typach systemów operacyjnych mogą tego nie robić.

Ta funkcja ma ograniczone zastosowanie, ponieważ proces nie może dokonywać dowolnych zmian. Przynajmniej całkowitej długości argumentów nie można zwiększyć, ponieważ program nie może zmienić lokalizacji, w której psbędą pobierane argumenty i nie może rozszerzyć obszaru poza swój pierwotny rozmiar. Długość można skutecznie zmniejszyć, umieszczając na końcu puste bajty, ponieważ argumenty są łańcuchami zakończonymi zerami w stylu C (jest to nie do odróżnienia od wielu pustych argumentów na końcu).

Jeśli naprawdę chcesz kopać, możesz spojrzeć na źródło implementacji typu open source. W Linuksie źródło psnie jest interesujące, wszystko, co tam zobaczysz, to to, że czyta argumenty wiersza poleceń z systemu plików proc , w . Kod generujący zawartość tego pliku znajduje się w jądrze, w . Część pamięci procesu (dostępna za pomocą ) przechodzi z adresu do ; adresy te są zapisywane w jądrze podczas uruchamiania procesu i nie można ich później zmienić./proc/PID/cmdlineproc_pid_cmdline_readfs/proc/base.caccess_remote_vmmm->arg_startmm->arg_end

Niektóre demony używają tej zdolności do odzwierciedlenia swojego statusu, np. Zmieniają argv[1]ciąg na taki jak startinglub availablelub exiting. Wiele wariantów uniksowych ma setproctitledo tego funkcję. Niektóre programy wykorzystują tę zdolność do ukrywania poufnych danych. Zauważ, że ma to ograniczone zastosowanie, ponieważ argumenty wiersza poleceń są widoczne podczas uruchamiania procesu.

Większość języków wysokiego poziomu kopiuje argumenty do obiektów łańcuchowych i nie daje możliwości modyfikacji oryginalnej pamięci. Oto program w C, który demonstruje tę zdolność poprzez argvbezpośrednią zmianę elementów.

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

Przykładowe dane wyjściowe:

./a.out hello world
0000000 11111 22222

Możesz zobaczyć argvmodyfikację w kodzie źródłowym curl. Curl definiuje funkcję, cleanargwsrc/tool_paramhlp.c której służy do zmiany argumentu na wszystkie spacje za pomocą memset. W src/tool_getparam.ctej funkcji korzysta się kilka razy, np. Poprzez redagowanie hasła użytkownika . Ponieważ funkcja jest wywoływana z parsowania parametrów, dzieje się to na początku wywołania curl, ale zrzucenie wiersza poleceń, zanim to nastąpi, nadal spowoduje wyświetlenie haseł.

Ponieważ argumenty są przechowywane we własnej pamięci procesu, nie można ich zmienić z zewnątrz, chyba że za pomocą debugera.

Gilles
źródło
Wspaniały! Tak więc, jeśli chodzi o ten fragment specyfikacji, rozumiem, że oznacza to, że byłoby zgodne z POSIX, aby twoje jądro zapisywało oryginalne argumenty wiersza poleceń poza pamięcią odczytu / zapisu procesu ( oprócz kopiowania w pamięci odczytu-zapisu) ? A potem mają psargumentów Raport z tego kawałka pamięci jądra, ignorując wszelkie zmiany dokonane w procesach odczytu i zapisu pamięci? Ale (jeśli mam rację?) Większość odmian systemu UNIX nawet nie robi tego pierwszego, więc nie można zrobić psimplementacji tego drugiego bez modyfikacji jądra, ponieważ oryginalne dane nigdzie nie są przechowywane?
Wildcard,
1
@Wildcard Correct. Mogą istnieć implementacje Uniksa, które zachowują oryginalność, ale nie sądzę, żeby którekolwiek z nich były powszechne. Język C pozwala treść argvwpisów zostać zmieniony (nie można ustawić argv[i], ale można napisać argv[i][0]? Poprzez argv[i][strlen(argv[i])]), więc nie musi być kopią w pamięci procesu.
Gilles
2
Odpowiednia funkcja w kodzie źródłowym curl: github.com/curl/curl/blob/master/src/tool_paramhlp.c#L139
sebasth
4
@Wildcard, Solaris to robi. Linia poleceń widoczna przez / usr / ucb / ps jest kopią będącą własnością procesu (zmienną). Linia poleceń widoczna przez / usr / bin / ps jest kopią będącą własnością jądra (niezmienną). Jądro zachowuje tylko pierwsze 80 znaków. Wszystko inne jest obcinane.
BowlOfRed
1
@Wildcard Rzeczywiście końcowe wartości null są pustymi argumentami. Na pswyjściu wiele pustych argumentów wygląda na to, że nic tam nie ma, ale tak, robi to różnicę, jeśli sprawdzisz, ile jest spacji i możesz obserwować bardziej bezpośrednio z /proc/PID/cmdline.
Gilles,
14

Inne odpowiedzi dobrze odpowiadają na pytanie w sposób ogólny. Aby dokładnie odpowiedzieć „ Jak osiągnąć ten efekt? Czy jest to gdzieś w kodzie źródłowym curl? ”:

W sekcji przetwarzania argumentów z kodu źródłowego curl The -uopcja jest obsługiwana w sposób następujący:

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

A cleanarg()funkcja jest zdefiniowana w następujący sposób:

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
}

Możemy więc wyraźnie zobaczyć, że argument nazwa użytkownika: hasło w argvjest zastąpiony spacjami, jak opisano w innych odpowiedziach.

Cyfrowa trauma
źródło
Podoba mi się to, że komentarz cleanargwyraźnie stwierdza, że ​​robi to, o co pyta pytanie!
Floris,
3

Proces może nie tylko odczytać jego parametry, ale także je zapisać.

Nie jestem programistą, więc nie znam tych rzeczy, ale może być możliwe z zewnątrz z podejściem podobnym do zmiany parametrów środowiska:

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

Hauke ​​Laging
źródło
OK, ale uruchomienie np. bash -c 'awk 1 /proc/$$/cmdline; set -- something; awk 1 /proc/$$/cmdline'Pokazuje, że przynajmniej w powłoce ustawienie parametrów różni się od modyfikacji tego, co jądro postrzega jako parametry procesu.
Wildcard,
4
@Wildcard pozycyjne argumenty skryptu powłoki są początkowo kopie z niektórych argumentów wiersza poleceń procesu powłoki. Większość powłok nie pozwala skryptowi zmieniać oryginalnych argumentów.
Gilles,
@Gilles, tak, to był punkt mojego komentarza. :) Ogólne stwierdzenie, że proces może to zrobić (pierwsze zdanie tej odpowiedzi) nie odpowiada, czy można to osiągnąć za pomocą istniejących funkcji powłoki. Odpowiedź na to pytanie brzmi „nie”, co domyśliłem się na samym dole mojego pytania.
Wildcard,