[пока не настоящий ответ]
При использовании с исполняемым скриптом, который она -взломала, ни
execl("/path/to/script", "new_av0", (void*)0)
в C, ни
exec -a new_av0 /path/to/script
в оболочках, поддерживающих exec -a
, сможет установить $0
илиsys.argv[0]
(или любой другой синтаксис языка, используемый для ссылки на нулевой аргумент )в этом скрипте.
При выполнении интерпретатора скрипта -bang, ядро безвозвратно отбрасывает исходный нулевой аргумент, вставляя на его место имя интерпретатора, любой необязательный аргумент строки -bang и путь к сценарий.
Любое решение обязательно будет сложным, ограниченным или зависящим от языка/системы.
Например, сценарий оболочки можно обмануть с помощью:
sh -c '. /path/to/script' new_av0
Но очевидно, что это не повлияет на командную строку в том виде, в каком она появляется в выводе ps
, /proc/
, /proc/
и т. д.
Спасибо @mosvy. Я реализовал его решение с помощью libcap, и, похоже, оно работает так, как ожидалось.
void inheritCapabilities()
{
cap_t caps;
caps = cap_get_proc();
if (caps == NULL)
throw "Failed to load capabilities";
printf("DEBUG: Loaded Capabilities: %s\n", cap_to_text(caps, NULL));
cap_value_t cap_list[1];
cap_list[0] = CAP_DAC_READ_SEARCH;
if (cap_set_flag(caps, CAP_INHERITABLE, 1, cap_list, CAP_SET) == -1)
throw "Failed to set inheritable";
printf("DEBUG: Loaded Capabilities: %s\n", cap_to_text(caps, NULL));
if (cap_set_proc(caps) == -1)
throw "Failed to set proc";
printf("DEBUG: Loaded Capabilities: %s\n", cap_to_text(caps, NULL));
caps = cap_get_proc();
if (caps == NULL)
throw "Failed to load capabilities";
printf("DEBUG: Loaded Capabilities: %s\n", cap_to_text(caps, NULL));
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_DAC_READ_SEARCH, 0, 0) == -1)
throw "Failed to pr_cap_ambient_raise! Error: " + errno;
}
main() {
inheritCapabilities();
char *catargv[5];
catargv[0] = (char *)"cmd";
catargv[1] = (char *)"arg1";
catargv[2] = (char *)"arg2";
catargv[3] = (char *)"arg3";
catargv[4] = NULL;
if (execvp(catargv[0], catargv) == -1)
throw "Failed! command";
}
Прежде всего, тебе следует убраться system(3)
с дороги; в отличие от того, что вы предлагаете system(3)
, это не просто fork+exec
, а нечто довольно сложное, включающее изменение расположения сигналов, ожидание дочернего элемента и использование /bin/sh
в качестве оболочки (, которая может удалять или добавлять возможности в зависимости от его капризы и предположения мейнтейнера, путаница с переменными окружения, скриптами инициализации исходников и прочими забавными вещами ). Использование только execv*(2)
вместо system(3)
избавит вас от всех этих ложных сложностей.
Во-вторых, вам следует внимательно изучить раздел «Трансформация возможностей во время execve()
» на странице руководства capabilities(7)
. Я не собираюсь копировать -и вставлять сюда, но в основном это сводится к:Возможности НЕ наследуются через execve (), если только они не добавлены в окружающий набор потока (процесса ), и они не могут быть добавлены туда, если они уже не находятся в наследуемом наборе потока .(«Наследуемые» возможности из метаданных файла — это просто маска , ограничивающая возможности потока ).
Таким образом, для того, чтобы иметь возможности, унаследованные через execve()
, вы должны и)скопировать их из разрешенных в наследуемый набор (, который вы можете сделать с системным вызовомcapset(2)
[1] )и b)добавить их в окружающий набор (, что вы могли бы сделать сprctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE)
).
Собираем все воедино:
$ cat capexec.c
#include <sys/prctl.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/capability.h>
#include <err.h>
int main(int ac, char **av){
static char *dav[] = { "/bin/bash", 0 };
struct __user_cap_header_struct hs;
struct __user_cap_data_struct ds[2];
hs.version = 0x20080522; /*_LINUX_CAPABILITY_VERSION_3;*/
hs.pid = getpid();
if(syscall(SYS_capget, &hs, ds)) err(1, "capget");
ds[0].inheritable = ds[0].permitted;
if(syscall(SYS_capset, &hs, ds)) err(1, "capset");
if(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_DAC_READ_SEARCH, 0, 0)) err(1, "prctl(pr_cap_ambient_raise)");
av = ac < 2 ? dav : av + 1;
execvp(*av, av);
err(1, "execvp %s", *av);
}
$ cc -Wall capexec.c -o capexec
# as root
# setcap cap_dac_read_search+ip /tmp/capexec
$./capexec dd if=/dev/sda of=/dev/null count=1
1+0 records in
1+0 records out
512 bytes copied, 0.000299173 s, 1.7 MB/s
[1] в документации рекомендуется использовать библиотеку libcap; части этого примера были вырезаны из хака, который я написал для старой версии Android, где не было libcap и отсутствовали многие определения заголовков. преобразование его для использования оболочек libcap оставлено читателю в качестве упражнения.