[목차]
1. Sandbox란?
2. SECCOMP란
2-1. SECCOMP 모드
2-1-1. STRICT_MODE
2-1-2. FILTER_MODE
2-2. 코드 적용 방법
2-2-1. seccomp 라이브러리 사용
2-2-2. BPF문법을 사용
3. seccomp-tools
1. Sandbox란
- 외부의 공격으로부터 시스템을 보호하기 위해 설계된 기법
- 애플리케이션이 꼭 필요한 시스템 콜만 실행하게 하거나, 파일의 접근만을 허용하게 하여
공격을 어렵게 하기 위한 보안 메커니즘 중 하나
(2가지 방법)
- Allow List : 전체를 차단한 후 원하는 시스템 콜만 허용
- Deny List : 전체를 허용한 후 차단할 시스템 콜만 선택
(sandbox에서 주의점)
- 각 애플리케이션은 실행 목적과 기능이 천차만별이기 때문에 정확한 이해가 있는 개발자가 샌드박스를 직접 명시해야한다.
- if) 샌드박스를 적용 시에 애플리케이션에 대한 기능과 보안 관련 지식이 부족하다면?
=> 서비스의 접근성을 과하게 해치거나 일부 기능이 정상적으로 실행되지 않거나, 우회가능
2. SECCOMP이란?
(SECure COMputing mode)
- 리눅스 커널에서 샌드박스 메커니즘을 제공하는 컴퓨터 보안 기능
- 이 기능을 사용시 애플리케이션에서 불필요한 시스템 콜의 호출을 방지가능
(SECCOMP 구성 코드)
int __secure_computing(const struct seccomp_data *sd) {
int mode = current->seccomp.mode;
int this_syscall;
...
this_syscall = sd ? sd->nr : syscall_get_nr(current, task_pt_regs(current));
switch (mode) {
case SECCOMP_MODE_STRICT:
__secure_computing_strict(this_syscall); /* may call do_exit */
return 0;
case SECCOMP_MODE_FILTER:
return __seccomp_filter(this_syscall, sd, false);
...
}
}
[SECCOMP이 생긴 이유]
- 애플리케이션에서 외부의 시스템 명령어를 실행하지 않는다면 execve와 같은 시스템 콜이 실행될 필요가 없다.
- 보통 이 시스템 콜은 공격시 임의 명령어를 실행하기 위해 사용된다.
- 따라서 SECCOMP 기능을 설정하면 이런 시스템 콜은 허용되지 않아서 즉시 종료되어
애플리케이션이 취약점이 존재해도 외부의 공격으로부터 피해를 최소화 가능하다.
(SECCOMP mode)
- STRICT_MODE
- read, write, exit, sigreturn 시스템 콜의 호출만을 허용 - FILTER_MODE
- 원하는 시스템 콜의 호출을 허용하거나 거부할 수 있음
(SECCOMP 코드 적용 방법)
- seccomp 라이브러리 함수를 이용한 방법
- 필터링에 주로 쓰이는 BPF문법을 적용하는 방법
<BPF - Berkeley Packet Filter>
(적용 예시)
// Name: strict_mode.c
// Compile: gcc -o strict_mode strict_mode.c
#include <fcntl.h>
#include <linux/seccomp.h>
#include <sys/prctl.h>
#include <unistd.h>
void init_filter() { prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT); }
int main() {
char buf[256];
int fd = 0;
init_filter();
write(1, "OPEN!\n", 6);
fd = open("/bin/sh", O_RDONLY);
write(1, "READ!\n", 6);
read(fd, buf, sizeof(buf) - 1);
write(1, buf, sizeof(buf));
return 0;
}
root@74de726ce0af:/home/user/study# ./strict_mode
OPEN
Killed
=> 허용되지 않는 open 시스템콜이 실행되었기 때문에 "OPEN!" 출력이후 프로그램이 종료
2-1. SECCOMP 모드
2-1-1. STRICT_MODE
: read, write, exit, sigreturn 시스템 콜의 호출만을 허용하여
이외에 시스템 콜 호출 요청이 들어오면 SIGKILL 시그널을 발생하고 프로그램을 종료
[주의점]
해당 mode는 매우 적은 시스템 콜만을 허용하여
다양한 기능을 수행하는 애플리케이션에서 적용할 수 없음

