Как curl защищает пароль от появления в выводе ps?

68

Некоторое время назад я заметил, что имена пользователей и пароли, заданные в curlкачестве аргументов командной строки, не отображаются в psвыходных данных (хотя, конечно, они могут появляться в вашей истории bash).

Они также не появляются в /proc/PID/cmdline.

(Длина объединенного аргумента имя пользователя / пароль может быть получена, хотя.)

Демонстрация ниже:

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

Как достигается этот эффект? Это где-то в исходном коде curl? (Я предполагаю, что это curlособенность, а не psособенность? Или это какая-то особенность ядра?)


Кроме того: это может быть достигнуто извне исходного кода двоичного исполняемого файла? Например, с помощью команд оболочки, возможно, в сочетании с правами root?

Другими словами, могу ли я как-то замаскировать аргумент, появляющийся в /procили в psвыходных данных (я думаю, то же самое), которые я передал какой-то произвольной команде оболочки? (Я предполагаю, что ответом будет «нет», но, кажется, стоит включить эту лишнюю половину вопроса.)

Wildcard
источник
16
Не ответ, но учтите, что такой подход небезопасен . Между запуском программы и очисткой строк аргументов существует окно гонки, во время которого любой пользователь может прочитать пароль. Не принимайте конфиденциальные пароли в командной строке.
R ..
1
Слабо связанные: кому принадлежат переменные окружения? и кто-нибудь на практике использует environнепосредственно для доступа к переменным среды? - нижняя строка: список аргументов, как и список переменных среды, находится в памяти пользовательского процесса для чтения / записи и может быть изменен пользовательским процессом.
Скотт
1
@ JPhi1618, просто сделайте первый символ вашего grepшаблона классом символов. Напримерps -ef | grep '[c]url'
Wildcard
1
@ Мило, это не очень сложно. Некоторые регулярные выражения соответствуют друг другу, а некоторые нет. curlсоответствует, curlно [c]urlне соответствует [c]url. Если вам нужно больше подробностей, задайте новый вопрос, и я буду рад ответить.
Wildcard

Ответы:

78

Когда ядро ​​выполняет процесс, оно копирует аргументы командной строки для чтения-записи памяти, принадлежащей процессу (в стеке, по крайней мере, в Linux). Процесс может записывать в эту память как любую другую память. Когда psотображается аргумент, он читает все, что хранится по этому конкретному адресу в памяти процесса. Большинство программ сохраняют исходные аргументы, но их можно изменить. В описании POSIXps говорится, что

Не определено, является ли представленная строка версией списка аргументов, как она была передана команде при ее запуске, или является версией аргументов, поскольку они могли быть изменены приложением. Приложения не могут зависеть от возможности изменить свой список аргументов и от того, что это изменение будет отражено в выводе ps.

Причина, по которой это упоминается, заключается в том, что большинство вариантов Unix отражают это изменение, но реализации POSIX в других типах операционных систем могут этого не делать.

Эта функция имеет ограниченное использование, потому что процесс не может вносить произвольные изменения. По крайней мере, общая длина аргументов не может быть увеличена, потому что программа не может изменить место, где psбудут извлекать аргументы, и не может расширить область за пределы своего первоначального размера. Длина может быть эффективно уменьшена путем помещения нулевых байтов в конец, потому что аргументы - это строки с нулевым символом в конце в стиле C (это неразличимо от наличия множества пустых аргументов в конце).

Если вы действительно хотите копать, вы можете посмотреть на источник реализации с открытым исходным кодом. В Linux источник psне интересен, все, что вы увидите, это то, что он читает аргументы командной строки из файловой системы proc , в . Код, который генерирует содержимое этого файла, находится в ядре, в . Часть памяти процесса (доступная с помощью ) идет от адреса к ; эти адреса записываются в ядре при запуске процесса и не могут быть изменены впоследствии./proc/PID/cmdlineproc_pid_cmdline_readfs/proc/base.caccess_remote_vmmm->arg_startmm->arg_end

Некоторые демоны используют эту способность, чтобы отразить их статус, например, они меняют их argv[1]на строки, подобные startingили availableили exiting. Многие варианты Unix имеют setproctitleфункцию для этого. Некоторые программы используют эту возможность, чтобы скрыть конфиденциальные данные. Обратите внимание, что это ограниченное использование, поскольку аргументы командной строки видны при запуске процесса.

