Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

订阅源联系隐私© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
ifuncd-up — GNU IFUNC 是 CVE-2024-3094 背后的真正元凶 | Kitploit
工具/GitHubGitHub/robertdfrench/ifuncd-up
漏洞分析动态代码分析 (DAST)漏洞利用逆向工程二进制分析供应链安全论文与研究学习与教育
GitHubrobertdfrench/ifuncd-up

ifuncd-up

GNU IFUNC 是 CVE-2024-3094 背后的真正元凶

查看仓库
601276天前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
网站
分享
回应 Hacker News...

我看到你们这些在[橙色网站][hn]上的家伙在找我麻烦。我将在下面做出一些精选回应,但首先我提出一个挑战:我将向第一个能够在不使用 ifunc 的情况下演示此攻击的人送出我自己的 500 美元。我真心感兴趣,并愿意为这种启发付费。Fork 这个仓库并提交一个包含你可用 PoC 的 PR,如果你赢了,我会私下询问你的邮寄地址。现在开始回应:

这是找错树了。

孩子,我就住在错误的树上,我想对谁吠就对谁吠。

这对漏洞利用并非必要,

你对漏洞利用才不必要!

如果我们想增加针对以 root 身份运行的任意代码的保护,总是有 selinux。

一旦加载,此攻击不需要跨越任何进一步的系统调用边界。所以是的,我们本可以限制一个“额外”的 root 会话,但我们仍然会有不请自来的客人进入机器!

这是什么,比尔博的生日派对??除了参加派对事务外不得入内!!

  1. IFUNC 并不是在 main 之前运行代码的唯一方式。

但它是一种不必要的在内存保护设置之前运行代码的方式。

  1. 他们提出的替代方案可以说更不安全,因为函数指针将在进程的整个生命周期内保持可写,

我们可以用 mprotect 来临时应对!参见上面 修改 LD_PRELOAD 小节之前的最后一句。

是的,这篇博客被误导了。

抱歉,这篇博客是无引导的。所有这些恶作剧都是我自己做的!没有人骗我变得这么愚蠢。

IFUNC 应该由[客户端]软件本身实现,

@CountWSS 💯 没错

一系列明显的流程失败来自 Github 维护者直到……

这是我将认真回应的唯一一点:

我认为,认为这件事始于他的错误,对 xz-utils 维护者来说极其不公平,对社区来说也相当危险。它始于没有人愿意帮助维护这个项目。攻击者依赖 ifunc 作为技术漏洞,并将我们对 xz-utils 的集体疏忽作为社会漏洞。我认为,将 Collin 先生的行为视为除了英勇的、多年如一日的社区服务奉献之外的任何东西,都是可耻的。

而且 Bruce Schneier 同意我的观点,所以……很遗憾,你的论点完蛋了。

语言可能比需要的更严厉了一些

你不会相信我的朋友们先让我把这篇改得温和了多少。

Linux 发行版不应该如此自视甚高,以至于期望 OpenBSD 遵从并适应他们的烂摊子

@debazel!!!我喜欢。

完全是胡说八道。

好吧,那部分说得准确。

IFUNC'd up

为什么你不应该为 [CVE-2024-3094][nvd] 责怪 xz-utils。也 看看我的 ETSA Talk!

I think IFUNC'd up

CVE-2024-3094,更常被称为“xz-utils 后门”,是全球网络安全的一次险胜。如果这次攻击没有在关键时刻被 [Andres Freund][freund] 发现,我们星球上的大多数 SSH 服务器早就开始向这次攻击背后的势力授予 root 访问权限了。

不幸的是,太多分析都集中在[恶意代码][JiaT75]是如何进入 xz-utils 仓库的。相反,我想论证的是,关键开源软件中的两个长期设计决策才使这次攻击成为可能:[将 OpenSSH 链接到 SystemD][biebl],以及 [GNU IFUNC][sourceware] 的存在。

开始之前:本文的许多讨论涉及 Linux 上动态链接的复杂细节。如果你需要复习,请查看 dynamic_linking.md。

CVE-2024-3094 快速回顾

