Lucene search

K
nessusThis script is Copyright (C) 2010-2022 Tenable Network Security, Inc.PADDING_ORACLE_MS10-070.NASL
HistoryOct 08, 2010 - 12:00 a.m.

MS10-070: Vulnerability in ASP.NET Could Allow Information Disclosure (2418042) (uncredentialed check)

2010-10-0800:00:00
This script is Copyright (C) 2010-2022 Tenable Network Security, Inc.
www.tenable.com
198

There is an information disclosure vulnerability in ASP.NET, part of the .NET framework. Information can be leaked due to improper error handling during encryption padding.

A remote attacker could exploit this to decrypt and modify an ASP.NET application’s server-encrypted data. In .NET Framework 3.5 SP1 and above, an attacker could exploit this to download any file within the ASP.NET application, including web.config.

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

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

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

  script_cve_id("CVE-2010-3332");
  script_bugtraq_id(43316);
  script_xref(name:"MSFT", value:"MS10-070");
  script_xref(name:"MSKB", value:"2416447");
  script_xref(name:"MSKB", value:"2416451");
  script_xref(name:"MSKB", value:"2416468");
  script_xref(name:"MSKB", value:"2416469");
  script_xref(name:"MSKB", value:"2416470");
  script_xref(name:"MSKB", value:"2416471");
  script_xref(name:"MSKB", value:"2416472");
  script_xref(name:"MSKB", value:"2416473");
  script_xref(name:"MSKB", value:"2416474");
  script_xref(name:"MSKB", value:"2418240");
  script_xref(name:"MSKB", value:"2418241");

  script_name(english:"MS10-070: Vulnerability in ASP.NET Could Allow Information Disclosure (2418042) (uncredentialed check)");

  script_set_attribute(attribute:"synopsis", value:
"The version of the .NET framework installed on the remote host has an
information disclosure vulnerability.");
  script_set_attribute(attribute:"description", value:
"There is an information disclosure vulnerability in ASP.NET, part of
the .NET framework.  Information can be leaked due to improper error
handling during encryption padding.

A remote attacker could exploit this to decrypt and modify an ASP.NET
application's server-encrypted data.  In .NET Framework 3.5 SP1 and
above, an attacker could exploit this to download any file within the
ASP.NET application, including web.config.");
  script_set_attribute(attribute:"see_also", value:"https://docs.microsoft.com/en-us/security-updates/SecurityBulletins/2010/ms10-070");
  script_set_attribute(attribute:"solution", value:
"Microsoft has released a set of patches for Windows XP, 2003, Vista,
2008, 7, and 2008 R2.");
  script_set_cvss_base_vector("CVSS2#AV:N/AC:L/Au:N/C:P/I:N/A:N");
  script_set_cvss_temporal_vector("CVSS2#E:POC/RL:OF/RC:C");

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

  script_set_attribute(attribute:"vuln_publication_date", value:"2010/09/17");
  script_set_attribute(attribute:"patch_publication_date", value:"2010/09/28");
  script_set_attribute(attribute:"plugin_publication_date", value:"2010/10/08");

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

  script_category(ACT_ATTACK);
  script_family(english:"Windows");

  script_copyright(english:"This script is Copyright (C) 2010-2022 Tenable Network Security, Inc.");

  script_dependencies("webmirror.nasl", "http_version.nasl");
  script_require_ports("Services/www", 80);

  exit(0);
}

include("global_settings.inc");
include("misc_func.inc");
include("http.inc");

function base64url_decode(str)
{
  local_var cstr,padlen;

  # strip last char
  cstr = substr(str, 0, strlen(str) - 2);

  # num of '=' to pad
  padlen = str[strlen(str) -1];

  cstr = str_replace(string:cstr, find:"-",replace:"+");
  cstr = str_replace(string:cstr, find:"_",replace:"/");
  cstr += crap(data:"=",length:padlen);

  return base64_decode(str:cstr);
}

function base64url_encode(str)
{
  local_var cstr, idx, padchars;

  cstr = base64(str:str);

  # look for '='
  idx = stridx(cstr,"=");

  if(idx != -1)
  {
    padchars  = substr(cstr, idx, strlen(cstr) -1);

    cstr      = substr(cstr, 0, idx -1);
    cstr      += strlen(padchars);
  }
  else # no padding
    cstr += "0";

  cstr = str_replace(string:cstr, find:"+",replace:"-");
  cstr = str_replace(string:cstr, find:"/",replace:"_");

  return cstr;
}

#
# parse link like url?arg1=value1&arg2=value2...
#
# ret['url']    = url part
# ret['args']   = array of 'arg' associative arrays
#
function parse_link(link)
{
  local_var ret, arg_pair_l, arg_pair, array, arg, match;

  match = eregmatch(string:link,pattern:"^(.+)\?(.+)$");

  # link with no arguments
  if(! match)
  {
    ret['url'] = link;
    return ret;
  }

  ret['url'] = match[1];
  arg_pair_l = split(match[2],sep:"&", keep:FALSE);

  foreach arg_pair(arg_pair_l)
  {
    array = split(arg_pair,sep:"=",keep:FALSE);
    arg[array[0]]  = array[1];
  }

  ret['args'] = arg;

  return ret;
}

# Perform the axd check with the given d and t arguments
function check_axd_go(port, path, d, t)
{
  local_var req, res, axd, fixed, original, final_url, links, array, item;

  # Make sure we have all the arguments we need
  if(isnull(path) || isnull(d) || isnull(t))
    return NULL;

  #decode
  original = base64url_decode(str:d);

  #change the last byte
  fixed = original;
  fixed[strlen(fixed)-1] = raw_string(ord(fixed[strlen(fixed) - 1]) -1);

  #re-encode
  fixed = base64url_encode(str:fixed);

  #build the final url to request
  final_url = "/" + path + '?d=' + fixed + '&t=' + t;

  #Resend the request with the changed padding
  req = http_mk_get_req(port:port, item: final_url, version: 11);
  res = http_send_recv_req(port:port, req:req, fetch404:TRUE, exit_on_fail:TRUE);

  # See if the page contained a padding error
  if("adding is invalid" >< res[2])
  {
    return path + " returned a padding error.";
  }
  else if(("CryptographicException" >< res[2]) || ("Bad Data" >< res[2]))
  {
    return path + " returned a runtime error.";
  }
  else if("404" >< res[0])
  {
    exit(0, "The web server on port " + port + " returned a 404 error on " + path + " with invalid padding.");
  }
  else if("302" >< res[0])
  {
    exit(0, "The web server on port " + port + " returned a HTTP Redirect on " + path + " with invalid padding, which may indicate mitigation is in place.");
  }
  else
  {
    return NULL;
  }
}

function check_axd(port, path)
{
  local_var req, res, axd, fixed, original, final_url, links, array, item;
  local_var link, result;
  local_var args;
  req = http_mk_get_req(port:port, item:path, version: 11);
  res = http_send_recv_req(port:port, req:req, exit_on_fail:TRUE, fetch404:TRUE);

  links = egrep(pattern:'\\.axd', string:res[2]);

  if(!links)
    return NULL;

  array = split(links, sep:'\n');

  foreach item(array)
  {
    item = chomp(item);

    axd = eregmatch(pattern:'[\'"]([^"\']+\\.axd[^\'"]*)["\']', string:item);

    if(!isnull(axd))
    {
      if("http" >!< axd[0])
      {
        link = parse_link(link:axd[1]);
 	args = link['args'];
        result = check_axd_go(port:port, path:link['url'], d:args['d'], t:args['t']);

        if(!isnull(result))
        {
          return result;
        }
      }
    }
  }
}

function check_viewstate_go(port, path, viewstate, event_validation)
{
  local_var viewstate_bin, fixed, postdata, res;

  # make sure we have all the arguments we need
  if(isnull(path) || isnull(viewstate) || isnull(event_validation))
    return NULL;

  # Decode
  viewstate_bin = base64_decode(str: viewstate);

  # Modify the last character in the string to induce a padding error
  fixed = viewstate_bin;
  fixed[strlen(fixed)-1] = raw_string(ord(fixed[strlen(fixed) - 1]) -1);

  # Re-encode
  fixed = base64(str:fixed);

  # URL-encode the strings (we only have to worry about three symbols)
  fixed = str_replace(string:fixed, find:"+",replace:"%2b");
  fixed = str_replace(string:fixed, find:"/",replace:"%2f");
  fixed = str_replace(string:fixed, find:"=",replace:"%3d");
  event_validation = str_replace(string:event_validation, find:"+",replace:"%2b");
  event_validation = str_replace(string:event_validation, find:"/",replace:"%2f");
  event_validation = str_replace(string:event_validation, find:"=",replace:"%3d");

  postdata = "__VIEWSTATE=" + fixed + "&" + "__EVENTVALIDATION=" + event_validation + "&__VIEWSTATEENCRYPTED=''";

  res = http_send_recv3(method: "POST", item: "/", port: port, content_type: "application/x-www-form-urlencoded", data: postdata, exit_on_fail:TRUE, fetch404:TRUE);

  if("adding is invalid" >< res[2])
  {
    return "Viewstate at " + path + " returned a padding error.";
  }
  else if("rypto" >< res[2] && 'xception' >< res[2])
  {
    return "Viewstate at " + path + " returned a cryptographic exception.";
  }
  else
  {
    return NULL;
  }

}

function mk_list()
{
  if (isnull(_FCT_ANON_ARGS[0]))	return make_list();
  else					return make_list(_FCT_ANON_ARGS[0]);
}

function check_viewstate(port, path)
{
  local_var req, res, viewstate, event_validation;

  req = http_mk_get_req(port:port, item:path, version: 11);
  res = http_send_recv_req(port:port, req:req, exit_on_fail:TRUE, fetch404:TRUE);

  if("__VIEWSTATE" >!< res[2])
  {
    return NULL;
  }

  if("__VIEWSTATEENCRYPTED" >!< res[2])
  {
    return NULL;
  }

  viewstate = eregmatch(pattern:'<[^>]+hidden[^>]+name=["\']__VIEWSTATE[^>]+value=["\']([^"\']+)["\']', string:res[2]);
  event_validation = eregmatch(pattern:'<[^>]+hidden[^>]+name=["\']__EVENTVALIDATION[^>]+value=["\']([^"\']+)["\']', string:res[2]);

  if(isnull(viewstate) || isnull(event_validation))
  {
    return NULL;
  }

  return check_viewstate_go(port:port, path:path, viewstate:viewstate[1], event_validation:event_validation[1]);
}

var port, axd_files, viewstate_files;
var axd_count, viewstate_count;


port = get_http_port(default:80);

# Get a list of .axd files from the webspider script. If CGI scanning is off,
# this will be less effective.
axd_files = get_kb_list("www/" + port + "/content/extensions/axd");

if(isnull(axd_files))
{
  var result;
  # If we don't have the webmirror extension, check the root folder
  result = check_axd(port:port, path:'/');

  if(!isnull(result))
  {
    security_warning(port:port, extra:'\n' + result + '\n');
    exit(0);
  }
}
else
{
  axd_files = make_list(axd_files);
  axd_count = 0;

  foreach axd(axd_files)
  {
    var d_list, t_list;
    d_list = get_kb_list("www/" + port + "/cgi-params" + axd + "/d");
    t_list = get_kb_list("www/" + port + "/cgi-params" + axd + "/t");

    if(!isnull(d_list) && !isnull(t_list))
    {
      var max, i;

      d_list = make_list(d_list);
      t_list = make_list(t_list);

      max = max_index(d_list);

      for(i = 0; i < max; i++)
      {
        var d, t;
        d = d_list[i];
        t = t_list[i];
        if(isnull(t))
          t = '';

        result = check_axd_go(port:port, path:axd, d:d, t:t);
        if(!isnull(result))
        {
          security_warning(port:port, extra:'\n' + result + '\n');
          exit(0);
        }
      }

      # Limit the number of files we check
      if(axd_count > 4)
        break;
      axd_count++;
    }
  }
}

# Get a list of all .cgis. If CGI scanning is turned off, again, this will be more complicated
viewstate_files = get_kb_list('www/' + port + '/cgi');
if(isnull(viewstate_files))
{
  # Check the root path only
  var result;
  result = check_viewstate(port:port, path:'/');
  if(!isnull(result))
  {
    security_warning(port:port, extra:'\n' + result + '\n');
    exit(0);
  }
}
else
{
  viewstate_files = make_list(viewstate_files);
  viewstate_count = 0;

  # Search our viewstate files for one with __VIEWSTATEENCRYPTED
  foreach file(viewstate_files)
  {
    var viewstateencrypted;

    viewstate_encrypted = get_kb_list("www/" + port + "/cgi-params" + file + "/__VIEWSTATEENCRYPTED");

    if(!isnull(viewstate_encrypted))
    {
      var viewstate, event_validation, result;

      lVS = mk_list(get_kb_list("www/" + port + "/cgi-params" + file + "/__VIEWSTATE"));
      foreach viewstate (lVS)
      {
        lEV = mk_list(get_kb_list("www/" + port + "/cgi-params" + file + "/__EVENTVALIDATION"));
	foreach event_validation (lEV)
	{
	  result = check_viewstate_go(port:port, path:file, viewstate:viewstate, event_validation:event_validation);

	  if(!isnull(result))
	  {
	    security_warning(port:port, extra:'\n' + result + '\n');
	    exit(0);
	  }
        }
      }
    }

    # Limit the number of files we check
    if(viewstate_count > 4)
      break;
    viewstate_count++;
  }

}

exit(0, "The web server on port " + port + " didn't have a vulnerable .axd file or encrypted viewstate that could be found.");

VendorProductVersionCPE
microsoft.net_frameworkcpe:/a:microsoft:.net_framework