Sucuri Security – Auditing, Malware Scanner and Security Hardening - Version 1.4

Version Description

  • Added post-hack options (reset all passwords).
  • Added last-login.
  • Added more hardening and the option to revert any hardening done.
Download this release

Release Info

Developer yorman
Plugin Icon 128x128 Sucuri Security – Auditing, Malware Scanner and Security Hardening
Version 1.4
Comparing to
See all releases

Code changes from version 1.3 to 1.4

inc/css/sucuriscan-default-css.css CHANGED
@@ -2,14 +2,14 @@
2
  * Copyright (C) 2010-2012 Sucuri Security - http://sucuri.net
3
  * Released under the GPL - see LICENSE file for details.
4
  */
5
-
6
  .sucuriscan_header {
7
  background: #333;
8
  border-bottom-left-radius:5px;
9
  border-bottom-right-radius:5px;
10
  border-top-left-radius:5px;
11
  border-top-right-radius:5px;
12
- height: 38px;
13
  margin: 16px 0 8px;
14
  min-width: 255px;
15
  padding: 10px;
@@ -27,7 +27,7 @@
27
  float: left;
28
  margin-left: 10px;
29
  padding: 3px 0 0;
30
- text-shadow:#000 0 1px 0;
31
  }
32
 
33
  .sucuriscan-maincontent {
@@ -56,4 +56,8 @@
56
  #sucuri-latest-posts.sucuriscan-sidebar {
57
  background-color:#ececec;
58
  border-color:#999;
59
- }
 
 
 
 
2
  * Copyright (C) 2010-2012 Sucuri Security - http://sucuri.net
3
  * Released under the GPL - see LICENSE file for details.
4
  */
5
+
6
  .sucuriscan_header {
7
  background: #333;
8
  border-bottom-left-radius:5px;
9
  border-bottom-right-radius:5px;
10
  border-top-left-radius:5px;
11
  border-top-right-radius:5px;
12
+ height: 38px;
13
  margin: 16px 0 8px;
14
  min-width: 255px;
15
  padding: 10px;
27
  float: left;
28
  margin-left: 10px;
29
  padding: 3px 0 0;
30
+ text-shadow:#000 0 1px 0;
31
  }
32
 
33
  .sucuriscan-maincontent {
56
  #sucuri-latest-posts.sucuriscan-sidebar {
57
  background-color:#ececec;
58
  border-color:#999;
59
+ }
60
+
61
+ .sucuriscan-maincontent a.lastlogins-showall{
62
+ margin: 10px auto 0 auto;
63
+ }
inc/tpl/sucuri-wp-lastlogins.html.tpl ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="wrap">
2
+ <h2 id="warnings_hook"></h2>
3
+ <div class="sucuriscan_header"><img src="%%SUCURI.SucuriURL%%/inc/images/logo.png">
4
+ <h2>Sucuri Security WordPress Plugin</h2>
5
+ </div>
6
+
7
+ <div class="postbox-container" style="width:75%;">
8
+ <div class="sucuriscan-maincontent">
9
+ <div class="postbox">
10
+ <div class="inside">
11
+ <h2 align="center">Sucuri Plugin Last-Logins</h2>
12
+ </div>
13
+ </div>
14
+
15
+ <div id="poststuff">
16
+ <div class="postbox">
17
+ <h3>Post-Hack - User logins (latest 10, newest to oldest)</h3>
18
+ <div class="inside">
19
+ <table class="wp-list-table widefat">
20
+ <thead>
21
+ <tr>
22
+ <th class="manage-column column-cb check-column">
23
+ <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
24
+ <input id="cb-select-all-1" type="checkbox">
25
+ </th>
26
+ <th class="manage-column">Username</th>
27
+ <th class="manage-column">Email</th>
28
+ <th class="manage-column">IP Address</th>
29
+ <th class="manage-column">Date/Time</th>
30
+ </tr>
31
+ </thead>
32
+
33
+ <tbody>
34
+ %%SUCURI.UserList%%
35
+ </tbody>
36
+ </table>
37
+
38
+ <a href="%%SUCURI.CurrentURL%%&limit=0" class="button button-primary lastlogins-showall" style="%%SUCURI.UserList.ShowAll%%">Show all results</a>
39
+ </div>
40
+ </div>
41
+ </div><!-- End poststuff -->
42
+
43
+ </div><!-- End sucuriscan-maincontent -->
44
+ </div><!-- End postbox-container -->
45
+
46
+ %%SUCURI.SucuriWPSidebar%%
47
+
48
+ </div><!-- End wrap -->
inc/tpl/sucuri-wp-lastlogins.snippet.tpl ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <tr>
2
+ <th class="check-column">
3
+ <input type="checkbox" name="user_ids[]" value="%%SUCURI.UserList.UserId%%" />
4
+ </th>
5
+ <td>%%SUCURI.UserList.Username%%</td>
6
+ <td><a href="mailto:%%SUCURI.UserList.Email%%">%%SUCURI.UserList.Email%%</a></td>
7
+ <td>%%SUCURI.UserList.RemoteAddr%%</td>
8
+ <td>%%SUCURI.UserList.Datetime%%</td>
9
+ </tr>
inc/tpl/sucuri-wp-posthack.html.tpl ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="wrap">
2
+ <h2 id="warnings_hook"></h2>
3
+ <div class="sucuriscan_header"><img src="%%SUCURI.SucuriURL%%/inc/images/logo.png">
4
+ <h2>Sucuri Security WordPress Plugin</h2>
5
+ </div>
6
+
7
+ <div class="postbox-container" style="width:75%;">
8
+ <div class="sucuriscan-maincontent">
9
+ <div class="postbox">
10
+ <div class="inside">
11
+ <h2 align="center">Sucuri Plugin Post-Hack</h2>
12
+ </div>
13
+ </div>
14
+
15
+ <div id="poststuff">
16
+ <div class="postbox">
17
+ <h3>Post-Hack - Update WP-Config Keys</h3>
18
+ <div class="inside">
19
+ <form method="post">
20
+ <input type="hidden" name="sucuri_posthack_nonce" value="%%SUCURI.PosthackNonce%%" />
21
+ <input type="hidden" name="sucuri_posthack_action" value="update_wpconfig" />
22
+
23
+ <p>
24
+ Use this button to update the security keys stored in the <code>wp-config.php</code>
25
+ file, we will use the official Wordpress Secret-Key API Generator. After the
26
+ update your current session will be closed and you'll need to login again.
27
+ </p>
28
+
29
+ <p>
30
+ <input type="hidden" name="sucuri_update_wpconfig" value="0" />
31
+ <input type="checkbox" name="sucuri_update_wpconfig" value="1" />
32
+ <label for="sucuri_update_wpconfig">I understand that this operation can not be reverted.</label>
33
+ </p>
34
+
35
+ <input type="submit" value="Update WP-Config Keys" class="button button-primary" />
36
+ </form>
37
+
38
+ <div style="%%SUCURI.WPConfigUpdate.Display%%" class="sucuri_update_wpconfig_process">
39
+ <textarea>%%SUCURI.WPConfigUpdate.NewConfig%%</textarea>
40
+ </div>
41
+ </div>
42
+ </div>
43
+
44
+ <div class="postbox">
45
+ <h3>Post-Hack - Reset user password</h3>
46
+ <div class="inside">
47
+ <form method="post">
48
+ <input type="hidden" name="sucuri_posthack_nonce" value="%%SUCURI.PosthackNonce%%" />
49
+ <input type="hidden" name="sucuri_posthack_action" value="reset_password" />
50
+
51
+ <p>
52
+ Use this button to reset the current password for some specific users or for all
53
+ of them. We will send an email to each of those users adivising the password change
54
+ that includes the new password automatically generated by Wordpress. After the
55
+ password reset your current session will be closed and you'll need to login again.
56
+ </p>
57
+
58
+ <table class="wp-list-table widefat">
59
+ <thead>
60
+ <tr>
61
+ <th class="manage-column column-cb check-column">
62
+ <label class="screen-reader-text" for="cb-select-all-1">Select All</label>
63
+ <input id="cb-select-all-1" type="checkbox">
64
+ </th>
65
+ <th class="manage-column column-name">Username</th>
66
+ <th class="manage-column column-description">Display name</th>
67
+ <th class="manage-column column-description">Email address</th>
68
+ </tr>
69
+ </thead>
70
+
71
+ <tbody>
72
+ %%SUCURI.ResetPassword.UserList%%
73
+ </tbody>
74
+ </table>
75
+
76
+ <p>
77
+ <input type="hidden" name="sucuri_reset_password" value="0" />
78
+ <input type="checkbox" name="sucuri_reset_password" value="1" />
79
+ <label for="sucuri_reset_password">I understand that this operation can not be reverted.</label>
80
+ </p>
81
+
82
+ <input type="submit" value="Reset User Password" class="button button-primary" />
83
+ </form>
84
+ </div>
85
+ </div>
86
+ </div><!-- End poststuff -->
87
+
88
+ </div><!-- End sucuriscan-maincontent -->
89
+ </div><!-- End postbox-container -->
90
+
91
+ %%SUCURI.SucuriWPSidebar%%
92
+
93
+ </div><!-- End wrap -->
inc/tpl/sucuri-wp-resetpassword.snippet.tpl ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <tr>
2
+ <th class="check-column">
3
+ <input type="checkbox" name="user_ids[]" value="%%SUCURI.ResetPassword.UserId%%" />
4
+ </th>
5
+ <td>%%SUCURI.ResetPassword.Username%%</td>
6
+ <td>%%SUCURI.ResetPassword.Displayname%%</td>
7
+ <td><a href="mailto:%%SUCURI.ResetPassword.Email%%">%%SUCURI.ResetPassword.Email%%</a></td>
8
+ </tr>
inc/tpl/sucuri-wp-sidebar.html.tpl ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="postbox-container" style="width:25%;min-width:200px;max-width:350px;">
2
+ <div id="sidebar">
3
+ <div id="sitecleanup" class="sucuriscan-sidebar">
4
+ <h2><span class="promo">Is your website infected with malware? Blacklisted by Google?</span></h2>
5
+ <p>Don't know where to start? Get cleared today by <a href="http://sucuri.net/signup">Sucuri Security</a>!
6
+ </p>
7
+ <p>
8
+ <a class="button-primary" href="http://sucuri.net/tour">Read more &#187;</a>
9
+ </p>
10
+ </div>
11
+
12
+ <div id="sucuri-latest-posts" class="sucuriscan-sidebar">
13
+ <h2><span class="promo">Stay updated with WordPress security news. </span></h2>
14
+ <p>Check out the <a href="http://blog.sucuri.net/">Sucuri Blog</a>!
15
+ </p>
16
+ <p>
17
+ <a class="button-primary" href="http://blog.sucuri.net/">Read more &#187;</a>
18
+ </p>
19
+ </div>
20
+
21
+ </div>
22
+ </div>
lib/core_integrity.php CHANGED
@@ -65,6 +65,9 @@ function sucuriwp_core_integrity_check()
65
  {
66
 
67
  global $wp_version;
 
 
 
68
  $cp = 0;
69
  $updates = get_core_updates();
70
  if (!is_array($updates))
@@ -104,32 +107,49 @@ function sucuriwp_core_integrity_check()
104
 
105
  $added = @array_diff_assoc( $wp_core_hashes, $wp_core_latest_hashes ); //files added
106
  $removed = @array_diff_assoc( $wp_core_latest_hashes, $wp_core_hashes ); //files deleted
 
107
  $compcurrent = @array_diff_key( $wp_core_hashes, $added ); //remove all added files from current filelist
108
  $complog = @array_diff_key( $wp_core_latest_hashes, $removed ); //remove all deleted files from old file list
109
- $changed = array(); //array of changed files
110
 
111
  //compare file hashes and mod dates
112
  foreach ( $compcurrent as $currfile => $currattr) {
113
 
114
  if ( array_key_exists( $currfile, $complog ) ) {
115
 
116
- //if attributes differ added to changed files array
117
  if ( strcmp( $currattr['md5'], $complog[$currfile]['md5'] ) != 0 ) {
118
- $changed[$currfile]['md5'] = $currattr['md5'];
119
  }
120
 
121
  }
122
 
123
  }
124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  //get count of changes
126
  $addcount = sizeof( $added );
127
  $removecount = sizeof( $removed );
128
- $changecount = sizeof( $changed );
129
 
130
  sucuriscan_core_integrity_wrapper($added, "Core File Added: $addcount");
131
  sucuriscan_core_integrity_wrapper($removed, "Core File Removed: $removecount");
132
- sucuriscan_core_integrity_wrapper($changed, "Core File Modified: $changecount");
133
  }
