앞서 SECCOMP가 무엇이고, 어떤 식으로 동작하는지에 대해서 간단히 알아보았다.
그러면 이 SECCOMP가 실제로 어떻게 적용되어 있고, 이를 우회하는 방법에 대해서 알아보자.
seccomp-tools를 사용해서 확인한다. 다음은 그 예시이다 :
seccomp-tools dump ./bypass_seccomp
line CODE JT JF K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x08 0xc000003e if (A != ARCH_X86_64) goto 0010
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x05 0xffffffff if (A != 0xffffffff) goto 0010
0005: 0x15 0x04 0x00 0x00000001 if (A == write) goto 0010
0006: 0x15 0x03 0x00 0x00000002 if (A == open) goto 0010
0007: 0x15 0x02 0x00 0x0000003b if (A == execve) goto 0010
0008: 0x15 0x01 0x00 0x00000142 if (A == execveat) goto 0010
0009: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0010: 0x06 0x00 0x00 0x00000000 return KILL
do_syscall_64함수의 내부를 볼 필요가 있다.
__visible noinstr void do_syscall_64(struct pt_regs *regs, int nr)
{
add_random_kstack_offset();
nr = syscall_enter_from_user_mode(regs, nr);
instrumentation_begin();
if (!do_syscall_x64(regs, nr) && !do_syscall_x32(regs, nr) && nr != -1) {
/* Invalid system call, but still a system call. */
regs->ax = __x64_sys_ni_syscall(regs);
}
instrumentation_end();
syscall_exit_to_user_mode(regs);
}
do_syscall_x64
static __always_inline bool do_syscall_x64(struct pt_regs *regs, int nr)
{
/*
* Convert negative numbers to very high and thus out of range
* numbers for comparisons.
*/
unsigned int unr = nr;
if (likely(unr < NR_syscalls)) {
unr = array_index_nospec(unr, NR_syscalls);
regs->ax = sys_call_table[unr](regs);
return true;
}
return false;
}
do_syscall_x32
static __always_inline bool do_syscall_x32(struct pt_regs *regs, int nr)
{
/*
* Adjust the starting offset of the table, and convert numbers
* < __X32_SYSCALL_BIT to very high and thus out of range
* numbers for comparisons.
*/
unsigned int xnr = nr - __X32_SYSCALL_BIT;
if (IS_ENABLED(CONFIG_X86_X32_ABI) && likely(xnr < X32_NR_syscalls)) {
xnr = array_index_nospec(xnr, X32_NR_syscalls);
regs->ax = x32_sys_call_table[xnr](regs);
return true;
}
return false;
}
__X32_SYSCALL_BIT
을 빼주는 부분이다.#define __X32_SYSCALL_BIT 0x40000000
그래서 0x40000000이 어떤 문제를 일으킬 수 있을까?
- 위의 SECCOMP에 따르면, execve를 실행할 수 없기 때문에 쉘을 따는 것은 어려워 보인다. 하지만, 0x40000000을 비교하는 부분이 없다면?
- syscall을 호출할 때에 32비트 execve syscall번호 + 0x40000000한 값을 인자로 넘겨주게 되면 SECCOMP에 걸리지 않고 성공적으로 실행될 수 있게 된다.
요기까지..
출처