seccomp学习 (1)
文章目录
- 0x01. seccomp规则添加原理
- A. 默认规则
- B. 自定义规则
- 0x02. seccomp沙箱“指令”格式
- 实例
- Task 01
- Task 02
- 0x03. 总结
今天打了ACTF-2023,惊呼已经不认识seccomp了,在被一道盲打题折磨了一整天之后,实在是不想面向题目高强度学习了。但是seccomp这个东西必然是要系统性的重学一遍了,绝不能把知识面仅限于orw。
学习目标:了解seccomp的保护原理,掌握常用的seccomp绕过姿势,学会手写seccomp BPF指令等。
0x01. seccomp规则添加原理
说到seccomp,都知道它是用来限制进程的系统调用的,但是对于Linux系统而言,有这么多的进程,seccomp又是如何精准拦截定义了规则的进程中调用的非法的系统调用呢?
这就又不得不进入一个令人不适的环节了——Linux源代码阅读。
在目前使用的Linux系统中,有两个系统调用与seccomp有关,一个是prctl
,另一个是seccomp
,系统调用号分别为157和317,对应的内核函数为sys_prctl
和sys_seccomp
:
SYSCALL_DEFINE3(seccomp, unsigned int, op, unsigned int, flags,void __user *, uargs)
{return do_seccomp(op, flags, uargs);
}
SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,unsigned long, arg4, unsigned long, arg5)
{...switch (option) {...case PR_GET_SECCOMP:error = prctl_get_seccomp();break;case PR_SET_SECCOMP:error = prctl_set_seccomp(arg2, (char __user *)arg3);break;...}...
}long prctl_set_seccomp(unsigned long seccomp_mode, void __user *filter)
{unsigned int op;void __user *uargs;switch (seccomp_mode) {case SECCOMP_MODE_STRICT:op = SECCOMP_SET_MODE_STRICT;/** Setting strict mode through prctl always ignored filter,* so make sure it is always NULL here to pass the internal* check in do_seccomp().*/uargs = NULL;break;case SECCOMP_MODE_FILTER:op = SECCOMP_SET_MODE_FILTER;uargs = filter;break;default:return -EINVAL;}/* prctl interface doesn't have flags, so they are always zero. */return do_seccomp(op, 0, uargs);
}
可以看到,如果将prctl
系统调用的第一个参数设置为PR_SET_SECCOMP
,最终调用的与sys_seccomp
相同,都是do_seccomp
。这也是设置seccomp
规则的入口函数。
/* Common entry point for both prctl and syscall. */
static long do_seccomp(unsigned int op, unsigned int flags,void __user *uargs)
{switch (op) {case SECCOMP_SET_MODE_STRICT:if (flags != 0 || uargs != NULL)return -EINVAL;return seccomp_set_mode_strict();case SECCOMP_SET_MODE_FILTER:return seccomp_set_mode_filter(flags, uargs);case SECCOMP_GET_ACTION_AVAIL:if (flags != 0)return -EINVAL;return seccomp_get_action_avail(uargs);case SECCOMP_GET_NOTIF_SIZES:if (flags != 0)return -EINVAL;return seccomp_get_notif_sizes(uargs);default:return -EINVAL;}
}
上面就是do_seccomp
函数的定义。我们要重点关注的是前面两个switch分支,一个是SECCOMP_SET_MODE_STRICT
A. 默认规则
添加默认规则的逻辑在seccomp_set_mode_strict
中实现:
static long seccomp_set_mode_strict(void)
{const unsigned long seccomp_mode = SECCOMP_MODE_STRICT;long ret = -EINVAL;spin_lock_irq(¤t->sighand->siglock);if (!seccomp_may_assign_mode(seccomp_mode))goto out;#ifdef TIF_NOTSCdisable_TSC();
#endifseccomp_assign_mode(current, seccomp_mode, 0);ret = 0;out:spin_unlock_irq(¤t->sighand->siglock);return ret;
}static inline bool seccomp_may_assign_mode(unsigned long seccomp_mode)
{assert_spin_locked(¤t->sighand->siglock);if (current->seccomp.mode && current->seccomp.mode != seccomp_mode)return false;return true;
}#define SECCOMP_MODE_STRICT 0
#define SECCOMP_MODE_FILTER 1
函数中的current
是一个task_struct
实例,表示当前内核进程。在加锁之后,调用了一个seccomp_may_assign_mode
函数用于判断。从这个判断函数可以发现,当我们使用BPF定义规则(此时mode为SECCOMP_MODE_FILTER
)时,就不能再切换成严格模式了,否则该函数返回false
,直接跳过了规则修改流程。
随后进入主要的规则添加逻辑seccomp_assign_mode
函数:
static inline void seccomp_assign_mode(struct task_struct *task,unsigned long seccomp_mode,unsigned long flags)
{assert_spin_locked(&task->sighand->siglock);task->seccomp.mode = seccomp_mode;/** Make sure SYSCALL_WORK_SECCOMP cannot be set before the mode (and* filter) is set.*/smp_mb__before_atomic();/* Assume default seccomp processes want spec flaw mitigation. */if ((flags & SECCOMP_FILTER_FLAG_SPEC_ALLOW) == 0)arch_seccomp_spec_mitigate(task);set_task_syscall_work(task, SECCOMP);
}/* Valid flags for SECCOMP_SET_MODE_FILTER */
#define SECCOMP_FILTER_FLAG_TSYNC (1UL << 0)
#define SECCOMP_FILTER_FLAG_LOG (1UL << 1)
#define SECCOMP_FILTER_FLAG_SPEC_ALLOW (1UL << 2)
#define SECCOMP_FILTER_FLAG_NEW_LISTENER (1UL << 3)
#define SECCOMP_FILTER_FLAG_TSYNC_ESRCH (1UL << 4)
/* Received notifications wait in killable state (only respond to fatal signals) */
#define SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV (1UL << 5)#define set_task_syscall_work(t, fl) \set_bit(SYSCALL_WORK_BIT_##fl, &task_thread_info(t)->syscall_work)enum syscall_work_bit {SYSCALL_WORK_BIT_SECCOMP,SYSCALL_WORK_BIT_SYSCALL_TRACEPOINT,SYSCALL_WORK_BIT_SYSCALL_TRACE,SYSCALL_WORK_BIT_SYSCALL_EMU,SYSCALL_WORK_BIT_SYSCALL_AUDIT,SYSCALL_WORK_BIT_SYSCALL_USER_DISPATCH,SYSCALL_WORK_BIT_SYSCALL_EXIT_TRAP,
};
在这个函数之中,设置了当前进程的mode
,随后出现了一个判断,判断成功时执行arch_seccomp_spec_mitigate
函数。这个函数的内部逻辑比较复杂,先略过。最后调用set_task_syscall_work
,这是一个宏定义,定义如上所示,就是设置一个位,表示这个线程已经开启了seccomp检查。
B. 自定义规则
对于自定义规则而言,添加的过程要复杂许多。
static long seccomp_set_mode_filter(unsigned int flags,const char __user *filter)
{const unsigned long seccomp_mode = SECCOMP_MODE_FILTER;struct seccomp_filter *prepared = NULL;long ret = -EINVAL;int listener = -1;struct file *listener_f = NULL;/* Validate flags. */if (flags & ~SECCOMP_FILTER_FLAG_MASK)return -EINVAL;/** In the successful case, NEW_LISTENER returns the new listener fd.* But in the failure case, TSYNC returns the thread that died. If you* combine these two flags, there's no way to tell whether something* succeeded or failed. So, let's disallow this combination if the user* has not explicitly requested no errors from TSYNC.*/if ((flags & SECCOMP_FILTER_FLAG_TSYNC) &&(flags & SECCOMP_FILTER_FLAG_NEW_LISTENER) &&((flags & SECCOMP_FILTER_FLAG_TSYNC_ESRCH) == 0))return -EINVAL;/** The SECCOMP_FILTER_FLAG_WAIT_KILLABLE_SENT flag doesn't make sense* without the SECCOMP_FILTER_FLAG_NEW_LISTENER flag.*/if ((flags & SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV) &&((flags & SECCOMP_FILTER_FLAG_NEW_LISTENER) == 0))return -EINVAL;/* Prepare the new filter before holding any locks. */prepared = seccomp_prepare_user_filter(filter);if (IS_ERR(prepared))return PTR_ERR(prepared);if (flags & SECCOMP_FILTER_FLAG_NEW_LISTENER) {listener = get_unused_fd_flags(O_CLOEXEC);if (listener < 0) {ret = listener;goto out_free;}listener_f = init_listener(prepared);if (IS_ERR(listener_f)) {put_unused_fd(listener);ret = PTR_ERR(listener_f);goto out_free;}}/** Make sure we cannot change seccomp or nnp state via TSYNC* while another thread is in the middle of calling exec.*/if (flags & SECCOMP_FILTER_FLAG_TSYNC &&mutex_lock_killable(¤t->signal->cred_guard_mutex))goto out_put_fd;spin_lock_irq(¤t->sighand->siglock);if (!seccomp_may_assign_mode(seccomp_mode))goto out;if (has_duplicate_listener(prepared)) {ret = -EBUSY;goto out;}ret = seccomp_attach_filter(flags, prepared);if (ret)goto out;/* Do not free the successfully attached filter. */prepared = NULL;seccomp_assign_mode(current, seccomp_mode, flags);
out:spin_unlock_irq(¤t->sighand->siglock);if (flags & SECCOMP_FILTER_FLAG_TSYNC)mutex_unlock(¤t->signal->cred_guard_mutex);
out_put_fd:if (flags & SECCOMP_FILTER_FLAG_NEW_LISTENER) {if (ret) {listener_f->private_data = NULL;fput(listener_f);put_unused_fd(listener);seccomp_notify_detach(prepared);} else {fd_install(listener, listener_f);ret = listener;}}
out_free:seccomp_filter_free(prepared);return ret;
}
函数中有很多的判断条件,当这些判断条件不满足时,会直接返回一个错误值。需要注意的是flags & ~SECCOMP_FILTER_FLAG_MASK = 0
,也就是flags
除了最低6位其他位必须全为0。
通过3个判断之后,调用了seccomp_prepare_user_filter
函数初始化struct seccomp_filter
结构体实例。
struct seccomp_filter {refcount_t refs;refcount_t users;bool log;bool wait_killable_recv;struct action_cache cache;struct seccomp_filter *prev;struct bpf_prog *prog;struct notification *notif;struct mutex notify_lock;wait_queue_head_t wqh;
};static struct seccomp_filter *
seccomp_prepare_user_filter(const char __user *user_filter)
{struct sock_fprog fprog;struct seccomp_filter *filter = ERR_PTR(-EFAULT);#ifdef CONFIG_COMPATif (in_compat_syscall()) {struct compat_sock_fprog fprog32;if (copy_from_user(&fprog32, user_filter, sizeof(fprog32)))goto out;fprog.len = fprog32.len;fprog.filter = compat_ptr(fprog32.filter);} else /* falls through to the if below. */
#endifif (copy_from_user(&fprog, user_filter, sizeof(fprog)))goto out;filter = seccomp_prepare_filter(&fprog);
out:return filter;
}struct sock_fprog { /* Required for SO_ATTACH_FILTER. */unsigned short len; /* Number of filter blocks */struct sock_filter __user *filter;
};struct sock_filter { /* Filter block */__u16 code; /* Actual filter code */__u8 jt; /* Jump true */__u8 jf; /* Jump false */__u32 k; /* Generic multiuse field */
};
从上面的结构体定义和函数定义可以看出,我们传入的用户态指针需要是sock_fprog
结构体实例,Linux中定义了一个seccomp规则的最大长度为4096,即len必须位于(0,4096],上面的sock_filter
可以理解为seccomp沙箱的一条“指令”。在seccomp_prepare_user_filter
中也有一些检查,通过返回值我们就可以知道是针对什么的检查,后面两个是EACCES
和ENOMEM
,一个是权限相关,一个是内存不够,一般都不会发生。随后就是将用户传递的过滤器中的内容保存到seccomp_filter
实例中返回。
初始化seccomp_filter
完成后,我们先略过后面对一些flags的特殊处理,判断了一下是否能够加载规则,随后调用了seccomp_attach_filter
,主要是处理已有的flags,随后将新的filter规则添加到头部的位置,使用prev
属性连接成一个单链表,如下所示。
static long seccomp_attach_filter(unsigned int flags,struct seccomp_filter *filter)
{unsigned long total_insns;struct seccomp_filter *walker;assert_spin_locked(¤t->sighand->siglock);/* Validate resulting filter length. */total_insns = filter->prog->len;for (walker = current->seccomp.filter; walker; walker = walker->prev)total_insns += walker->prog->len + 4; /* 4 instr penalty */if (total_insns > MAX_INSNS_PER_PATH)return -ENOMEM;.../** If there is an existing filter, make it the prev and don't drop its* task reference.*/filter->prev = current->seccomp.filter;seccomp_cache_prepare(filter);current->seccomp.filter = filter;atomic_inc(¤t->seccomp.filter_count);/* Now that the new filter is in place, synchronize to all threads. */if (flags & SECCOMP_FILTER_FLAG_TSYNC)seccomp_sync_threads(flags);return 0;
}
以上就是过滤器添加的大致流程。
0x02. seccomp沙箱“指令”格式
seccomp沙箱的每一条指令的长度都是8字节,分为4个字段——code、jt、jf、k。
struct sock_filter { /* Filter block */__u16 code; /* Actual filter code */__u8 jt; /* Jump true */__u8 jf; /* Jump false */__u32 k; /* Generic multiuse field */
};
在Linux中定义了一些方便编写seccomp code的宏定义(code含义定义在 /include/uapi/linux/bpf_common.h
中),这里引用资料中的注释便于理解:
#ifndef BPF_STMT
#define BPF_STMT(code, k) { (unsigned short)(code), 0, 0, k }
#endif
#ifndef BPF_JUMP
#define BPF_JUMP(code, k, jt, jf) { (unsigned short)(code), jt, jf, k }
#endif/* Instruction classes */
#define BPF_CLASS(code) ((code) & 0x07) //指定操作的类别
#define BPF_LD 0x00 //将值复制到累加器中
#define BPF_LDX 0x01 //将值加载到索引寄存器中
#define BPF_ST 0x02 //将累加器中的值存到暂存器
#define BPF_STX 0x03 //将索引寄存器的值存储在暂存器中
#define BPF_ALU 0x04 //用索引寄存器或常数作为操作数在累加器上执行算数或逻辑运算
#define BPF_JMP 0x05 //跳转
#define BPF_RET 0x06 //返回
#define BPF_MISC 0x07 // 其他类别/* ld/ldx fields */
#define BPF_SIZE(code) ((code) & 0x18)
#define BPF_W 0x00 /* 32-bit */ //字
#define BPF_H 0x08 /* 16-bit */ //半字
#define BPF_B 0x10 /* 8-bit */ //字节
/* eBPF BPF_DW 0x18 64-bit */ //双字
#define BPF_MODE(code) ((code) & 0xe0)
#define BPF_IMM 0x00 //常数
#define BPF_ABS 0x20 //固定偏移量的数据包数据(绝对偏移)
#define BPF_IND 0x40 //可变偏移量的数据包数据(相对偏移)
#define BPF_MEM 0x60 //暂存器中的一个字
#define BPF_LEN 0x80 //数据包长度
#define BPF_MSH 0xa0/* alu/jmp fields */
#define BPF_OP(code) ((code) & 0xf0) //当操作码类型为ALU时,指定具体运算符
#define BPF_ADD 0x00
#define BPF_SUB 0x10
#define BPF_MUL 0x20
#define BPF_DIV 0x30
#define BPF_OR 0x40
#define BPF_AND 0x50
#define BPF_LSH 0x60
#define BPF_RSH 0x70
#define BPF_NEG 0x80
#define BPF_MOD 0x90
#define BPF_XOR 0xa0//当操作码是jmp时指定跳转类型
#define BPF_JA 0x00
#define BPF_JEQ 0x10
#define BPF_JGT 0x20
#define BPF_JGE 0x30
#define BPF_JSET 0x40
#define BPF_SRC(code) ((code) & 0x08)
#define BPF_K 0x00 //常数
#define BPF_X 0x08 //索引寄存器
在笔者查资料的时候,发现这个BPF不仅能用来编写seccomp规则,它更像是一个较为成熟的汇编语言+胶水语言,并在2014年就拥有了自己的执行引擎eBPF。这又是一个完全的知识体系。
网络上针对BPF大多是通过C等进行编译获得BPF代码,但对于seccomp而言,我们要做的是直接编写BPF code。但专用于seccomp的BPF除了通用的BPF语法之外,还有一些额外的定义:
/** All BPF programs must return a 32-bit value.* The bottom 16-bits are for optional return data.* The upper 16-bits are ordered from least permissive values to most,* as a signed value (so 0x8000000 is negative).** The ordering ensures that a min_t() over composed return values always* selects the least permissive choice.*/
#define SECCOMP_RET_KILL_PROCESS 0x80000000U /* kill the process */
#define SECCOMP_RET_KILL_THREAD 0x00000000U /* kill the thread */
#define SECCOMP_RET_KILL SECCOMP_RET_KILL_THREAD
#define SECCOMP_RET_TRAP 0x00030000U /* disallow and force a SIGSYS */
#define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */
#define SECCOMP_RET_USER_NOTIF 0x7fc00000U /* notifies userspace */
#define SECCOMP_RET_TRACE 0x7ff00000U /* pass to a tracer or disallow */
#define SECCOMP_RET_LOG 0x7ffc0000U /* allow after logging */
#define SECCOMP_RET_ALLOW 0x7fff0000U /* allow *//* Masks for the return value sections. */
#define SECCOMP_RET_ACTION_FULL 0xffff0000U
#define SECCOMP_RET_ACTION 0x7fff0000U
#define SECCOMP_RET_DATA 0x0000ffffU
上面定义了seccomp BPF的返回值,从注释可知,返回值的低16bit用于传递其他数据,高16bit用于传递返回值的优先级。当一个系统调用匹配了多个seccomp规则时,会优先使用优先级高的返回值,这里从SECCOMP_RET_KILL_PROCESS
的优先级最高,SECCOMP_RET_ALLOW
最低,如果一个系统调用匹配了两个规则,返回值分别为SECCOMP_RET_KILL
和SECCOMP_RET_ALLOW
,那么最终将会选择SECCOMP_RET_KILL
作为返回值,即杀死触发这个系统调用的线程。
/*** struct seccomp_data - the format the BPF program executes over.* @nr: the system call number* @arch: indicates system call convention as an AUDIT_ARCH_* value* as defined in <linux/audit.h>.* @instruction_pointer: at the time of the system call.* @args: up to 6 system call arguments always stored as 64-bit values* regardless of the architecture.*/
struct seccomp_data {int nr;__u32 arch;__u64 instruction_pointer;__u64 args[6];
};
上面这段代码定义了一些编写seccomp BPF code可能会用到的东西,根据注释可知,我们可以在BPF code中获取该系统调用的:系统调用号、处理器架构、指令地址、6个参数的值。具体选择获取什么通过字段k来决定,k相当于seccomp_data
结构体的偏移量,若指定k=0
,则为获取nr
,即系统调用号,若k=4
,则为获取处理器架构等。
我们以一个实例对seccomp BPF code进行理解,尝试通过机器码恢复code本身。
line CODE JT JF K
=================================0000: 0x20 0x00 0x00 0x00000004 LD | ABS | Word, R0 = arch0001: 0x15 0x00 0x19 0xc000003e JMP | JEQ after 0x19, R0 == AUDIT_ARCH_X86_64 ?0002: 0x20 0x00 0x00 0x00000000 LD | ABS | Word, R0 = nr0003: 0x35 0x00 0x01 0x40000000 JMP | JGE after 0x01, R0 >= 0x40000000 ?0004: 0x15 0x00 0x16 0xffffffff JMP | JEQ after 0x16, R0 == 0xFFFFFFFF ?0005: 0x15 0x15 0x00 0x00000000 JMP | JEQ after 0x15, R0 == 0 ?0006: 0x15 0x14 0x00 0x00000001 JMP | JEQ after 0x14, R0 == 1 ?0007: 0x15 0x13 0x00 0x00000002 JMP | JEQ after 0x13, R0 == 2 ?...0026: 0x06 0x00 0x00 0x7fff0000 return SECCOMP_RET_ALLOW0027: 0x06 0x00 0x00 0x00000000 return SECCOMP_RET_KILL
注意第二行的K字段,这里的K指的是AUDIT_ARCH_X86_64
,定义于/include/uapi/linux/audit.h
,其中为所有架构都定义了独特的标识符,而0xc000003e则是AUDIT_ARCH_X86_64
的值。对于整个seccomp code而言,可能需要的外部数据也就只有seccomp_data
了。
下面,我们就来通过一些具体的程序示例巩固一下我们的学习成果,使用seccomp BPF code完成自定义的filter规则。
实例
Task 01
实现seccomp BPF filter,过滤x86-64之外所有架构的所有系统调用,过滤execve。
实现代码:
#include <stdio.h>
#include <sys/prctl.h>
#include <linux/seccomp.h>
#include <linux/filter.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/unistd.h>
#include <linux/audit.h>
#include <stddef.h>int main(){struct sock_filter filter[] = {BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch)),BPF_JUMP(BPF_JMP | BPF_JEQ, AUDIT_ARCH_X86_64, 0, 4),BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),BPF_STMT(BPF_ALU | BPF_K | BPF_SUB, 59),BPF_JUMP(BPF_JMP | BPF_JEQ, 0, 0, 1),BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL)};struct sock_fprog prog = {.len = (unsigned short)(sizeof(filter) / sizeof(struct sock_filter)),.filter = filter,};prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);system("echo HELLO");
}
上述代码实现了对处理器架构与execve的检查,使用了一个ALU
类型指令将系统调用号减去59,随后与0相比较。
对于seccomp BPF code而言,使用一个寄存器实际上已经足够了,对于多个返回值,我们可以在BPF code的最后几行进行统一定义,在编写前面的代码时,由于跳转指令的数量不确定,有时可能需要预留跳转数,在code编写完成后再进行计算。而对于seccomp的多个检查,我们完全可以将code除了返回之外的所有代码分片看待,每一片都进行一个检查,不同分片之间互不影响,每个分片中只使用一个寄存器即可完成检查,因此总的seccomp BPF code也只需要一个寄存器即可实现,这就使得我们不需要了解所有的BPF指令即可完美编写seccomp BPF filter。
在加载seccomp规则之前,代码中还执行了一次prctl
。这里引用参考资料:
PR_SET_NO_NEW_PRIVS():是在Linux 3.5 之后引入的特性,当一个进程或者子进程设置了PR_SET_NO_NEW_PRIVS 属性,则其不能访问一些无法共享的操作,如setuid、chroot等。配置seccomp-BPF的程序必须拥有Capabilities 中 的CAP_SYS_ADMIN,或者程序已经定义了no_new_privs属性。 若不这样做 非 root 用户使用该程序时 seccomp保护将会失效,设置了 PR_SET_NO_NEW_PRIVS 位后能保证 seccomp 对所有用户都能起作用
Task 02
实现seccomp BPF filter,过滤x86-64之外所有架构的所有系统调用,不允许第一个参数为3的read系统调用。
实现代码:
#include <stdio.h>
#include <sys/prctl.h>
#include <linux/seccomp.h>
#include <linux/filter.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/unistd.h>
#include <linux/audit.h>
#include <stddef.h>
#include <fcntl.h>int main(){struct sock_filter filter[] = {BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch)),BPF_JUMP(BPF_JMP | BPF_JEQ, AUDIT_ARCH_X86_64, 0, 5),BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),BPF_JUMP(BPF_JMP | BPF_JEQ, 0, 0, 2),BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])),BPF_JUMP(BPF_JMP | BPF_JEQ, 3, 1, 0),BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL)};struct sock_fprog prog = {.len = (unsigned short)(sizeof(filter) / sizeof(struct sock_filter)),.filter = filter,};prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);int fd = open("/bin/ls", 0);char buffer[8];printf("%d\n", fd);read(fd, buffer, 8);
}
注意BPF_JUMP
宏定义的使用,后面的2个参数分别表示条件成立时跳过前面几条指令,条件不成立时跳过前面几条指令。在上面的代码中,首先判断处理器架构,如果不是x86_64则跳转到KILL
,随后首先判断系统调用号是不是3,不是则跳转到ALLOW
,是则继续执行,判断第一个参数是不是3,如果是则跳转到KILL
。
0x03. 总结
本文简要分析了seccomp添加规则的流程,以及seccomp BPF的编写方法。
在后面的文章中,我们将尝试尽可能分析CTF pwn题中所有与seccomp有关的绕过姿势,并通过具体的示例进行学习。
相关文章:
seccomp学习 (1)
文章目录 0x01. seccomp规则添加原理A. 默认规则B. 自定义规则 0x02. seccomp沙箱“指令”格式实例Task 01Task 02 0x03. 总结 今天打了ACTF-2023,惊呼已经不认识seccomp了,在被一道盲打题折磨了一整天之后,实在是不想面向题目高强度学习了。…...