134
  }
135
 
65
  {
66
 
67
  global $wp_version;
68
+
69
+ $curlang = get_bloginfo("language");
70
+
71
  $cp = 0;
72
  $updates = get_core_updates();
73
  if (!is_array($updates))
107
 
108
  $added = @array_diff_assoc( $wp_core_hashes, $wp_core_latest_hashes ); //files added
109
  $removed = @array_diff_assoc( $wp_core_latest_hashes, $wp_core_hashes ); //files deleted
110
+ unset($removed['wp_version']); //ignore wp_version key
111
  $compcurrent = @array_diff_key( $wp_core_hashes, $added ); //remove all added files from current filelist
112
  $complog = @array_diff_key( $wp_core_latest_hashes, $removed ); //remove all deleted files from old file list
113
+ $modified = array(); //array of modified files
114
 
115
  //compare file hashes and mod dates
116
  foreach ( $compcurrent as $currfile => $currattr) {
117
 
118
  if ( array_key_exists( $currfile, $complog ) ) {
119
 
120
+ //if attributes differ added to modified files array
121
  if ( strcmp( $currattr['md5'], $complog[$currfile]['md5'] ) != 0 ) {
122
+ $modified[$currfile]['md5'] = $currattr['md5'];
123
  }
124
 
125
  }
126
 
127
  }
128
 
129
+ //ignore some junk files
130
+ if($curlang != "en_US")
131
+ {
132
+ //ignore added files
133
+ unset($added['./licencia.txt']);
134
+
135
+ //ignore removed files
136
+ unset($removed['./license.txt']);
137
+
138
+ //ignore modified files
139
+ unset($modified['./wp-includes/version.php']);
140
+ unset($modified['./wp-admin/setup-config.php']);
141
+ unset($modified['./readme.html']);
142
+ unset($modified['./wp-config-sample.php']);
143
+ }
144
+
145
  //get count of changes
146
  $addcount = sizeof( $added );
147
  $removecount = sizeof( $removed );
148
+ $changecount = sizeof( $modified );
149
 
150
  sucuriscan_core_integrity_wrapper($added, "Core File Added: $addcount");
151
  sucuriscan_core_integrity_wrapper($removed, "Core File Removed: $removecount");
152
+ sucuriscan_core_integrity_wrapper($modified, "Core File Modified: $changecount");
153
  }
154
  }
155
 
lib/hardening.php CHANGED
@@ -34,7 +34,7 @@ function sucuriscan_harden_ok($message)
34
  return( '<div id="message" class="updated"><p>'.$message.'</p></div>');
35
  }
36
 
37
- function sucuriscan_harden_status($status, $type, $messageok, $messagewarn,
38
  $desc = NULL, $updatemsg = NULL)