(동작 순서)
- 애플리케이션에서 시스템 콜이 호출
- __secure_computing함수에 먼저 진입
- 해당 함수에서 전달된 시스템 콜 번호가 model_syscalls, model_syscalls_32에
미리 정의된 번호와 일치하는지 검사 - 일치하지 않는다면 SIGKILL시그널을 전달하고 SECCOMP_RET_KILL을 반환
<model_syscall배열 - 시스템 콜의 번호를 저장하고 있는 변수>
2-1-2. FILTER_MODE
- 원하는 시스템 콜의 호출을 허용하거나 거부 가능
- 애플리케이션 기능에 맞게 유연하게 시스템 콜의 실행을 허용, 거부 가능
(알아야하는 SECCOMP에서 제공하는 함수)

## 2-2. 코드 적용 방법
### 2-2-1. SECCOMP 라이브러리 사용
(ALLOW_LIST)
// Name: libseccomp_alist.c
// Complie: gcc -o libseccomp_alist libseccomp_alist.c -lseccomp
#include <fcntl.h>
#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <unistd.h>
void sandbox() {
scmp_filter_ctx ctx;
ctx = seccomp_init(SCMP_ACT_KILL);
if (ctx == NULL){
printf("seccomp error\n");
exit(0);
}
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigreturn), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 0);
seccomp_load(ctx);
}
int banned() {fork();} //
int main(int argc, char *argv[]){
char buf[256];
int fd;
memset(buf, 0, sizeof(buf));
sandbox();
if(argc < 2){ // ????
banned();
}
fd = open("/bin/sh", O_RDONLY);
read(fd, buf, sizeof(buf) -1);
write(1, buf, sizeof(buf));
}
- "SCMP_ACT_KILL"을 통해 모든 시스템 콜의 호출을 차단
- seccomp_rule_add함수의 3번째 인자로 허용할 시스템콜 추가
=> 가변인자로 값을 전달하지 않으면 fork함수가 실행되어 프로그램 종료
(DENY_LIST)
// Name: libseccomp_dlist.c
// Compile: gcc -o libseccomp_dlist libseccomp_dlist.c -lseccomp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/prctl.h>
#include <seccomp.h>
void sandbox(){
scmp_filter_ctx ctx;
ctx = seccomp_init(SCMP_ACT_ALLOW); // 모든 시스템콜을 허용
if(ctx == NULL){
exit(0);
}
// Deny규칙 추가
seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(open), 0);
seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(openat), 0);
seccomp_load(ctx);
}
int main(int argc, char *argv[]){
char buf[256];
int fd;
memset(buf, 0, sizeof(buf));
sandbox();
fd = open("/bin/sh", O_RDONLY);
read(fd, buf, sizeof(buf)-1);
write(1, buf, sizeof(buf));
}
- "SCMP_ACT_ALL"을 통해 모든 시스템 콜의 호출을 허용
- seccomp_rule_add함수의 3번째 인자로 차단할 시스템콜 추가
2-2-2. BPF문법을 사용
: 라이브러리 함수를 통해 규칙을 정의한 것과 같이 BPF를 사용해서
특정 시스템 콜 호출시에 어떻게 처리할지 명령어를 통해 구현할 수 있음
BPF란?
커널에서 지원하는 Virtual Machine(VM)으로,
본래에는 네트워크 패킷을 분석하고 필터링하는 목적으로 사용
=> 임의 데이터를 비교하고, 결과에 따라 특정 구문으로 분기하는 명령어를 제공
<VM인만큼 다양한 명령어와 타입이 존재>
(SECCOMP을 적용하는데 알아야하는 명령어)

