Vulnerabilities > CVE-2019-4087 - Out-of-bounds Write vulnerability in IBM Spectrum Protect Operations Center

047910
CVSS 9.8 - CRITICAL
Attack vector
NETWORK
Attack complexity
LOW
Privileges required
NONE
Confidentiality impact
HIGH
Integrity impact
HIGH
Availability impact
HIGH
network
low complexity
ibm
CWE-787
critical
nessus

Summary

IBM Spectrum Protect Servers 7.1 and 8.1 and Storage Agents are vulnerable to a stack-based buffer overflow, caused by improper bounds checking by servers and storage agents in response to specifically crafted communication exchanges. By sending an overly long request, a remote attacker could overflow a buffer and execute arbitrary code on the system with instance id privileges or cause the server or storage agent to crash. IBM X-Force ID: 157510.

Common Weakness Enumeration (CWE)

Nessus

NASL familyGeneral
NASL idIBM_SPECTRUM_PROTECT_CVE-2019-4087.NASL
descriptionThe IBM Spectrum Protect server or storage agent running on the remote host is affected by a remote code execution vulnerability due to improper invalidation of user-supplied data during communication exchanges. An unauthenticated, remote attacker can exploit this, via a series of specially crafted messages, to execute arbitrary code on the system with instance id privileges or cause the server or storage agent to crash.
last seen2020-03-18
modified2020-03-13
plugin id134564
published2020-03-13
reporterThis script is Copyright (C) 2020 and is owned by Tenable, Inc. or an Affiliate thereof.
sourcehttps://www.tenable.com/plugins/nessus/134564
titleIBM Spectrum Protect Server and Storage Agent RCE
code
#
# (C) Tenable Network Security, Inc.
#

include("compat.inc");

