Air-Gapped Sites — Defence and Government
The account nobody used turned out to be the strongest one
Goal
Expand the effective permissions from account, role, and permission data, find unused permissions, dormant accounts, separation-of-duties violations, and privilege escalation paths to build a reduction plan, and then judge again with it applied to show by numbers that the violations decreased.
Why it matters
Over time, a permission list becomes a state that nobody can explain. In an air-gapped network, the only channel through which data can be moved outside is an account with the permission to do so, so this list is in effect the list of export paths. That is why the audit asks "who can do what," and the answer has to be computed from data, not from memory. The hard part of this lab is not the rules but the structure. If role inheritance is circular, a naive recursion does not stop, and for an account that can grant permissions, following only one step misses the actual reachable range.
Steps
- With
python3, create six data files in/root/least/data. Use the generation script as is. - Write the effective permissions per account, with inheritance expanded, in
/root/least/effective.json, and the two roles that inherit each other in/root/least/cycle.txt. - Write the held permissions that were not used during the observation period in
/root/least/unused.csvasaccount_id,perm. - Write the accounts that have been idle for 90 days or more as of the reference date or are marked as terminated in
/root/least/dormant.csvasaccount_id,days_idle,reason. - Write the pairs in the effective permissions that violate the separation of duties rules in
/root/least/sod.csvasaccount_id,rule_id,perm_a,perm_b. - Write, per account, the additional permissions reachable by adding roles to itself in
/root/least/escalation.json. - Decide revoke or keep and the reason for each assignment and write it in
/root/least/plan.csvasaccount_id,role,action,reason. - Leave the assignments with the reduction plan applied in
/root/least/after/assignments.csv, and a report comparing the counts before and after in/root/least/after/report.json.
Notes
- The reference date is in
/root/least/data/asof.txt. If you use today's date fromdate, the same data gives a different answer every day. - The separation of duties rules and the number of days for the dormancy threshold are in
/root/least/data/policy.json. Do not hard-code the numbers in the code; read them from that file. - When expanding inheritance, descend while remembering the roles already visited. Otherwise a
RecursionErroroccurs. - In step 7, "the permissions that assignment alone grants" are the permissions that disappear when you remove that assignment. If another role already grants the same permission, that assignment grants nothing.
- Common mistake 1: following only one step in step 6. A role that is granted can grant yet another role.
- Common mistake 2: overwriting the original assignment file in step 8. The pre-application data must remain so you can compare before and after.
- Standards documents: NIST SP 800-53 Rev 5, NIST SP 800-171 Rev 3. The rule numbers in this lab (such as SOD-01) are not control numbers from the standards but synthetic numbers used only within the data.
Create the data that will be the basis of the judgment
With python3, create six files in /root/least/data: accounts.csv, roles.json, assignments.csv, usage.csv, policy.json, and asof.txt. Use the generation script that uses no random numbers, as it is.
There is no sample to download in an air-gapped network, so create the data yourself first. Only if it uses no random numbers does everyone get the same data no matter who runs it how many times, and you can check one another's judgments against each other. The grader converts the file contents to a canonical form and checks fingerprints, so if you edit the data by hand, all the later steps get blocked.
Expand inheritance to see what accounts really hold
In /root/least/effective.json, write the effective permission list per account, and in /root/least/cycle.txt, write the names of the roles that inherit each other, one per line.
Roles inherit other roles, and inheritance goes through multiple levels. If you do not remember the roles already visited, the recursion does not end — and that fact itself is one of the things to find in this data. If two roles inherit each other, their effective permissions are the same.
Count the permissions held but never used
In /root/least/unused.csv, put account_id,perm on the first line, and write, one per line, the effective permissions that are not in usage.csv.
The usage records are pairs of account and permission. Subtracting the set of used permissions from the set of effective permissions leaves the answer. Remember that this list is a questionnaire, not a verdict — a permission used once a quarter does not show up in the observation period.
Separate dormant accounts by the reference date
In /root/least/dormant.csv, put account_id,days_idle,reason on the first line, and write the accounts whose idle days counted from the reference date are at or above the threshold in policy.json or whose status is terminated. The reason is one of 미접속·퇴직·미접속+퇴직 (the Korean words for "idle," "terminated," and "idle plus terminated").
The reference date is in asof.txt. If you use today's date, the answer differs between yesterday and today from the same data, and it cannot be used as audit material. The number of days is the reference date minus the last login date, and if both reasons overlap, write both.
Find permission pairs that must not be held together
In /root/least/sod.csv, put account_id,rule_id,perm_a,perm_b on the first line, and for each rule in policy.json, write the accounts that hold both permissions.
A rule applies to a pair, not to a single permission. If both a and b are in the effective permission set, it is a violation. Do not hard-code the rule numbers in the code; loop over policy.json to judge — even if rules are added, the code must stay the same.
Follow it all the way to permissions an account can add for itself
In /root/least/escalation.json, write per account the list of permissions it does not have now but can reach by adding roles to itself. Do not include accounts that have nothing more to gain.
An account that holds a permission to grant permissions can attach a role to itself. If the role attached that way can grant yet another role, it goes one step further. Repeat until there are no more roles to attach, and then subtract the permissions originally held from the permissions reached.
Decide for each assignment whether to revoke or keep
In /root/least/plan.csv, put account_id,role,action,reason on the first line and write one line per assignment. action is revoke or keep, and reason is one of 휴면·중복·미사용·사용 (the Korean words for "dormant," "duplicate," "unused," and "used").
The order of judgment changes the result. If it is a dormant account, revoke everything without weighing reasons. After that, if the assignment alone grants no permission at all, it is a duplicate, and if it does but none of them was used, it is unused. The rest are kept.
Apply it, judge again, and see whether it decreased
Leave the assignments with the reduction plan applied in /root/least/after/assignments.csv, and in /root/least/after/report.json write nine counts: assignments_before, assignments_after, sod_before, sod_after, unused_before, unused_after, escalation_before, escalation_after, and dormant.
Leave the original data as it is and write the result of applying it to a new file. That way you can compare before and after, and you get the same result even if it is graded again. Do not count the numbers in the report by hand; recompute them from the assignments after applying with the same function as the earlier steps — the grader does the same computation.