PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.7.0
Defender Security – Malware Scanner, Login Security & Firewall v5.7.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / behavior / scan / class-malware-quick-scan.php

class-malware-quick-scan.php in Defender Security – Malware Scanner, Login Security & Firewall 5.7.0, at src/behavior/scan/class-malware-quick-scan.php

180 lines 5.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles malware quick scan.
4 *
5 * @package WP_Defender\Behavior\Scan
6 */
7
8 namespace WP_Defender\Behavior\Scan;
9
10 use Countable;
11 use WP_Defender\Traits\IO;
12 use Calotes\Component\Behavior;
13 use WP_Defender\Behavior\Scan\Malware_Scan;
14 use WP_Filesystem_Base;
15
16 /**
17 * This will fast travel all yara rules to quickly get files seem suspicious, this is catchy.
18 */
19 class Malware_Quick_Scan extends Behavior {
20
21 use IO;
22
23 /**
24 * Holds the content of the file being scanned.
25 *
26 * @var string
27 */
28 private $content;
29
30 /**
31 * Perform a quick scan on the specified file using the provided rules.
32 *
33 * @param string $file The file to scan.
34 * @param array $rules The rules to apply during the scan.
35 *
36 * @return array An array containing the total number of issues found and detailed information about each issue.
37 */
38 public function do_quick_scan( string $file, array $rules ): array {
39 global $wp_filesystem;
40 // Initialize the WP filesystem, no more using 'file-put-contents' function.
41 if ( ! $wp_filesystem instanceof WP_Filesystem_Base ) {
42 require_once ABSPATH . '/wp-admin/includes/file.php';
43 WP_Filesystem();
44 }
45 $total_issues = 0;
46 $qs_detail = array();
47 if ( file_exists( $file ) ) {
48 $content = $wp_filesystem->get_contents( $file );
49 $this->content = $content;
50 foreach ( $rules as $rule ) {
51 $strings = $rule['strings'];
52 $local_issues = array(
53 'identifier' => $rule['identifier'],
54 'catches' => array(),
55 );
56 foreach ( $strings as $string ) {
57 $offset = false;
58 if ( 0 === $string['type'] ) {
59 $offset = strpos( $content, $string['text'] );
60 $text = $string['text'];
61 } else {
62 $pattern = "/{$string['text']}/";
63 $is_match = preg_match_all(
64 $pattern,
65 $content,
66 $matches,
67 PREG_PATTERN_ORDER | PREG_OFFSET_CAPTURE
68 );
69 if ( $is_match ) {
70 $offset = $matches[0][0][1];
71 $text = $matches[0][0][0];
72 }
73 }
74 if ( false === $offset ) {
75 continue;
76 }
77 $local_issues['catches'][] = array(
78 'id' => $string['id'],
79 'text' => $text,
80 'offset' => $offset,
81 );
82 }
83 if ( empty( $local_issues['catches'] ) ) {
84 continue;
85 }
86
87 if ( $this->condition_valid( $local_issues['catches'], $rule ) ) {
88 ++$total_issues;
89 $qs_detail[] = $local_issues;
90 }
91 }
92 }
93
94 return array( $total_issues, $qs_detail );
95 }
96
97 /**
98 * Checks if the condition is valid based on the given rule.
99 *
100 * @param array $found The found items.
101 * @param array $rule The rule to check against.
102 *
103 * @return bool Returns true if the condition is valid, false otherwise.
104 */
105 private function condition_valid( $found, $rule ): bool {
106 $condition = explode( 'and', $rule['condition'] );
107 $count = 0;
108 foreach ( $condition as $item ) {
109 $patterns = array(
110 'x_of_them' => '/([a-z0-9]+)\s*of\s*(.*)/',
111 'id_at_pos' => '/(\$[a-zA-Z0-9]+)\s*at\s*(\d+)/',
112 );
113 foreach ( $patterns as $func => $pattern ) {
114 if ( preg_match( $pattern, $item, $match ) ) {
115 ++$count;
116 $ret = $this->$func( $found, $match, $rule );
117 if ( false === $ret ) {
118 return false;
119 }
120 }
121 }
122 }
123 if ( 0 === $count ) {
124 $this->log( $rule['identifier'], Malware_Scan::MALWARE_LOG );
125 $this->log( $found, Malware_Scan::MALWARE_LOG );
126
127 return false;
128 }
129
130 return true;
131 }
132
133 /**
134 * Determines if a certain number of items in an array meet a certain condition.
135 *
136 * @param array $found The array of items to check.
137 * @param array $check_info The match array containing the number of items to check and the condition.
138 * @param array $rule The rule array containing the strings to check against.
139 *
140 * @return bool Returns true if the number of items that meet the condition is greater than or equal to the
141 * specified number, false otherwise.
142 */
143 private function x_of_them( $found, $check_info, $rule ): bool {
144 $num = $check_info[1];
145 $num = 'all' === $num ? count( $rule['strings'] ) : $num;
146 $num = 'any' === $num ? 1 : $num;
147
148 if ( '(s*)' === $check_info[2] ) {
149 // Hard code in this situation.
150 --$num;
151 }
152 /**
153 * Todo: uncomment the following lines if you need to get log details.
154 * $this->log( sprintf( 'num %s', $num ), Malware_Scan::MALWARE_LOG );
155 * $this->log( sprintf( 'count %s', count( $found ) ), Malware_Scan::MALWARE_LOG );
156 */
157 return ( is_array( $found ) || $found instanceof Countable ? count( $found ) : 0 ) >= $num;
158 }
159
160 /**
161 * Checks if the given ID is at the specified position in the content.
162 *
163 * @param array $found The array of items to check.
164 * @param array $check_info The match array containing the ID and position to check.
165 * @param array $rule The rule array containing the strings to check against.
166 *
167 * @return bool Returns true if the ID is at the specified position in the content, false otherwise.
168 */
169 private function id_at_pos( $found, $check_info, $rule ): bool {
170 // Get the text to check.
171 foreach ( $found as $item ) {
172 if ( $item['id'] === $check_info[1] ) {
173 return strpos( $this->content, $item['text'] ) === $check_info[2];
174 }
175 }
176
177 return false;
178 }
179 }
180