Большинство языков высокого уровня копируют аргументы в строковые объекты и не дают возможности изменить исходное хранилище. Вот программа на C, которая демонстрирует эту способность путем 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;
}

Образец вывода:

./a.out hello world
0000000 11111 22222

Вы можете увидеть argvмодификацию в исходном коде curl. Curl определяет функцию, cleanargвsrc/tool_paramhlp.c которой используется для изменения аргумента для всех пробелов с помощью memset. В src/tool_getparam.cэтой функции используется несколько раз, например, путем редактирования пароля пользователя . Поскольку функция вызывается из парсинга параметров, это происходит на раннем этапе вызова curl, но выгрузка командной строки до того, как это произойдет, покажет все пароли.

Поскольку аргументы хранятся в собственной памяти процесса, их нельзя изменить извне, кроме как с помощью отладчика.

Жиль "ТАК - перестань быть злым"
источник
Большой! Итак, что касается этого фрагмента спецификаций, я понимаю, что это означает, что было бы POSIX-совместимым, чтобы заставить ваше ядро ​​хранить исходные аргументы командной строки процесса вне памяти чтения-записи процесса (в дополнение к копии в памяти чтения-записи) ? И затем получить psаргументы отчета из этого куска памяти ядра, игнорируя любые изменения, внесенные в память чтения-записи процессов? Но (если я правильно понял?) Большинство вариантов UNIX даже не делают первое, так что вы не можете заставить psреализацию сделать второе без модификаций ядра, поскольку исходные данные нигде не хранятся?
Wildcard
1
@Wildcard Правильно. Могут быть реализации Unix, которые сохраняют оригинал, но я не думаю, что какие-либо из распространенных делают. Язык C позволяет argvизменять содержимое записей (вы не можете установить argv[i], но вы можете записать argv[i][0]через argv[i][strlen(argv[i])]), поэтому должна быть копия в памяти процесса.
Жиль "ТАК - перестань быть злым"
2
Соответствующая функция в исходном коде curl: github.com/curl/curl/blob/master/src/tool_paramhlp.c#L139
Себастьян
4
@Wildcard, Солярис делает это. Командная строка, видимая / usr / ucb / ps, является собственной (изменяемой) копией процесса. Командная строка, видимая / usr / bin / ps, является принадлежащей ядру (неизменяемой) копией. Ядро содержит только первые 80 символов. Все остальное усекается.
BowlOfRed
1
@Wildcard Действительно, завершающие нули являются пустыми аргументами. В psвыводе множество пустых аргументов выглядит так, будто там ничего нет, но да, это имеет значение, если вы проверите, сколько есть пробелов, и вы можете наблюдать более непосредственно из /proc/PID/cmdline.
Жиль "ТАК ... перестать быть злым"
14

Другие ответы хорошо отвечают на вопрос в целом. Чтобы конкретно ответить « Как достигается этот эффект? Это где-то в исходном коде curl? »:

В разделе аргумента синтаксического анализа исходного кода локонов , то -uопция обрабатываются следующим образом :

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

И cleanarg()функция определяется следующим образом:

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
}

Таким образом, мы можем явно видеть, что аргумент username: password in argvперезаписывается пробелами, как описано в других ответах.

Цифровая травма
источник
Мне нравится, что в комментариях cleanargпрямо говорится, что он делает то, что задает вопрос!
Флорис
3

Процесс может не только читать свои параметры, но и записывать их.

Я не разработчик, поэтому я не знаком с этим, но это может быть возможно извне с подходом, аналогичным изменению параметров среды:

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

Хауке Лагинг
источник
Хорошо, но запуск, например, bash -c 'awk 1 /proc/$$/cmdline; set -- something; awk 1 /proc/$$/cmdline'показывает, что по крайней мере в оболочке установка параметров отличается от модификации того, что ядро ​​видит в качестве параметров процесса.
Wildcard
4
@Wildcard позиционных аргументы в сценарии оболочек являются изначально копией из некоторых аргументов командной строки процесса оболочки в. Большинство оболочек не позволяют сценарию изменять исходные аргументы.
Жиль "ТАК - перестань быть злым"
@ Жиль, да, это был смысл моего комментария. :) То, что общее утверждение о том, что процесс может сделать это (первое предложение этого ответа) не отвечает на вопрос, может ли это быть достигнуто существующими функциями оболочки. Ответ на это, кажется, «нет», о чем я и догадался в самом низу моего вопроса.
Wildcard