【问题标题】:When setting execution bit on PT_GNU_STACK program header, why do all segments of the process become executable在 PT_GNU_STACK 程序头上设置执行位时,为什么进程的所有段都变为可执行
【发布时间】:2020-09-06 14:56:34
【问题描述】:

在尝试控制段上的可执行位时,我发现了加载程序如何使用PT_GNU_STACK 的一个巨大的怪癖。

根据elf(5) 手册页,PT_GNU_STACK 用作:

Linux 内核使用 GNU 扩展,通过 p_flags 成员中设置的标志来控制堆栈的状态。

execstack 手册页也支持这个:

... ELF 二进制文件和共享库现在可以 被标记为需要可执行堆栈或不需要它。这 标记是通过 PT_GNU_STACK 程序中的 p_flags 字段完成的 标题条目。

但是,除了设置堆栈可执行文件之外,当我设置该位时,几乎所有段都变为可执行文件。

例如,当我运行 sleep 时,我得到了这个内存映射

sleep 100 & cat /proc/$!/maps
[1] 1260
561460d8d000-561460d94000 r-xp 00000000 08:01 524383                     /bin/sleep
561460f94000-561460f95000 r--p 00007000 08:01 524383                     /bin/sleep
561460f95000-561460f96000 rw-p 00008000 08:01 524383                     /bin/sleep
561462eca000-561462eeb000 rw-p 00000000 00:00 0                          [heap]
7f02b08b9000-7f02b0b97000 r--p 00000000 08:01 1966102                    /usr/lib/locale/locale-archive
7f02b0b97000-7f02b0d7e000 r-xp 00000000 08:01 655384                     /lib/x86_64-linux-gnu/libc-2.27.so
7f02b0d7e000-7f02b0f7e000 ---p 001e7000 08:01 655384                     /lib/x86_64-linux-gnu/libc-2.27.so
7f02b0f7e000-7f02b0f82000 r--p 001e7000 08:01 655384                     /lib/x86_64-linux-gnu/libc-2.27.so
7f02b0f82000-7f02b0f84000 rw-p 001eb000 08:01 655384                     /lib/x86_64-linux-gnu/libc-2.27.so
7f02b0f84000-7f02b0f88000 rw-p 00000000 00:00 0 
7f02b0f88000-7f02b0faf000 r-xp 00000000 08:01 655375                     /lib/x86_64-linux-gnu/ld-2.27.so
7f02b11a3000-7f02b11a5000 rw-p 00000000 00:00 0 
7f02b11af000-7f02b11b0000 r--p 00027000 08:01 655375                     /lib/x86_64-linux-gnu/ld-2.27.so
7f02b11b0000-7f02b11b1000 rw-p 00028000 08:01 655375                     /lib/x86_64-linux-gnu/ld-2.27.so
7f02b11b1000-7f02b11b2000 rw-p 00000000 00:00 0 
7ffc74d95000-7ffc74db6000 rw-p 00000000 00:00 0                          [stack]
7ffc74dfa000-7ffc74dfd000 r--p 00000000 00:00 0                          [vvar]
7ffc74dfd000-7ffc74dff000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

但如果我先设置PT_GNU_STACK 执行位,我会得到

execstack -s `which sleep` ; sleep 100 & cat /proc/$!/maps
[1] 1282
55b27b14f000-55b27b156000 r-xp 00000000 08:01 537509                     /bin/sleep
55b27b356000-55b27b357000 r-xp 00007000 08:01 537509                     /bin/sleep
55b27b357000-55b27b358000 rwxp 00008000 08:01 537509                     /bin/sleep
55b27bae6000-55b27bb07000 rwxp 00000000 00:00 0                          [heap]
7f99b5359000-7f99b5637000 r-xp 00000000 08:01 1966102                    /usr/lib/locale/locale-archive
7f99b5637000-7f99b581e000 r-xp 00000000 08:01 655384                     /lib/x86_64-linux-gnu/libc-2.27.so
7f99b581e000-7f99b5a1e000 ---p 001e7000 08:01 655384                     /lib/x86_64-linux-gnu/libc-2.27.so
7f99b5a1e000-7f99b5a22000 r-xp 001e7000 08:01 655384                     /lib/x86_64-linux-gnu/libc-2.27.so
7f99b5a22000-7f99b5a24000 rwxp 001eb000 08:01 655384                     /lib/x86_64-linux-gnu/libc-2.27.so
7f99b5a24000-7f99b5a28000 rwxp 00000000 00:00 0 
7f99b5a28000-7f99b5a4f000 r-xp 00000000 08:01 655375                     /lib/x86_64-linux-gnu/ld-2.27.so
7f99b5c43000-7f99b5c45000 rwxp 00000000 00:00 0 
7f99b5c4f000-7f99b5c50000 r-xp 00027000 08:01 655375                     /lib/x86_64-linux-gnu/ld-2.27.so
7f99b5c50000-7f99b5c51000 rwxp 00028000 08:01 655375                     /lib/x86_64-linux-gnu/ld-2.27.so
7f99b5c51000-7f99b5c52000 rwxp 00000000 00:00 0 
7fffc6a4d000-7fffc6a6e000 rwxp 00000000 00:00 0                          [stack]
7fffc6b36000-7fffc6b39000 r--p 00000000 00:00 0                          [vvar]
7fffc6b39000-7fffc6b3b000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

除了[vvar] 和一个由libc 支持的段之外的每个段现在都可以执行。

