audit-labs/audit-tools
A collection of scripts, queries, and other goodies you can use in an audit.
clone: git clone https://gitbay.org/audit-labs/audit-tools.git
v1.0.0: applications/aws/README.md · raw
1> **NOTE**: Authentication uses the standard AWS credential chain (environment
2> variables, shared config/credentials, SSO profiles, instance roles). This tool
3> never handles access keys directly. Read-only permissions are enough — IAM
4> `Get*`/`List*`, S3 `s3:GetBucket*` + `s3:ListAllMyBuckets`, EC2
5> `ec2:DescribeRegions`/`DescribeSecurityGroups`, `cloudtrail:DescribeTrails` +
6> `GetTrailStatus`, `config:DescribeConfigurationRecorders*`, and for the SSO
7> check `sso:List*`/`sso:Describe*`, `identitystore:Describe*`, and
8> `organizations:ListAccounts`. The SecurityAudit managed policy covers these.
9
10---
11
12# `audit.py` — Unified AWS Audit Tool
13
14Runs all collectors against the account reachable with your active AWS
15credentials and writes a timestamped audit package to disk. This is the tool the
16interactive TUI (`audit_tui.py`) drives.
17
18## Setup
19
20```bash
21export AWS_PROFILE=my-profile # optional; else the default chain
22export AWS_DEFAULT_REGION=us-east-1 # optional
23export AWS_AUDIT_ACCOUNT=my-account # optional; only for the SSO check
24```
25
26If you authenticate with `aws login` / IAM Identity Center (SSO), those
27credentials use the AWS Common Runtime provider, which needs the `crt` extra.
28It is included via `botocore[crt]` in the `aws` extra (`pip install ".[aws]"`);
29if you installed boto3 separately, run `pip install "botocore[crt]"`. Without it you'll see
30`MissingDependencyException: ... requires an additional dependency`.
31
32## Usage
33
34```bash
35# Basic run — uses the active credentials / default profile
36python audit.py
37
38# Named profile and region, custom output directory
39python audit.py --profile my-profile --region us-east-1 --out ./output
40
41# SSO assignments for a specific account in the organization
42python audit.py --account my-account
43```
44
45## Output
46
47Creates a directory: `<out>/aws_audit_<profile>_<YYYY-MM-DD>/`
48
49| File | Contents |
50|---|---|
51| `iam_users.csv` | IAM users with MFA status, access-key count/age, console password, last use |
52| `password_policy.csv` | Account IAM password policy (length, complexity, rotation, reuse) |
53| `account_security.csv` | Account summary — root MFA, root access keys, and resource counts |
54| `s3_public_access.csv` | Per-bucket Public Access Block, policy public status, and ACL public exposure |
55| `open_security_groups.csv` | Security-group ingress rules open to `0.0.0.0/0` or `::/0`, across all regions |
56| `cloudtrail.csv` | CloudTrail trails — logging status, multi-region, log-file validation |
57| `config_recorders.csv` | AWS Config recording status per region (gaps are flagged) |
58| `sso_assignments.csv` | IAM Identity Center permission-set assignments per account (Identity Center + Organizations) |
59| `summary.txt` | Row counts per section |
60
61`open_security_groups.csv` and `config_recorders.csv` scan every enabled region,
62so they take longer on accounts with many regions.
63
64Checks that aren't available (no password policy, no Identity Center instance,
65missing permissions) are skipped with a warning; the rest still run.
66
67The shell scripts below remain for CloudShell or CLI-only environments where
68Python and boto3 aren't set up.
69
70---
71
72# `aws_iam_users.sh`
73
74*Note*: This example uses an account titled `cmc`, which has access provisioned to it through IAM.
75
76``` bash
77chmod +x aws_iam_users.sh
78./aws_iam_users.sh
79```
80
81``` text
82Fetching IAM Identity Center and Account details...
83Successfully found Account 'cmc' with ID: 214941490075
84---
85Step 2: Finding all permission sets provisioned to 'cmc'...
86Found provisioned permission sets. Now checking assignments for each...
87---
88 -> Fetching policies for Permission Set: arn:aws:sso:::permissionSet/ssoins-68041bff81588aa3/ps-6ea9be6a2332b891
89 -> Found Assignment: testgroup1 (GROUP) -> AdministratorAccess
90 -> Found Assignment: iamtestuser1 (USER) -> AdministratorAccess
91 -> Found Assignment: testgroup2 (GROUP) -> AdministratorAccess
92 -> Fetching policies for Permission Set: arn:aws:sso:::permissionSet/ssoins-68041bff81588aa3/ps-590510f2a285016d
93 -> Found Assignment: iamtestuser1 (USER) -> Billing
94---
95Success! Report saved to 'report_cmc.json'
96The file contains all user/group assignments and their policies for account 'cmc'.
97```
98
99``` bash
100cat report_cmc.json
101```
102
103```json
104[
105 {
106 "principal": {
107 "type": "GROUP",
108 "name": "testgroup1"
109 },
110 "permission_set": {
111 "name": "AdministratorAccess",
112 "policies": {
113 "managed_policies": [
114 "arn:aws:iam::aws:policy/AdministratorAccess"
115 ],
116 "inline_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"Statement2\",\"Effect\":\"Deny\",\"Action\":[\"a4b:*\"],\"Resource\":[\"*\"]}]}"
117 }
118 }
119 },
120 {
121 "principal": {
122 "type": "USER",
123 "name": "iamtestuser1"
124 },
125 "permission_set": {
126 "name": "AdministratorAccess",
127 "policies": {
128 "managed_policies": [
129 "arn:aws:iam::aws:policy/AdministratorAccess"
130 ],
131 "inline_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"Statement2\",\"Effect\":\"Deny\",\"Action\":[\"a4b:*\"],\"Resource\":[\"*\"]}]}"
132 }
133 }
134 },
135 {
136 "principal": {
137 "type": "GROUP",
138 "name": "testgroup2"
139 },
140 "permission_set": {
141 "name": "AdministratorAccess",
142 "policies": {
143 "managed_policies": [
144 "arn:aws:iam::aws:policy/AdministratorAccess"
145 ],
146 "inline_policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"Statement2\",\"Effect\":\"Deny\",\"Action\":[\"a4b:*\"],\"Resource\":[\"*\"]}]}"
147 }
148 }
149 },
150 {
151 "principal": {
152 "type": "USER",
153 "name": "iamtestuser1"
154 },
155 "permission_set": {
156 "name": "Billing",
157 "policies": {
158 "managed_policies": [
159 "arn:aws:iam::aws:policy/job-function/Billing"
160 ],
161 "inline_policy": ""
162 }
163 }
164 }
165]
166```
167
168# `aws_password_policy`
169
170To test a password policy against AWS, I have created two steps:
171
172**Step 1: Gather AWS Policy**
173
174Run the script on your CloudShell or using the `aws` command
175
176``` bash
177chmod +x gather_policy.sh
178./gather_policy.sh
179```
180
181This will produce a JSON file as the output, with both metadata and the password policy/
182
183``` json
184{
185 "metadata": {
186 "report_timestamp_utc": "2025-12-15T01:29:52Z",
187 "os_user": "cloudshell-user",
188 "hostname": "",
189 "working_directory": "/home/cloudshell-user",
190 "aws_profile": "default",
191 "aws_region": "eu-west-1",
192 "aws_caller_identity": {
193 "UserId": "214941490075",
194 "Account": "214941490075",
195 "Arn": "arn:aws:iam::214941490075:root"
196 }
197 },
198 "PasswordPolicy": {
199 "MinimumPasswordLength": 8,
200 "RequireSymbols": true,
201 "RequireNumbers": true,
202 "RequireUppercaseCharacters": true,
203 "RequireLowercaseCharacters": true,
204 "AllowUsersToChangePassword": true,
205 "ExpirePasswords": true,
206 "MaxPasswordAge": 90,
207 "PasswordReusePrevention": 4,
208 "HardExpiry": false
209 }
210}
211```
212
213**Step 2: Test AWS**
214
215Use this file as the input to the `evaluate_policy.py` script. This Python script will ask you what you expect the values to be (e.g., what are the requirements in the company's policy?).
216
217``` bash
218uv run evaluate_policy.py policy_report.json
219```
220
221This will ask you for inputs dynamically (all are optional) and will return both a table of results in the shell, as well as a CSV file for further testing and/or documentation.
222
223*Shell Output:*
224
225``` text
226=== Expected / Minimum Values (press <Enter> for N/A) ===
227
228Enter expected value for 'Minimum password length' (int) or press <Enter> to skip: 8
229Enter expected value for 'Require symbols (!@#$…)' (bool) or press <Enter> to skip: true
230Enter expected value for 'Require numbers (0‑9)' (bool) or press <Enter> to skip: true
231Enter expected value for 'Require uppercase letters (A‑Z)' (bool) or press <Enter> to skip: true
232Enter expected value for 'Require lowercase letters (a‑z)' (bool) or press <Enter> to skip: true
233Enter expected value for 'Allow users to change password' (bool) or press <Enter> to skip: true
234Enter expected value for 'Expire passwords (enable aging)' (bool) or press <Enter> to skip: true
235Enter expected value for 'Maximum password age (days)' (int) or press <Enter> to skip: 90
236Enter expected value for 'Prevent password reuse (last N)' (int) or press <Enter> to skip: 4
237Enter expected value for 'Hard expiry (no grace period)' (bool) or press <Enter> to skip: false
238
239Audit CSV written to: policy_audit_20251215T014323Z.csv
240
241Summary:
242 1. Minimum password length → PASS
243 2. Require symbols (!@#$…) → PASS
244 3. Require numbers (0‑9) → PASS
245 4. Require uppercase letters (A‑Z) → PASS
246 5. Require lowercase letters (a‑z) → PASS
247 6. Allow users to change password → PASS
248 7. Expire passwords (enable aging) → PASS
249 8. Maximum password age (days) → PASS
250 9. Prevent password reuse (last N) → PASS
251 10. Hard expiry (no grace period) → PASS
252
253--- End of report ---
254```
255
256*CSV Output:*
257
258``` csv
259# report_timestamp_utc: 2025-12-15T01:29:52Z
260# os_user: cloudshell-user
261# hostname:
262# working_directory: /home/cloudshell-user
263# aws_profile: default
264# aws_region: eu-west-1
265"# aws_caller_identity: {'UserId': '214941490075', 'Account': '214941490075', 'Arn': 'arn:aws:iam::214941490075:root'}"
266
267Rule#,Policy‑Item,Expected,Actual,Result
2681,Minimum password length,8,8,PASS
2692,Require symbols (!@#$…),true,true,PASS
2703,Require numbers (0‑9),true,true,PASS
2714,Require uppercase letters (A‑Z),true,true,PASS
2725,Require lowercase letters (a‑z),true,true,PASS
2736,Allow users to change password,true,true,PASS
2747,Expire passwords (enable aging),true,true,PASS
2758,Maximum password age (days),90,90,PASS
2769,Prevent password reuse (last N),4,4,PASS
27710,Hard expiry (no grace period),false,false,PASS
278```
279
280# `aws_s3_buckets.sh`
281
282This script requires one non-interactive step. Simply run the script:
283
284``` bash
285chmod +x aws_s3_buckets.sh
286./aws_s3_buckets.sh
287```
288
289The shell will show you each bucket discovered during the scanning process, as well as the final result. This final result is a combination of the bucket's Public Access Block (PAB), Policy Status (IsPublic), and ACLs (AllUsers Group).
290
291``` text
292Starting FULL S3 Public Access Audit for the CURRENT account...
293---
2941. Retrieving all bucket names...
295Processing bucket: 13bf5920-a09f-47bc-a75a-394a09f18d6a
296 Region determined: eu-west-1
297 Final Status: FALSE
298Processing bucket: c67fa6bd-2fd5-4bc5-825d-587fb535bf2e
299 Region determined: eu-west-1
300 Final Status: FALSE
301---
302✅ Audit Complete.
303Final report saved to **s3_full_public_access_audit.csv**
304BucketName,Region,PAB_FullyRestricted,Policy_IsPublic,ACL_AllUsersRead,ACL_AllUsersWrite,OverallPublicStatus
30513bf5920-a09f-47bc-a75a-394a09f18d6a,eu-west-1,FALSE-VULNERABLE,No Policy,FALSE,FALSE,"FALSE"
306c67fa6bd-2fd5-4bc5-825d-587fb535bf2e,eu-west-1,TRUE,No Policy,FALSE,FALSE,"FALSE"
307```
308
309It will also save the results shown above to the `s3_full_public_access_audit.csv` file:
310
311``` csv
312BucketName,Region,PAB_FullyRestricted,Policy_IsPublic,ACL_AllUsersRead,ACL_AllUsersWrite,OverallPublicStatus
31313bf5920-a09f-47bc-a75a-394a09f18d6a,eu-west-1,FALSE-VULNERABLE,No Policy,FALSE,FALSE,"FALSE"
314c67fa6bd-2fd5-4bc5-825d-587fb535bf2e,eu-west-1,TRUE,No Policy,FALSE,FALSE,"FALSE"
315```