BPF Macro
(1) BPF_Macro
BPF_STMT(opcode, operand)
oprand에 해당하는 값을 명시한 opcode로 값을 가져온다.
< opcode - 인자로 전달된 값에서 몇번째 인덱스에서 몇바이트를 가져올 것인지를 지정가능 >
(2) BPF_JUMP
BPF_JUMP(opcode, operand, true_offset, false_offset)
- BPF_STMT매크로를 통해 저장한 값과 oprand를 opcode에 정의한 코드로 비교
- 비교 결과에 따라 특정 오프셋으로 분기
(ALLOW LIST)
ex)
BPF를 통해 지정한 시스템 콜의 호출만을 허용하는 예제 코드
// Name: secbpf_alist.c
// Compile: gcc -o secbpf_alist secbpf_alist.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <stddef.h>
#include <fcntl.h>
#include <linux/audit.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/unistd.h> // 왜 따로 쓰지?
#define ALLOW_SYSCALL(name) \
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_##name, 0, 1), \
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW)
#define KILL_PROCESS BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL)
#define syscall_nr (offsetof(struct seccomp_data, nr))
#define arch_nr (offsetof(struct seccomp_data, arch))
/* architecture x86_64 */
#define ARCH_NR AUDIT_ARCH_X86_64
int sandbox() {
struct sock_filter filter[] = {
/* Validate architecture. */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, arch_nr),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARCH_NR, 1, 0),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL),
/* Get system call number. */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_nr),
/* List allowed syscalls. */
ALLOW_SYSCALL(rt_sigreturn),
ALLOW_SYSCALL(open),
ALLOW_SYSCALL(openat),
ALLOW_SYSCALL(read),
ALLOW_SYSCALL(write),
ALLOW_SYSCALL(exit_group), // 그냥 exit가 아니라 exit_group?
KILL_PROCESS,
};
struct sock_fprog prog = {
.len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
.filter = filter,
};
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
perror("prctl(PR_SET_NO_NEW_PRIVS)\n");
return -1;
}
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1) {
perror("Seccomp filter error\n");
return -1;
}
return 0;
}
void banned() { fork(); }
int main(int argc, char* argv[]) {
char buf[256];
int fd;
memset(buf, 0, sizeof(buf));
sandbox();
if (argc < 2) {
banned();
}
fd = open("/bin/sh", O_RDONLY);
read(fd, buf, sizeof(buf) - 1);
write(1, buf, sizeof(buf));
return 0;
}
=> sandbox함수 - filter 구조체에 BPF코드가 작성되어이있음
1 - 아키텍처 검사

=> 현재 아키텍처가 x86_64가 아니라면 프로그램 종료
2 - 시스템 콜 검사

- 호출된 시스템 콜의 번호를 저장
- 이후 ALLOW_SYSCALL매크로로 허용할 시스템콜 저장
(DENY_LIST)
ex)
BPF를 통해 지정한 시스템 콜을 호출하지 못하도록 하는 예제코드
// Name: secbpf_dlist.c
// Compile: gcc -o secbpf_dlist secbpf_dlist.c
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <fcntl.h>
#include <linux/audit.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/unistd.h>
#define DENY_SYSCALL(name) \
BPF_JUMP(BPF_RET + BPF_JEQ + BPF_K, __NR_##name, 0, 1), \
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL)
#define MAINTAIN_PROCESS \
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW)
#define syscall_nr \
(offsetof(struct seccomp_data, nr))
#define arch_nr \
(offsetof(struct seccomp_data, arch))
/* architecture x86_64 */
#define ARCH_NR AUDIT_ARCH_X86_64
int sandbox(){
struct sock_filter filter[] = {
/* Validate architecture */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, arch_nr),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARCH_NR, 1, 0),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL),
/* Get system call number */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_nr),
/* List allowed syscalls */
DENY_SYSCALL(open),
DENY_SYSCALL(openat),
MAINTAIN_PROCESS,
};
struct sock_fprog prog = {
.len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
.filter = filter,
};
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1){
perror("prctl(PR_SET_NO_NEW_PRIVS)\n");
return -1;
}
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1){
perror("Seccomp filter error\n");
return -1;
}
return 0;
}
int main(int argc, char* argv[]){
char buf[256];
int fd;
memset(buf, 0, sizeof(buf));
sandbox();
fd = open("/bin/sh", O_RDONLY);
read(fd, buf, sizeof(buf)-1);
write(1, buf, sizeof(buf));
return 0;
}
1 - 아키텍처 검사

=> 현재 아키텍처가 x86_64가 아니라면 프로그램 종료
2 - 시스템 콜 검사

