seccomp argument restriction
seccomp가 syscall을 제한 할 수 있는 것은 모두가 알것이다. 그렇다면 seccomp가 syscall을 할때 인자를 제한할 수 있는 것도 아는가? 하면 대부분이 모른다고 할 것이다.
https://dreamhack.io/wargame/challenges/2076
해당 문제를 풀면서 얻은 지식을 다룬다.
v5 = seccomp_init(0LL);
seccomp_rule_add(v5, 2147418112LL, 0LL, 0LL);
seccomp_rule_add(v5, 2147418112LL, 1LL, 0LL);
seccomp_rule_add(v5, 2147418112LL, 2LL, 1LL);
seccomp_rule_add(v5, 2147418112LL, 60LL, 0LL);
seccomp_rule_add(v5, 2147418112LL, 231LL, 0LL);seccomp는 일반적인 리눅스의 deny/allow list 처럼 처음에 모두를 차단할것인지 허가 할것인지를 정하고 이후에 deny/allow list를 작성하는 방식이다.
위 코드도 seccomp_init(0LL) 으로 모든 syscall을 차단하고 이후에 허가를 하는 방식이다.
int seccomp_rule_add(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, ...);함수 원형은 다음과 같은데.. 위에 코드를 보면
seccomp_rule_add(v5, 2147418112LL, 2LL, 1LL); 얘만 arg_cnt가 1로 되어있다. 얘를 분석하는 것이 해당 문제의 킥이었다.
arg_cnt는 추가적인 인자의 조건의 개수를 말한다. 이것이 1이라는 것은 syscall의 인자에 대한 조건이 있다는 것을 의미한다.
근데 왜 조건은 안보일까? 그건 ida가 해석을 하지 못한것이다. 따라서 어셈블리어로 보면
pxor xmm0, xmm0
movaps [rbp+var_20], xmm0
movq [rbp+var_10], xmm0
mov dword ptr [rbp+var_20+4], 4
lea rax, area
mov qword ptr [rbp+var_20+8], rax
mov rdi, [rbp+var_28]
sub rsp, 8
sub rsp, 18h
mov rcx, rsp
mov rax, qword ptr [rbp+var_20]
mov rdx, qword ptr [rbp+var_20+8]
mov [rcx], rax
mov [rcx+8], rdx
mov rax, [rbp+var_10]
mov [rcx+10h], rax
mov ecx, 1
mov edx, 2
mov esi, 7FFF0000h
mov eax, 0
call _seccomp_rule_add다음과 같은데
mov rax, qword ptr [rbp+var_20] ; [rbp+var_20] → 8바이트
mov rdx, qword ptr [rbp+var_20+8] ; [rbp+var_20+8] → 8바이트
mov [rcx], rax ; [rsp] = var_20[0:7]
mov [rcx+8], rdx ; [rsp+8] = var_20[8:15]
mov rax, [rbp+var_10] ; [rbp+var_10] = 8바이트
mov [rcx+10h], rax ; [rsp+16] = var_10이 부분에서 스택에 뭔가를 넣는 것을 알 수 있다.
이걸 분석해보면
offset | size | 의미
-----------------------
+0 | 4 | arg index (예: 0)
+4 | 4 | 비교 연산자 (예: SCMP_CMP_EQ → 0)
+8 | 8 | 비교 값 1 (예: 0xADDR or int)
+16 | 8 | 비교 값 2 (보통은 안씀)다음과 같다. 즉,
seccomp_rule_add(
ctx, // rdi
SCMP_ACT_ALLOW, // rsi = 0x7FFF0000
2, // rdx = syscall 번호
1, // rcx = 조건 1개
SCMP_CMP(0, SCMP_CMP_MASKED_EQ, &area, 0) // 스택에 올려짐
);다음과 같은것
여기서 의문이 들수 있는데 r8,r9 레지스터가 아직 남았는데 왜 seccomp_rule_add 함수의 5번째 인자가 스택에 들어가냐고 물을수 있다. 합당한 의문이다.
정확히 말하자면, 지금 보면 SCMP_CMP(0, SCMP_CMP_MASKED_EQ, &area, 0) 으로 구조체 자체가 인자로 들어간다.
SysV x86‑64 ABI에서는 aggregate가 2개의 8바이트(≤16B) 를 넘으면 레지스터로 쪼개 전달하지 않고 “메모리로 전달” 한다. 이런한 호출 규약을 지키지 위해서이다.
따라서 다음과 같이 스택에 넣어지기 때문에 ida도 파악하지 못한 것같다.
SCMP_CMP(0, SCMP_CMP_MASKED_EQ, &area, 0)어쨋든 해당 조건에 대해서 분석을 해보면
0 → 0번째 인자 ( rdi )
SCMP_CMP_MASKED_EQ → 같아야함
&area → area 라벨의 주소와
0 → 이럴때만 허용
이다. 이런 세세한 조건을 만들 수 있다는 것을 알았다.