39
  {
40
  if($desc != NULL)
@@ -45,16 +45,22 @@ function sucuriscan_harden_status($status, $type, $messageok, $messagewarn,
45
  if($status == 1)
46
  {
47
  echo '<h4>'.
48
- '<img style="position:relative;top:5px" height="22" width="22"'.
49
  'src="'.SUCURI_URL.'images/ok.png" /> &nbsp; '.
50
  $messageok.'.</h4>';
51
 
52
  if($updatemsg != NULL){ echo $updatemsg; }
 
 
 
 
 
 
53
  }
54
  else
55
  {
56
  echo '<h4>'.
57
- '<img style="position:relative;top:5px" height="22" width="22"'.
58
  'src="'.SUCURI_URL.'images/warn.png" /> &nbsp; '.
59
  $messagewarn. '.</h4>';
60
 
@@ -62,7 +68,7 @@ function sucuriscan_harden_status($status, $type, $messageok, $messagewarn,
62
 
63
  if($type != NULL)
64
  {
65
- echo '<input class="button-primary" type="submit" name="'.$type.'"
66
  value="Harden it!" />';
67
  }
68
  }
@@ -97,7 +103,7 @@ function sucuriscan_harden_version()
97
  sucuriscan_wrapper_open("Verify WordPress Version");
98
 
99
 
100
- sucuriscan_harden_status($cp, NULL,
101
  "WordPress is updated", "WordPress is not updated",
102
  NULL);
103
 
@@ -116,10 +122,10 @@ function sucuri_harden_removegenerator()
116
  {
117
  /* Enabled by default with this plugin. */
118
  $cp = 1;
119
-
120
  sucuriscan_wrapper_open("Remove WordPress Version");
121
 
122
- sucuriscan_harden_status($cp, "sucuri_harden_removegenerator",
123
  "WordPress version properly hidden", NULL,
124
  "It checks if your WordPress version is being hidden".
125
  " from being displayed in the generator tag ".
@@ -152,45 +158,64 @@ function sucuriscan_harden_upload()
152
  }
153
  }
154
 
155
- if(isset($_POST['sucuriscan_harden_upload']) &&
156
- isset($_POST['wpsucuri-doharden']) &&
157
- $cp == 0)
158
- {
159
- if(file_put_contents("$htaccess_upload",
160
- "\n<Files *.php>\ndeny from all\n</Files>")===FALSE)
161
  {
162
- $upmsg = sucuriscan_harden_error("ERROR: Unable to create .htaccess file.");
 
 
 
 
 
 
 
 
 
163
  }
164
- else
165
- {
166
- $upmsg = sucuriscan_harden_ok("COMPLETE: Upload directory successfully hardened");
167
- $cp = 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  }
169
  }
170
 
171
  sucuriscan_wrapper_open("Protect Uploads Directory");
172
- sucuriscan_harden_status($cp, "sucuriscan_harden_upload",
173
  "Upload directory properly hardened",
174
  "Upload directory not hardened",
175
  "It checks if your upload directory allows PHP ".
176
  "execution or if it is browsable.", $upmsg);
177
  sucuriscan_wrapper_close();
178
- }
179
 
180
  function sucuriscan_harden_wpcontent()
181
  {
182
  $cp = 1;
183
  $upmsg = NULL;
184
- $htaccess_content = ABSPATH."/wp-content/.htaccess";
185
 
186
- if(!is_readable($htaccess_content))
187
  {
188
  $cp = 0;
189
  }
190
  else
191
  {
192
  $cp = 0;
193
- $fcontent = file($htaccess_content);
194
  foreach($fcontent as $fline)
195
  {
196
  if(strpos($fline, "deny from all") !== FALSE)
@@ -201,45 +226,64 @@ function sucuriscan_harden_wpcontent()
201
  }
202
  }
203
 
204
- if(isset($_POST['sucuriscan_harden_wpcontent']) &&
205
- isset($_POST['wpsucuri-doharden']) &&
206
- $cp == 0)
207
- {
208
- if(file_put_contents("$htaccess_content",
209
- "\n<Files *.php>\ndeny from all\n</Files>")===FALSE)
210
  {
211
- $upmsg = sucuriscan_harden_error("ERROR: Unable to create .htaccess file.");
 
 
 
 
 
 
 
 
 
212
  }
213
- else
214
- {
215
- $upmsg = sucuriscan_harden_ok("COMPLETE: wp-content directory successfully hardened");
216
- $cp = 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  }
218
  }
219
 
220
  sucuriscan_wrapper_open("Restrict wp-content Access");
221
- sucuriscan_harden_status($cp, "sucuriscan_harden_wpcontent",
222
  "WP-content directory properly hardened",
223
  "WP-content directory not hardened",
224
  "This option blocks direct PHP access to any file inside wp-content. <p><strong>WARN: <span class='error-message'>Do not enable this option if ".
225
  "your site uses TimThumb or similar scripts.</span> If you enable and you need to disable, please remove the .htaccess from wp-content.</strong></p>", $upmsg);
226
  sucuriscan_wrapper_close();
227
- }
228
 
229
  function sucuriscan_harden_wpincludes()
230
  {
231
  $cp = 1;
232
  $upmsg = NULL;
233
- $htaccess_content = ABSPATH."/wp-includes/.htaccess";
234
 
235
- if(!is_readable($htaccess_content))
236
  {
237
  $cp = 0;
238
  }
239
  else
240
  {
241
  $cp = 0;
242
- $fcontent = file($htaccess_content);
243
  foreach($fcontent as $fline)
244
  {
245
  if(strpos($fline, "deny from all") !== FALSE)
@@ -250,29 +294,50 @@ function sucuriscan_harden_wpincludes()
250
  }
251
  }
252
 
253
- if(isset($_POST['sucuriscan_harden_wpincludes']) &&
254
- isset($_POST['wpsucuri-doharden']) &&
255
- $cp == 0)
256
- {
257
- if(file_put_contents("$htaccess_content",
258
- "\n<Files *.php>\ndeny from all\n</Files>\n<Files wp-tinymce.php>\nallow from all\n</Files>\n")===FALSE)
259
  {
260
- $upmsg = sucuriscan_harden_error("ERROR: Unable to create .htaccess file.");
 
 
 
 
 
 
 
 
 
261
  }
262
- else
263
- {
264
- $upmsg = sucuriscan_harden_ok("COMPLETE: wp-includes directory successfully hardened.");
265
- $cp = 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  }
267
  }
268
 
269
  sucuriscan_wrapper_open("Restrict wp-includes Access");
270
- sucuriscan_harden_status($cp, "sucuriscan_harden_wpincludes",
271
  "wp-includes directory properly hardened",
272
  "wp-includes directory not hardened",
273
  "This option blocks direct PHP access to any file inside wp-includes. ", $upmsg);
274
  sucuriscan_wrapper_close();
275
- }
276
 
277
  function sucuriscan_harden_phpversion()
278
  {
@@ -288,7 +353,7 @@ function sucuriscan_harden_phpversion()
288
  }
289
 
290
  sucuriscan_wrapper_open("Verify PHP Version");
291
- sucuriscan_harden_status($cp, NULL,
292
  "Using an updated version of PHP (v $phpv)",
293
  "The version of PHP you are using ($phpv) is not current, not recommended, and/or not supported",
294
  "This checks if you have the latest version of PHP installed.", NULL);
34
  return( '<div id="message" class="updated"><p>'.$message.'</p></div>');
35
  }
36
 
37
+ function sucuriscan_harden_status($status, $type, $messageok, $messagewarn,
38
  $desc = NULL, $updatemsg = NULL)
39
  {
40
  if($desc != NULL)
45
  if($status == 1)
46
  {
47
  echo '<h4>'.
48
+ '<img style="position:relative;top:5px" height="22" width="22"'.
49
  'src="'.SUCURI_URL.'images/ok.png" /> &nbsp; '.
50
  $messageok.'.</h4>';
51
 
52
  if($updatemsg != NULL){ echo $updatemsg; }
53
+
54
+ if($type != NULL)
55
+ {
56
+ echo "<input type='submit' name='{$type}_unharden' value='Revert hardening' class='button-secondary' />";
57
+ echo '<br /><br />';
58
+ }
59
  }
60
  else
61
  {
62
  echo '<h4>'.
63
+ '<img style="position:relative;top:5px" height="22" width="22"'.
64
  'src="'.SUCURI_URL.'images/warn.png" /> &nbsp; '.
65
  $messagewarn. '.</h4>';
66
 
68
 
69
  if($type != NULL)
70
  {
71
+ echo '<input class="button-primary" type="submit" name="'.$type.'"
72
  value="Harden it!" />';
73
  }
74
  }
103
  sucuriscan_wrapper_open("Verify WordPress Version");
104
 
105
 
106
+ sucuriscan_harden_status($cp, NULL,
107
  "WordPress is updated", "WordPress is not updated",
108
  NULL);
109
 
122
  {
123
  /* Enabled by default with this plugin. */
124
  $cp = 1;
125
+
126
  sucuriscan_wrapper_open("Remove WordPress Version");
127
 
128
+ sucuriscan_harden_status($cp, NULL,
129
  "WordPress version properly hidden", NULL,
130
  "It checks if your WordPress version is being hidden".
131
  " from being displayed in the generator tag ".
158
  }
159
  }
160
 
161
+ if( isset($_POST['wpsucuri-doharden']) ){
162
+ if( isset($_POST['sucuriscan_harden_upload']) && $cp == 0 )
 
 
 
 
163
  {
164
+ if(file_put_contents($htaccess_upload,
165
+ "\n<Files *.php>\ndeny from all\n</Files>")===FALSE)
166
+ {
167
+ $upmsg = sucuriscan_harden_error("ERROR: Unable to create .htaccess file.");
168
+ }
169
+ else
170
+ {
171
+ $upmsg = sucuriscan_harden_ok("COMPLETE: Upload directory successfully hardened");
172
+ $cp = 1;
173
+ }
174
  }
175
+
176
+ elseif( isset($_POST['sucuriscan_harden_upload_unharden']) ){
177
+ $htaccess_upload_writable = ( file_exists($htaccess_upload) && is_writable($htaccess_upload) ) ? TRUE : FALSE;
178
+ $htaccess_content = $htaccess_upload_writable ? file_get_contents($htaccess_upload) : '';
179
+
180
+ if( $htaccess_upload_writable ){
181
+ $cp = 0;
182
+ if( preg_match('/<Files \*\.php>\ndeny from all\n<\/Files>/', $htaccess_content, $match) ){
183
+ $htaccess_content = str_replace("<Files *.php>\ndeny from all\n</Files>", '', $htaccess_content);
184
+ file_put_contents($htaccess_upload, $htaccess_content, LOCK_EX);
185
+ }
186
+ sucuri_admin_notice('updated', '<strong>OK.</strong> WP-Content Uploads directory protection reverted.');
187
+ }else{
188
+ $harden_process = '<strong>Error.</strong> The <code>wp-content/uploads/.htaccess</code> does
189
+ not exists or is not writable, you will need to remove the following code manually there:
190
+ <code>&lt;Files *.php&gt;deny from all&lt;/Files&gt;</code>';
191
+ sucuri_admin_notice('error', $harden_process);
192
+ }
193
  }
194
  }
195
 
196
  sucuriscan_wrapper_open("Protect Uploads Directory");
197
+ sucuriscan_harden_status($cp, "sucuriscan_harden_upload",
198
  "Upload directory properly hardened",
199
  "Upload directory not hardened",
200
  "It checks if your upload directory allows PHP ".
201
  "execution or if it is browsable.", $upmsg);
202
  sucuriscan_wrapper_close();
203
+ }
204
 
205
  function sucuriscan_harden_wpcontent()
206
  {
207
  $cp = 1;
208
  $upmsg = NULL;
209
+ $htaccess_upload = ABSPATH."/wp-content/.htaccess";
210
 
211
+ if(!is_readable($htaccess_upload))
212
  {
213
  $cp = 0;
214
  }
215
  else
216
  {
217
  $cp = 0;
218
+ $fcontent = file($htaccess_upload);
219
  foreach($fcontent as $fline)
220
  {
221
  if(strpos($fline, "deny from all") !== FALSE)
226
  }
227
  }
228
 
229
+ if( isset($_POST['wpsucuri-doharden']) ){
230
+ if( isset($_POST['sucuriscan_harden_wpcontent']) && $cp == 0 )
 
 
 
 
231
  {
232
+ if(file_put_contents($htaccess_upload,
233
+ "\n<Files *.php>\ndeny from all\n</Files>")===FALSE)
234
+ {
235
+ $upmsg = sucuriscan_harden_error("ERROR: Unable to create .htaccess file.");
236
+ }
237
+ else
238
+ {
239
+ $upmsg = sucuriscan_harden_ok("COMPLETE: wp-content directory successfully hardened");
240
+ $cp = 1;
241
+ }
242
  }
243
+
244
+ elseif( isset($_POST['sucuriscan_harden_wpcontent_unharden']) ){
245
+ $htaccess_upload_writable = ( file_exists($htaccess_upload) && is_writable($htaccess_upload) ) ? TRUE : FALSE;
246
+ $htaccess_content = $htaccess_upload_writable ? file_get_contents($htaccess_upload) : '';
247
+
248
+ if( $htaccess_upload_writable ){
249
+ $cp = 0;
250
+ if( preg_match('/<Files \*\.php>\ndeny from all\n<\/Files>/', $htaccess_content, $match) ){
251
+ $htaccess_content = str_replace("<Files *.php>\ndeny from all\n</Files>", '', $htaccess_content);
252
+ file_put_contents($htaccess_upload, $htaccess_content, LOCK_EX);
253
+ }
254
+ sucuri_admin_notice('updated', '<strong>OK.</strong> WP-Content directory protection reverted.');
255
+ }else{
256
+ $harden_process = '<strong>Error.</strong> The <code>wp-content/.htaccess</code> does
257
+ not exists or is not writable, you will need to remove the following code manually there:
258
+ <code>&lt;Files *.php&gt;deny from all&lt;/Files&gt;</code>';
259
+ sucuri_admin_notice('error', $harden_process);
260
+ }
261
  }
262
  }
263
 
264
  sucuriscan_wrapper_open("Restrict wp-content Access");
265
+ sucuriscan_harden_status($cp, "sucuriscan_harden_wpcontent",
266
  "WP-content directory properly hardened",
267
  "WP-content directory not hardened",
268
  "This option blocks direct PHP access to any file inside wp-content. <p><strong>WARN: <span class='error-message'>Do not enable this option if ".
269
  "your site uses TimThumb or similar scripts.</span> If you enable and you need to disable, please remove the .htaccess from wp-content.</strong></p>", $upmsg);
270
  sucuriscan_wrapper_close();
271
+ }
272
 
273
  function sucuriscan_harden_wpincludes()
274
  {
275
  $cp = 1;
276
  $upmsg = NULL;
277
+ $htaccess_upload = ABSPATH."/wp-includes/.htaccess";
278
 
279
+ if(!is_readable($htaccess_upload))
280
  {
281
  $cp = 0;
282
  }
283
  else
284
  {
285
  $cp = 0;
286
+ $fcontent = file($htaccess_upload);
287
  foreach($fcontent as $fline)
288
  {
289
  if(strpos($fline, "deny from all") !== FALSE)
294
  }
295
  }
296
 
297
+ if( isset($_POST['wpsucuri-doharden']) ){
298
+ if( isset($_POST['sucuriscan_harden_wpincludes']) && $cp == 0 )
 
 
 
 
299
  {
300
+ if(file_put_contents($htaccess_upload,
301
+ "\n<Files *.php>\ndeny from all\n</Files>\n<Files wp-tinymce.php>\nallow from all\n</Files>\n")===FALSE)
302
+ {
303
+ $upmsg = sucuriscan_harden_error("ERROR: Unable to create .htaccess file.");
304
+ }
305
+ else
306
+ {
307
+ $upmsg = sucuriscan_harden_ok("COMPLETE: wp-includes directory successfully hardened.");
308
+ $cp = 1;
309
+ }
310
  }
311
+
312
+ elseif( isset($_POST['sucuriscan_harden_wpincludes_unharden']) ){
313
+ $htaccess_upload_writable = ( file_exists($htaccess_upload) && is_writable($htaccess_upload) ) ? TRUE : FALSE;
314
+ $htaccess_content = $htaccess_upload_writable ? file_get_contents($htaccess_upload) : '';
315
+
316
+ if( $htaccess_upload_writable ){
317
+ $cp = 0;
318
+ if( preg_match_all('/<Files (\*|wp-tinymce|ms-files)\.php>\n(deny|allow) from all\n<\/Files>/', $htaccess_content, $match) ){
319
+ foreach($match[0] as $restriction){
320
+ $htaccess_content = str_replace($restriction, '', $htaccess_content);
321
+ }
322
+ file_put_contents($htaccess_upload, $htaccess_content, LOCK_EX);
323
+ }
324
+ sucuri_admin_notice('updated', '<strong>OK.</strong> WP-Includes directory protection reverted.');
325
+ }else{
326
+ $harden_process = '<strong>Error.</strong> The <code>wp-includes/.htaccess</code> does
327
+ not exists or is not writable, you will need to remove the following code manually there:
328
+ <code>&lt;Files *.php&gt;deny from all&lt;/Files&gt;</code>';
329
+ sucuri_admin_notice('error', $harden_process);
330
+ }
331
  }
332
  }
333
 
334
  sucuriscan_wrapper_open("Restrict wp-includes Access");
335
+ sucuriscan_harden_status($cp, "sucuriscan_harden_wpincludes",
336
  "wp-includes directory properly hardened",
337
  "wp-includes directory not hardened",
338
  "This option blocks direct PHP access to any file inside wp-includes. ", $upmsg);
339
  sucuriscan_wrapper_close();
340
+ }
341
 
342
  function sucuriscan_harden_phpversion()
343
  {
353
  }
354
 
355
  sucuriscan_wrapper_open("Verify PHP Version");
356
+ sucuriscan_harden_status($cp, NULL,
357
  "Using an updated version of PHP (v $phpv)",
358
  "The version of PHP you are using ($phpv) is not current, not recommended, and/or not supported",
359
  "This checks if you have the latest version of PHP installed.", NULL);
readme.txt CHANGED
@@ -3,16 +3,16 @@ Contributors: dd@sucuri.net, dremeda
3
  Donate Link: http://sitecheck.sucuri.net
4
  Tags: malware, security, scan, spam, virus, sucuri, WordPress,
5
  Requires at least:3.2
6
- Stable tag:1.3
7
  Tested up to: 3.6
8
 
9
- The Sucuri Security - SiteCheck Malware Scanner plugin enables you to scan your WordPress site using Sucuri SiteCheck right in your dashboard.
10
 
11
  == Description ==
12
 
13
- Sucuri SiteCheck will check for malware, spam, blacklisting and other security issues like .htaccess redirects, hidden eval code, etc. The best thing about it is it's completely free.
14
 
15
- You can also scan your site at <a href="http://sitecheck.sucuri.net">SiteCheck.Sucuri.net</a>.
16
 
17
  Sucuri SiteCheck detects various types of malware, SPAM injections, website errors, disabled sites, database connection issues and code anomalies that require special attention to include:
18
 
@@ -37,6 +37,7 @@ There are a number of blacklisting authorities that monitor for malware, SPAM, a
37
  * Norton
38
  * AVG
39
  * Phish Tank (Phishing Specifically)
 
40
  * McAfee SiteAdvisor
41
  * Yandex
42
 
@@ -47,6 +48,14 @@ We augment the SiteCheck Malware Scanner with various. 1-click hardening options
47
  * Restrict wp-content Access
48
  * Restrict wp-includes Access
49
  * Verify PHP Version
 
 
 
 
 
 
 
 
50
 
51
 
52
  == Installation ==
@@ -58,6 +67,11 @@ We augment the SiteCheck Malware Scanner with various. 1-click hardening options
58
 
59
  == Changelog ==
60
 
 
 
 
 
 
61
  = 1.3 =
62
  * Removed some PHP warnings and code clean up.
63
  * Added WordPress integrity checks.
3
  Donate Link: http://sitecheck.sucuri.net
4
  Tags: malware, security, scan, spam, virus, sucuri, WordPress,
5
  Requires at least:3.2
6
+ Stable tag:1.4
7
  Tested up to: 3.6
8
 
9
+ The Sucuri Security - SiteCheck Malware Scanner plugin enables you to scan your WordPress site using Sucuri SiteCheck and verify the integrity of your core files right in your dashboard. It also includes post-hack options to help you reset passwords and secret keys in case it has been already hacked.
10
 
11
  == Description ==
12
 
13
+ Sucuri SiteCheck will check your site for malware, spam, blacklisting and other security issues like .htaccess redirects, hidden eval code, etc. The best thing about it is it's completely free.
14
 
15
+ You can also scan your site online at <a href="http://sitecheck.sucuri.net">SiteCheck.Sucuri.net</a>.
16
 
17
  Sucuri SiteCheck detects various types of malware, SPAM injections, website errors, disabled sites, database connection issues and code anomalies that require special attention to include:
18
 
37
  * Norton
38
  * AVG
39
  * Phish Tank (Phishing Specifically)
40
+ * ESET
41
  * McAfee SiteAdvisor
42
  * Yandex
43
 
48
  * Restrict wp-content Access
49
  * Restrict wp-includes Access
50
  * Verify PHP Version
51
+ * Disable the theme and plugin editors
52
+
53
+ On the newest versions of the plugin we also added an option to verify all WordPress core files for changes,
54
+ which can be useful to detect hidden backdoors.
55
+
56
+ Note that if your site is compromised and you need urgent help, you can leverage the
57
+ Sucuri plans here: http://sucuri.net (even if our free options are not finding
58
+ the compromise on your site).
59
 