- 호출된 시스템 콜의 번호를 저장
- 이후 DENY_SYSCALL매크로로 차단할 시스템콜 지정
3. seccomp-tools
SECCOMP가 적용된 바이너리의 분석을 도울뿐만 아니라
BPF 어셈블러/디스어셈블러를 제공하는 유용한 도구
(설치 방법)
$ sudo apt install gcc ruby-dev
$ gem install seccomp-tools
=> 관련 자세한 내용 및 사용법(https://github.com/david942j/seccomp-tools)
(사용 방법)
$ seccomp-tools dump [파일경로]

=> ALLOW_LIST - 허용하는 시스템 콜이 초록색으로 표시되어서 나옴
=> 추가적인 사용방법은 따로 찾아보고 정리해야할듯
## 참고링크
[목차]
1. Sandbox란?
2. SECCOMP란
2-1. SECCOMP 모드
2-1-1. STRICT_MODE
2-1-2. FILTER_MODE
2-2. 코드 적용 방법
2-2-1. seccomp 라이브러리 사용
2-2-2. BPF문법을 사용
3. seccomp-tools
1. Sandbox란
- 외부의 공격으로부터 시스템을 보호하기 위해 설계된 기법
- 애플리케이션이 꼭 필요한 시스템 콜만 실행하게 하거나, 파일의 접근만을 허용하게 하여
공격을 어렵게 하기 위한 보안 메커니즘 중 하나
(2가지 방법)
- Allow List : 전체를 차단한 후 원하는 시스템 콜만 허용
- Deny List : 전체를 허용한 후 차단할 시스템 콜만 선택
(sandbox에서 주의점)
- 각 애플리케이션은 실행 목적과 기능이 천차만별이기 때문에 정확한 이해가 있는 개발자가 샌드박스를 직접 명시해야한다.
- if) 샌드박스를 적용 시에 애플리케이션에 대한 기능과 보안 관련 지식이 부족하다면?
=> 서비스의 접근성을 과하게 해치거나 일부 기능이 정상적으로 실행되지 않거나, 우회가능
2. SECCOMP이란?
(SECure COMputing mode)
- 리눅스 커널에서 샌드박스 메커니즘을 제공하는 컴퓨터 보안 기능
- 이 기능을 사용시 애플리케이션에서 불필요한 시스템 콜의 호출을 방지가능
(SECCOMP 구성 코드)
int __secure_computing(const struct seccomp_data *sd) {
int mode = current->seccomp.mode;
int this_syscall;
...
this_syscall = sd ? sd->nr : syscall_get_nr(current, task_pt_regs(current));
switch (mode) {
case SECCOMP_MODE_STRICT:
__secure_computing_strict(this_syscall); /* may call do_exit */
return 0;
case SECCOMP_MODE_FILTER:
return __seccomp_filter(this_syscall, sd, false);
...
}
}
[SECCOMP이 생긴 이유]
- 애플리케이션에서 외부의 시스템 명령어를 실행하지 않는다면 execve와 같은 시스템 콜이 실행될 필요가 없다.
- 보통 이 시스템 콜은 공격시 임의 명령어를 실행하기 위해 사용된다.
- 따라서 SECCOMP 기능을 설정하면 이런 시스템 콜은 허용되지 않아서 즉시 종료되어
애플리케이션이 취약점이 존재해도 외부의 공격으로부터 피해를 최소화 가능하다.
(SECCOMP mode)
- STRICT_MODE
- read, write, exit, sigreturn 시스템 콜의 호출만을 허용 - FILTER_MODE
- 원하는 시스템 콜의 호출을 허용하거나 거부할 수 있음
(SECCOMP 코드 적용 방법)
- seccomp 라이브러리 함수를 이용한 방법
- 필터링에 주로 쓰이는 BPF문법을 적용하는 방법
<BPF - Berkeley Packet Filter>
(적용 예시)
// Name: strict_mode.c
// Compile: gcc -o strict_mode strict_mode.c
#include <fcntl.h>
#include <linux/seccomp.h>
#include <sys/prctl.h>
#include <unistd.h>
void init_filter() { prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT); }
int main() {
char buf[256];
int fd = 0;
init_filter();
write(1, "OPEN!\n", 6);
fd = open("/bin/sh", O_RDONLY);
write(1, "READ!\n", 6);
read(fd, buf, sizeof(buf) - 1);
write(1, buf, sizeof(buf));
return 0;
}
root@74de726ce0af:/home/user/study# ./strict_mode
OPEN
Killed
=> 허용되지 않는 open 시스템콜이 실행되었기 때문에 "OPEN!" 출력이후 프로그램이 종료
2-1. SECCOMP 모드
2-1-1. STRICT_MODE
: read, write, exit, sigreturn 시스템 콜의 호출만을 허용하여
이외에 시스템 콜 호출 요청이 들어오면 SIGKILL 시그널을 발생하고 프로그램을 종료
[주의점]
해당 mode는 매우 적은 시스템 콜만을 허용하여
다양한 기능을 수행하는 애플리케이션에서 적용할 수 없음

