[英]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.