Kernel 과 system call
리눅스 운영체제에서 핵심이되는 프로그램은 커널(kernel) 이다. 커널은 하드웨어 장치와 어플리케이션(소프트웨어) 사이에서 동작하며 두 계층 사이의 인터페이스를 담당한다. 그 외에도 메모리 관리, I/O 장치 관리 등 하드웨어의 모든 주요 기능을 제어하면서 효율적인 자원 관리를 수행하게 된다.
운영체제의 자원인 메모리 영역은 크게 2가지 공간인, Kernel space(커널 공간) 와 User space(사용자 공간) 로 구분할 수 있다. 사용자가 작성한 프로그램이나 어플리케이션들은 이 사용자 공간의 메모리를 사용하게 되고, 커널 자체의 코드에 대한 부분이나 커널 확장 등의 경우 커널 공간의 메모리를 사용한다.
또한 이 커널 공간에는 시스템 내부적인 제어를 위해 시스템콜(systemcall, SYSCALL)이라는 특별한 함수들이 존재한다. 프로세스를 제어하거나 파일/장치들을 조작하기 위한 함수들로 현재의 리눅스 운영체제에서는 약 470여개의 시스템 함수들이 있다.
사용자 공간에서 운영되는 어플리케이션 또한 이 시스템 함수를 사용하게 되며, 커널이 어플리케이션에게서 요청받아 시스템 함수를 호출(system call)하게 된다. 어플리케이션 뿐만 아니라 사용자가 터미널에서 사용하는 단순한 명령 조차도 내부적으로는 여러 번의 시스템콜을 수행하게 된다.
strace 명령어
$ which strace
/usr/bin/strace
사용자가 실행한 명령이 어떤 시스템 콜을 수행하는지 확인하기 위해 'strace' 명령을 수행할 수 있다. strace 는 시스템콜 확인 외에도, 시그널 전달, 프로세스 상태 변화 감지 등을 감시하는데도 사용되는, 사용자 공간의 툴(Tool)이다. 대부분의 리눅스 배포판에 기본적으로 설치되어 있다.
$ strace touch ./harang.log
execve("/usr/bin/touch", ["touch", "./harang.log"], [/* 24 vars */]) = 0
...
read(...)
fstat(...)
open("./harang.log", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, 0666) = 3
...
close(2) = 0
exit_group(0) = ?
+++ exited with 0 +++
$
이렇게 사용자가 touch 명령을 사용하여 harang.log 와 같은 빈 로그파일을 만드는 것만 해도 read(), open(), close() 등의 여러 시스템콜을 부르게 됨.
execve("/usr/bin/touch", ["touch", "./harang.log"], [/* 24 vars */]) = 0
위 첫번째 출력의 execve 출력에 대한 결과를 보면, 1) 사용한 명령어의 절대 경로와 2) 명령어에 대한 파라미터, 3) 실행에 사용된 환경 변수가 출력된다.
$ env | wc -l
24
환경 변수는 이렇게 env 명령어로 그 수를 확인해볼 수도 있다.
syscall을 사용하여 구동중인 프로세스를 확인하고 싶을 때는 프로세스의 PID 를 확인해야 한다.
$ pidof tail
26042
$ strace -p 26042
strace: Process 26042 attached
restart_syscall(<... resuming interrupted nanosleep ...>) = 0
fstat(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
read(3, "", 1024) = 0
nanosleep({1, 0}, 0x7fff5c8db4e0) = 0
...
위와 같이 백그라운드에서 수행되고 있는 tail 명령에 대하여 PID를 확인 후에 해당 명령에서 사용되고 있는 시스템 호출을 확인할 수 있다.
$ strace -c touch ./harang.log
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
25.76 0.000034 34 1 execve
24.24 0.000032 4 8 mmap
15.15 0.000020 5 4 mprotect
10.61 0.000014 4 4 open
6.06 0.000008 8 1 munmap
4.55 0.000006 6 1 1 access
3.03 0.000004 4 1 read
3.03 0.000004 1 7 close
3.03 0.000004 1 3 fstat
2.27 0.000003 1 4 brk
2.27 0.000003 3 1 arch_prctl
0.00 0.000000 0 1 dup2
0.00 0.000000 0 1 utimensat
------ ----------- ----------- --------- --------- ----------------
100.00 0.000132 37 1 total
$
위와 같이 '-c' 옵션을 추가하여 통계적으로 쉽게 확인할 수 있다. 이처럼 tail, touch 등의 간단한 명령어도 상당한 양의 시스템콜을 호출하게 되며, 일반적인 어플리케이션의 경우 초당 수백, 수천 번의 시스템콜을 수행하게 된다.
SECCOMP (SEcure COMPuting mode)
현재 배포되는 리눅스 운영체제에는 약 430 여개의 시스템 콜 함수들이 내장되어있다. 위에서 다루었듯이, 이러한 모든 시스템 콜 함수는 사용자 공간(user space)에서 사용자의 어플리케이션에 의해 사용된다.
그런데 실제로 사용자의 어플리케이션이 모든 시스템 콜 함수를 호출할 일은 없으며, 따라서 모든 시스템 콜 함수에 대한 접근 권한을 가질 필요는 없다.
그 예로, 2016년에 발견된 취약점인 Dirty cow(CVE-2016-5195) 를 들 수 있다. 이 취약점은 ptrace 라는 시스템 호출을 사용하는 프로그램이, 리눅스 커널 내의 메모리 서브 시스템에 대하여 COW(Copy-On-Write) 를 수행할 때 Race Condition 과 관련된 오류로 인해, 읽기 권한인 파일에 쓰기가 가능해지는 취약점이다. 이로 인해 중요 파일들을 덮어씀으로서 사실상 root 권한을 취득할 수 있게 된다.
기본적으로 Linux 커널은 사용자 공간에서 사용되는 프로그램에 의해 모든 시스템 호출이 가능하도록 허용하고 있으나, 위와 같은 취약점 공격 사례를 방지하기 위해, 어플리케이션이 실제로 작동하는데 필요한 시스템 콜 함수만을 사용하도록 제한하는 수단이 필요하다.
이런 경우 SECCOMP을 사용할 수 있다. 보안 컴퓨팅을 의미하는 Secure Computing Mode 의 줄임말인 Seccomp 은, 2005년(2005.5.8)에 처음 소개되었으며 버전 2.6.12부터 Linux 커널에 포함되었다. seccomp 은 샌드박스를 기반으로 한 매커니즘을 사용하여 어플리케이션이 필요한 시스템 호출만 사용하도록 커널 수준에서 제어할 수 있게 해준다.
$ grep -i seccomp /boot/config-$(uname -r)
CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
CONFIG_SECCOMP_FILTER=y
CONFIG_SECCOMP=y
위와 같이 seccomp 의 현재 설정을 확인할 수 있다. 가장 마지막 줄에 CONFIG_SECCOMP 의 값이 y로 설정되어 있을 경우, 현재 버전의 커널에서 seccomp 을 지원한다는 것을 의미한다.
$ apt install libseccomp-dev libseccomp2 seccomp
위 명령어로 설치 가능하다. (seccomp 의 상세 기능에 대하여는 별도 포스팅으로 다루기로 한다.)
seccomp 은 총 3개의 모드를 가지고 있다.
타입 | 의미 | 설명 |
mode0 | <DISABLED> | 비활성화 |
mode1 | <STRICT> | read(), write(), exit(), sigreturn() 을 제외한 모든 syscall 거부 |
mode2 | <FILTERED> | 선택적 필터링. 지정한 정책에 따라 syscall 마다 승인/거부 |
'소프트웨어 아키텍처 > Linux' 카테고리의 다른 글
[Ubuntu 보안] AppArmor 기본 개념과 설정 (0) | 2022.10.07 |
---|---|
[Ubuntu] 우분투 UFW 방화벽 사용하기 (0) | 2022.09.19 |
Linux - sudo 와 su 명령어의 차이 (0) | 2022.09.08 |
TLS 인증서 (3) - openssl 을 통해 key, csr, crt 파일 만들기 (0) | 2022.06.17 |
TLS 인증서 (2) - 신뢰할 수 있는 인증서 (0) | 2022.06.16 |
댓글