PluginProbe ʕ •ᴥ•ʔ
Advanced Access Manager – Access Governance for WordPress / trunk
Advanced Access Manager – Access Governance for WordPress vtrunk
6.8.4 6.8.5 6.9.0 6.9.1 6.9.10 6.9.11 6.9.12 6.9.13 6.9.14 6.9.15 6.9.16 6.9.17 6.9.18 6.9.19 6.9.2 6.9.20 6.9.21 6.9.22 6.9.23 6.9.24 6.9.25 6.9.26 6.9.27 6.9.28 6.9.29 6.9.3 6.9.30 6.9.31 6.9.32 6.9.33 6.9.34 6.9.35 6.9.36 6.9.37 6.9.38 6.9.39 6.9.4 6.9.41 6.9.42 6.9.43 6.9.44 6.9.45 6.9.46 6.9.47 6.9.48 6.9.49 6.9.5 6.9.51 6.9.6 6.9.7 6.9.8 6.9.9 7.0.0 7.0.0-alpha.6 7.0.0-alpha.7 7.0.0-beta.1 7.0.0-rc1 7.0.0-rc2 7.0.0-rc3 7.0.1 7.0.10 7.0.11 7.0.2 7.0.3 7.0.4 7.0.5 7.0.6 7.0.7 7.0.8 7.0.9 7.1.0 7.1.1 trunk 3.0 4.0 4.0.1 4.1 4.2 4.3 4.4 4.4.1 4.5 4.6 4.6.1 4.6.2 4.7 4.7.1 4.7.2 4.7.5 4.7.6 4.8 4.8.1 4.9 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 4.9.5.1 4.9.5.2 5.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1 5.1.1 5.10 5.11 5.2 5.2.1 5.2.5 5.2.6 5.2.7 5.3 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.4 5.4.1 5.4.2 5.4.3 5.4.3.1 5.4.3.2 5.5 5.5.1 5.5.2 5.6 5.6.1 5.6.1.1 5.7 5.7.1 5.7.2 5.7.3 5.8 5.8.1 5.8.2 5.8.3 5.9 5.9.1 5.9.1.1 5.9.2 5.9.2.1 5.9.3 5.9.4 5.9.5 5.9.6 5.9.6.1 5.9.6.2 5.9.6.3 5.9.7 5.9.7.1 5.9.7.2 5.9.7.3 5.9.8 5.9.8.1 5.9.9 5.9.9.1 6.0.0 6.0.1 6.0.2 6.0.3 6.0.4 6.0.5 6.1.0 6.1.1 6.2.0 6.2.1 6.2.2 6.3.0 6.3.1 6.3.2 6.3.3 6.4.0 6.4.1 6.4.2 6.4.3 6.5.0 6.5.1 6.5.2 6.5.3 6.5.4 6.6.0 6.6.1 6.6.2 6.6.3 6.6.4 6.7.0 6.7.1 6.7.2 6.7.3 6.7.4 6.7.5 6.7.6 6.7.7 6.7.8 6.7.9 6.8.0 6.8.1 6.8.2 6.8.3
advanced-access-manager / application / Audit / HighPrivilegeOrElevatedUserCheck.php
advanced-access-manager / application / Audit Last commit date
AuditCheckTrait.php 1 year ago CoreUserRoleOptionIntegrityCheck.php 1 year ago EditableFileSystemCheck.php 7 months ago ElevatedCoreRoleCheck.php 7 months ago EmptyUnusedRoleCheck.php 7 months ago HighPrivilegeContentModeratorCheck.php 7 months ago HighPrivilegeOrElevatedUserCheck.php 7 months ago HighPrivilegeRoleCheck.php 7 months ago HighPrivilegeUserCountCheck.php 7 months ago RestfulAutoDiscoverEndpointCheck.php 7 months ago RoleCapabilityNamingConventionCheck.php 7 months ago RoleIntegrityCheck.php 7 months ago RoleTransparencyCheck.php 7 months ago XmlRpcEndpointCheck.php 7 months ago
HighPrivilegeOrElevatedUserCheck.php
286 lines
1 <?php
2
3 /**
4 * ======================================================================
5 * LICENSE: This file is subject to the terms and conditions defined in *
6 * file 'license.txt', which is part of this source code package. *
7 * ======================================================================
8 */
9
10 /**
11 * Check for the high privilege users
12 *
13 * @package AAM
14 * @version 7.0.0
15 */
16 class AAM_Audit_HighPrivilegeOrElevatedUserCheck
17 {
18
19 use AAM_Audit_AuditCheckTrait;
20
21 /**
22 * Step ID
23 *
24 * @version 7.0.0
25 */
26 const ID = 'high_privilege_or_elevated_users';
27
28 /**
29 * Maximum number of users to iterate with one execution
30 *
31 * @version 7.0.0
32 */
33 const ITERATION_LIMIT = 2000;
34
35 /**
36 * Maximum number of iterations before stop
37 *
38 * This is done to avoid overloading DB with tons of issues
39 *
40 * @version 7.0.0
41 */
42 const MAX_ITERATIONS = 5;
43
44 /**
45 * List of core capabilities that can cause significant damage to the site
46 *
47 * @version 7.0.0
48 */
49 const HIGH_PRIVILEGE_CAPS = [
50 'edit_themes',
51 'edit_plugins',
52 'edit_files',
53 'activate_plugins',
54 'manage_options',
55 'delete_users',
56 'create_users',
57 'unfiltered_upload',
58 'unfiltered_html',
59 'update_plugins',
60 'delete_plugins',
61 'install_plugins',
62 'update_themes',
63 'install_themes',
64 'update_core',
65 'promote_users',
66 'delete_themes'
67 ];
68
69 /**
70 * Run the check
71 *
72 * @param array $params
73 *
74 * @return array
75 *
76 * @access public
77 * @static
78 *
79 * @version 7.0.0
80 */
81 public static function run($params = [])
82 {
83 $issues = [];
84 $result = array_merge([
85 'is_completed' => false,
86 'progress' => 0,
87 'offset' => 0,
88 // Calculate the maximum number of users we can check with this step
89 'max' => AAM::api()->config->get(
90 'service.security_audit.max_users_to_check',
91 self::ITERATION_LIMIT * self::MAX_ITERATIONS
92 )
93 ], $params);
94
95 try {
96 // Step #1. Let's determine how many users are on the site, but only if
97 // it is the first iteration of the check and fetch the batch of users
98 // for further processing
99 if ($result['progress'] === 0) {
100 $user_list = AAM::api()->users->get_users([
101 'number' => self::ITERATION_LIMIT,
102 'orderby' => 'ID'
103 ]);
104
105 // Capture the total number of users
106 $result['total_count'] = AAM::api()->users->get_user_count();
107
108 // Determine if total number of users is higher than allowed number
109 // of users for check
110 $result['has_overflow'] = $result['total_count'] > $result['max'];
111 } else {
112 $user_list = AAM::api()->users->get_users([
113 'number' => self::ITERATION_LIMIT,
114 'orderby' => 'ID',
115 'offset' => $result['offset']
116 ]);
117 }
118
119 // Increment the offset and move on
120 $result['offset'] += self::ITERATION_LIMIT;
121
122 // Step #2. Analyze the batch and determine how many users have high
123 // privilege access
124 array_push(
125 $issues,
126 ...self::_scan_for_high_privilege_users($user_list)
127 );
128
129 // Step #3. Determining if we actually done with the scan
130 if ($result['total_count'] <= $result['offset']
131 || ($result['max'] !== -1 && $result['offset'] >= $result['max'])
132 ) {
133 $result['is_completed'] = true;
134 } else {
135 $result['progress'] = $result['offset'] / $result['total_count'];
136 }
137 } catch (Exception $e) {
138 array_push($issues, self::_format_issue(
139 'APPLICATION_ERROR',
140 [
141 'message' => $e->getMessage()
142 ],
143 'error'
144 ));
145 }
146
147 if (count($issues) > 0) {
148 if (array_key_exists('issues', $result)) {
149 array_push($result['issues'], ...$issues);
150 } else {
151 $result['issues'] = $issues;
152 }
153 }
154
155 // Determine final status for the check
156 self::_determine_check_status($result);
157
158 return $result;
159 }
160
161 /**
162 * Get a collection of error messages for current step
163 *
164 * @return array
165 * @access private
166 * @static
167 *
168 * @version 7.0.0
169 */
170 private static function _get_message_templates()
171 {
172 return [
173 'HIGH_PRIVILEGE_CAPS_USER' => __(
174 'Detected high-privilege user %s (ID: %d) with caps: %s',
175 'advanced-access-manager'
176 ),
177 'ELEVATED_CAPS_USER' => __(
178 'Detected user %s (ID: %d) with elevated caps: %s',
179 'advanced-access-manager'
180 )
181 ];
182 }
183
184 /**
185 * @inheritDoc
186 *
187 * Let's not share any information (like IDs or names) about specific user
188 * accounts
189 *
190 * @version 7.0.0
191 */
192 public static function issues_to_shareable($results)
193 {
194 $response = [];
195
196 foreach($results['issues'] as $issue) {
197 $issue_code = $issue['code'];
198 if (!array_key_exists($issue_code, $response)) {
199 $response[$issue_code] = [
200 'type' => $issue['type'],
201 'code' => $issue_code,
202 'metadata' => [
203 'user_count' => 0,
204 'capabilities' => []
205 ]
206 ];
207 }
208
209 $response[$issue_code]['metadata']['user_count']++; // Increment #
210
211 $response[$issue_code]['metadata']['capabilities'] = array_unique(array_merge(
212 $response[$issue_code]['metadata']['capabilities'],
213 $issue['metadata']['caps']
214 ));
215 }
216
217 return $response;
218 }
219
220 /**
221 * Scan for high-privilege users
222 *
223 * @param array $user_list
224 *
225 * @return array
226 *
227 * @access private
228 * @static
229 *
230 * @version 7.0.0
231 */
232 private static function _scan_for_high_privilege_users($user_list)
233 {
234 $response = [];
235
236 // We are going to exclude the admin of the site
237 $admin_email = AAM::api()->db->read('admin_email');
238
239 foreach($user_list as $user) {
240 // Exclude current user and assume that they are the only Administrator
241 // with high-privilege access
242 if ($user->user_email !== $admin_email) {
243 $assigned_caps = array_keys(
244 array_filter($user->allcaps, function($v) {
245 return !empty($v);
246 })
247 );
248
249 $matched = array_intersect($assigned_caps, self::HIGH_PRIVILEGE_CAPS);
250
251 if (!empty($matched)) {
252 array_push($response, self::_format_issue(
253 'HIGH_PRIVILEGE_CAPS_USER',
254 [
255 'name' => $user->display_name,
256 'id' => $user->ID,
257 'caps' => $matched
258 ],
259 'critical'
260 ));
261 }
262
263 // Detecting if user has elevated privileges as well
264 $elevated_caps = array_keys(
265 array_filter($user->caps, function($v, $k) {
266 return !empty($v) && !wp_roles()->is_role($k);
267 }, ARRAY_FILTER_USE_BOTH)
268 );
269
270 if (!empty($elevated_caps)) {
271 array_push($response, self::_format_issue(
272 'ELEVATED_CAPS_USER',
273 [
274 'name' => $user->display_name,
275 'id' => $user->ID,
276 'caps' => $elevated_caps
277 ]
278 ));
279 }
280 }
281 }
282
283 return $response;
284 }
285
286 }