故事
情况1
我不小心在.data
部分中编写了我的汇编代码。我编译并执行它。即使我没有指定5.4.0-53-generic
这样的标志,该程序也可以在Linux execstack
下正常运行。
情况2:
之后,我在Linux 5.9.0-050900rc5-generic
下执行了该程序。程序得到了SIGSEGV
。我通过阅读/proc/$pid/maps
检查了虚拟内存权限。原来,该部分不可执行。
我认为Linux上有一个配置可以管理该权限。但是我不知道在哪里可以找到。
代码
[Linux 5.4.0-53-通用]
运行(正常)
ammarfaizi2@integral:/tmp$ uname -r
5.4.0-53-generic
ammarfaizi2@integral:/tmp$ cat test.asm
[section .data]
global _start
_start:
mov eax, 60
xor edi, edi
syscall
ammarfaizi2@integral:/tmp$ nasm --version
NASM version 2.14.02
ammarfaizi2@integral:/tmp$ nasm -felf64 test.asm -o test.o
ammarfaizi2@integral:/tmp$ ld test.o -o test
ammarfaizi2@integral:/tmp$ ./test
ammarfaizi2@integral:/tmp$ echo $?
0
ammarfaizi2@integral:/tmp$ md5sum test
7ffff5fd44e6ff0a278e881732fba525 test
ammarfaizi2@integral:/tmp$
检查权限(00400000-00402000 rwxp),因此它是可执行的。## Debug
gef➤ shell cat /proc/`pgrep test`/maps
00400000-00402000 rwxp 00000000 08:03 7471589 /tmp/test
7ffff7ffb000-7ffff7ffe000 r--p 00000000 00:00 0 [vvar]
7ffff7ffe000-7ffff7fff000 r-xp 00000000 00:00 0 [vdso]
7ffffffde000-7ffffffff000 rwxp 00000000 00:00 0 [stack]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
gef➤
[Linux 5.9.0-050900rc5-generic]运行(Segfault)
root@esteh:/tmp# uname -r
5.9.0-050900rc5-generic
root@esteh:/tmp# cat test.asm
[section .data]
global _start
_start:
mov eax, 60
xor edi, edi
syscall
root@esteh:/tmp# nasm --version
NASM version 2.14.02
root@esteh:/tmp# nasm -felf64 test.asm -o test.o
root@esteh:/tmp# ld test.o -o test
root@esteh:/tmp# ./test
Segmentation fault (core dumped)
root@esteh:/tmp# echo $?
139
root@esteh:/tmp# md5sum test
7ffff5fd44e6ff0a278e881732fba525 test
root@esteh:/tmp#
检查权限(00400000-00402000 rw-p),因此它不可执行。## Debug
gef➤ shell cat /proc/`pgrep test`/maps
00400000-00402000 rw-p 00000000 fc:01 2412 /tmp/test
7ffff7ff9000-7ffff7ffd000 r--p 00000000 00:00 0 [vvar]
7ffff7ffd000-7ffff7fff000 r-xp 00000000 00:00 0 [vdso]
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
gef➤
objdump -proot@esteh:/tmp# objdump -p test
test: file format elf64-x86-64
Program Header:
LOAD off 0x0000000000000000 vaddr 0x0000000000400000 paddr 0x0000000000400000 align 2**12
filesz 0x0000000000001009 memsz 0x0000000000001009 flags rw-
问题概括
.data
的5.4.0-53-generic
部分的默认权限是可执行的。 .data
的5.9.0-050900rc5-generic
部分的默认权限是而不是可执行文件。 最佳答案
这只是一个猜测:我认为罪魁祸首是在没有READ_IMPLIES_EXEC
段的情况下自动设置的PT_GNU_STACK
个性。
在5.4 kernel source we can find this piece of code中:
SET_PERSONALITY2(loc->elf_ex, &arch_state);
if (elf_read_implies_exec(loc->elf_ex, executable_stack))
current->personality |= READ_IMPLIES_EXEC;
那是唯一可以将RW部分转换为RWX部分的东西。对我来说,对PROC_EXEC
的任何其他使用似乎都没有改变或与此问题相关。executable_stack
设置为here:for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++)
switch (elf_ppnt->p_type) {
case PT_GNU_STACK:
if (elf_ppnt->p_flags & PF_X)
executable_stack = EXSTACK_ENABLE_X;
else
executable_stack = EXSTACK_DISABLE_X;
break;
但是,如果不存在PT_GNU_STACK
段,则该变量将保留its default value:int executable_stack = EXSTACK_DEFAULT;
现在,此工作流程在5.4和最新的内核源代码中都是相同的,更改的是elf_read_implies_exec
的定义:Linux 5.4:
/*
* 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)
Latest Linux:/*
* An executable for which elf_read_implies_exec() returns TRUE will
* have the READ_IMPLIES_EXEC personality flag set automatically.
*
* The decision process for determining the results are:
*
* CPU: | lacks NX* | has NX, ia32 | has NX, x86_64 |
* ELF: | | | |
* ---------------------|------------|------------------|----------------|
* missing PT_GNU_STACK | exec-all | exec-all | exec-none |
* PT_GNU_STACK == RWX | exec-stack | exec-stack | exec-stack |
* PT_GNU_STACK == RW | exec-none | exec-none | exec-none |
*
* exec-all : all PROT_READ user mappings are executable, except when
* backed by files on a noexec-filesystem.
* exec-none : only PROT_EXEC user mappings are executable.
* exec-stack: only the stack and PROT_EXEC user mappings are executable.
*
* *this column has no architectural effect: NX markings are ignored by
* hardware, but may have behavioral effects when "wants X" collides with
* "cannot be X" constraints in memory permission flags, as in
* https://lkml.kernel.org/r/20190418055759.GA3155@mellanox.com
*
*/
#define elf_read_implies_exec(ex, executable_stack) \
(mmap_is_ia32() && executable_stack == EXSTACK_DEFAULT)
请注意,在5.4版本中,如果未将堆栈显式标记为不可执行(通过elf_read_implies_exec
段),则PT_GNU_STACK
如何返回true值。在最新的资料中,现在的检查更具防御性:在ELF二进制文件中未找到
elf_read_implies_exec
段的情况下,PT_GNU_STACK
仅在32位可执行文件上为true。我汇编了程序,将其链接起来,没有找到
PT_GNU_STACK
段,所以这可能是原因。如果这确实是问题所在,并且如果我正确地遵循了代码,那么如果您将堆栈设置为二进制文件中的不可执行文件,则其数据部分将不再被映射为可执行文件(即使在Linux 5.4上也是如此)。
关于Linux可执行文件.data节的默认行为在5.4和5.9之间更改?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64833715/