Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20190425130218.GA10866@openwall.com>
Date: Thu, 25 Apr 2019 15:02:19 +0200
From: Solar Designer <solar@...nwall.com>
To: oss-security@...ts.openwall.com
Subject: Re: Linux kernel: no permission check during open() time of /proc/[pid]/maps in kernels < 3.18

On Thu, Apr 25, 2019 at 02:12:36PM +0200, Matthias Gerstner wrote:
> I stumbled over a leak of memory mappings for arbitrary processes in
> kernels older than version 3.18.
> 
> As it turns out the permissions check for the pseudo file in
> /proc/[pid]/maps in affected kernels is performed not during open() time
> but during read() time. This allows an unprivileged user to open a valid
> file descriptor for these maps files and pass it to privileged programs
> like setuid root binaries or D-Bus services running as root that support
> file descriptor passing in their interface.
> 
> The privileged program needs behave in a way that the passed file
> descriptor is read() with root premissions and the content is passed
> back to the unprivileged user in some way.

Looks like mostly a rediscovery of what was brought up in here by
Jason A. Donenfeld and further discussed with Djalal Harouni in 2012:

https://www.openwall.com/lists/oss-security/2012/02/08/2

and had already been fixed in grsecurity, given that I fixed it with:

* Sat Feb 25 2012 Solar Designer <solar-at-owl.openwall.com> 2.6.18-274.18.1.el5.028stab098.1.owl1
[...]
- Introduced protection against unintended self-read by a SUID/SGID program of
/proc/<pid>/mem and /proc/<pid>/*maps files, based on approaches taken in
recent grsecurity patches.

+++ linux-2.6.18-431.el5.028stab123.1-owl/fs/proc/task_mmu.c	2018-05-20 16:37:29 +0000
@@ -166,7 +166,7 @@ static int show_map_internal(struct seq_
 	struct proc_maps_private *priv = m->private;
 	struct task_struct *task = priv->task;
 #ifdef __i386__
-	struct mm_struct *tmm = get_task_mm(task);
+	struct mm_struct *tmm;
 #endif
 	struct vm_area_struct *vma = v;
 	struct mm_struct *mm = vma->vm_mm;
@@ -177,6 +177,13 @@ static int show_map_internal(struct seq_
 	dev_t dev = 0;
 	int len;
 
+	if (current->exec_id != m->exec_id)
+		return 0;
+
+#ifdef __i386__
+	tmm = get_task_mm(task);
+#endif
+
 	if (file) {
 		struct inode *inode = vma->vm_file->f_dentry->d_inode;
 		dev = inode->i_sb->s_dev;

Was this not fixed upstream until the permissions check on open() was
added in 2014?  I guess it also wasn't fixed in RHEL, since I carried
the above patch hunk into 2018+ as you can see (or maybe it became
redundant with RHEL's different fix for the issue - I don't recall).

The idea of passing the fd to D-Bus services, etc. might be a new one,
but the fix above should be sufficient against that as well due to the
exec_id's being globally unique (except between fork-without-exec
sibling processes):

 		/* execve success */
+		current->exec_id = atomic64_inc_return(&global_exec_counter);

Alexander

Powered by blists - more mailing lists

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.