(동작 순서)
- 애플리케이션에서 시스템 콜이 호출
- __secure_computing함수에 먼저 진입
- 해당 함수에서 전달된 시스템 콜 번호가 model_syscalls, model_syscalls_32에
미리 정의된 번호와 일치하는지 검사 - 일치하지 않는다면 SIGKILL시그널을 전달하고 SECCOMP_RET_KILL을 반환
<model_syscall배열 - 시스템 콜의 번호를 저장하고 있는 변수>
2-1-2. FILTER_MODE
- 원하는 시스템 콜의 호출을 허용하거나 거부 가능
- 애플리케이션 기능에 맞게 유연하게 시스템 콜의 실행을 허용, 거부 가능
(알아야하는 SECCOMP에서 제공하는 함수)

## 2-2. 코드 적용 방법
### 2-2-1. SECCOMP 라이브러리 사용
(ALLOW_LIST)
// Name: libseccomp_alist.c
// Complie: gcc -o libseccomp_alist libseccomp_alist.c -lseccomp
#include <fcntl.h>
#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <unistd.h>
void sandbox() {
scmp_filter_ctx ctx;
ctx = seccomp_init(SCMP_ACT_KILL);
if (ctx == NULL){
printf("seccomp error\n");
exit(0);
}
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigreturn), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 0);
seccomp_load(ctx);
}
int banned() {fork();} //
int main(int argc, char *argv[]){
char buf[256];
int fd;
memset(buf, 0, sizeof(buf));
sandbox();
if(argc < 2){ // ????
banned();
}
fd = open("/bin/sh", O_RDONLY);
read(fd, buf, sizeof(buf) -1);
write(1, buf, sizeof(buf));
}
- "SCMP_ACT_KILL"을 통해 모든 시스템 콜의 호출을 차단
- seccomp_rule_add함수의 3번째 인자로 허용할 시스템콜 추가
=> 가변인자로 값을 전달하지 않으면 fork함수가 실행되어 프로그램 종료
(DENY_LIST)
// Name: libseccomp_dlist.c
// Compile: gcc -o libseccomp_dlist libseccomp_dlist.c -lseccomp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/prctl.h>
#include <seccomp.h>
void sandbox(){
scmp_filter_ctx ctx;
ctx = seccomp_init(SCMP_ACT_ALLOW); // 모든 시스템콜을 허용
if(ctx == NULL){
exit(0);
}
// Deny규칙 추가
seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(open), 0);
seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(openat), 0);
seccomp_load(ctx);
}
int main(int argc, char *argv[]){
char buf[256];
int fd;
memset(buf, 0, sizeof(buf));
sandbox();
fd = open("/bin/sh", O_RDONLY);
read(fd, buf, sizeof(buf)-1);
write(1, buf, sizeof(buf));
}
- "SCMP_ACT_ALL"을 통해 모든 시스템 콜의 호출을 허용
- seccomp_rule_add함수의 3번째 인자로 차단할 시스템콜 추가
2-2-2. BPF문법을 사용
: 라이브러리 함수를 통해 규칙을 정의한 것과 같이 BPF를 사용해서
특정 시스템 콜 호출시에 어떻게 처리할지 명령어를 통해 구현할 수 있음
BPF란?
커널에서 지원하는 Virtual Machine(VM)으로,
본래에는 네트워크 패킷을 분석하고 필터링하는 목적으로 사용
=> 임의 데이터를 비교하고, 결과에 따라 특정 구문으로 분기하는 명령어를 제공
<VM인만큼 다양한 명령어와 타입이 존재>
(SECCOMP을 적용하는데 알아야하는 명령어)

