【问题标题】:Execute or skip ld.so.preload shared library code based upon conditions根据条件执行或跳过 ld.so.preload 共享库代码
【发布时间】:2019-07-05 23:15:49
【问题描述】:

TL;DR:我可以在执行二进制文件和 RUID != UID 时跳过加载放置在 ld.so.preload 中的共享库或其中的特定代码吗?

嗨。

我正在编写一个共享对象,一个库,我通过 ld.so.preload 加载它以挂钩一些函数。我想知道是否可以根据某些条件跳过加载此类库或其中的某些部分。

上下文:我正在处理TOCTTOU(Time To Check To Time To Use)漏洞,我正在编写一个userland 库,它将通过linux's loader ld.so.preload feature 加载。主要思想是挂钩所有对文件进行操作的函数,进行一些检查,然后调用原始函数,使其对用户和其他程序透明。

现在,问题是:因为我不想让系统过载,并且我希望我的库的影响和开销尽可能小,所以我想执行它,即挂钩函数,仅当 RUID != EUID 时。这是基于文件的 TOCTTOU 前提之一,当攻击者的权限低于易受攻击的应用程序时,就会发生这种情况。

目前我想到的唯一方法是在每个函数声明周围加上:

if(RUID == EUID){
   call original function;
} else {
   do my checkings;
   call original function;
}

编辑:上面的代码可以替换为等效的更短的代码:

if(RUID != EUID){
   do my checkings;
}
call original function;

但这实际上非常糟糕,因为我要挂钩近 50 个函数以及使用 __attribute 构造函数和析构函数,这意味着在每个函数中都使用 if-else 块填充我的代码。

请记住,ld.so.preload 在任何其他库之前加载列出的库。

我想知道是否有任何方法可以根据RUID != EUID 条件不加载库,或者加载库但跳过挂钩代码。

【问题讨论】:

  • 我真的不认为你可以自动地做到这一点,而不必编写一些像你展示的那样的代码。但是,您可以构建一个非常小的包装程序来检查 RUID == EUID:如果它们相等,则执行 exec(target...),否则首先预加载您的库,然后执行 exec(target...)
  • 由于我的库是系统范围的,我无法为其创建任何包装器。 @MarcoBonelli 也就是说,我使用 -shared -ldl 标志对其进行编译,并将其绝对路径写入 /etc/ld.so.preload 文件中。
  • 那么我认为您无能为力,除了自己重新编译加载器并在查看 ld.so.preload 之前添加所需的检查
  • 我猜你是对的,我无能为力。我认为重新编译加载程序本身有点激烈。我不能假设放置在 ld.so.preload 中的每个库都需要 RUID != EUID 检查。
  • 嗯,当然可以检查lib的路径和名称。

标签: c shared-libraries ld


【解决方案1】:

由于我的库是系统范围的,我无法为其创建任何包装器。

您希望将您的库预加载到 setuid 或 setgid 程序中。

由于任何系统上此类程序的数量都非常少且静态已知(您可以通过每小时的 cron 作业找到它们),因此最简单的解决方案是为每个此类程序创建一个包装器。

包装器本身可以是一个非常小的程序,它具有与“目标”程序完全相同的 setuid / setgid 权限,并执行相当于

/bin/env "LD_PRELOAD=..." target-prog args

【讨论】:

  • 您是对的,但请记住我正在解决 TOCTTOU 漏洞。它的前提之一是“攻击者的权限低于易受攻击的程序”。也就是说,攻击者利用(利用)setuid 来根植易受攻击的程序,因为该漏洞允许攻击者提升权限。我知道 LD_PRELOAD 会这样做,但我需要在 RUID != EUID 时准确地执行我的库,这就是为什么 LD_PRELOAD 在我的上下文中没有用。
  • @Razvi 我最初颠倒了你想要的行为。答案已更新。
猜你喜欢
  • 2021-07-16
  • 1970-01-01
  • 1970-01-01
  • 2018-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-08
相关资源
最近更新 更多