self::MAX_FILE_SIZE_TO_SCAN ) { return $file; } $content = self::read_head_and_tail( $tmp, $size ); list( $score, $matched ) = Argus_Malware_Scanner::score_content( $content ); if ( $score >= Argus_Malware_Scanner::THRESHOLD_HIGH ) { $file['error'] = __( 'For your website\'s security, ARGUS Defence blocked this upload: its content matched known malicious patterns.', 'argus-wordpress-defence' ); self::record_block( $name, 'content-heuristic', 'critical', $score, $matched ); } return $file; } protected static function read_head_and_tail( $path, $size ) { $fh = fopen( $path, 'rb' ); // phpcs:ignore WordPress.WP.AlternativeFunctions if ( ! $fh ) { return ''; } $head = fread( $fh, min( self::SCAN_CHUNK_BYTES, $size ) ); // phpcs:ignore WordPress.WP.AlternativeFunctions $tail = ''; if ( $size > self::SCAN_CHUNK_BYTES * 2 ) { fseek( $fh, -self::SCAN_CHUNK_BYTES, SEEK_END ); $tail = fread( $fh, self::SCAN_CHUNK_BYTES ); // phpcs:ignore WordPress.WP.AlternativeFunctions } elseif ( $size > self::SCAN_CHUNK_BYTES ) { $tail = fread( $fh, $size - self::SCAN_CHUNK_BYTES ); // phpcs:ignore WordPress.WP.AlternativeFunctions } fclose( $fh ); // phpcs:ignore WordPress.WP.AlternativeFunctions return $head . $tail; } protected static function record_block( $filename, $rule, $severity, $score = null, array $matched = array() ) { $user = wp_get_current_user(); $who = $user && $user->exists() ? $user->user_login : 'unknown'; Argus_Findings::record( 'malware', $severity, array( 'what_happened' => sprintf( 'Blocked a malicious file upload before it reached your website: %s', $filename ), 'why_it_matters' => 'A file matching this pattern could execute code on your server if it had been allowed through.', 'what_argus_found' => 'executable-extension' === $rule ? sprintf( 'The filename "%s" has a PHP-executable extension, which is never allowed as a media upload.', $filename ) : sprintf( 'The file content scored %d on ARGUS\'s heuristic scanner (matched: %s), consistent with a webshell or obfuscated payload.', (int) $score, implode( ', ', wp_list_pluck( $matched, 'id' ) ) ), 'when_it_happened' => current_time( 'mysql' ), 'why_suspicious' => 'Legitimate media uploads (images, documents, video) never contain executable code or PHP-executable extensions.', 'what_could_be_affected' => 'If this upload had succeeded, it could have given an attacker code execution on your server.', 'what_should_you_do' => sprintf( 'No action needed -- ARGUS already blocked this upload before it reached your website. Uploaded by: %s.', $who ), ), array( 'filename' => $filename, 'rule' => $rule, 'score' => $score, 'matched_rules' => $matched, 'uploaded_by' => $who ) ); Argus_Events::record( 'upload_blocked', $severity, sprintf( 'Blocked upload "%s" (%s)', $filename, $rule ), array( 'filename' => $filename, 'rule' => $rule, 'uploaded_by' => $who ) ); } }