PluginProbe
Patchstack – WordPress & Plugins Security / 2.2.9
Patchstack – WordPress & Plugins Security v2.2.9
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.9, at includes/upload.php

299 lines 10.2 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 $lastid = get_option( 'patchstack_firewall_log_lastid', 0 );
102 $items = $wpdb->get_results( $wpdb->prepare( 'SELECT ip, log_date, request_uri, user_agent, fid, method, post_data FROM ' . $wpdb->prefix . 'patchstack_firewall_log WHERE id > %d ORDER BY id', $lastid ) );
103
104 // No need to synchronize if there are no new logs present.
105 if ( $wpdb->num_rows == 0 ) {
106 return;
107 }
108
109 // Construct the array to be uploaded to our API.
110 $logs = [];
111 foreach ( $items as $item ) {
112
113 // Entries that we don't want to store on the API side.
114 if ( stripos( $item->request_uri, 'wp-comments-post' ) !== false ) {
115 continue;
116 }
117
118 // Push to entries to be uploaded.
119 $logs[] = [
120 'ip' => $item->ip,
121 'fid' => $item->fid,
122 'request_uri' => $item->request_uri,
123 'user_agent' => $item->user_agent,
124 'method' => $item->method,
125 'log_date' => $item->log_date,
126 'post_data' => $item->post_data,
127 ];
128 }
129
130 // JSON encode the logs and upload.
131 $logs = json_encode( $logs );
132 $results = $this->plugin->api->upload_firewall_logs(
133 [
134 'logs' => $logs,
135 'type' => 'firewall',
136 ]
137 );
138 if ( isset( $results['errors'] ) ) {
139 return;
140 }
141
142 // Get the most recent id of the logs.
143 $lastid = $wpdb->get_var( 'SELECT id FROM ' . $wpdb->prefix . 'patchstack_firewall_log ORDER BY id DESC LIMIT 0, 1' );
144 update_option( 'patchstack_firewall_log_lastid', $lastid );
145
146 // Delete logs that are older than 2 weeks.
147 $wpdb->query( 'DELETE FROM ' . $wpdb->prefix . 'patchstack_firewall_log WHERE log_date < DATE_SUB(NOW(), INTERVAL 14 DAY)' );
148 }
149
150 /**
151 * Synchronize the activity logs with our API.
152 *
153 * @return void
154 */
155 public function upload_activity_logs() {
156 global $wpdb;
157
158 // Determine if we should upload failed logins to the app.
159 $where = " AND action != 'failed login' ";
160 if ( $this->get_option( 'patchstack_activity_log_failed_logins_db', 0 ) == 1 ) {
161 $where = ' ';
162 }
163
164 // Do we have data to upload?
165 $lastid = get_option( 'patchstack_eventlog_lastid', 0 );
166 $items = $wpdb->get_results( $wpdb->prepare( 'SELECT author, ip, object, object_id, object_name, action, date FROM ' . $wpdb->prefix . 'patchstack_event_log WHERE id > %d' . $where . 'ORDER BY id', [ $lastid ] ) );
167 if ( $wpdb->num_rows == 0 ) {
168 return;
169 }
170
171 // Send to the API.
172 $logs = json_encode( $items );
173 $results = $this->plugin->api->upload_activity_logs( [ 'logs' => $logs ] );
174 if ( isset( $results['errors'] ) ) {
175 return;
176 }
177
178 // Get the most recent id of the logs.
179 $lastid = $wpdb->get_var( 'SELECT id FROM ' . $wpdb->prefix . 'patchstack_event_log ORDER BY id DESC LIMIT 0, 1' );
180 update_option( 'patchstack_eventlog_lastid', $lastid );
181
182 // Delete logs that are older than 2 weeks.
183 $wpdb->query( 'DELETE FROM ' . $wpdb->prefix . 'patchstack_event_log WHERE date < DATE_SUB(NOW(), INTERVAL 14 DAY)' );
184 }
185
186 /**
187 * Obtain information about the software that the user has installed.
188 * This includes plugins, themes, WordPress and PHP version.
189 *
190 * @return array
191 */
192 public function get_software_data() {
193 if ( ! function_exists( 'get_plugins' ) ) {
194 require_once ABSPATH . 'wp-admin/includes/plugin.php';
195 }
196 if ( ! function_exists( 'get_plugin_updates' ) ) {
197 require_once ABSPATH . 'wp-admin/includes/update.php';
198 }
199
200 // Refetch updates data if we are performing a plugin listener related action.
201 if ( isset( $_POST['webarx_secret'] ) ) {
202 @require_once ABSPATH . 'wp-includes/update.php';
203 @wp_update_themes();
204 @wp_update_plugins();
205 }
206
207 // Fetch list of plugins.
208 $all_plugin = get_plugins();
209 $installed_plugins = array_keys( $all_plugin );
210 $updatable_plugins = get_plugin_updates();
211 $software_list = [];
212
213 foreach ( $installed_plugins as $plugin ) {
214 if ( ! file_exists( WP_PLUGIN_DIR . '/' . $plugin ) ) {
215 continue;
216 }
217
218 $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
219 $new_version = empty( $updatable_plugins[ $plugin ]->update->new_version ) ? '' : $updatable_plugins[ $plugin ]->update->new_version;
220 $plugin_name = empty( $plugin_data['Name'] ) ? '' : $plugin_data['Name'];
221 $plugin_version = empty( $plugin_data['Version'] ) ? '' : $plugin_data['Version'];
222
223 if ( ! empty( $plugin_name ) && ! empty( $plugin_version ) ) {
224
225 // Determine the active state.
226 if ( isset( $_GET['action'], $_GET['plugin'] ) && $_GET['action'] == 'deactivate' && $_GET['plugin'] == $plugin) {
227 $active = 0;
228 } else {
229 $active = (int) is_plugin_active( $plugin );
230 }
231
232 $software_list[] = [
233 'sw_type' => 'plugin',
234 'sw_name' => $plugin_name,
235 'sw_cur_ver' => $plugin_version,
236 'sw_new_ver' => $new_version,
237 'sw_key' => $plugin,
238 'sw_active' => $active
239 ];
240 }
241 }
242
243 // Fetch list of themes.
244 $themes = wp_get_themes();
245 $themes_keys = array_keys( $themes );
246 $updatable_themes = get_theme_updates();
247
248 foreach ( $themes_keys as $theme_key ) {
249 $themes_data = $themes[ $theme_key ];
250 $theme_temporary = empty( $updatable_themes[ $theme_key ] ) ? '' : $updatable_themes[ $theme_key ];
251 $theme_new_version = empty( $updatable_themes[ $theme_key ] ) || ! isset( $theme_temporary->update, $theme_temporary->update['new_version'] ) ? '' : $theme_temporary->update['new_version'];
252 $theme_name = $themes_data->get( 'Name' );
253 $theme_version = $themes_data->get( 'Version' );
254
255 if ( ! empty( $theme_name ) && ! empty( $theme_version ) ) {
256 $software_list[] = [
257 'sw_type' => 'theme',
258 'sw_name' => $theme_name,
259 'sw_cur_ver' => $theme_version,
260 'sw_new_ver' => $theme_new_version,
261 'sw_key' => $theme_key,
262 ];
263 }
264 }
265
266 // Fetch WordPress version.
267 global $wp_version;
268 $core_updates = get_core_updates();
269 $new_wp_version = ( ! empty( $core_updates ) && $core_updates[0]->response == 'upgrade' ) ? $core_updates[0]->version : '';
270 $software_list[] = [
271 'sw_type' => 'wordpress',
272 'sw_name' => 'WordPress',
273 'sw_cur_ver' => $wp_version,
274 'sw_new_ver' => $new_wp_version,
275 ];
276
277 // Fetch PHP version.
278 $software_list[] = [
279 'sw_type' => 'php',
280 'sw_name' => 'PHP',
281 'sw_cur_ver' => phpversion(),
282 'sw_new_ver' => '',
283 ];
284
285 // Fetch database server version.
286 global $wpdb;
287 if ( ! is_null( $wpdb ) ) {
288 $software_list[] = [
289 'sw_type' => 'database',
290 'sw_name' => 'Database',
291 'sw_cur_ver' => $wpdb->get_var( 'SELECT VERSION()' ),
292 'sw_new_ver' => ''
293 ];
294 }
295
296 return $software_list;
297 }
298 }
299