用prctl系统调用实现自定义过滤规则的seccomp

Posted by on July 12, 2018 · 5 mins read

最近在研究怎么用patchkit写一个pwn通防,以便在AWD比赛中多苟一秒。

因为patchkit虽然可以使用C语言,但不能使用任何库函数,什么都得自己实现,所以我的想法是用prctl syscall来用汇编实现,先尝试在64位系统中实现,prctl的系统调用号为157.

prctl 函数原形为 prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)

其中 prog为如下的结构体

struct sock_fprog {	/* Required for SO_ATTACH_FILTER. */
unsigned short	len;	/* Number of filter blocks */
struct sock_filter *filter;
};

Filter为指向一个BPF结构体数组指针,Len为结构体个数。

struct sock_filter {	/* Filter block */
	__u16	code;   /* Actual filter code */
	__u8	jt;	/* Jump true */
	__u8	jf;	/* Jump false */
	__u32	k;      /* Generic multiuse field */
};

然而这两个结构体并不是那么好写,好在有一个叫seccomp_export_bpf的函数能够将设置的seccomp以bpf的形式导出,如下.

//要使用seccomp需要安装库
sudo apt install libseccomp-dev libseccomp2 seccomp
//gcc -g simple_syscall_seccomp.c -o simple_syscall_seccomp -lseccomp
#include <unistd.h>
#include <seccomp.h>
#include <linux/seccomp.h>
#include <fcntl.h>
int main(void){
  scmp_filter_ctx ctx;
  ctx = seccomp_init(SCMP_ACT_ALLOW); 

  seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(socket), 0);
  seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(connect), 0);
  seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(bind), 0);
  seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(listen), 0);
  seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(clone), 0);  
  seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execve), 0); 
  int fd = open("./bpf.out",O_WRONLY|O_CREAT);
  seccomp_export_bpf(ctx,fd);
  close(fd);
  seccomp_load(ctx);
  system("/bin/sh");
}

用seccomp_rule_add可以方便的添加一些自定义规则,导出bpf

$hexdump bpf.out -C
00000000  20 00 00 00 04 00 00 00  15 00 00 09 3e 00 00 c0  | ...........>...|
00000010  20 00 00 00 00 00 00 00  35 00 07 00 00 00 00 40  | .......5......@|
00000020  15 00 06 00 29 00 00 00  15 00 05 00 2a 00 00 00  |....).......*...|
00000030  15 00 04 00 31 00 00 00  15 00 03 00 32 00 00 00  |....1.......2...|
00000040  15 00 02 00 38 00 00 00  15 00 01 00 3b 00 00 00  |....8.......;...|
00000050  06 00 00 00 00 00 ff 7f  06 00 00 00 00 00 00 00  |................|
00000060

整理成结构体后,先用c测试一下,如果能成功拦截,就说明这个结构体没有问题。

#include <stdio.h> 
#include <unistd.h>  
#include <linux/seccomp.h>
#include <seccomp.h>
#include<fcntl.h>
#include <sys/prctl.h>
#include <linux/filter.h>
struct sock_filter sfi[] = {
		{0x20,0x00,0x00,0x00000004},
		{0x15,0x00,0x09,0xc000003e},
		{0x20,0x00,0x00,0x00000000},
		{0x35,0x07,0x00,0x40000000},
		{0x15,0x06,0x00,0x00000029},
		{0x15,0x05,0x00,0x0000002a},
		{0x15,0x04,0x00,0x00000031},
		{0x15,0x03,0x00,0x00000032},
		{0x15,0x02,0x00,0x00000038},
		{0x15,0x01,0x00,0x0000003b},
		{0x06,0x00,0x00,0x7fff0000},
		{0x06,0x00,0x00,0x00000000}
};
struct sock_fprog sfp = {12,sfi};
int main() {
  printf("step 1: unrestricted\n");
  prctl(PR_SET_NO_NEW_PRIVS,1,0,0,0);
  prctl(PR_SET_SECCOMP,SECCOMP_MODE_FILTER,&sfp);
  system("/bin/sh");
  return 0; 
}

最后改成汇编:

	push rbp;
	mov rbp,rsp;
	mov r15,6;
	push r15;
	mov r15,7FFF000000000006H;
	push r15;	
	mov r15,3B00010015H;
	push r15;
	mov r15 , 3800020015h;
	push r15;
	mov r15 , 3200030015h;
	push r15;
	mov r15 , 3100040015h;
	push r15;
	mov r15 , 2A00050015h;
	push r15;
	mov r15 , 2900060015h;
	push r15;
	mov r15 , 4000000000070035h;
	push r15;
	mov r15 , 20h;
	push r15;
	mov r15 , 0C000003E09000015h;
	push r15;
	mov r15 , 400000020h;
	push r15;
	mov r15,rsp;
	push r15;
	mov r15 , 0ch;
	push r15;
	mov r15,rsp;	
	push r15;
	mov rdi,38;
	mov rsi,1;
	mov rdx,0;
	mov rcx,0;
	mov r8,0;
	mov rax,157;
	syscall;
	mov rdi,22;
	mov rsi,2;
	mov rdx,r15;
	mov rax,157;
	syscall;
	leave;	
	ret;

更具体的使用方法放在github上了:

传送门