Role Mapping Grants More Than Intended
LDAP is the authentication strategy for a 300-user deployment. Two LDAP groups map to Splunk roles: splunk-ops โ power, and splunk-contractors โ a custom role contractor_ro whose srchIndexesAllowed is orders;cases and whose srchFilter is host=ext-*. A contractor who belongs to exactly one LDAP group reports that the filter does not work: they can search index=_internal and see events from every host in orders. Investigation shows the LDAP strategy's defaultRole is user, and that a previous administrator edited the user role's srchIndexesAllowed to include _internal. A stale role temp_power still exists and is pulled in through importRoles on contractor_ro. SAML is also enabled for the same population, mapping a groups attribute to roles.
1. Why is the contractor's search filter ineffective, and why can they reach `_internal`? Explain the underlying rule.
Read the worked answer
Splunk does not pick one role and use its restrictions. A user's effective permission set is the union of every role they hold, including roles inherited through importRoles.
That produces two separate failures here:
- Search filters are combined permissively.
srchFiltervalues from multiple roles are OR'ed, not AND'ed. The contractor holdscontractor_ro(filterhost=ext-*) plus thedefaultRoleofuser, which carries no meaningful filter.host=ext-* OR (no restriction)returns everything - the more permissive role wins and the restriction silently evaporates. A filter is only a restriction if every role the user holds carries a non-trivial filter. - Index access is additive, and someone edited the
userrole to include_internalinsrchIndexesAllowed. BecausedefaultRole = userapplies to every authenticated account, that one edit gave internal-index visibility to the entire 300-person population, including contractors.
The temp_power role compounds it: contractor_ro imports it, so anyone in the contractor group inherits power-level capabilities and index breadth regardless of what contractor_ro itself says. importRoles is the most common unintended privilege escalation in Splunk role design.
Note also that SAML is enabled for the same users: if the SAML groups attribute maps them into additional roles, those roles union in as well.
2. How do you determine what a specific user can actually do, rather than reasoning from the role definitions on paper?
Read the worked answer
Query the effective state through REST rather than reading the role pages.
- Effective roles for the account (this returns assigned and inherited roles):
| rest /services/authentication/users splunk_server=local
| search title="jdoe"
| fields title roles realname
If roles lists something unexpected, you have found your escalation path before touching a search.
- The permissions behind those roles:
| rest /services/authorization/roles splunk_server=local
| search title IN (contractor_ro, user, temp_power)
| table title srchIndexesAllowed srchIndexesDefault srchFilter imported_roles
- Empirical test as the user, which catches what the config view misses: log in as the contractor (or use a test account in the same group) and run
index=_internal | stats count
index=orders | stats count by host
If _internal returns rows, or the orders results show hosts outside ext-*, the union is confirmed.
- Check the identity source too. Re-run the same check after an LDAP logon vs a SAML logon, because the groups claim and the LDAP group mapping can produce different role sets for the same person. Never assume the two authentication paths are equivalent - the user will use whichever one is offered first.
3. What is the target design here, so contractors stay constrained while LDAP and SAML coexist?
Read the worked answer
Rebuild around two principles: constrain by index, and never let an unconstrained role attach to a constrained user.
- Never rely on
srchFilteras the primary boundary. Because filters OR together across roles, a filter is one inheritance change away from being void, and it also taxes search performance. Use it only as a refinement for users who hold exactly one role. The real boundary should besrchIndexesAllowed: contractors get exactlyorders;cases, nothing more. - Fix
defaultRole. SettingdefaultRole = userfor every authenticated account is the root cause of the_internalexposure. Either revert theuserrole'ssrchIndexesAllowedto a sane baseline and grant_internalonly via an admin role, or setdefaultRoleto a purpose-built minimal role. - Break the inheritance. Remove
importRoles = temp_powerfromcontractor_ro, and deletetemp_poweronce you have confirmed no active user depends on it. Audit every role forimportRolesand forsrchIndexesAllowedcontaining*,_internal,_auditor_introspection. - Make the two identity paths deterministic. Give LDAP and SAML non-overlapping role namespaces, and make the SAML groups attribute map contractors to the constrained role only. Test both logon paths, because a user who can authenticate two ways gets the union of both role sets.
- Detect drift. Schedule a saved search over
/services/authorization/rolesthat alerts when any role holding_internalis granted to a user outside the admin group - this class of misconfiguration is introduced by well-meaning admins, not attackers, so monitoring is the only durable control.