60
 
61
  == Installation ==
67
 
68
  == Changelog ==
69
 
70
+ = 1.4 =
71
+ * Added post-hack options (reset all passwords).
72
+ * Added last-login.
73
+ * Added more hardening and the option to revert any hardening done.
74
+
75
  = 1.3 =
76
  * Removed some PHP warnings and code clean up.
77
  * Added WordPress integrity checks.
sucuri.php CHANGED
@@ -7,7 +7,7 @@ Description: The <a href="http://sucuri.net">Sucuri Security</a> - SiteCheck Mal
7
  You can also scan your site at <a href="http://sitecheck.sucuri.net">SiteCheck.Sucuri.net</a>.
8
 
9
  Author: Sucuri Security
10
- Version: 1.3
11
  Author URI: http://sucuri.net
12
  */
13
 
@@ -18,8 +18,11 @@ if(!function_exists('add_action'))
18
  }
19
 
20
  define('SUCURISCAN','sucuriscan');
21
- define('SUCURISCAN_VERSION','1.3');
22
  define( 'SUCURI_URL',plugin_dir_url( __FILE__ ));
 
 
 
23
 
24
  /* Requires files. */
25
  //require_once(dirname(__FILE__ ) . '/inc/scripts.php');
@@ -54,6 +57,12 @@ function sucuriscan_menu()
54
 
