Total Tests:

OWASP Top 10 in 2021: Software and Data Integrity Failures Practical Overview

Monday, October 18, 2021 By Read Time: 5 min.

Software and Data Integrity Failures is #8 in the current OWASP Top Ten Most Critical Web Application Security Risks.


OWASP Top 10 in 2021: Software and Data Integrity Failures Practical Overview

Software and Data Integrity Failures

The new Software and Data Integrity Failures OWASP entry covers 10 CWEs, related to data and software integrity, such as CWE-502: deserialization of untrusted data, CWE-345: Insufficient data authenticity, CWE-494: Download of code without integrity check.

Do you want to have an in-depth understanding of all modern aspects of
Software and Data Integrity Failures?
Read this article and bookmark it to get back later, we regularly update this page.

If critical data used by the application is not verified, attackers can tamper with it, which can lead to quite serious issues, such as introduction of malicious code into software.

Many applications now include automatic software update functionality, which raises concerns about data integrity during the update process. If attackers are able to perform MitM attack and push malicious code to the application during the update process, it is very important that such updates are never installed, otherwise the application gets compromised.

How to Detect Software and Data Integrity Failures Vulnerabilities
Website Security Test
  • GDPR & PCI DSS Test
  • Website CMS Security Test
  • CSP & HTTP Headers Check
  • WordPress & Drupal Scanning
Try For Free

Attack vectors

In 2021 over 18000 organizations received a malicious update onto their SolarWinds Orion installation. The attackers were able to compromise the vendor’s CI/CD pipeline and introduced a malicious code directly into original software.

Missing integrity checks issues are very common in SOHO segment as well. Numerous home routers, set-top boxes, IoT devices do not verify firmware integrity, which leads to devices takeover.


Example

To demonstrate the vulnerability let’s have a look at the following code:

  1. function admin_init() {
  2.     if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
  3.          add_action( 'wp_ajax_swpsmtp_clear_log', array( $this, 'clear_log' ) );
  4.          add_action( 'wp_ajax_swpsmtp_self_destruct', array( $this, 'self_destruct_handler' ) );
  5.     }
  6.  
  7.     //view log file
  8.     if ( isset( $_GET[ 'swpsmtp_action' ] ) ) {
  9.         if ( $_GET[ 'swpsmtp_action' ] === 'view_log' ) {
  10.         $log_file_name = $this->opts[ 'smtp_settings' ][ 'log_file_name' ];
  11.         if ( ! file_exists( plugin_dir_path( __FILE__ ) . $log_file_name ) ) {
  12.             if ( $this->log( "Easy WP SMTP debug log file\r\n\r\n" ) === false ) {
  13.             wp_die( 'Can\'t write to log file. Check if plugin directory  (' . plugin_dir_path( __FILE__ ) . ') is writeable.' );
  14.             };
  15.         }
  16.         $logfile = fopen( plugin_dir_path( __FILE__ ) . $log_file_name, 'rb' );
  17.         if ( ! $logfile ) {
  18.             wp_die( 'Can\'t open log file.' );
  19.         }
  20.         header( 'Content-Type: text/plain' );
  21.         fpassthru( $logfile );
  22.         die;
  23.         }
  24.     }
  25.  
  26.     //check if this is export settings request
  27.     $is_export_settings = filter_input( INPUT_POST, 'swpsmtp_export_settings', FILTER_SANITIZE_NUMBER_INT );
  28.     if ( $is_export_settings ) {
  29.         $data                    = array();
  30.         $opts                    = get_option( 'swpsmtp_options', array() );
  31.         $data[ 'swpsmtp_options' ]       = $opts;
  32.         $swpsmtp_pass_encrypted          = get_option( 'swpsmtp_pass_encrypted', false );
  33.         $data[ 'swpsmtp_pass_encrypted' ]    = $swpsmtp_pass_encrypted;
  34.         if ( $swpsmtp_pass_encrypted ) {
  35.         $swpsmtp_enc_key         = get_option( 'swpsmtp_enc_key', false );
  36.         $data[ 'swpsmtp_enc_key' ]   = $swpsmtp_enc_key;
  37.         }
  38.         $smtp_test_mail          = get_option( 'smtp_test_mail', array() );
  39.         $data[ 'smtp_test_mail' ]    = $smtp_test_mail;
  40.         $out                 = array();
  41.         $out[ 'data' ]           = serialize( $data );
  42.         $out[ 'ver' ]            = 1;
  43.         $out[ 'checksum' ]       = md5( $out[ 'data' ] );
  44.  
  45.         $filename = 'easy_wp_smtp_settings.txt';
  46.         header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
  47.         header( 'Content-Type: text/plain' );
  48.         echo serialize( $out );
  49.         exit;
  50.     }
  51.  
  52.     $is_import_settings = filter_input( INPUT_POST, 'swpsmtp_import_settings', FILTER_SANITIZE_NUMBER_INT );
  53.     if ( $is_import_settings ) {
  54.          $err_msg = __( 'Error occurred during settings import', 'easy-wp-smtp' );
  55.          if ( empty( $_FILES[ 'swpsmtp_import_settings_file' ] ) ) {
  56.             echo $err_msg;
  57.             wp_die();
  58.         }
  59.         $in_raw = file_get_contents( $_FILES[ 'swpsmtp_import_settings_file' ][ 'tmp_name' ] );
  60.         try {
  61.             $in = unserialize( $in_raw );
  62.             if ( empty( $in[ 'data' ] ) ) {
  63.                  echo $err_msg;
  64.                  wp_die();
  65.             }
  66.             if ( empty( $in[ 'checksum' ] ) ) {
  67.                  echo $err_msg;
  68.                  wp_die();
  69.             }
  70.             if ( md5( $in[ 'data' ] ) !== $in[ 'checksum' ] ) {
  71.                  echo $err_msg;
  72.                  wp_die();
  73.             }
  74.             $data = unserialize( $in[ 'data' ] );
  75.             foreach ( $data as $key => $value ) {
  76.                  update_option( $key, $value );
  77.             }
  78.             set_transient( 'easy_wp_smtp_settings_import_success', true, 60 * 60 );
  79.             $url = admin_url() . 'options-general.php?page=swpsmtp_settings';
  80.             wp_safe_redirect( $url );
  81.             exit;
  82.         } catch ( Exception $ex ) {
  83.             echo $err_msg;
  84.             wp_die();
  85.         }
  86.     }
  87. }

This is the admin_init() function from the easy-wp-smtp.php file of the Easy WP SMTP WordPress plugin version 1.3.9. The unserialize() function is called to the “data” string and then the values are used to update WordPress settings with the update_option() function.

Apart from unsafe unserialize() call, the plugin allows unauthenticated file upload, as the affected function is by design accessible by non-authenticated users.

This vulnerability was exploited in the wild by attackers to alter the value of the WordPress wp_user_roles option in order to assign administrative privileges to all registered WordPress users.


Solution

Developers should follow the steps below in order to protect the application against data integrity failures:

  • Always sign your application components to make sure that both software and data that come from the trusted source and have not been altered
  • Make sure that dependencies and third-party libraries use only trusted repositories.
  • Review the source code of your application to avoid unwanted configuration changes or introduction on malicious code into the software.
  • Make sure that no serialized data is sent unencrypted or unsigned to the clients to avoid data tampering or replay attacks.

Latest news and insights on AI and Machine Learning for application security testing, web, mobile and IoT security vulnerabilities, and application penetration testing.
How We Help Ask a Question