[英]How to solve process control reported by Fortify
我正在處理由 fortify 報告的以下過程控制問題,該問題在https://vulncat.fortify.com/en/detail?id=desc.dataflow.abap.process_control#C%2FC%2B%2B中有所描述
filename.c 中的 function load() 在第 3 行調用 dlopen()。調用加載庫時未指定絕對路徑。 它可能導致程序使用攻擊者提供的惡意庫。
我有以下 function,它在不同的代碼位置被調用。
void* load(char* name)
{
void* handle;
handle = dlopen(name, RTLD_LAZY | RTLD_GLOBAL);
return(handle);
}
void somefunc()
{
void *login_module_handle = load("/home/myuser/load_this_shared_lib.so");
}
這里我已經是用絕對路徑了,但是不明白為什么Fortify還是報錯。
Fortify 的可能建議如下所示。
- 只要有可能,庫應該由應用程序控制並使用絕對路徑執行。
- 在編譯時路徑未知的情況下,例如對於跨平台應用程序,絕對路徑應該在執行期間從已知值構造。
任何建議都會有所幫助。
理論上, load
function 可以被稱為其他不通過完整路徑的地方,或者更糟糕的是,用戶填充的變量未正確檢查。
在這種情況下, load
可能更好地實現為宏:
#define load(name) dlopen(name, RTLD_LAZY | RTLD_GLOBAL)
然后當替換發生時,實際上給dlopen
一個字符串常量,指定 Fortify 應該能夠看到的絕對路徑。
這里有兩個相關但獨立的問題:
如何在運行時以安全的方式加載動態庫
如何讓 Fortify 看到安全得到妥善處理
Fortify 推薦的常用方法是定義一個目錄的固定路徑,插件必須位於該目錄中。 假定只有具有足夠權限的用戶才能訪問該目錄。 模式類似於
#define _GNU_SOURCE
#ifndef PLUGIN_PATH
#define PLUGIN_PATH "/usr/lib/thisapp/plugins"
#endif
#ifndef PLUGIN_SUFFIX
#define PLUGIN_SUFFIX ".so"
#endif
// Try to load dynamic library 'name' under PLUGIN_PATH.
// Returns NULL if failed, with errno set.
// If this returns NULL and errno==0, use dlerror() to
// get the error message.
void *load(const char *name)
{
// Empty or NULL name is not allowed, and it may not start with '.'.
if (!name || !*name || *name == '.') {
errno = EINVAL;
return NULL;
}
// Name may not contain '/'.
if (strchr(name, '/')) {
errno = EINVAL;
return NULL;
}
char *path = NULL;
int pathlen = asprintf(&path, "%s/%s%s", PLUGIN_PATH, name, PLUGIN_SUFFIX);
if (pathlen < 1 || !path) {
errno = ENOMEM;
return NULL;
}
void *handle = dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
free(path);
errno = 0; /* If handle is NULL, look at dlerror() */
return handle;
}
通常,在/usr/lib
樹中以應用程序命名的固定路徑,例如/usr/lib/application/plugins/
可以被認為是安全的,假設它已安裝,因此該目錄及其所有父目錄都由超級用戶擁有並且一個特權組,對其他組沒有寫入權限(即drwxrwxr-x
或drwr-xr-x
)。
如果用戶可以指定庫的完整路徑,則必須審查路徑上的每個目錄,以驗證它們是否可以被非特權用戶修改。 如果有任何可疑之處,則圖書館不安全。
如果進程有足夠的權限( CAP_LEASE
能力,或以特權用戶身份運行),庫文件本身可以以只讀方式打開,並在其上放置讀取租約(請參閱fcntl() )。 如果任何進程打開文件進行寫入,這將失敗,這意味着另一個進程能夠在我們使用它時修改它。 如果租約被授予,那么任何其他試圖打開文件進行寫入的進程都會導致它們阻塞,並且該進程會收到信號通知。 此進程可以延遲(但不完全阻止)其他進程打開庫,最長為租約中斷時間,通常為 45 秒。 這樣,安全進程可以檢測是否有人(甚至特權)在使用插件時試圖修改它。
有了有效的讀取租約,就可以檢查文件所有權和模式(通過fstat()
使用用於以只讀方式打開它的文件描述符),查看它是否為特權用戶所有以及是否只能由特權用戶修改。 如果非特權用戶可以修改它,則它是不安全的。
在所有上述檢查之后,給定文件描述符編號FD
,提供給dlopen()
的路徑是/proc/self/fd/FD
。 這將重用相同的文件描述符,確保我們沒有 TOCTTOU 競爭 window(檢查時間到使用時間)。
Fortify 是否承認上述措施,或者只是在看到dlopen()
被使用時抱怨,我不知道:我自己不使用它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.