55
  add_submenu_page('sucuriscan', 'WordPress Integrity', 'WordPress Integrity', 'manage_options',
56
  'sucuriscan_core_integrity', 'sucuriscan_core_integrity_page');
 
 
 
 
 
 
57
  }
58
 
59
  /* Sucuri malware scan page. */
@@ -91,15 +100,15 @@ function sucuri_scan_page()
91
 
92
  <form action="" method="post">
93
  <input type="hidden" name="wpsucuri-doscan" value="wpsucuri-doscan" />
94
- <input class="button-primary" type="submit" name="wpsucuri_doscanrun" value="Scan this site now!" />
95
  </form>
96
 
97
- <p><strong>If you have any questions about these checks or this plugin, contact us at support@sucuri.net or visit <a href="http://sucuri.net">sucuri.net</a></strong></p>
98
 
99
  </div><!-- End sucuriscan-maincontent -->
100
  </div><!-- End postbox-container -->
101
 
102
- <?php include_once("lib/sidebar.php"); ?>
103
 
104
  </div><!-- End Wrap -->
105
 
@@ -159,8 +168,10 @@ function sucuriscan_print_scan()
159
  }
160
  echo "<br />";
161
  }
162
- echo '<i>More details here <a href="http://sitecheck.sucuri.net/scanner/?&scan='.home_url().'">http://sitecheck.sucuri.net/scanner/?&scan='.home_url().'</a></i>';
163
 
 
 
164
  echo "<hr />\n";
165
  if(isset($res['BLACKLIST']['WARN']))
166
  {
@@ -229,7 +240,7 @@ function sucuriscan_print_scan()
229
  </div><!-- End sucuriscan-maincontent -->
230
  </div><!-- End postbox-container -->
231
 
232
- <?php include_once("lib/sidebar.php"); ?>
233
 
234
  </div><!-- End Wrap -->
235
 
@@ -278,7 +289,7 @@ function sucuriscan_hardening_page()
278
  </div><!-- End sucuriscan-maincontent -->
279
  </div><!-- End postbox-container -->
280
 
281
- <?php include_once("lib/sidebar.php"); ?>
282
 
283
  </div><!-- End Wrap -->
284
 
@@ -313,7 +324,7 @@ function sucuriscan_core_integrity_page()
313
  </div><!-- End sucuriscan-maincontent -->
314
  </div><!-- End postbox-container -->
315
 
316
- <?php include_once("lib/sidebar.php"); ?>
317
 
318
  </div><!-- End Wrap -->
319
 
@@ -325,4 +336,363 @@ function sucuriscan_core_integrity_page()
325
  add_action('admin_menu', 'sucuriscan_menu');
326
  remove_action('wp_head', 'wp_generator');
327
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  ?>
7
  You can also scan your site at <a href="http://sitecheck.sucuri.net">SiteCheck.Sucuri.net</a>.
8
 
9
  Author: Sucuri Security
10
+ Version: 1.4
11
  Author URI: http://sucuri.net
12
  */
13
 
18
  }
