Detection rules › Panther
AWS IAM User Not In Conflicting Groups
This policy validates that IAM users are not in IAM groups that are considered mutually exclusive. For example, in some workflows developers are responsible for dev environments and sysadmins are responsible for prod environments. In this situation no (or very few) users should be in both sysadmin and developer groups. This is in following with the principle of least privilege.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation | T1078 Valid Accounts |
Rule body yaml
AnalysisType: policy
Filename: aws_iam_user_not_in_conflicting_groups.py
PolicyID: "AWS.IAM.User.NotInConflictingGroups"
DisplayName: "AWS IAM User Not In Conflicting Groups"
Enabled: false
ResourceTypes:
- AWS.IAM.User
Tags:
- AWS
- Configuration Required
- Security, Identity & Compliance
- PCI
- Privilege Escalation:Valid Accounts
Reports:
PCI:
- 7.2.2
MITRE ATT&CK:
- TA0004:T1078
Severity: Medium
Description: >
This policy validates that IAM users are not in IAM groups that are considered mutually exclusive. For example, in some workflows developers are responsible for dev environments and sysadmins are responsible for prod environments. In this situation no (or very few) users should be in both sysadmin and developer groups. This is in following with the principle of least privilege.
Runbook: Remove the IAM user from one of the conflicting groups.
Reference: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_groups.html
Tests:
- Name: User In Conflicting Groups
ExpectedResult: false
Resource:
{
"Arn": "arn:aws:iam::123456789012:user/Bobert",
"CreateDate": "2019-05-10T21:49:56Z",
"CredentialReport":
{
"ARN": "arn:aws:iam::123456789012:user/Bobert",
"AccessKey1Active": true,
"AccessKey1LastRotated": "2019-05-10T21:49:57Z",
"AccessKey1LastUsedDate": "0001-01-01T00:00:00Z",
"AccessKey1LastUsedRegion": "N/A",
"AccessKey1LastUsedService": "N/A",
"AccessKey2Active": false,
"AccessKey2LastRotated": "0001-01-01T00:00:00Z",
"AccessKey2LastUsedDate": "0001-01-01T00:00:00Z",
"AccessKey2LastUsedRegion": "N/A",
"AccessKey2LastUsedService": "N/A",
"Cert1Active": false,
"Cert1LastRotated": "0001-01-01T00:00:00Z",
"Cert2Active": false,
"Cert2LastRotated": "0001-01-01T00:00:00Z",
"MfaActive": false,
"PasswordEnabled": false,
"PasswordLastChanged": "2019-05-21T22:10:11Z",
"PasswordLastUsed": "0001-01-01T00:00:00Z",
"PasswordNextRotation": "2019-08-14T22:10:11Z",
"UserCreationTime": "2019-05-10T21:49:56Z",
"UserName": "Bobert",
},
"Groups":
[
{
"Arn": "arn:aws:iam::123456789012:group/ExampleGroup",
"CreateDate": "2019-01-01T00:00:00Z",
"GroupId": "ABCDEFGHIJKLMNOP",
"GroupName": "PROD_ADMIN",
"Path": "/",
},
{
"Arn": "arn:aws:iam::123456789012:group/ExampleGroup",
"CreateDate": "2019-01-01T00:00:00Z",
"GroupId": "ABCDEFGHIJKLMNOP",
"GroupName": "DEV",
"Path": "/",
},
],
"InlinePolicyNames": null,
"ManagedPolicyNames": ["IAMUserChangePassword"],
"PasswordLastUsed": null,
"Path": "/",
"PermissionsBoundary": null,
"Tags": null,
"UserId": "ASDFASDFASDFASDF",
"UserName": "Bobert",
"VirtualMFA": null,
}
- Name: User Not In Conflicting Groups
ExpectedResult: true
Resource:
{
"Arn": "arn:aws:iam::123456789012:user/Bobert",
"CreateDate": "2019-05-10T21:49:56Z",
"CredentialReport":
{
"ARN": "arn:aws:iam::123456789012:user/Bobert",
"AccessKey1Active": true,
"AccessKey1LastRotated": "2019-05-10T21:49:57Z",
"AccessKey1LastUsedDate": "0001-01-01T00:00:00Z",
"AccessKey1LastUsedRegion": "N/A",
"AccessKey1LastUsedService": "N/A",
"AccessKey2Active": false,
"AccessKey2LastRotated": "0001-01-01T00:00:00Z",
"AccessKey2LastUsedDate": "0001-01-01T00:00:00Z",
"AccessKey2LastUsedRegion": "N/A",
"AccessKey2LastUsedService": "N/A",
"Cert1Active": false,
"Cert1LastRotated": "0001-01-01T00:00:00Z",
"Cert2Active": false,
"Cert2LastRotated": "0001-01-01T00:00:00Z",
"MfaActive": true,
"PasswordEnabled": true,
"PasswordLastChanged": "2019-05-21T22:10:11Z",
"PasswordLastUsed": "0001-01-01T00:00:00Z",
"PasswordNextRotation": "2019-08-14T22:10:11Z",
"UserCreationTime": "2019-05-10T21:49:56Z",
"UserName": "Bobert",
},
"Groups":
[
{
"Arn": "arn:aws:iam::123456789012:group/ExampleGroup",
"CreateDate": "2019-01-01T00:00:00Z",
"GroupId": "ABCDEFGHIJKLMNOP",
"GroupName": "ExampleGroup",
"Path": "/",
},
],
"InlinePolicyNames": null,
"ManagedPolicyNames": ["IAMUserChangePassword"],
"PasswordLastUsed": null,
"Path": "/",
"PermissionsBoundary": null,
"Tags": null,
"UserId": "ASDFASDFASDFASDF",
"UserName": "Bobert",
"VirtualMFA": null,
}
Detection logic
Rule logic imperative Python
GROUP_CONFLICTS = [
{"PROD_ADMIN", "DEV"},
]
def policy(resource):
group_names = {group["GroupName"] for group in resource["Groups"] or []}
for conflict_set in GROUP_CONFLICTS:
if len(group_names.intersection(conflict_set)) > 1:
return False
return True
The parser cannot express this rule's logic as a field filter; the imperative Python above is the detection.