Linux指令【上】
目录 目录结构 ls cd stat touch mkdir whoami 查看当前帐号是谁 who 查看当前有哪些人在使用 pwd 当前的工作目录 目录结构 目录结构就是一颗多叉树的样子 路径 我们从 / 目录开始,定位一个叶子文件的…...

RK3568-clock
pll锁相环 总线 gating rk3568.dtsi pmucru: clock-controller@fdd00000 {compatible = "rockchip,rk3568-pmucru";reg = <0x0 0xfdd00000 0x0 0x1000>;rockchip,grf = <&grf>;rockchip,pmugrf = <&pmugrf>;#clock-cells = <1>;#re…...

新恶意软件使用 MSIX 软件包来感染 Windows
人们发现,一种新的网络攻击活动正在使用 MSIX(一种 Windows 应用程序打包格式)来感染 Windows PC,并通过将隐秘的恶意软件加载程序放入受害者的 PC 中来逃避检测。 Elastic Security Labs 的研究人员发现,开发人员通常…...

干货!数字IC后端入门学习笔记
很多同学想要了解IC后端,今天大家分享了数字IC后端的学习入门笔记,供大家学习参考。 很多人对于后端设计的概念比较模糊,需要做什么也都不甚清楚。 有的同学认为就是跑跑 flow、掌握各类工具。 事实上,后端设计的工作远不止于此。…...

力扣:144. 二叉树的前序遍历(Python3)
题目: 给你二叉树的根节点 root ,返回它节点值的 前序 遍历。 来源:力扣(LeetCode) 链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 示例: 示例 1: 输…...

【数据挖掘 | 数据预处理】缺失值处理 重复值处理 文本处理 确定不来看看?
🤵♂️ 个人主页: AI_magician 📡主页地址: 作者简介:CSDN内容合伙人,全栈领域优质创作者。 👨💻景愿:旨在于能和更多的热爱计算机的伙伴一起成长!!&…...

二叉树问题——前/中/后/层遍历(递归与栈)
摘要 博文主要介绍二叉树的前/中/后/层遍历(递归与栈)方法 一、前/中/后/层遍历问题 144. 二叉树的前序遍历 145. 二叉树的后序遍历 94. 二叉树的中序遍历 102. 二叉树的层序遍历 二、二叉树遍历递归解析 // 前序遍历递归LC144_二叉树的前序遍历 class Solution {publi…...
Nor Flash和Nand Flash的区别——笔记
NorFlash:串行存储器、读取速度比较快(比NandFlash快),适合用于存储程序代码和执行代码,但NorFlash写入速度比较慢、容量比较小。数据线和地址线是分开的。 NandFlash:并行存储器、写入速度比较快…...

7+共病思路。WGCNA+多机器学习+实验简单验证,易操作
今天给同学们分享一篇共病WGCNA多机器学习实验的生信文章“Shared diagnostic genes and potential mechanism between PCOS and recurrent implantation failure revealed by integrated transcriptomic analysis and machine learning”,这篇文章于2023年5月16日发…...

开发者看亚马逊云科技1024【文末有福利~】
1024,2023年的1024,注定是不平凡的1024,AIGC已经成为了整个年度的主题,亚马逊云科技在这个开发者每年最重要的日子,举办了生成式AI构建者大会,让我们一起再次了解本次生成式AI构建者大会,回顾会…...

操作系统(Linux)外壳程序shell 、用户、权限
文章目录 操作系统和shell外壳Linux用户普通用户的创建和删除用户的切换 Linux 权限Linux 权限分类文件访问权限修改文件的权限权限掩码粘滞位 大家好,我是纪宁。 这篇文章将介绍 Linux的shell外壳程序,Linux用户切换机Linux权限的内容。 操作系统和shel…...

C文件操作
目录 1. 什么是文件 2. 为什么要有文件 3. 文件名 4. 文件类型 5. 文件指针 6. 文件的打开和关闭 7. 文件的顺序读写 7.1. fgetc 7.2. fputc 7.3. fgets 7.4. fputs 7.5. fscanf 7.6. fprintf 7.8. sscanf 7.9. sprintf 7.9. fread 7.10. fwrite 8. 文件的随…...

drawio特性
drawio的特性 drawio是领先的基于Web技术的草图和图表功能功能的应用。 保证数据的安全 集成了各种不同的平台,和提供了在线的免费编辑器,可以使用app.diagrams.net来方案,drawio本身不会存储用户的数据。 随着互联网时代的发展࿰…...
LLM-Embedder
1. 目标 训出一个统一的embedding模型LLM-Embedder,旨在全面支持LLM在各种场景中的检索增强 2. 模型的四个关键检索能力 knowledge:解决knowledge-intensive任务memory:解决long-context modelingexample:解决in-context learn…...
xsync 集群远程同步脚本
xsync 集群分发 脚本 (1)需求:循环复制文件到所有节点的相同目录下 (2)需求分析: (a)rsync 命令原始拷贝: rsync -av /opt/module roothadoop103:/opt/(b&am…...

30秒get视频号视频如何下载,保存视频号视频到本地方法!
终于可以告别无法下载视频号视频的烦恼啦!下面是一些只需 30 秒就能get到的t视频号视频如何下载方法,让我们一起来探索如何保存视频号视频到本地方法吧! 首先,要记得这些方法仅适用于个人观看或学习使用,不可用于商业用…...

优化改进YOLOv5算法:加入SPD-Conv模块,让小目标无处遁形——(超详细)
1 SPD-Conv模块 论文:https://arxiv.org/pdf/2208.03641v1.pdf 摘要:卷积神经网络(CNNs)在计算即使觉任务中如图像分类和目标检测等取得了显著的成功。然而,当图像分辨率较低或物体较小时,它们的性能会灾难性下降。这是由于现有CNN常见的设计体系结构中有缺陷,即使用卷积…...

【数据结构】搜索树 与 Java集合框架中的Set,Map
作者主页:paper jie_博客 本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。 本文录入于《JAVA数据结构》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力…...

掌握组件缓存:解开Vue.js中<keep-alive>的奥秘
🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云…...

龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...

听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...

云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...