19
 
20
  define('SUCURISCAN','sucuriscan');
21
+ define('SUCURISCAN_VERSION','1.4');
22
  define( 'SUCURI_URL',plugin_dir_url( __FILE__ ));
23
+ define('SUCURISCAN_PLUGIN_FOLDER', 'sucuri-scanner');
24
+ /* Sucuri Free/Paid Plugin will use the same tablename, check: sucuriscan_lastlogins_table_exists() */
25
+ define('SUCURISCAN_LASTLOGINS_TABLENAME', "{$table_prefix}sucuri_lastlogins");
26
 
27
  /* Requires files. */
28
  //require_once(dirname(__FILE__ ) . '/inc/scripts.php');
57
 
58
  add_submenu_page('sucuriscan', 'WordPress Integrity', 'WordPress Integrity', 'manage_options',
59
  'sucuriscan_core_integrity', 'sucuriscan_core_integrity_page');
60
+
61
+ add_submenu_page('sucuriscan', 'Post-Hack', 'Post-Hack', 'manage_options',
62
+ 'sucuriscan_posthack', 'sucuriscan_posthack_page');
63
+
64
+ add_submenu_page('sucuriscan', 'Last Logins', 'Last Logins', 'manage_options',
65
+ 'sucuriscan_lastlogins', 'sucuriscan_lastlogins_page');
66
  }
67
 
68
  /* Sucuri malware scan page. */
100
 
101
  <form action="" method="post">
102
  <input type="hidden" name="wpsucuri-doscan" value="wpsucuri-doscan" />
103
+ <input class="button button-primary button-hero load-customize" type="submit" name="wpsucuri_doscanrun" value="Scan this site now!" />
104
  </form>
105
 
106
+ <p><strong>If you have any questions about these checks or this plugin, contact us at <a href="mailto:info@sucuri.net">info@sucuri.net</a> or visit <a href="http://sucuri.net">sucuri.net</a></strong></p>
107
 
108
  </div><!-- End sucuriscan-maincontent -->
109
  </div><!-- End postbox-container -->
110
 
111
+ <?php echo sucuriscan_get_template('sucuri-wp-sidebar.html.tpl') ?>
112
 
113
  </div><!-- End Wrap -->
114
 
168
  }
169
  echo "<br />";
170
  }
171
+ echo '<i>More details here: <a href="http://sitecheck.sucuri.net/scanner/?&scan='.home_url().'">http://sitecheck.sucuri.net/scanner/?&scan='.home_url().'</a></i>';
172
 
173
+ echo "<hr />\n";
174
+ echo '<i>If our free scanner did not detect any issue, you may have a more complicated and hidden problem. You can try our <a href="admin.php?page=sucuriscan_core_integrity">WordPress integrity checks</a> or sign up with Sucuri <a target="_blank" href="http://sucuri.net/signup">here</a> for a complete and in depth scan+cleanup (not included in the free checks).</i>';
175
  echo "<hr />\n";
176
  if(isset($res['BLACKLIST']['WARN']))
