PluginProbe
Patchstack – WordPress & Plugins Security / 2.2.10
Patchstack – WordPress & Plugins Security v2.2.10
2.3.7 trunk 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.17 2.1.18 2.1.19 2.1.2 2.1.20 2.1.21 2.1.22 2.1.23 2.1.24 2.1.25 2.1.3 2.1.4 2.1.5 2.1.6 All 49 releases
patchstack / includes / upload.php

upload.php in Patchstack – WordPress & Plugins Security 2.2.10, at includes/upload.php

332 lines 10.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // Do not allow the file to be called directly.
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 /**
9 * This class is used to upload the local logs to our API so it can
10 * be shown on the app.
11 */
12 class P_Upload extends P_Core {
13
14 /**
15 * Add the actions required to upload logs to our API.
16 *
17 * @param Patchstack $core
18 * @return void
19 */
20 public function __construct( $core ) {
21 parent::__construct( $core );
22
23 // In case the software has never been synchronized, force it.
24 if ( ! get_option( 'patchstack_software_data_hash', false ) && ! get_option( 'patchstack_software_upload_attempted', false ) ) {
25 $this->upload_software();
26 }
27
28 // Register the actions.
29 add_action( 'patchstack_send_software_data', [ $this, 'upload_software' ] );
30 add_action( 'patchstack_send_hacker_logs', [ $this, 'upload_firewall_logs' ] );
31 add_action( 'patchstack_send_event_logs', [ $this, 'upload_activity_logs' ] );
32
33 // In case a plugin or upgrade has been performed, re-synchronize with the app.
34 add_action( 'activated_plugin', [ $this, 'upload_software' ] );
35 add_action( 'deactivated_plugin', [ $this, 'upload_software' ] );
36 add_action( 'deleted_plugin', [ $this, 'upload_software' ] );
37 add_action( 'upgrader_process_complete', [ $this, 'upload_software' ] );
38 add_action( '_core_updated_successfully', [ &$this, 'upload_software' ] );
39 }
40
41 /**
42 * Synchronize the software data with our API.
43 * This includes plugins, themes, WordPress and PHP version.
44 *
45 * @return void|array
46 */
47 public function upload_software() {
48 // Get the software data and hash.
49 $data = $this->get_software_data();
50 $hash = sha1( json_encode( $data ) );
51
52 // Do not sync for no reason.
53 if ( ! defined( 'DOING_CRON' ) && ! isset( $_POST['webarx_secret'] ) && get_option( 'patchstack_software_data_hash', false ) === $hash && ! is_admin() && ! defined( 'WP_CLI ' ) ) {
54 return;
55 }
56
57 // Make sure to not keep calling this function.
58 update_option( 'patchstack_software_upload_attempted', true );
59
60 // Synchronize the software list with the API.
61 $results = $this->plugin->api->upload_software( [ 'software' => json_encode( $data ) ] );
62 if ( isset( $results['success'] ) ) {
63 update_option( 'patchstack_software_data_hash', $hash );
64
65 // The result will also contain a list of all vulnerable plugins on the site that is returned by the API.
66 // If the auto update setting is enabled for vulnerable plugins, perform the update once the 15 minute
67 // scheduled task "patchstack_update_plugins" is executed.
68 $update = get_site_option( 'patchstack_auto_update', [] );
69 if ( isset( $results['vulnerable'] ) && is_array( $update ) && in_array( 'vulnerable', $update ) ) {
70 update_site_option( 'patchstack_vulnerable_plugins', $results['vulnerable'] );
71 }
72
73 // If we have vulnerable plugins, determine if we had them before and if not, pull latest firewall rules.
74 if ( isset( $results['vulnerable'] ) && count( $results['vulnerable'] ) > 0 ) {
75 $prev = get_site_option( 'patchstack_latest_vulnerable', [] );
76 foreach ( $results['vulnerable'] as $vuln ) {
77 if ( ! in_array ( $vuln, $prev ) ) {
78 do_action( 'patchstack_post_dynamic_firewall_rules' );
79 break;
80 }
81 }
82
83 update_site_option( 'patchstack_latest_vulnerable', $results['vulnerable'] );
84 } else {
85 update_site_option( 'patchstack_latest_vulnerable', [] );
86 }
87
88 return $results;
89 }
90
91 return;
92 }
93
94 /**
95 * Synchronize the firewall logs with our API.
96 *
97 * @return void
98 */
99 public function upload_firewall_logs() {
100 global $wpdb;
101
102 // Attempt to fetch data, if any.
103 $lastId = get_option( 'patchstack_firewall_log_lastid', 0 );
104 $successId = $lastId;
105 while ( true ) {
106 // Pull the data from the database, in batches of 100.
107 $items = $wpdb->get_results(
108 $wpdb->prepare(
109 'SELECT id, ip, log_date, request_uri, user_agent, fid, method, post_data FROM ' . $wpdb->prefix . 'patchstack_firewall_log WHERE id > %d ORDER BY id LIMIT 0,100',
110 $lastId
111 )
112 );
113
114 // No need to continue if we have no data.
115 if ( $wpdb->num_rows == 0 ) {
116 break;
117 }
118
119 // Construct the array to be uploaded to our API.
120 $logs = [];
121 foreach ( $items as $item ) {
122
123 // Entries that we don't want to store on the API side.
124 if ( stripos( $item->request_uri, 'wp-comments-post' ) !== false ) {
125 continue;
126 }
127
128 // Push to entries to be uploaded.
129 $logs[] = [
130 'ip' => $item->ip,
131 'fid' => $item->fid,
132 'request_uri' => $item->request_uri,
133 'user_agent' => $item->user_agent,
134 'method' => $item->method,
135 'log_date' => $item->log_date,
136 'post_data' => $item->post_data,
137 ];
138
139 $lastId = $item->id;
140 }
141
142 // JSON encode the logs and upload.
143 $logs = json_encode( $logs );
144 $results = $this->plugin->api->upload_firewall_logs(
145 [
146 'logs' => $logs,
147 'type' => 'firewall',
148 ]
149 );
150
151 if ( isset( $results['errors'] ) ) {
152 update_option( 'patchstack_firewall_log_lastid', $successId );
153 break;
154 }
155
156 $successId = $lastId;
157 }
158
159 // Set lastid to 0.
160 update_option( 'patchstack_firewall_log_lastid', 0 );
161
162 // Delete the logs.
163 $wpdb->query( 'DELETE FROM ' . $wpdb->prefix . 'patchstack_firewall_log' );
164 }
165
166 /**
167 * Synchronize the activity logs with our API.
168 *
169 * @return void
170 */
171 public function upload_activity_logs() {
172 global $wpdb;
173
174 // Determine if we should upload failed logins to the app.
175 $where = " AND action != 'failed login' ";
176 if ( $this->get_option( 'patchstack_activity_log_failed_logins_db', 0 ) == 1 ) {
177 $where = ' ';
178 }
179
180 // Attempt to fetch data, if any.
181 $lastId = get_option( 'patchstack_eventlog_lastid', 0 );
182 $successId = $lastId;
183 while ( true ) {
184 // Pull the data from the database, in batches of 100.
185 $items = $wpdb->get_results(
186 $wpdb->prepare(
187 'SELECT id, author, ip, object, object_id, object_name, action, date FROM ' . $wpdb->prefix . 'patchstack_event_log WHERE id > %d' . $where . 'ORDER BY id LIMIT 0,100',
188 $lastId
189 ),
190 ARRAY_A
191 );
192
193 // No need to continue if we have no data.
194 if ( $wpdb->num_rows == 0 ) {
195 break;
196 }
197
198 // Get the last ID in the result set.
199 $lastId = $items[count($items) - 1]['id'];
200
201 // Send to the API.
202 $logs = json_encode( $items );
203 $results = $this->plugin->api->upload_activity_logs( [ 'logs' => $logs ] );
204 if ( isset( $results['errors'] ) ) {
205 update_option( 'patchstack_eventlog_lastid', $successId );
206 break;
207 }
208
209 $successId = $lastId;
210 }
211
212 // Set lastid to 0.
213 update_option( 'patchstack_eventlog_lastid', 0 );
214
215 // Delete the logs.
216 $wpdb->query( 'DELETE FROM ' . $wpdb->prefix . 'patchstack_event_log' );
217 }
218
219 /**
220 * Obtain information about the software that the user has installed.
221 * This includes plugins, themes, WordPress and PHP version.
222 *
223 * @return array
224 */
225 public function get_software_data() {
226 if ( ! function_exists( 'get_plugins' ) ) {
227 require_once ABSPATH . 'wp-admin/includes/plugin.php';
228 }
229 if ( ! function_exists( 'get_plugin_updates' ) ) {
230 require_once ABSPATH . 'wp-admin/includes/update.php';
231 }
232
233 // Refetch updates data if we are performing a plugin listener related action.
234 if ( isset( $_POST['webarx_secret'] ) ) {
235 @require_once ABSPATH . 'wp-includes/update.php';
236 @wp_update_themes();
237 @wp_update_plugins();
238 }
239
240 // Fetch list of plugins.
241 $all_plugin = get_plugins();
242 $installed_plugins = array_keys( $all_plugin );
243 $updatable_plugins = get_plugin_updates();
244 $software_list = [];
245
246 foreach ( $installed_plugins as $plugin ) {
247 if ( ! file_exists( WP_PLUGIN_DIR . '/' . $plugin ) ) {
248 continue;
249 }
250
251 $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
252 $new_version = empty( $updatable_plugins[ $plugin ]->update->new_version ) ? '' : $updatable_plugins[ $plugin ]->update->new_version;
253 $plugin_name = empty( $plugin_data['Name'] ) ? '' : $plugin_data['Name'];
254 $plugin_version = empty( $plugin_data['Version'] ) ? '' : $plugin_data['Version'];
255
256 if ( ! empty( $plugin_name ) && ! empty( $plugin_version ) ) {
257
258 // Determine the active state.
259 if ( isset( $_GET['action'], $_GET['plugin'] ) && $_GET['action'] == 'deactivate' && $_GET['plugin'] == $plugin) {
260 $active = 0;
261 } else {
262 $active = (int) is_plugin_active( $plugin );
263 }
264
265 $software_list[] = [
266 'sw_type' => 'plugin',
267 'sw_name' => $plugin_name,
268 'sw_cur_ver' => $plugin_version,
269 'sw_new_ver' => $new_version,
270 'sw_key' => $plugin,
271 'sw_active' => $active
272 ];
273 }
274 }
275
276 // Fetch list of themes.
277 $themes = wp_get_themes();
278 $themes_keys = array_keys( $themes );
279 $updatable_themes = get_theme_updates();
280
281 foreach ( $themes_keys as $theme_key ) {
282 $themes_data = $themes[ $theme_key ];
283 $theme_temporary = empty( $updatable_themes[ $theme_key ] ) ? '' : $updatable_themes[ $theme_key ];
284 $theme_new_version = empty( $updatable_themes[ $theme_key ] ) || ! isset( $theme_temporary->update, $theme_temporary->update['new_version'] ) ? '' : $theme_temporary->update['new_version'];
285 $theme_name = $themes_data->get( 'Name' );
286 $theme_version = $themes_data->get( 'Version' );
287
288 if ( ! empty( $theme_name ) && ! empty( $theme_version ) ) {
289 $software_list[] = [
290 'sw_type' => 'theme',
291 'sw_name' => $theme_name,
292 'sw_cur_ver' => $theme_version,
293 'sw_new_ver' => $theme_new_version,
294 'sw_key' => $theme_key,
295 ];
296 }
297 }
298
299 // Fetch WordPress version.
300 global $wp_version;
301 $core_updates = get_core_updates();
302 $new_wp_version = ( ! empty( $core_updates ) && $core_updates[0]->response == 'upgrade' ) ? $core_updates[0]->version : '';
303 $software_list[] = [
304 'sw_type' => 'wordpress',
305 'sw_name' => 'WordPress',
306 'sw_cur_ver' => $wp_version,
307 'sw_new_ver' => $new_wp_version,
308 ];
309
310 // Fetch PHP version.
311 $software_list[] = [
312 'sw_type' => 'php',
313 'sw_name' => 'PHP',
314 'sw_cur_ver' => phpversion(),
315 'sw_new_ver' => '',
316 ];
317
318 // Fetch database server version.
319 global $wpdb;
320 if ( ! is_null( $wpdb ) ) {
321 $software_list[] = [
322 'sw_type' => 'database',
323 'sw_name' => 'Database',
324 'sw_cur_ver' => $wpdb->get_var( 'SELECT VERSION()' ),
325 'sw_new_ver' => ''
326 ];
327 }
328
329 return $software_list;
330 }
331 }
332