Linux

C/C++ 中 system() 呼叫的可繼承能力

  • April 23, 2020

目前我正在嘗試通過閱讀http://man7.org/linux/man-pages/man7/capabilities.7.html來了解 Linux 中的功能

我創建了一個具有該功能的小型 C++ 應用程序CAP_DAC_READ_SEARCH+eip

該功能適用於應用程序。但我有一個system()電話在裡面

system("cat /dev/mtdX > targetFile");

我怎樣才能繼承這個呼叫的能力?

編輯:

我知道這system()是由fork()+驅動的execl()。在文件中提到,fork()子程序獲得與父程序相同的功能。但是為什麼不繼承讀取能力呢?

感謝@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用作包裝器(根據其維護者的突發奇想和假設,可能會刪除或添加功能,弄亂環境變數,來源初始化腳本和其他有趣的東西)。使用 justexecv*(2)而不是system(3)將消除所有那些虛假的並發症。

其次,您應該深入了解聯機幫助頁中的“能力轉換期間execve()”部分。capabilities(7)我不打算在這裡複製粘貼它,但它基本上歸結為:除非將它們添加到執行緒(程序)的環境集中,否則不會通過 execve() 繼承功能,並且它們不能在那裡添加,除非它們已經在執行緒的可繼承集中。(文件元數據中的“可繼承”功能只是一個遮罩,限制了執行緒的功能)。

因此,為了通過execve()您繼承的功能應該**a)**將它們從允許的集合複製到可繼承的集合(您可以使用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 編寫的 hack 中提取的,其中沒有 libcap,並且缺少許多標頭定義。將其轉換為使用 libcap 包裝器留給讀者作為練習。

引用自:https://unix.stackexchange.com/questions/580434