LINUX] Seccomp

노션으로 옮김·2020년 5월 2일
3

Study

목록 보기
23/33
post-thumbnail

Seccomp

Seccomp(secure computing mode)는 리눅스에서 sandbox 기반으로 시스템콜을 허용 및 차단하여 공격의 가능성을 막는 리눅스 보안 메커니즘이다.

https://en.wikipedia.org/wiki/Seccomp

Seccomp 기능은 prctl()이라는 함수로 사용할 수 있다.

prctl

prctl()은 프로세스를 관리하는 함수이다.
정의는 다음과 같다.

int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);

중요한 것은 모드를 결정하는 option이다.
이 값에 따라서 나머지 전달되는 인자값이 달라지는데 이 인자에 PR_SET_SECCOMP 상수를 전달하면 seccomp를 설정할 수 있다.

SECCOMP_SET_MODE_STRICT

prctlseccomp를 설정하는 방법은 두 가지가 있다.
첫 번째가 SECCOMP_SET_MODE_STRICT 이다.

허용되는 시스템콜을 read(2), write(2), _exit(2), sigreturn(2)(but not exit_group(2))로 제한하는 것이다.
그 외 시스템 콜에 대해서는 SIGKILL을 발생시킨다.

설정방법은 다음과 같다.

prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT);

SECCOMP_SET_MODE_FILTER

Berkeley Packet Filter (BPF)라는 필터식을 이용해 시스템콜을 관리하는 모드이다.

BPF는 리눅스 커널에서 네트워크 패킷을 필터링할 때 사용하는 VM이다. 자세한 개념은 다음 링크를 참조

https://ssup2.github.io/theory_analysis/Linux_BPF/

이 모드를 사용하기 위해선 쓰레드의 no_new_privs 비트가 설정되어 있어야 하며 다음의 코드로 설정가능하다.

prctl(PR_SET_NO_NEW_PRIVS, 1);

그 후 아래의 코드로 필터식을 전달하면 된다.

prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, args);

argsprctl이 필터를 처리하기 위해 사용하는 sock_fprog라는 구조체의 주소값이다.

struct sock_fprog {
	unsigned short      len;    /* Number of BPF instructions */
	struct sock_filter *filter; /* Pointer to array of
                                              BPF instructions */
};

배열로 구성한 필터식(filter)을 저장하고 있다.
filter에 대해 좀 더 알아보자.


Berkeley Packet Filtering Expression

리눅스의 seccomp에서 시스템콜을 관리하기 위해 사용하며 prctl가 사용하는 sock_fprog 구조체 안에 저장되는 값이다.

필터식은 시스템콜이 호출될 때 실행되고, 필터식의 결과로 반환되는 값에 의해 실행여부가 결정된다.

sock_fprog 안의 filter의 정의를 보자.

struct sock_filter {            /* Filter block */
   __u16 code;                 /* Actual filter code */
   __u8  jt;                   /* Jump true */
   __u8  jf;                   /* Jump false */
   __u32 k;                    /* Generic multiuse field */
};

BPF는 어셈블리 언어와 비슷하다.

code 값으로 인스트럭션을 결정하고, jtjf는 조건에 따른 분기점, k는 사용되는 상수값이나 기타 다양한 용도로 사용된다.

예시를 확인해보자.
bobctf2017의 megabox 문제에서 사용하는 필터식이다.

root@kali:/work/ctf/BOBCTF2017/megabox_d# seccomp-tools dump ./megabox > dump
root@kali:/work/ctf/BOBCTF2017/megabox_d# cat dump
your name... this step is for performance :) line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x01 0x00 0xc000003e  if (A == ARCH_X86_64) goto 0003
 0002: 0x06 0x00 0x00 0x00000000  return KILL
 0003: 0x20 0x00 0x00 0x00000000  A = sys_number
 0004: 0x15 0x00 0x01 0x0000000f  if (A != rt_sigreturn) goto 0006
 0005: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0006: 0x15 0x00 0x01 0x000000e7  if (A != exit_group) goto 0008
 0007: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0008: 0x15 0x00 0x01 0x0000003c  if (A != exit) goto 0010
 0009: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0010: 0x15 0x00 0x01 0x00000000  if (A != read) goto 0012
 0011: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0012: 0x15 0x00 0x01 0x00000001  if (A != write) goto 0014
 0013: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0014: 0x06 0x00 0x00 0x00000000  return KILL

일부만 짚어서 보면, code0x20을 가질 때는 대입 연산을 나타내며 마지막 k가 대입되는 값을 가리킨다.
k4일 때는 아키텍쳐가 저장되고, 0일 때는 sys_number
가 저장되는 것을 알 수 있다.

마찬가지로, code0x15일 때는 if문을 나타낸다. k는 비교대상 값이며 비교 후에 참일 경우 jt에 해당하는 벡터만큼, 거짓일 경우 jf에 해당하는 벡터만큼 분기하는 것을 확인할 수 있다.

마지막으로 return 명령어이다. code0x06을 가질 때 return을 나타내며 이것은 최종적으로 실행여부를 결정하는 명령이다.

return ALLOW

는 호출된 시스템콜을 허용하겠다는 말이며

return KILL

은 종료하겠다는 의미이다.


출처

https://chromium.googlesource.com/chromium/src.git/+/master/docs/linux/sandboxing.md
: 샌드박싱 개념

https://www.kernel.org/doc/html/v4.16/userspace-api/seccomp_filter.html
: bpf

http://man7.org/linux/man-pages/man2/seccomp.2.html
: seccomp manpage

https://www.kernel.org/doc/Documentation/networking/filter.txt
: 필터식에 대해 자세히

0개의 댓글