BPF Macro
(1) BPF_Macro
BPF_STMT(opcode, operand)
oprand에 해당하는 값을 명시한 opcode로 값을 가져온다.
< opcode - 인자로 전달된 값에서 몇번째 인덱스에서 몇바이트를 가져올 것인지를 지정가능 >
(2) BPF_JUMP
BPF_JUMP(opcode, operand, true_offset, false_offset)
- BPF_STMT매크로를 통해 저장한 값과 oprand를 opcode에 정의한 코드로 비교
- 비교 결과에 따라 특정 오프셋으로 분기
(ALLOW LIST)
ex)
BPF를 통해 지정한 시스템 콜의 호출만을 허용하는 예제 코드
// Name: secbpf_alist.c
// Compile: gcc -o secbpf_alist secbpf_alist.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <stddef.h>
#include <fcntl.h>
#include <linux/audit.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/unistd.h> // 왜 따로 쓰지?
#define ALLOW_SYSCALL(name) \
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_##name, 0, 1), \
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW)
#define KILL_PROCESS BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL)
#define syscall_nr (offsetof(struct seccomp_data, nr))
#define arch_nr (offsetof(struct seccomp_data, arch))
/* architecture x86_64 */
#define ARCH_NR AUDIT_ARCH_X86_64
int sandbox() {
struct sock_filter filter[] = {
/* Validate architecture. */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, arch_nr),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARCH_NR, 1, 0),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL),
/* Get system call number. */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_nr),
/* List allowed syscalls. */
ALLOW_SYSCALL(rt_sigreturn),
ALLOW_SYSCALL(open),
ALLOW_SYSCALL(openat),
ALLOW_SYSCALL(read),
ALLOW_SYSCALL(write),
ALLOW_SYSCALL(exit_group), // 그냥 exit가 아니라 exit_group?
KILL_PROCESS,
};
struct sock_fprog prog = {
.len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
.filter = filter,
};
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
perror("prctl(PR_SET_NO_NEW_PRIVS)\n");
return -1;
}
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1) {
perror("Seccomp filter error\n");
return -1;
}
return 0;
}
void banned() { fork(); }
int main(int argc, char* argv[]) {
char buf[256];
int fd;
memset(buf, 0, sizeof(buf));
sandbox();
if (argc < 2) {
banned();
}
fd = open("/bin/sh", O_RDONLY);
read(fd, buf, sizeof(buf) - 1);
write(1, buf, sizeof(buf));
return 0;
}
=> sandbox함수 - filter 구조체에 BPF코드가 작성되어이있음
1 - 아키텍처 검사

=> 현재 아키텍처가 x86_64가 아니라면 프로그램 종료
2 - 시스템 콜 검사

- 호출된 시스템 콜의 번호를 저장
- 이후 ALLOW_SYSCALL매크로로 허용할 시스템콜 저장
(DENY_LIST)
ex)
BPF를 통해 지정한 시스템 콜을 호출하지 못하도록 하는 예제코드
// Name: secbpf_dlist.c
// Compile: gcc -o secbpf_dlist secbpf_dlist.c
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <fcntl.h>
#include <linux/audit.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/unistd.h>
#define DENY_SYSCALL(name) \
BPF_JUMP(BPF_RET + BPF_JEQ + BPF_K, __NR_##name, 0, 1), \
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL)
#define MAINTAIN_PROCESS \
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW)
#define syscall_nr \
(offsetof(struct seccomp_data, nr))
#define arch_nr \
(offsetof(struct seccomp_data, arch))
/* architecture x86_64 */
#define ARCH_NR AUDIT_ARCH_X86_64
int sandbox(){
struct sock_filter filter[] = {
/* Validate architecture */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, arch_nr),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARCH_NR, 1, 0),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL),
/* Get system call number */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_nr),
/* List allowed syscalls */
DENY_SYSCALL(open),
DENY_SYSCALL(openat),
MAINTAIN_PROCESS,
};
struct sock_fprog prog = {
.len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
.filter = filter,
};
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1){
perror("prctl(PR_SET_NO_NEW_PRIVS)\n");
return -1;
}
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1){
perror("Seccomp filter error\n");
return -1;
}
return 0;
}
int main(int argc, char* argv[]){
char buf[256];
int fd;
memset(buf, 0, sizeof(buf));
sandbox();
fd = open("/bin/sh", O_RDONLY);
read(fd, buf, sizeof(buf)-1);
write(1, buf, sizeof(buf));
return 0;
}
1 - 아키텍처 검사

=> 현재 아키텍처가 x86_64가 아니라면 프로그램 종료
2 - 시스템 콜 검사

- 호출된 시스템 콜의 번호를 저장
- 이후 DENY_SYSCALL매크로로 차단할 시스템콜 지정
3. seccomp-tools
SECCOMP가 적용된 바이너리의 분석을 도울뿐만 아니라
BPF 어셈블러/디스어셈블러를 제공하는 유용한 도구
(설치 방법)
$ sudo apt install gcc ruby-dev
$ gem install seccomp-tools
=> 관련 자세한 내용 및 사용법(https://github.com/david942j/seccomp-tools)
(사용 방법)
$ seccomp-tools dump [파일경로]

=> ALLOW_LIST - 허용하는 시스템 콜이 초록색으로 표시되어서 나옴
=> 추가적인 사용방법은 따로 찾아보고 정리해야할듯
## 참고링크