man ptrace 获取到如下内容:
Since Linux 4.4, the ptrace(2) PTRACE_SECCOMP_GET_FILTER operation can be used to dump a process's seccomp filters.
PTRACE_SECCOMP_GET_FILTER (since Linux 4.4)
This operation allows the tracer to dump the tracee's classic BPF filters.
addr is an integer specifying the index of the filter to be dumped. The most recently installed filter has the index 0. If addr is greater than the num‐
ber of installed filters, the operation fails with the error ENOENT.
data is either a pointer to a struct sock_filter array that is large enough to store the BPF program, or NULL if the program is not to be stored.
Upon success, the return value is the number of instructions in the BPF program. If data was NULL, then this return value can be used to correctly size
the struct sock_filter array passed in a subsequent call.
This operation fails with the error EACCES if the caller does not have the CAP_SYS_ADMIN capability or if the caller is in strict or filter seccomp mode.
If the filter referred to by addr is not a classic BPF filter, the operation fails with the error EMEDIUMTYPE.
This operation is available if the kernel was configured with both the CONFIG_SECCOMP_FILTER and the CONFIG_CHECKPOINT_RESTORE options.
manual 信息表明使用 PTRACE_SECCOMP_GET_FILTER 参数就能够获取到进程的 seccomp filter 内容。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static int install_filter(int nr, int arch, int error) {
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (offsetof(struct seccomp_data, arch))),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3),
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (offsetof(struct seccomp_data, nr))),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (error & SECCOMP_RET_DATA)),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
};
struct sock_fprog prog = {
.len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
.filter = filter,
};
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
perror("prctl(PR_SET_SECCOMP)");
return 1;
}
return 0;
}
int main(int argc, char const *argv[]) {
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
perror("prctl(NO_NEW_PRIVS)");
return 1;
}
install_filter(__NR_chroot, AUDIT_ARCH_X86_64, EPERM);
install_filter(__NR_socket, AUDIT_ARCH_X86_64, EPERM);
return system(argv[1]);
}
上述文件给当前进程注册两个 seccomp filter 规则,用以禁止进程使用 chroot 与 socket 系统调用,然后使用 system 函数执行传递的命令。
将上述文件保存为 seccomp.c 并编译生成 seccomp 程序。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char const *argv[]) {
struct sock_filter *filter = NULL;
int number = 0;
int index = -1;
int pid;
if (argc != 3) {
printf("%s pid filter-index", argv[0]);
exit(-1);
}
pid = atoi(argv[1]);
index = atoi(argv[2]);
memset(&filter, 0x00, sizeof(filter));
ptrace(PTRACE_ATTACH, pid);
number = ptrace(PTRACE_SECCOMP_GET_FILTER, pid, (void *)index, NULL);
filter = malloc(sizeof(struct sock_filter) * number);
ptrace(PTRACE_SECCOMP_GET_FILTER, pid, (void *)index, (void *)filter);
for (int i = 0; i < number; i++) {
printf("0x%x%x%x\n", filter[i].code, filter[i].jt,
filter[i].jf, filter[i].k);
}
}
上述代码首先使用 ptrace跟踪目标进程,然后使用 PTRACE_SECCOMP_GET_FILTER参数执行 ptrace并将最后一个参数设置为 NULL来获取 index 指向的 seccomp filter 的 bpf 字节码长度,然后申请相应大小的 sock_filter。此后重新使用 PTRACE_SECCOMP_GET_FILTER参数执行 ptrace获取到进程的 seccomp filter,最后将获取到的指令码打印出来。
将上述文件保存为 dump.c 并编译生成 dump 程序。
执行如下命令添加 cap_sys_admin 权限:
[longyu@debian] tmp $ sudo setcap cap_sys_admin+ep ./dump
查看添加后的权限:
[longyu@debian] tmp $ sudo getcap ./dump
./dump cap_sys_admin=ep
成功添加权限后就可以使用 dump 程序来 dump 进程的 seccomp filter 内容。
测试示例如下:
[longyu@debian] tmp $ ./dump 33449 0
0x2000
0x1503
0x2000
0x1501
0x600
0x600
[longyu@debian] tmp $ ./dump 33449 1
0x2000
0x1503
0x2000
0x1501
0x600
0x600
获取成功!
PTRACE_SECCOMP_GET_FILTER参数执行 ptrace前需要先使用 PTRACE_ATTACH 与待 trace 进程的 pid 参数来调用 ptrace trace 到目标进程中。