有大量优秀的文章概述了 xz-utils 后门的高层细节,比如 Dan Goodin 的 [What we know about the xz Utils backdoor that almost infected the world][goodin1] 和 Sam James 的 [FAQ on the xz-utils backdoor (CVE-2024-3094)][thesamesam] gist。我们不需要在这里重新赘述所有这些,所以为了本文的目的,这里是一个非常粗略的回顾:

  • 一些 Linux 发行版修改 OpenSSH,使其依赖 SystemD
  • SystemD 依赖 xz-utils,而 xz-utils 使用 GNU IFUNC
  • 因此,xz-utils 最终进入了 OpenSSH 的地址空间
  • 这允许 ifunc 修改 SSH 服务器中的代码```mermaid flowchart TD G["GNU IFUNC"] A["OpenSSH (OpenBSD)"] B["Portable OpenSSH
    (Linux / macOS / etc)"] C[OpenSSH + IFUNC] D[xz-utils] E["SystemD (Linux)"] A -->|Remove OpenBSD specifics| B B -->|Add SystemD specifics| C D --> E E --> C C --> F["Mayhem"] G --> D
## 为什么 Linux 发行版要修改 OpenSSH?
简短的回答是:他们不得不这么做。OpenSSH 是由
OpenBSD 社区开发、为 OpenBSD 社区服务的,他们根本
不在乎 Linux。[Portable OpenSSH][mindrot] 项目是一个
尽力而为的补丁集合,用通用 POSIX 组件替换 OpenBSD 特有的
组件,并在适用的情况下加入一些平台特定的
代码。在实践中,SSH 的软件供应链最终看起来
大致是这样的:```mermaid
flowchart TD
  subgraph OpenBSD Folks
    A[OpenBSD]
    B[OpenSSH]
    H[improvements]
  end
  B-->A
  A-->H
  H-->B

  B-->C
  C[Portable OpenSSH]

  subgraph Debian Folks
    D[Debian SSH]
    G[improvements]
  end
  C-->D
  D-->G
  G-->C

  subgraph Fedora Folks
    J[Fedora SSH]
    K[improvements]
  end
  C-->J
  J-->K
  K-->C

OpenBSD 版本的 OpenSSH 是所有其他版本的源头,其大多数改进都来自 OpenBSD 社区内部。这些变更向下游流入 Portable OpenSSH 项目,该项目尝试以不特定于 OpenBSD 的方式重新实现新功能。这正是 SSH 能够在 Linux、macOS、FreeBSD 甚至 Windows 等平台上运行的原因。

但事情并未止步于此。一些操作系统在 Portable OpenSSH 提供的基础上进行了进一步的定制。例如,Apple 为 ssh-add 添加了 [--apple-use-keychain][keith] 标志,以帮助其与 macOS 密码管理器集成。

在 CVE-2024-3094 的案例中,Fedora 和 Debian 为其 OpenSSH 分支维护了自己的 [SystemD 补丁][biebl],以修复 [sshd 重启时的竞态条件][schmidt]。因此,SSH 的实际供应链开始变成这样:```mermaid flowchart TD A[OpenSSH] B[Portable OpenSSH] C[Debian SSH] D[Fedora SSH] A-->B B-->C B-->D C<-->|SystemD Patches|D

这些补丁从未进入 Portable OpenSSH,因为 Portable OpenSSH 的开发者们[“对依赖 libsystemd 不感兴趣”][djmdjm]。它们也从未进入上游 OpenSSH,因为 OpenBSD 根本不需要支持 SystemD。



### 关于“关注点分离”的担忧
这看起来无害,但它反映了开源领域,尤其是 Linux 中一个更大的问题:操作系统的关键组件由互不相识、互不交流的人开发。

* 为 SystemD 修补 OpenSSH 的人是否知道(或关心)libsystemd 依赖于 xz-utils?
* SystemD 的开发者是否知道(或关心)xz-utils 已经开始使用 ifunc?
* OpenSSH 的开发者是否知道(或关心)ifunc 是什么?在 OpenBSD 上它显然不是什么常见的东西。

从某种意义上说,这种沟通上的断裂是开源的一个特性:我可以根据我的需求调整你的工作,而不必为此打扰你。但它也可能导致一定程度的间接性,使得关键的设计假设(例如传统的动态链接过程)无法得到维护。

[康威定律][conway]的一个明显推论是:如果你在交付你的组织架构图,你也在交付那些存在于组织架构图裂缝中的 bug。这里没有任何一个人或团队真正犯了错误,但事后看来,很明显攻击者察觉到 Debian/Fedora SSH 的左手并不知道 xz-utils 的右手在做什么。




## GNU IFUNC *本应* 做什么?
它允许你在运行时决定要使用某个函数的哪个版本。它通过给你一个机会来运行**任意代码**,从而影响链接器如何解析符号。

![](https://assets.kitploit.com/production/public/readmes/31477/ea52603b42c9b38ff0f8f693b09c5d5c0d107a4175f3be4e1e48b39c9ca3eb55.png)



### 检测 CPU 特性
假设你有一个必须在各种 x86 CPU 上运行的应用程序。根据当前 CPU 的具体特性,你可能希望为同一任务使用不同的算法。IFUNC 最初的想法是允许程序在第一次调用某个函数时检查 CPU 特性,此后使用最适合该 CPU 的实现。

看看 [`cpu_demo.c`](https://github.com/robertdfrench/ifuncd-up/blob/trunk/code/cpu_demo.c):```c
void print_cpu_info() __attribute__((ifunc ("resolve_cpu_info")));
void print_avx2() { printf("AVX2 is present.\n"); }
void print_nope() { printf("AVX2 is missing.\n"); }

