$ k=v p &
[1] 3028
Есть ли способ p
изменить содержимое, /proc/3028/environ
чтобы не упоминать, k=v
пока p
работает?
$ k=v p &
[1] 3028
Есть ли способ p
изменить содержимое, /proc/3028/environ
чтобы не упоминать, k=v
пока p
работает?
Ответы:
В Linux вы можете перезаписать значение строк окружения в стеке.
Таким образом, вы можете скрыть запись, переписав ее нулями или чем-то еще:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[], char* envp[]) {
char cmd[100];
while (*envp) {
if (strncmp(*envp, "k=", 2) == 0)
memset(*envp, 0, strlen(*envp));
envp++;
}
sprintf(cmd, "cat /proc/%u/environ", getpid());
system(cmd);
return 0;
}
Беги как:
$ env -i a=foo k=v b=bar ./wipe-env | hd
00000000 61 3d 66 6f 6f 00 00 00 00 00 62 3d 62 61 72 00 |a=foo.....b=bar.|
00000010
k=v
был перезаписан \0\0\0
.
Обратите внимание, что setenv("k", "", 1)
перезапись значения не будет работать, так как в этом случае выделяется новая "k="
строка.
Если вы не изменили k
переменную окружения с помощью setenv()
/ putenv()
, то вы также должны сделать что-то вроде этого, чтобы получить адрес k=v
строки в стеке (ну, одного из них):
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[]) {
char cmd[100];
char *e = getenv("k");
if (e) {
e -= strlen("k=");
memset(e, 0, strlen(e));
}
sprintf(cmd, "cat /proc/%u/environ", getpid());
system(cmd);
return 0;
}
Обратите внимание, что он удаляет только одну из k=v
записей, полученных в среде. Обычно есть только один, но ничто не мешает кому-либо передать оба k=v1
и k=v2
(или k=v
два раза) в список env, переданный execve()
. Это было причиной уязвимостей в прошлом, таких как CVE-2016-2381 . Это может действительно произойти bash
до «шеллшока» при экспорте переменной и функции с одним и тем же именем.
В любом случае всегда будет маленькое окно, в течение которого строка env var еще не была переопределена, поэтому вы можете захотеть найти другой способ передачи секретной информации в команду (например, конвейер), если вы предоставляете ее через /proc/pid/environ
это проблема.
Также обратите внимание, что, в отличие от /proc/pid/cmdline
, /proc/pid/environment
доступен только процессам с одинаковым euid или root (или root только если euid и ruid процесса не совпадают).
Вы можете скрыть это значение от них /proc/pid/environ
, но они все равно смогут получить любую другую копию строки, которую вы сделали, например, подключив к ней отладчик.
См. Https://www.kernel.org/doc/Documentation/security/Yama.txt, чтобы узнать, как это сделать, по крайней мере, пользователям без полномочий root.
Это не было необходимо переписать строки выше ( на самом деле не на ) стека основного потока на Linux с 2010 года.
Оба /proc/self/cmdline
и /proc/self/environ
могут быть изменены самим процессом во время выполнения, путем вызова prctl()
функции с соответственно PR_SET_MM_ARG_START
+ PR_SET_MM_ARG_END
или PR_SET_MM_ENV_START
+ PR_SET_MM_ENV_END
. Они непосредственно устанавливают указатели памяти в пространство памяти приложения процесса, которое хранится в ядре для каждого процесса, которые используются для извлечения содержимого /proc/${PID}/cmdline
и /proc/${PID}/environ
, и, следовательно, командной строки и среды, сообщаемых ps
командой.
Поэтому нужно просто создать новый аргумент или строку окружения (не вектор, обратите внимание - ␀
указанная память должна быть фактическими строковыми данными, объединенными и -delimited) и сообщить ядру, где оно находится.
Это задокументировано на странице руководства Linux для этой prctl(2)
функции, а также на environ(7)
странице руководства. Что не задокументировано, так это то, что ядро отклоняет любую попытку установить начальный адрес выше конечного адреса или конечный адрес ниже начального адреса; или (пере) установить любой адрес на ноль. Кроме того, это не оригинальный механизм, предложенный Брайаном Донланом в 2009 году, который позволял устанавливать начало и конец одной операции, атомарно. Более того, ядро не предоставляет способа получить текущие значения этих указателей.
Это затрудняет изменение среды и областей командной строки с помощью prctl()
. Необходимо вызывать prctl()
функцию до четырех раз, потому что первые попытки могут привести к попыткам установить начальный указатель выше конечного указателя, в зависимости от того, где старые и новые данные находятся в памяти. Нужно вызывать его еще четыре раза, если вы хотите убедиться, что это не приведет к тому, что другие процессы в системе получат возможность проверить произвольный диапазон пространства памяти процесса в период, когда начинается новый запуск. был установлен, но новый конец / начало не было.
Один атомарный системный вызов, который устанавливает весь диапазон за один раз, был бы намного проще для прикладных программ в безопасном использовании.
Еще одна проблема заключается в том, что без веской причины (учитывая проверки в ядре, перезаписываемость исходных областей данных в любом случае и тот факт, что эквиваленты не являются привилегированными операциями ни на одной из BSD), в Linux это требует суперпользователя. привилегии.
Я написал довольно простые setprocargv()
и setprocenvv()
функции для своих наборов инструментов, которые используют это. Программы с цепочечной загрузкой из наборов инструментов, которые являются встроенными, например, setenv
и foreground
, таким образом, отражают аргументы цепочки команд и среду, в которой это позволяет Linux.
# / package / admin / nosh / command / clearenv setenv WIBBLE колебание, передний план, пауза \; правда & [1] 1057 # hexdump -C / proc / 1057 / cmdline 00000000 66 6f 72 65 67 72 6f 75 6e 64 00 70 61 75 73 65 | foreground.pause | 00000010 00 3b 00 74 72 75 65 00 |.;. Верно. | 00000018 # hexdump -C / proc / 1057 / Environment 00000000 57 49 42 42 4c 45 3d 77 6f 62 62 6c 65 00 | WIBBLE = колебание. | 0000000e # hexdump -C / proc / 1058 / cmdline 00000000 70 61 75 73 65 00 | пауза. | 00000006 # hexdump -C / proc / 1058 / Environment 00000000 57 49 42 42 4c 45 3d 77 6f 62 62 6c 65 00 | WIBBLE = колебание. | 0000000e #
Обратите внимание, что это не препятствует вещам, которые отслеживают процесс и получают доступ к его памяти напрямую другими способами (а не через эти два псевдофайла), и, конечно, оставляет окно перед изменением строк, где эта информация может быть видна, просто как и при перезаписи данных над стеком основного потока. И так же, как в случае с перезаписью данных, это не учитывает библиотеки времени выполнения, которые делают копии среды (в куче) при различных обстоятельствах. В общем, не рассматривайте это как такой же хороший механизм для передачи «секретов» программе, как (скажем), когда она наследует дескриптор открытого файла в конец чтения безымянного канала, считываемого во входной буфер полностью под вашим контролем что вы потом вытрите.
/proc/$pid/stat
(помимо других значений , которые вы , возможно , потребуется в struct prctl_mm_map
). Смотрите также мой пример filter_env.c для небольшой демонстрации. JdeBP, вы можете добавить ссылки на ваши setprocargv()
/ setprocenvv()
функции?