`.data` 섹션에 대한 Linux 기본 동작

Nov 14 2020

이야기

사례 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$ 

Permission (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 일반]

실행 (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 -p

root@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-

질문

  1. Linux에서 기본 ELF 섹션 권한을 관리하는 구성은 어디에 있습니까?
  2. 권한에 대한 내 관찰이 정확합니까?

요약

  • .dataLinux의 섹션에 대한 기본 권한 5.4.0-53-generic은 실행 가능합니다.
  • .dataLinux의 섹션에 대한 기본 권한 5.9.0-050900rc5-generic은 실행 가능 하지 않습니다 .

답변

7 MargaretBloom Nov 14 2020 at 19:04

이것은 추측 일뿐입니다. 범인은 세그먼트 READ_IMPLIES_EXEC가 없을 때 자동으로 설정되는 성격 이라고 생각합니다 PT_GNU_STACK.

에서 5.4 커널 소스 우리는이 코드 조각을 찾을 수 있습니다 :

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설정되어 여기 :

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세그먼트가 없으면 해당 변수는 기본값을 유지 합니다 .

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)

최신 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/[email protected]
 *
 */
#define elf_read_implies_exec(ex, executable_stack) \
    (mmap_is_ia32() && executable_stack == EXSTACK_DEFAULT)

5.4 버전에서 elf_read_implies_exec스택이 ( 세그먼트 를 통해 ) 실행 불가능한 것으로 명시 적으로 표시 되지 않은 경우 어떻게 true 값을 반환 했는지 주목하십시오 PT_GNU_STACK.

최신 소스에서 검사는 이제 더 방어 적입니다. ELF 바이너리에서 세그먼트가 발견 elf_read_implies_exec되지 않은 경우 32 비트 실행 파일에서만 적용됩니다 PT_GNU_STACK.

프로그램을 조립하고 연결 한 후 PT_GNU_STACK세그먼트를 찾지 못 했으므로 이것이 이유 일 수 있습니다.
이것이 실제로 문제이고 코드를 올바르게 따랐다면 스택을 바이너리에서 실행 가능하지 않은 것으로 설정하면 데이터 섹션이 더 이상 실행 파일로 매핑되지 않아야합니다 (Linux 5.4에서도 마찬가지 임).

7 JosephSible-ReinstateMonica Nov 14 2020 at 18:58

바이너리가 없습니다 PT_GNU_STACK. 따라서이 변경은 commit9fccc5c0c99f238aa1b0460fccbdb30a887e7036 에 의해 발생한 것으로 보입니다 .

From 9fccc5c0c99f238aa1b0460fccbdb30a887e7036 Mon Sep 17 00:00:00 2001
From: Kees Cook <[email protected]>
Date: Thu, 26 Mar 2020 23:48:17 -0700
Subject: x86/elf: Disable automatic READ_IMPLIES_EXEC on 64-bit

With modern x86 64-bit environments, there should never be a need for
automatic READ_IMPLIES_EXEC, as the architecture is intended to always
be execute-bit aware (as in, the default memory protection should be NX
unless a region explicitly requests to be executable).

There were very old x86_64 systems that lacked the NX bit, but for those,
the NX bit is, obviously, unenforceable, so these changes should have
no impact on them.

Suggested-by: Hector Marco-Gisbert <[email protected]>
Signed-off-by: Kees Cook <[email protected]>
Signed-off-by: Borislav Petkov <[email protected]>
Reviewed-by: Jason Gunthorpe <[email protected]>
Link: https://lkml.kernel.org/r/[email protected]
---
 arch/x86/include/asm/elf.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
index 397a1c74433ec..452beed7892bb 100644
--- a/arch/x86/include/asm/elf.h
+++ b/arch/x86/include/asm/elf.h
@@ -287,7 +287,7 @@ extern u32 elf_hwcap2;
  *                 CPU: | lacks NX*  | has NX, ia32     | has NX, x86_64 |
  * ELF:                 |            |                  |                |
  * ---------------------|------------|------------------|----------------|
- * missing PT_GNU_STACK | exec-all   | exec-all         | exec-all       |
+ * 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      |
  *
@@ -303,7 +303,7 @@ extern u32 elf_hwcap2;
  *
  */
 #define elf_read_implies_exec(ex, executable_stack)    \
-   (executable_stack == EXSTACK_DEFAULT)
+   (mmap_is_ia32() && executable_stack == EXSTACK_DEFAULT)
 
 struct task_struct;
 
-- 
cgit 1.2.3-1.el7

이것은 5.8 시리즈에 처음 등장했습니다. 어셈블리 파일이 프로젝트에 포함 된 경우 mmap의 예기치 않은 실행 권한 도 참조하십시오 .