177
  {
240
  </div><!-- End sucuriscan-maincontent -->
241
  </div><!-- End postbox-container -->
242
 
243
+ <?php echo sucuriscan_get_template('sucuri-wp-sidebar.html.tpl') ?>
244
 
245
  </div><!-- End Wrap -->
246
 
289
  </div><!-- End sucuriscan-maincontent -->
290
  </div><!-- End postbox-container -->
291
 
292
+ <?php echo sucuriscan_get_template('sucuri-wp-sidebar.html.tpl') ?>
293
 
294
  </div><!-- End Wrap -->
295
 
324
  </div><!-- End sucuriscan-maincontent -->
325
  </div><!-- End postbox-container -->
326
 
327
+ <?php echo sucuriscan_get_template('sucuri-wp-sidebar.html.tpl') ?>
328
 
329
  </div><!-- End Wrap -->
330
 
336
  add_action('admin_menu', 'sucuriscan_menu');
337
  remove_action('wp_head', 'wp_generator');
338
 
339
+ function sucuriscan_send_mail($to='', $subject='', $message='', $data_set=array(), $debug=FALSE)
340
+ {
341
+ $headers = array();
342
+ $subject = ucwords(strtolower($subject));
343
+ $wp_domain = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : get_option('siteurl');
344
+ if( get_option('sucuri_wp_prettify_mails')!='disabled' ){
345
+ $headers = array( 'Content-type: text/html' );
346
+ $data_set['PrettifyType'] = 'html';
347
+ }
348
+ $message = sucuriscan_prettify_mail($subject, $message, $data_set);
349
+
350
+ if($debug){
351
+ die($message);
352
+ }else{
353
+ wp_mail($to, "Sucuri WP Notification: {$wp_domain}: {$subject}" , $message, $headers);
354
+ }
355
+ }
356
+
357
+ function sucuriscan_admin_notice($type='updated', $message='')
358
+ {
359
+ if( !empty($message) ): ?>
360
+ <div class="<?php echo $type; ?>"><p><?php _e($message); ?></p></div>
361
+ <?php endif;
362
+ }
363
+
364
+ function sucuriscan_prettify_mail($subject='', $message='', $data_set=array())
365
+ {
366
+ $current_user = wp_get_current_user();
367
+
368
+ $prettify_type = isset($data_set['PrettifyType']) ? $data_set['PrettifyType'] : 'txt';
369
+ $real_ip = isset($_SERVER['SUCURI_RIP']) ? $_SERVER['SUCURI_RIP'] : $_SERVER['REMOTE_ADDR'];
370
+
371
+ $mail_variables = array(
372
+ 'TemplateTitle'=>'Sucuri WP Notification',
373
+ 'Subject'=>$subject,
374
+ 'Website'=>get_option('siteurl'),
375
+ 'RemoteAddress'=>$real_ip,
376
+ 'Message'=>$message,
377
+ 'User'=>$current_user->display_name,
378
+ 'Time'=>current_time('mysql')
379
+ );
380
+ foreach($data_set as $var_key=>$var_value){
381
+ $mail_variables[$var_key] = $var_value;
382
+ }
383
+
384
+ return sucuriscan_get_template("sucuri-wp-notification.{$prettify_type}.tpl", $mail_variables);
385
+ }
386
+
387
+ function sucuriscan_get_template($template='', $template_variables=array()){
388
+ $template_content = '';
389
+ $template_path = WP_PLUGIN_DIR.'/'.SUCURISCAN_PLUGIN_FOLDER."/inc/tpl/{$template}";
390
+
391
+ if( file_exists($template_path) && is_readable($template_path) ){
392
+ $template_content = file_get_contents($template_path);
393
+ foreach($template_variables as $tpl_key=>$tpl_value){
394
+ $template_content = str_replace("%%SUCURI.{$tpl_key}%%", $tpl_value, $template_content);
395
+ }
396
+ }
397
+ return $template_content;
398
+ }
399
+
400
+ function sucuriscan_wp_sidebar_gen()
401
+ {
402
+ return sucuriscan_get_template('sucuri-wp-sidebar.html.tpl');
403
+ }
404
+
405
+ function sucuriscan_get_new_config_keys()
406
+ {
407
+ $request = wp_remote_get('https://api.wordpress.org/secret-key/1.1/salt/');
408
+ if( !is_wp_error($request) || wp_remote_retrieve_response_code($request) === 200 ){
409
+ if( preg_match_all("/define\('([A-Z_]+)',[ ]+'(.*)'\);/", $request['body'], $match) ){
410
+ $new_keys = array();
411
+ foreach($match[1] as $i=>$value){
412
+ $new_keys[$value] = $match[2][$i];
413
+ }
414
+ return $new_keys;
415
+ }
416
+ }
417
+ return FALSE;
418
+ }
419
+
420
+ function sucuriscan_set_new_config_keys()
421
+ {
422
+ $new_wpconfig = '';
423
+ $wp_config_path = ABSPATH.'wp-config.php';
424
+ $wp_config_lines = file($wp_config_path);
425
+ $new_keys = sucuriscan_get_new_config_keys();
426
+ $old_keys = array();
427
+ $old_keys_string = $new_keys_string = '';
428
+
429
+ foreach($wp_config_lines as $wp_config_line){
430
+ $wp_config_line = str_replace("\n", '', $wp_config_line);
431
+
432
+ if( preg_match("/define\('([A-Z_]+)',([ ]+)'(.*)'\);/", $wp_config_line, $match) ){
433
+ $key_name = $match[1];
434
+ if( array_key_exists($key_name, $new_keys) ){
435
+ $white_spaces = $match[2];
436
+ $old_keys[$key_name] = $match[3];
437
+ $wp_config_line = "define('{$key_name}',{$white_spaces}'{$new_keys[$key_name]}');";
438
+
439
+ $old_keys_string .= "define('{$key_name}',{$white_spaces}'{$old_keys[$key_name]}');\n";
440
+ $new_keys_string .= "{$wp_config_line}\n";
441
+ }
442
+ }
443
+
444
+ $new_wpconfig .= "{$wp_config_line}\n";
445
+ }
446
+
447
+ $response = array(
448
+ 'updated'=>is_writable($wp_config_path),
449
+ 'old_keys'=>$old_keys,
450
+ 'old_keys_string'=>$old_keys_string,
451
+ 'new_keys'=>$new_keys,
452
+ 'new_keys_string'=>$new_keys_string,
453
+ 'new_wpconfig'=>$new_wpconfig
454
+ );
455
+ if( $response['updated'] ){
456
+ file_put_contents($wp_config_path, $new_wpconfig, LOCK_EX);
457
+ }
458
+ return $response;
459
+ }
460
+
461
+ function sucuriscan_new_password($user_id=0)
462
+ {
463
+ $user_id = intval($user_id);
464
+ $current_user = wp_get_current_user();
465
+
466
+ if( $user_id>0 && $user_id!=$current_user->ID ){
467
+ $user = get_userdata($user_id);
468
+ $new_password = wp_generate_password(15, TRUE, FALSE);
469
+
470
+ $data_set = array( 'User'=>$user->display_name );
471
+ $message = "The password for your user account in the website mentioned has been changed by an administrator,
472
+ this is the new password automatically generated by the system, please update ASAP.<br>
473
+ <div style='display:inline-block;background:#ddd;font-family:monaco,monospace,courier;
474
+ font-size:30px;margin:0;padding:15px;border:1px solid #999'>{$new_password}</div>";
475
+ sucuriscan_send_mail($user->user_email, 'Changed password', $message, $data_set);
476
+
477
+ wp_set_password($new_password, $user_id);
478
+
479
+ return TRUE;
480
+ }
481
+ return FALSE;
482
+ }
483
+
484
+ function sucuriscan_posthack_page()
485
+ {
486
+ if( !current_user_can('manage_options') )
487
+ {
488
+ wp_die(__('You do not have sufficient permissions to access this page.') );
489
+ }
490
+
491
+ // Page pseudo-variables initialization.
492
+ $template_variables = array(
493
+ 'SucuriURL'=>SUCURI_URL,
494
+ 'PosthackNonce'=>wp_create_nonce('sucuri_posthack_nonce'),
495
+ 'SucuriWPSidebar'=>sucuriscan_wp_sidebar_gen(),
496
+ 'WPConfigUpdate.Display'=>'display:none',
497
+ 'WPConfigUpdate.NewConfig'=>'',
498
+ 'ResetPassword.UserList'=>''
499
+ );
500
+
501
+ // Process form submission
502
+ if( isset($_POST['sucuri_posthack_action']) ){
503
+ if( !wp_verify_nonce($_POST['sucuri_posthack_nonce'], 'sucuri_posthack_nonce') )
504
+ {
505
+ wp_die(__('Wordpress Nonce verification failed, try again going back and checking the form.') );
506
+ }
507
+
508
+ switch($_POST['sucuri_posthack_action']){
509
+ case 'update_wpconfig':
510
+ $update_wpconfig = ( isset($_POST['sucuri_update_wpconfig']) && $_POST['sucuri_update_wpconfig']==1 ) ? TRUE : FALSE;
511
+
512
+ if( $update_wpconfig ){
513
+ $wpconfig_process = sucuriscan_set_new_config_keys();
514
+ $template_variables['WPConfigUpdate.Display'] = 'display:block';
515
+
516
+ if( $wpconfig_process['updated']===TRUE ){
517
+ sucuriscan_admin_notice('updated', '<strong>OK.</strong> WP-Config keys updated successfully. In the textarea bellow you will see the old-keys and the new-keys updated.');
518
+ $template_variables['WPConfigUpdate.NewConfig'] .= "// Old Keys\n";
519
+ $template_variables['WPConfigUpdate.NewConfig'] .= $wpconfig_process['old_keys_string'];
520
+ $template_variables['WPConfigUpdate.NewConfig'] .= "//\n";
521
+ $template_variables['WPConfigUpdate.NewConfig'] .= "// New Keys\n";
522
+ $template_variables['WPConfigUpdate.NewConfig'] .= $wpconfig_process['new_keys_string'];
523
+ }else{
524
+ sucuriscan_admin_notice('error', '<strong>Error.</strong> The wp-config.php file is not writable, please copy and paste the code shown bellow in the textarea into that file manually.');
525
+ $template_variables['WPConfigUpdate.NewConfig'] = $wpconfig_process['new_wpconfig'];
526
+ }
527
+ }else{
528
+ sucuriscan_admin_notice('error', '<strong>Error.</strong> You need to confirm that you understand the risk of this operation');
529
+ }
530
+ break;
531
+ case 'reset_password':
532
+ $reset_password = ( isset($_POST['sucuri_reset_password']) && $_POST['sucuri_reset_password']==1 ) ? TRUE : FALSE;
533
+
534
+ if( $reset_password ){
535
+ $user_identifiers = $_POST['user_ids'];
536
+ $pwd_changed = $pwd_not_changed = array();
537
+ arsort($user_identifiers);
538
+
539
+ foreach($user_identifiers as $user_id){
540
+ if( sucuriscan_new_password($user_id) ){
541
+ $passwords_changed[] = $user_id;
542
+ }else{
543
+ $pwd_not_changed[] = $user_id;
544
+ }
545
+ }
546
+ if( !empty($pwd_changed) ){
547
+ sucuriscan_admin_notice('updated', '<strong>OK.</strong> Password changed successfully for users: '.implode(', ',$pwd_changed));
548
+ }
549
+ if( !empty($pwd_not_changed) ){
550
+ sucuriscan_admin_notice('error', '<strong>Error.</strong> Password change failed for users: '.implode(', ',$pwd_not_changed));
551
+ }
552
+ }else{
553
+ sucuriscan_admin_notice('error', '<strong>Error.</strong> You need to confirm that you understand the risk of this operation');
554
+ }
555
+ break;
556
+ default:
557
+ wp_die(__('Sucuri WP Plugin, invalid form action, go back and try again.'));
558
+ break;
559
+ }
560
+ }
561
+
562
+ // Fill the user list for ResetPassword action.
563
+ $user_list = get_users();
564
+ foreach($user_list as $user){
565
+ $user_snippet = sucuriscan_get_template('sucuri-wp-resetpassword.snippet.tpl', array(
566
+ 'ResetPassword.UserId'=>$user->ID,
567
+ 'ResetPassword.Username'=>$user->user_login,
568
+ 'ResetPassword.Displayname'=>$user->display_name,
569
+ 'ResetPassword.Email'=>$user->user_email
570
+ ));
571
+ $template_variables['ResetPassword.UserList'] .= $user_snippet;
572
+ }
573
+
574
+ echo sucuriscan_get_template('sucuri-wp-posthack.html.tpl', $template_variables);
575
+ }
576
+
577
+ function sucuriscan_lastlogins_page()
578
+ {
579
+ if( !current_user_can('manage_options') )
580
+ {
581
+ wp_die(__('You do not have sufficient permissions to access this page.') );
582
+ }
583
+
584
+ // Page pseudo-variables initialization.
585
+ $template_variables = array(
586
+ 'SucuriURL'=>SUCURI_URL,
587
+ 'PosthackNonce'=>wp_create_nonce('sucuri_posthack_nonce'),
588
+ 'SucuriWPSidebar'=>sucuriscan_wp_sidebar_gen(),
589
+ 'UserList'=>'',
590
+ 'CurrentURL'=>site_url().'/wp-admin/admin.php?page='.$_GET['page']
591
+ );
592
+
593
+ $limit = isset($_GET['limit']) ? intval($_GET['limit']) : 10;
594
+ $template_variables['UserList.ShowAll'] = $limit>0 ? 'display:table' : 'display:none';
595
+
596
+ $user_list = sucuriscan_get_logins($limit);
597
+ foreach($user_list as $user){
598
+ $user_snippet = sucuriscan_get_template('sucuri-wp-lastlogins.snippet.tpl', array(
599
+ 'UserList.UserId'=>$user->ID,
600
+ 'UserList.Username'=>$user->user_login,
601
+ 'UserList.Email'=>$user->user_email,
602
+ 'UserList.RemoteAddr'=>$user->user_remoteaddr,
603
+ 'UserList.Datetime'=>$user->user_lastlogin
604
+ ));
605
+ $template_variables['UserList'] .= $user_snippet;
606
+ }
607
+
608
+ echo sucuriscan_get_template('sucuri-wp-lastlogins.html.tpl', $template_variables);
609
+ }
610
+
611
+ function sucuriscan_set_flashdata($key='', $value='')
612
+ {
613
+ /* Use wp-sucuri_ to give compatibility between Sucuri Free/Paid Plugin */
614
+ $session_name = "wp-sucuri_{$key}";
615
+ $expire = time() + 60*5;
616
+ setcookie($session_name, $value, $expire, SITECOOKIEPATH.'wp-admin');
617
+ }
618
+
619
+ function sucuriscan_get_flashdata()
620
+ {
621
+ /* Use wp-sucuri_ to give compatibility between Sucuri Free/Paid Plugin */
622
+ foreach($_COOKIE as $key=>$value){
623
+ if( preg_match('/^(wp\-sucuri_.*)$/', $key) ){
624
+ sucuriscan_admin_notice('updated', $value);
625
+ setcookie($key, NULL, time()-3600);
626
+ }
627
+ }
628
+ }
629
+ add_action('admin_init', 'sucuriscan_get_flashdata');
630
+
631
+ function sucuriscan_lastlogins_table_exists()
632
+ {
633
+ global $wpdb;
634
+ if( defined('SUCURISCAN_LASTLOGINS_TABLENAME') ){
635
+ $table_name = SUCURISCAN_LASTLOGINS_TABLENAME;
636
+
637
+ if( $wpdb->get_var("SHOW TABLES LIKE '{$table_name}'")!=$table_name ){
638
+ $sql = 'CREATE TABLE '.$table_name.' (
639
+ id int(11) NOT NULL AUTO_INCREMENT,
640
+ user_id bigint(20) NOT NULL,
641
+ user_login varchar(60),
642
+ user_remoteaddr varchar(255),
643
+ user_lastlogin DATETIME DEFAULT "0000-00-00 00:00:00" NOT NULL,
644
+ UNIQUE KEY id(id)
645
+ )';
646
+
647
+ require_once(ABSPATH.'wp-admin/includes/upgrade.php');
648
+ dbDelta($sql);
649
+ }
650
+ }
651
+ }
652
+ add_action('plugins_loaded', 'sucuriscan_lastlogins_table_exists');
653
+
654
+ function sucuriscan_set_lastlogin($user_login='')
655
+ {
656
+ global $wpdb;
657
+ if( defined('SUCURISCAN_LASTLOGINS_TABLENAME') ){
658
+ $table_name = SUCURISCAN_LASTLOGINS_TABLENAME;
659
+ $current_user = get_user_by('login', $user_login);
660
+
661
+ sucuriscan_set_flashdata('lastlogin', 'Last user login at '.date('Y/M/d H:i:s').' from '.$_SERVER['REMOTE_ADDR']);
662
+
663
+ $wpdb->insert($table_name, array(
664
+ 'user_id'=>$current_user->ID,
665
+ 'user_login'=>$current_user->user_login,
666
+ 'user_remoteaddr'=>isset($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:'127.0.0.1',
667
+ 'user_lastlogin'=>current_time('mysql')
668
+ ));
669
+ }
670
+ }
671
+ add_action('wp_login', 'sucuriscan_set_lastlogin', 50);
672
+
673
+ function sucuriscan_get_logins($limit=10, $user_id=0)
674
+ {
675
+ global $wpdb;
676
+ if( defined('SUCURISCAN_LASTLOGINS_TABLENAME') ){
677
+ $table_name = SUCURISCAN_LASTLOGINS_TABLENAME;
678
+
679
+ $sql = "SELECT * FROM {$table_name} RIGHT JOIN {$wpdb->prefix}users ON {$table_name}.user_id = {$wpdb->prefix}users.ID";
680
+ if( !is_admin() ){
681
+ $current_user = wp_get_current_user();
682
+ $sql .= chr(32)."WHERE {$wpdb->prefix}users.user_login = '{$current_user->user_login}'";
683
+ }
684
+ if( $user_id>0 ){
685
+ $where_append = strpos('WHERE ', $sql)===FALSE ? 'WHERE' : 'AND';
686
+ $sql .= chr(32)."{$where_append} {$table_name}.user_id = '{$user_id}'";
687
+ }
688
+ $sql .= chr(32)."ORDER BY {$table_name}.id DESC";
689
+ if( preg_match('/^([0-9]+)$/', $limit) && $limit>0 ){
690
+ $sql .= chr(32)."LIMIT {$limit}";
691
+ }
692
+ return $wpdb->get_results($sql);
693
+ }
694
+
695
+ return FALSE;
696
+ }
697
+
698
  ?>
sucuriscan_core_integrity.php CHANGED
@@ -61,29 +61,29 @@ function sucuriscan_core_integrity_lib()
61
 
62
  sucuriscan_core_integrity_function_wrapper(
63
  'sucuriwp_core_integrity_check',
64
- 'Check wp-include, wp-admin, and top directory files against the latest WordPress version.'
65
  );
66
  sucuriscan_core_integrity_function_wrapper(
67
  'sucuriwp_list_admins',
68
- 'Check Administrator Users.'
69
  );
70
  sucuriscan_core_integrity_function_wrapper(
71
  'sucuriwp_content_check',
72
- 'Check wp-content files modified in the past 3 days.'
73
  );
74
  sucuriscan_core_integrity_function_wrapper(
75
  'sucuriwp_check_plugins',
76
- 'Check outdated active plugins in there.'
77
  );
78
  sucuriscan_core_integrity_function_wrapper(
79
  'sucuriwp_check_themes',
80
- 'Check outdated themes in there.'
81
  );
82
  ?>
83
 
84
  </div>
85
 
86
- <p align="center"><strong>If you have any questions about these checks or this plugin, contact us at support@sucuri.net or visit <a href="http://sucuri.net">Sucuri Security</a></strong></p>
87
 
88
  <?php
89
  }
61
 
62
  sucuriscan_core_integrity_function_wrapper(
63
  'sucuriwp_core_integrity_check',
64
+ 'This test will check wp-includes, wp-admin, and the top directory files against the latest WordPress hashing database. If any of those files were modified, it is a big sign of a possible compromise.'
65
  );
66
  sucuriscan_core_integrity_function_wrapper(
67
  'sucuriwp_list_admins',
68
+ 'List all administrator users and their latest login time.'
69
  );
70
  sucuriscan_core_integrity_function_wrapper(
71
  'sucuriwp_content_check',
72
+ 'This test will list all files inside wp-content that have been modified in the past 3 days.'
73
  );
74
  sucuriscan_core_integrity_function_wrapper(
75
  'sucuriwp_check_plugins',
76
+ 'This test will list any outdated (active) plugins.'
77
  );
78
  sucuriscan_core_integrity_function_wrapper(
79
  'sucuriwp_check_themes',
80
+ 'This test will list any outdated theme.'
81
  );
82
  ?>
83
 
84
  </div>
85
 
86
+ <p align="center"><strong>If you have any questions about these tests or this plugin, contact us at info@sucuri.net or visit <a href="http://sucuri.net">Sucuri Security</a></strong></p>
87
 
88
  <?php
89
  }