Як curl захищає пароль від появи в PS-виведенні?


68

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

Вони також не з'являються в /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виводу (те саме, я думаю), що я перейшов до якоїсь довільної команди оболонки? (Я б здогадався, що відповідь на це "ні", але, здається, варто включити це додаткове півзапитання.)



16
Не відповідь, але зауважте, що такий підхід не є безпечним . Між запуском програми та очищенням рядків аргументів є гоночне вікно, під час якого будь-який користувач може прочитати пароль. Не приймайте конфіденційні паролі в командному рядку.
Р ..

1
Мало пов'язане: До кого належать змінні середовища? і чи використовує хтось на практиці environбезпосередньо для доступу до змінних оточуючих середовищ? - нижній рядок: список аргументів, як і список змінних оточуючих середовищ, знаходиться в пам’яті процесу читання / запису користувача та може бути змінений користувацьким процесом.
Скотт

1
@ JPhi1618, просто зробіть перший символ вашого grepмалюнка класом символів. Наприкладps -ef | grep '[c]url'
Wildcard

1
@mpy, т не дуже складно. Деякі реджекси відповідають собі, а деякі ні. curlвідповідає, curlале [c]urlне відповідає [c]url. Якщо вам потрібна детальніше, задайте нове запитання, і я з радістю відповім.
Wildcard

Відповіді:


78

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

Не визначено, чи представлена ​​рядок є версією списку аргументів, як вона була передана команді під час її запуску, або це версія аргументів, оскільки вони, можливо, були змінені програмою. Програми не можуть залежати від можливості змінити список аргументів і відображення цієї модифікації у результатах PS.

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

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

Якщо ви дійсно хочете копати, ви можете подивитися джерело реалізації з відкритим кодом. У 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цій функції використовується кілька разів, наприклад , шляхом про видаленні на призначений для користувача пароль . Оскільки функція викликається при аналізі параметра, це відбувається на початку виклику згортання, але демпінг командного рядка до цього все одно відображатиме будь-які паролі.

Оскільки аргументи зберігаються у власній пам'яті процесу, їх неможливо змінити ззовні, за винятком використання налагоджувача.


Чудово! Отже, щодо цього фрагмента специфікації я розумію, що це означає, що було би сумісно з 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
sebasth

4
@Wildcard, Solaris це робить. Командний рядок, який бачить / 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
}

Таким чином, ми можемо чітко бачити, що аргумент ім'я користувача: пароль 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 позиційних арґументів на сценарії оболонок є спочатку копією з деяких аргументів командного рядка процесу оболонки в. Більшість оболонок не дозволяють сценарію змінювати вихідні аргументи.
Жиль

@Gilles, так, це був пункт мого коментаря. :) Це загальне твердження про те, що процес може це зробити (перше речення цієї відповіді) не відповідає, чи можна цього досягти за допомогою існуючих функцій оболонки. Відповідь на це, здається, "ні", про що я здогадався в самому дні мого питання.
Wildcard
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.