Bug 1045643

Summary: CVE-2013-6456 libvirt: vulnerability in virInitctlSetRunLevel [rhel-7.0]
Product: Red Hat Enterprise Linux 7 Reporter: Eric Blake <eblake>
Component: libvirtAssignee: Eric Blake <eblake>
Status: CLOSED CURRENTRELEASE QA Contact: Virtualization Bugs <virt-bugs>
Severity: unspecified Docs Contact:
Priority: unspecified    
Version: 7.0CC: acathrow, ajia, carnil, dyuan, eblake, lsu, mmcallis, mzhan, pmatouse
Target Milestone: rcKeywords: Security, SecurityTracking
Target Release: ---   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: libvirt-1.1.1-24.el7 Doc Type: Release Note
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2014-07-30 12:39:57 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 1048627    

Description Eric Blake 2013-12-20 22:54:30 UTC
Description of problem:
Public debian bug #732394 points out a vulnerability where a malicious guest can use symlinks to cause the LXC driver to manipulate unintended files in the host during the virDomainShutdown, virDomainReboot, virDomainDeviceAttach, and virDomainDeviceDettach APIs.

The libvirt-security list has been notified (private archives: https://www.redhat.com/mailman/private/libvirt-security/2013-December/msg00018.html), and we are now awaiting assignment of a CVE to cover this issue.  Public patches are underway for the virDomainShutdown/virDomainReboot issues (v4 is incomplete, v5 not posted yet: https://www.redhat.com/archives/libvir-list/2013-December/msg01182.html), at the time of this BZ, the virDomainDevice{Attach,Dettach} issue still needs work.

Version-Release number of selected component (if applicable):
libvirt-1.1.1-16.el7

How reproducible:
100%

Steps to Reproduce:
1.
2.
3.

Actual results:


Expected results:


Additional info:

Description
-----------

The LXC driver will open paths under /proc/$PID/root for some
operations it performs on running guests. For the virDomainShutdown
and virDomainReboot APIs it will use this to access the /dev/initctl
path in the container. For the virDomainDeviceAttach /
virDomainDeviceDettach APIs it will use this to create device nodes
in the container's /dev filesystem. If any of the path components
under control of the container are symlinks the container can cause
the libvirtd daemon to access the incorrect files.

Impact
------

A container can cause the administrator to shutdown or reboot the
host OS if /dev/initctl in the container is made to be an absolute
symlink back to itself or /run/initctl. A container can cause the
host administrator to mknod in an arbitrary host directory when
invoking the virDomainDeviceAttach API by replacing '/dev' with an
absolute symlink. A container can cause the host administrator to
delete host device when invoking the virDomainDeviceDettach API by
replacing '/dev' with an absolute symlink.

Workaround
----------

Do not use the virDomainShutdown or virDomainReboot APIs without
also passing the VIR_DOMAIN_SHUTDOWN_SIGNAL or
VIR_DOMAIN_REBOOT_SIGNAL flags respectively. These will cause the
LXC driver to send a SIGTERM or SIGHUP signal respectively, to the
init process instead of using /dev/initct.. Do not use the
virDomainDeviceAttach or virDomainDeviceDetach APIs at all unless
the guest OS is trusted.

Comment 1 Eric Blake 2013-12-20 22:56:26 UTC
   Reported on: 20131217
  Published on: 20131217
   Reported by: Reco <recoverym4n>

Comment 4 Murray McAllister 2014-01-02 23:16:03 UTC
This issue was assigned CVE-2013-6456 (CVE bug not yet filed, sorry about that!)

Comment 9 Luwen Su 2014-03-14 05:09:44 UTC
Follow Eric's advice , verified this one via strace with 
libvirt-1.1.1-27.el7.x86_64 on RHEL7 platform.

There are something need to be noted here first ,
1.Since rhel7 use systemd to control , which not sysinit or upstart that used on other distributions , like debian , it's hard to reproduce the "host reboot" issue here.

2.Due to /run/initctl is no longer recommanded for using through systemd's document , i just create a fake /run/initctl in container and ln it to /dev/initctl to watch if libvirtd try to open it when call related virDomainShutdown , virDomainReboot APIs.

3.I use two libvirt versions which pre-patch and post-patch to contrast 
  A.if libvirtd will continue share a same namespace with container
  B.if libvirtd will try to open the /dev/initctl
  C.if libvirtd will fork a child process to execute the action about open /dev/initctl in container
  D.if can destroy the container successfully once the child process has broken , and there is no affect in host after do that

Below is the steps.
1.Prepare a default /bin/bash container
#virsh -c lxc:/// dumpxml test
<domain type='lxc' id='8631'>
  <name>test</name>
  <uuid>4c669f82-cb0d-4bf4-98aa-eb0933075efa</uuid>
  <memory unit='KiB'>1048576</memory>
  <currentMemory unit='KiB'>1048576</currentMemory>
  <vcpu placement='static'>1</vcpu>
  <resource>
    <partition>/machine</partition>
  </resource>
  <os>
    <type arch='x86_64'>exe</type>
    <init>/bin/sh</init>
  </os>
  <clock offset='utc'/>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>restart</on_crash>
  <devices>
    <emulator>/usr/libexec/libvirt_lxc</emulator>
    <filesystem type='mount' accessmode='passthrough'>
      <source dir='/'/>
      <target dir='/'/>
    </filesystem>
    <interface type='network'>
      <mac address='00:16:3e:e5:02:c5'/>
      <source network='default'/>
      <target dev='vnet0'/>
    </interface>
    <console type='pty' tty='/dev/pts/3'>
      <source path='/dev/pts/3'/>
      <target type='lxc' port='0'/>
      <alias name='console0'/>
    </console>
  </devices>
  <seclabel type='none' model='selinux'/>
</domain>

2.start the container  opening another terminal to use strace 
#virsh -c lxc:/// start test
On another terminal
# strace -o output.txt -f -F -p $(pidof libvirtd)

3.Run the command inside the container
sh-4.2# touch /run/initctl
sh-4.2# ln -s /run/initctl /dev/initctl

4.Remains the container start first, then try to reboot/shutdown it on host
#virsh -c lxc:/// reboot test

5.Grep the strace output file to check whether libvirtd try to open /proc/$pid/root/dev/initctl

On libvirt-1.1.1-23.el7.x86_64 , which is a pre-patch version

#cat output.txt | grep -i initctl
8353  open("/proc/8635/root//dev/initctl", O_WRONLY|O_NOCTTY|O_NONBLOCK|O_CLOEXEC <unfinished ...>

That means first the container and the host shared a same namespace and then
libvirtd try to open it on host.


On libvirt-1.1.1-27.el7.x86_64

cat output.txt | grep -i initctl
1458  open("/dev/initctl", O_WRONLY|O_NOCTTY|O_NONBLOCK|O_CLOEXEC) = -1 ELOOP (Too many levels of symbolic links)

Apparently , with the post-patch version , the container already have an individual namespace. Use fake file and then ln it to initctl may lead some error but will not harm the host itself

6.Make up error message for step 4
libvirt-1.1.1-23.el7.x86_64:

virsh # reboot test
error: Failed to reboot domain test
error: Cannot open init control /proc/8635/root//dev/initctl: Too many levels of symbolic links


libvirt-1.1.1-27.el7.x86_64:
virsh # reboot test
error: Failed to reboot domain test
error: internal error: Child process (1458) unexpected exit status 1

libvirtd will fork a child process to execute the behaviour


7.Destroy the container
#virsh -c lxc:/// destroy test

#systemctl status libvirtd
libvirtd.service - Virtualization daemon
   Loaded: loaded (/usr/lib/systemd/system/libvirtd.service; enabled)
   Active: active (running) since Fri 2014-03-14 13:06:59 CST; 1min 17s ago
 Main PID: 6997 (libvirtd)
   CGroup: /system.slice/libvirtd.service
           ├─3011 /sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf
           └─6997 /usr/sbin/libvirtd

Have no affect on systemd and host.


So set this one VERIFIED

Comment 11 Petr Matousek 2014-07-30 12:39:57 UTC
Fixed in 7.0 GA, closing.