static void* resolve_cpu_info(void) {
    __builtin_cpu_init();

	if (__builtin_cpu_supports("avx2")) {
		return print_avx2;
	} else {
		return print_nope;
	}
}

int main() {
	print_cpu_info();
	return 0;
}

这个程序展示了 IFUNC 最常见的用法:它询问 CPU 是否支持某些特性,并根据所支持的 特性提供不同的函数实现。在这个例子中,我们的函数 print_cpu_info 最终会打印 "AVX2 is present" 或 "AVX2 is missing",具体取决于你的 CPU 有多古老。

探测进程环境

虽然 IFUNC 旨在探测 CPU 能力,但没有什么能阻止你在解析器中运行更复杂的代码。例如, tty_demo.c 展示了如何根据 STDOUT 是文件还是终端来加载不同的 函数实现:```c // Print Green text to the Terminal void print_to_tty(const char *message) { const char *green_start = "\033[32m"; const char *color_reset = "\033[0m"; printf("%sTTY: %s%s\n", green_start, message, color_reset); }

// Print plain text to a file void print_to_file(const char *message) { printf("FILE: %s\n", message); }

void print_message(const char *message)
attribute((ifunc("resolve_print_function")));

void (*resolve_print_function(void))(const char *) { struct termios term;

// Ask the kernel whether stdout is a file or a tty
int result = ioctl(STDOUT_FILENO, TCGETS, &term);
if (result == 0) {
    // stdout is a terminal
    return print_to_tty;
} else {
    // stdout is not a terminal
    return print_to_file;
}

}

int main() { print_message("Hello, World!"); return 0; }

这并不是 IFUNC 的预期用途,但它展示了什么是可能的:你可以在任何使用了你所声明的 IFUNC 的程序中,在 `main` 之前运行任意代码。





## IFUNC 可能是个坏主意
![](https://assets.kitploit.com/production/public/readmes/31477/6d420d0d1b4f4c49a3ba8a4b2103d7f79d284c9d8fbda4449e6e6d0afa5b3cfa.png)

GNU IFUNC 难以实现,难以正确使用,而且(作为所谓的性能工具)并不比替代方案快多少。正如我们在 CVE-2024-3094 中看到的那样,它也是软件供应链攻击的一个非常强大的工具。

IFUNC 在 GNU C 库中被广泛使用,这大概没什么问题。那些人正是它最初为之开发的群体,他们与实际实现 IFUNC 的编译器和链接器团队紧密相连。他们最有能力理解其中的权衡,而且有大量 libc 函数受益于针对特定 CPU 的实现。我认为我们应该将 IFUNC 视为 glibc 的内部接口,并避免在其他应用程序中使用它。



### 它太令人困惑,难以安全使用
ifunc 使用起来实在太困难了。有太多[边缘情况][nagy],而[官方文档][gnu-cfa]也[很匮乏][sourceware]。这给用户带来了误导性的印象,以为采用 ifunc 是直截了当的。

即使在 ifunc 可用数年之后,所宣传的接口[仍然无法工作][agner]。GCC 开发者称其为[一个错误][odonell],并考虑添加警告以弥补 IFUNC 的脆弱性:

> 使 IFUNC 健壮所需的 glibc 解决方案尚未到位,因此我们应尽我们所能警告用户它可能会出问题。

也不仅仅是 IFUNC。Apple Mach-O 有一个类似的功能叫做 `.symbol_resolver`,他们[“后悔添加了它”][rjmccall]。



### 它削弱了 RELRO
通过允许在全局偏移表仍然可写时运行任意代码,[RELRO](https://github.com/robertdfrench/ifuncd-up/blob/trunk/dynamic_linking.md#relro) 所提供的保护[变得毫无意义][binarly-io]。

这一点值得注意,因为 RELRO 宣称自己是保护动态加载符号完整性的一种方式。从用户的角度来看(你,作为编译器和链接器的用户),这违反了[最小惊讶原则][pola]:没有理性的人会期望*加载一个动态库*竟然会破坏一个旨在*保护动态库*的安全功能。

![](https://assets.kitploit.com/production/public/readmes/31477/ad252f1cb2937e3f8b168ece1e3f15455dcb0d7647b8715dff7435f3babc0e1c.png)



### 它并不总是必要的
还有多种其他方式来处理这种情况。它们各有不同的权衡,但都比 IFUNC 简单得多。所有这些都比 IFUNC 更具可移植性,更容易理解,也更难被利用。
下载工具