본문 바로가기
  • 오늘처럼
소프트웨어 아키텍처/Linux

strace 를 통한 syscall 확인과 seccomp

by bluefriday 2022. 9. 22.
반응형

Kernel 과 system call

리눅스 운영체제에서 핵심이되는 프로그램은 커널(kernel) 이다. 커널은 하드웨어 장치와 어플리케이션(소프트웨어) 사이에서 동작하며 두 계층 사이의 인터페이스를 담당한다. 그 외에도 메모리 관리, I/O 장치 관리 등 하드웨어의 모든 주요 기능을 제어하면서 효율적인 자원 관리를 수행하게 된다. 

Linux Kernel

운영체제의 자원인 메모리 영역은 크게 2가지 공간인, Kernel space(커널 공간) 와 User space(사용자 공간) 로 구분할 수 있다. 사용자가 작성한 프로그램이나 어플리케이션들은 이 사용자 공간의 메모리를 사용하게 되고, 커널 자체의 코드에 대한 부분이나 커널 확장 등의 경우 커널 공간의 메모리를 사용한다.

또한 이 커널 공간에는 시스템 내부적인 제어를 위해 시스템콜(systemcall, SYSCALL)이라는 특별한 함수들이 존재한다. 프로세스를 제어하거나 파일/장치들을 조작하기 위한 함수들로 현재의 리눅스 운영체제에서는 약 470여개의 시스템 함수들이 있다.

System Call

사용자 공간에서 운영되는 어플리케이션 또한 이 시스템 함수를 사용하게 되며, 커널이 어플리케이션에게서 요청받아 시스템 함수를 호출(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 마다 승인/거부

 

댓글