Возможность, наследуемая для системного ()вызова в C/C++

[пока не настоящий ответ]

При использовании с исполняемым скриптом, который она -взломала, ни

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//cmdline, /proc//commи т. д.

3
16.04.2020, 13:43
2 ответа

Спасибо @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";
}
2
19.03.2021, 02:28

Прежде всего, тебе следует убраться 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 оставлено читателю в качестве упражнения.

3
19.03.2021, 02:28

Теги

Похожие вопросы