我已经尝试过我的 Arch 设置、Ubuntu Server VM (Bionic) 和 Mint Desktop (Tessa) - 都得到了相同的结果。

加载程序为什么要这样做?

【问题讨论】:

  • 如何更改PT_GNU_STACK?这是哪种架构?
  • 这是常见 Linux 发行版上的 x86_64 ELF。如果您可以使用execstack,您可以使用它。如果execstack 不可用,您可以手动(例如使用hexedit)编辑该程序头的权限位。
  • 它们不再是:最近 x86-64 Linux 的行为发生了变化,PT_GNU_STACK / .note.GNU-stack 不再使 everything 可执行,而只是实际堆栈。 Linux default behavior against `.data` section
  • @PeterCordes,GNU 的人说是内核负责这个。您知道这是在哪个内核版本中更改的吗?
  • 是的,该链接解释了内核方面,并链接到更改它的提交。我没有查看是哪个内核引入的,但它是在 5.4 之后的某个时间

标签: linux ld elf glibc


【解决方案1】:

这只是 Linux 内核(不是用户空间)在某些架构上所做的事情。来自内核源代码中的fs/binfmt_elf.cexecutable_stack 是基于PT_GNU_STACK 保护标志设置的):

    if (elf_read_implies_exec(*elf_ex, executable_stack))
        current->personality |= READ_IMPLIES_EXEC;

在 x86 上,我们有这个(来自 arch/x86/include/asm/elf.h):

/*
 * An executable for which elf_read_implies_exec() returns TRUE will
 * have the READ_IMPLIES_EXEC personality flag set automatically.
 */
#define elf_read_implies_exec(ex, executable_stack) \
  (executable_stack != EXSTACK_DISABLE_X)

所以PT_GNU_STACK不直接控制堆栈的保护标志,而是READ_IMPLIES_EXEC个人。

我假设内核这样做是因为许多架构(amd64、i386、s390x)最初都是 read-implies-exec,所以可以想象人们有依赖这种行为的旧二进制文件。通过标记此类旧二进制文件的方法,人们仍然可以通过增强的内存保护执行来运行他们的系统,只需根据需要选择退出旧二进制文件的子集。

如果您使用显式加载程序调用运行二进制文件,您应该只看到一个可执行堆栈,例如使用经过适当修补的cat 程序:

$ /lib64/ld-linux-x86-64.so.2 ./cat /proc/self/maps
555555cc7000-555555ce8000 rw-p 00000000 00:00 0                          [heap]
7fbdc90cb000-7fbdc90ed000 rw-p 00000000 00:00 0 
7fbdc90ed000-7fbdd60d2000 r--p 00000000 fd:02 1688290164                 /usr/lib/locale/locale-archive
7fbdd60d2000-7fbdd60f7000 r--p 00000000 fd:02 690045363                  /usr/lib64/libc-2.30.so
7fbdd60f7000-7fbdd6246000 r-xp 00025000 fd:02 690045363                  /usr/lib64/libc-2.30.so
7fbdd6246000-7fbdd6290000 r--p 00174000 fd:02 690045363                  /usr/lib64/libc-2.30.so
7fbdd6290000-7fbdd6291000 ---p 001be000 fd:02 690045363                  /usr/lib64/libc-2.30.so
7fbdd6291000-7fbdd6294000 r--p 001be000 fd:02 690045363                  /usr/lib64/libc-2.30.so
7fbdd6294000-7fbdd6297000 rw-p 001c1000 fd:02 690045363                  /usr/lib64/libc-2.30.so
7fbdd6297000-7fbdd629d000 rw-p 00000000 00:00 0 
7fbdd62bb000-7fbdd62bd000 r--p 00000000 00:24 3253068                    /tmp/cat
7fbdd62bd000-7fbdd62c2000 r-xp 00002000 00:24 3253068                    /tmp/cat
7fbdd62c2000-7fbdd62c5000 r--p 00007000 00:24 3253068                    /tmp/cat
7fbdd62c5000-7fbdd62c6000 r--p 00009000 00:24 3253068                    /tmp/cat
7fbdd62c6000-7fbdd62c7000 rw-p 0000a000 00:24 3253068                    /tmp/cat
7fbdd62c7000-7fbdd62c9000 r--p 00000000 fd:02 690045355                  /usr/lib64/ld-2.30.so
7fbdd62c9000-7fbdd62e9000 r-xp 00002000 fd:02 690045355                  /usr/lib64/ld-2.30.so
7fbdd62e9000-7fbdd62f1000 r--p 00022000 fd:02 690045355                  /usr/lib64/ld-2.30.so
7fbdd62f2000-7fbdd62f3000 r--p 0002a000 fd:02 690045355                  /usr/lib64/ld-2.30.so
7fbdd62f3000-7fbdd62f4000 rw-p 0002b000 fd:02 690045355                  /usr/lib64/ld-2.30.so
7fbdd62f4000-7fbdd62f5000 rw-p 00000000 00:00 0 
7ffdabe6a000-7ffdabe89000 rwxp 00000000 00:00 0                          [stack]
7ffdabe89000-7ffdabe8b000 rw-p 00000000 00:00 0 
7ffdabfed000-7ffdabff1000 r--p 00000000 00:00 0                          [vvar]
7ffdabff1000-7ffdabff3000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-21
    • 1970-01-01
    • 1970-01-01
    • 2023-02-23
    • 1970-01-01
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多