if (description)
{
  script_id(134564);
  script_version("1.1");
  script_set_attribute(attribute:"plugin_modification_date", value:"2020/03/13");

  script_name(english:"IBM Spectrum Protect Server and Storage Agent RCE");
  script_set_attribute(attribute:"synopsis", value:
"A backup service running on the remote host is affected by a
remote code execution vulnerability.");
  script_set_attribute(attribute:"description", value:
"The IBM Spectrum Protect server or storage agent running on the
remote host is affected by a remote code execution vulnerability due
to improper invalidation of user-supplied data during communication
exchanges. An unauthenticated, remote attacker can exploit this, via
a series of specially crafted messages, to execute arbitrary code on
the system with instance id privileges or cause the server or storage
agent to crash.");
  script_set_attribute(attribute:"see_also", value:"https://www.ibm.com/support/pages/node/882472");
  script_set_attribute(attribute:"solution", value:
"Upgrade to IBM Spectrum Protect 7.1.9.300 or 8.1.8 or later.");
  script_set_cvss_base_vector("CVSS2#AV:N/AC:L/Au:N/C:C/I:C/A:C");
  script_set_cvss3_base_vector("CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H");
  script_set_attribute(attribute:"cvss_score_source", value:"CVE-2019-4087");

  script_set_attribute(attribute:"vuln_publication_date", value:"2019/06/28");
  script_set_attribute(attribute:"patch_publication_date", value:"2019/06/28");
  script_set_attribute(attribute:"plugin_publication_date", value:"2020/03/13");

  script_set_attribute(attribute:"plugin_type", value:"remote");
  script_set_attribute(attribute:"cpe", value:"cpe:/a:ibm:tivoli_storage_manager");
  script_set_attribute(attribute:"cpe", value:"x-cpe:/a:ibm:spectrum_protect");
  script_end_attributes();

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

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

  script_dependencies("ibm_tsm_detect.nasl");
  script_require_ports("tsm-agent",1500);

  exit(0);

}

include('audit.inc');
include('global_settings.inc');
include('byte_func.inc');
include('misc_func.inc');

##
# Send a 'verb' request.
#
# @param socket Socket to send the verb.
# @param code Verb code.
# @param data Verb data.
#
# @return None
##
function send_verb (socket, code, data)
{
 local_var header, len, req;

  len = strlen(data);

  if(code < 0x100)
  {
    header =
      mkword(len+4) +
      mkbyte(code) +
      mkbyte(0xa5);  # magic
  }
  else
  {
    header =
      mkword(0) +
      mkbyte(8) +
      mkbyte(0xa5)  +
      mkdword(code) +
      mkdword(len+12);
  }
  
 req = header + data;
 send(socket:socket, data:req);
}

##
# Receive a 'verb' response.
#
# @param socket Socket to receive the response.
#
# @return {"code":<verb_code>, "data":<verb_data>} or NULL on error.
#
##
function recv_verb(socket)
{
 local_var code, header, data, len;

 header = recv(socket:socket, length:4, min:4, timeout:10);
 if (isnull(header)) return NULL;

 # We expect at least 4 bytes
 if(strlen(header) < 4) return NULL;

 # Check magic byte
 if (ord(header[3]) != 0xa5) return NULL;

 # Get the verb code 
 code = ord(header[2]);

 # Got an extended verb code
 if(code == 8)
 {
  header = recv(socket:socket, length:8, min:8, timeout:10);
  if (isnull(header) || strlen(header) < 8) return NULL;

  code = getdword(blob:header, pos:0);
  len  = getdword(blob:header, pos:4);
  if (len < 12) return NULL;
  len = len - 12;
 }
 else
 {
  len = getword(blob:header, pos:0);
  if (len < 4) return NULL;
  len = len - 4;
 }

 data = recv(socket:socket, length:len, min:len, timeout:10);

 return {'code':code, 'data':data};
}

port = get_service(svc:'tsm-agent', default:1500, exit_on_fail:TRUE);

if(get_port_transport(port) == ENCAPS_IP)
  ssl = FALSE;
else
  ssl = TRUE;

soc = open_sock_tcp(port);
if(!soc) audit(AUDIT_SOCK_FAIL,port);

# Send the Identify verb message.
#
# Need to send this verb message so that the server is in the right
# state to process the subsequent Negotiate verb message.
send_verb(socket:soc, code:0x1d, data:NULL);
res = recv_verb(socket:soc);
if(isnull(res) || res.code != 0x1e)
{
  close(soc);
  exit(1, 'Failed to receive a IdentifyResp message.');
}

# Send the Negotiate verb message.
#
# Need to send this verb message so that the server is in the right
# state to process the subsequent TransportMethod verb message.
user = 'nessus';
data = mkbyte(1) +      # version
  mkword(0x1b) +        # dataOffset to user from msg start
  mkdword(0x40000000) + # supportedMethods
  mkword(2) +           # sslMode
  mkword(0) +           # username offset within 'data'?
  mkword(strlen(user)) +  # username length
  mkbyte(1) +           # idType
  mkbyte(7) +           # sessType
  user;                 # username
send_verb(socket:soc, code:0x3E0000, data:data);
res = recv_verb(socket:soc);
if(isnull(res) || res.code != 0x3E0010)
{
  close(soc);
  exit(1, 'Failed to receive a NegotiateResp message.');
}

# Send the TransportMethod verb message.
#
# This verb is used to switch transport method (i.e., TCP to SSL)
#
# To detect the vulnerability, we send the verb message with:
#
#  1) The transport to switch to matches the current transport.
#     For example, if the plugin currently uses SSL to communicate
#     with the server, it requests to switch to SSL. Similarly, if
#     it is on TCP, it requests to switch to TCP.
#  2) Total verb message length > 19 bytes.
#
# The vulnerable server does not check the verb message length, and it
# continues to process the verb message but does nothing because we
# requested to switch to the same transport we are currently on.
# As a result, it will continue to process our subsequent Authenticate
# verb message, which will result in a AuthenticateResp verb message
# from the server.
#
# The patched server checks the verb message length to ensure it
# doesn't exceed 19 bytes. If so, it shuts down the connection
# without processing any subsequent incoming verb messages.
# As a result, the patched server will not respond to our
# Authenticate verb message.
#
if (ssl)
  new_transport = 2;
else
  new_transport = 1;
data = mkbyte(1) +  # version
  mkword(0xf) +     # dataOffset to transport from msg start
  mkdword(new_transport) +  # Transport to switch to
                            # 1 - TCP, 2 - SSL
  'A';  # Extra data to cause length check failure on the patched
        # server but no crash on the vulnerable server.
send_verb(socket:soc, code:0x3E0040, data:data);

# Send the Authenticate verb message.
pass = SCRIPT_NAME;
pass += crap(data:'\x00', length:0x40 - strlen(pass));
data = mkbyte(1) +        # version
  mkword(0x13) +          # dataOffset to password from msg start
  mkdword(0) +            # password offset within 'data'?
  mkword(strlen(pass)) +  # password length
  pass;                   # password
send_verb(socket:soc, code:0x3E0020, data:data);
res = recv_verb(socket:soc);
close(soc);

# Got a AuthenticateResp: vulnerable
if(!isnull(res) && res.code == 0x3E0030)
{
  extra = "Nessus was able to detect the vulnerability by sending a specially crafted 'TransportMethod' message to the remote service listening on port " + port + '.';
  security_report_v4(
    port      : port,
    severity  : SECURITY_HOLE,
    extra     : extra
  );
}
else
  audit(AUDIT_HOST_NOT, 'affected');