ocinferno 0.5.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ocinferno-0.5.5/LICENSE +28 -0
- ocinferno-0.5.5/PKG-INFO +273 -0
- ocinferno-0.5.5/README.md +247 -0
- ocinferno-0.5.5/cli/main.py +140 -0
- ocinferno-0.5.5/cli/module_actions.py +784 -0
- ocinferno-0.5.5/cli/workspace_instructions.py +1790 -0
- ocinferno-0.5.5/core/api_logger.py +297 -0
- ocinferno-0.5.5/core/config.py +105 -0
- ocinferno-0.5.5/core/console.py +643 -0
- ocinferno-0.5.5/core/contracts.py +75 -0
- ocinferno-0.5.5/core/db.py +1175 -0
- ocinferno-0.5.5/core/http_policy.py +97 -0
- ocinferno-0.5.5/core/session.py +3198 -0
- ocinferno-0.5.5/core/utils/module_helpers.py +1419 -0
- ocinferno-0.5.5/core/utils/selection_helpers.py +196 -0
- ocinferno-0.5.5/core/utils/service_runtime.py +341 -0
- ocinferno-0.5.5/modules/apigateway/enumeration/enum_apigateway.py +301 -0
- ocinferno-0.5.5/modules/apigateway/utilities/helpers.py +495 -0
- ocinferno-0.5.5/modules/artifactregistry/enumeration/enum_artifactregistry.py +186 -0
- ocinferno-0.5.5/modules/artifactregistry/utilities/helpers.py +217 -0
- ocinferno-0.5.5/modules/blockchain/enumeration/enum_blockchain.py +329 -0
- ocinferno-0.5.5/modules/blockchain/utilities/helpers.py +174 -0
- ocinferno-0.5.5/modules/cloudguard/enumeration/enum_cloudguard.py +136 -0
- ocinferno-0.5.5/modules/cloudguard/utilities/helpers.py +291 -0
- ocinferno-0.5.5/modules/containerinstances/enumeration/enum_container_instances.py +91 -0
- ocinferno-0.5.5/modules/containerinstances/utilities/helpers.py +48 -0
- ocinferno-0.5.5/modules/containerregistry/enumeration/enum_containerregistry.py +162 -0
- ocinferno-0.5.5/modules/containerregistry/utilities/helpers.py +81 -0
- ocinferno-0.5.5/modules/core/enumeration/enum_core_block_storage.py +120 -0
- ocinferno-0.5.5/modules/core/enumeration/enum_core_compute.py +890 -0
- ocinferno-0.5.5/modules/core/enumeration/enum_core_network.py +163 -0
- ocinferno-0.5.5/modules/core/utilities/blockstorage_helpers.py +173 -0
- ocinferno-0.5.5/modules/core/utilities/compute_helpers.py +1111 -0
- ocinferno-0.5.5/modules/core/utilities/compute_management_helpers.py +104 -0
- ocinferno-0.5.5/modules/core/utilities/virtual_network_helpers.py +445 -0
- ocinferno-0.5.5/modules/dataflow/enumeration/enum_dataflow.py +117 -0
- ocinferno-0.5.5/modules/dataflow/utilities/helpers.py +154 -0
- ocinferno-0.5.5/modules/datascience/enumeration/enum_datascience.py +149 -0
- ocinferno-0.5.5/modules/datascience/utilities/helpers.py +333 -0
- ocinferno-0.5.5/modules/desktops/enumeration/enum_desktops.py +167 -0
- ocinferno-0.5.5/modules/desktops/utilities/helpers.py +218 -0
- ocinferno-0.5.5/modules/devops/enumeration/enum_devops.py +198 -0
- ocinferno-0.5.5/modules/devops/utilities/helpers.py +166 -0
- ocinferno-0.5.5/modules/dns/enumeration/enum_dns.py +159 -0
- ocinferno-0.5.5/modules/dns/utilities/helpers.py +119 -0
- ocinferno-0.5.5/modules/email/enumeration/enum_email.py +186 -0
- ocinferno-0.5.5/modules/email/utilities/helpers.py +250 -0
- ocinferno-0.5.5/modules/everything/enumeration/enum_all.py +1002 -0
- ocinferno-0.5.5/modules/everything/enumeration/enum_config_check.py +78 -0
- ocinferno-0.5.5/modules/everything/utilities/config_audit.py +3475 -0
- ocinferno-0.5.5/modules/everything/utilities/enum_all_summary.py +352 -0
- ocinferno-0.5.5/modules/filestorage/enumeration/enum_filestorage.py +259 -0
- ocinferno-0.5.5/modules/filestorage/utilities/helpers.py +369 -0
- ocinferno-0.5.5/modules/functions/enumeration/enum_functions.py +149 -0
- ocinferno-0.5.5/modules/functions/utilities/helpers.py +88 -0
- ocinferno-0.5.5/modules/identityclient/enumeration/enum_comp.py +199 -0
- ocinferno-0.5.5/modules/identityclient/enumeration/enum_identity.py +88 -0
- ocinferno-0.5.5/modules/identityclient/utilities/helpers.py +2466 -0
- ocinferno-0.5.5/modules/iot/enumeration/enum_iot.py +139 -0
- ocinferno-0.5.5/modules/iot/utilities/helpers.py +262 -0
- ocinferno-0.5.5/modules/kubernetes/enumeration/enum_kubernetes.py +199 -0
- ocinferno-0.5.5/modules/kubernetes/utilities/helpers.py +137 -0
- ocinferno-0.5.5/modules/logging/enumeration/enum_logs.py +187 -0
- ocinferno-0.5.5/modules/logging/utilities/helpers.py +74 -0
- ocinferno-0.5.5/modules/managedkafka/enumeration/enum_managedkafka.py +193 -0
- ocinferno-0.5.5/modules/managedkafka/utilities/helpers.py +152 -0
- ocinferno-0.5.5/modules/networkfirewall/enumeration/enum_networkfirewall.py +185 -0
- ocinferno-0.5.5/modules/networkfirewall/utilities/helpers.py +149 -0
- ocinferno-0.5.5/modules/networkloadbalancer/enumeration/enum_network_load_balancers.py +84 -0
- ocinferno-0.5.5/modules/networkloadbalancer/utilities/helpers.py +53 -0
- ocinferno-0.5.5/modules/notifications/enumeration/enum_notifications.py +123 -0
- ocinferno-0.5.5/modules/notifications/utilities/helpers.py +91 -0
- ocinferno-0.5.5/modules/objectstorage/enumeration/enum_objectstorage.py +241 -0
- ocinferno-0.5.5/modules/objectstorage/utilities/helpers.py +455 -0
- ocinferno-0.5.5/modules/opengraph/enumeration/enum_oracle_cloud_hound_data.py +866 -0
- ocinferno-0.5.5/modules/opengraph/utilities/dynamic_group_membership_graph_builder.py +206 -0
- ocinferno-0.5.5/modules/opengraph/utilities/group_membership_graph_builder.py +278 -0
- ocinferno-0.5.5/modules/opengraph/utilities/helpers/__init__.py +61 -0
- ocinferno-0.5.5/modules/opengraph/utilities/helpers/constants.py +391 -0
- ocinferno-0.5.5/modules/opengraph/utilities/helpers/context.py +1114 -0
- ocinferno-0.5.5/modules/opengraph/utilities/helpers/core_helpers.py +398 -0
- ocinferno-0.5.5/modules/opengraph/utilities/helpers/graph_utils.py +533 -0
- ocinferno-0.5.5/modules/opengraph/utilities/helpers/iam_conditionals.py +4801 -0
- ocinferno-0.5.5/modules/opengraph/utilities/helpers/matching_rules_engine.py +792 -0
- ocinferno-0.5.5/modules/opengraph/utilities/helpers/policy_parser_enrichment.py +157 -0
- ocinferno-0.5.5/modules/opengraph/utilities/iam_policy_advanced_relation_graph_builder.py +2707 -0
- ocinferno-0.5.5/modules/opengraph/utilities/iam_policy_base_relation_graph_builder.py +2342 -0
- ocinferno-0.5.5/modules/opengraph/utilities/identity_domain_graph_builder.py +1948 -0
- ocinferno-0.5.5/modules/opengraph/utilities/resource_scope_graph_builder.py +1056 -0
- ocinferno-0.5.5/modules/resourcemanager/enumeration/enum_resourcemanager.py +181 -0
- ocinferno-0.5.5/modules/resourcemanager/utilities/helpers.py +354 -0
- ocinferno-0.5.5/modules/resourcescheduler/enumeration/enum_resource_schedules.py +94 -0
- ocinferno-0.5.5/modules/resourcescheduler/utilities/helpers.py +49 -0
- ocinferno-0.5.5/modules/tagging/enumeration/enum_tagging.py +154 -0
- ocinferno-0.5.5/modules/tagging/utilities/helpers.py +169 -0
- ocinferno-0.5.5/modules/vault/enumeration/enum_vault.py +273 -0
- ocinferno-0.5.5/modules/vault/utilities/helpers.py +929 -0
- ocinferno-0.5.5/ocinferno.egg-info/PKG-INFO +273 -0
- ocinferno-0.5.5/ocinferno.egg-info/SOURCES.txt +103 -0
- ocinferno-0.5.5/ocinferno.egg-info/dependency_links.txt +1 -0
- ocinferno-0.5.5/ocinferno.egg-info/entry_points.txt +2 -0
- ocinferno-0.5.5/ocinferno.egg-info/requires.txt +15 -0
- ocinferno-0.5.5/ocinferno.egg-info/top_level.txt +3 -0
- ocinferno-0.5.5/pyproject.toml +49 -0
- ocinferno-0.5.5/setup.cfg +4 -0
ocinferno-0.5.5/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, NetSPI
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
ocinferno-0.5.5/PKG-INFO
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ocinferno
|
|
3
|
+
Version: 0.5.5
|
|
4
|
+
Summary: OCI offensive security assessment and enumeration framework.
|
|
5
|
+
Author: NetSPI
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Intended Audience :: Information Technology
|
|
11
|
+
Classifier: Topic :: Security
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: PyYAML==6.0.3
|
|
16
|
+
Requires-Dist: oci==2.169.0
|
|
17
|
+
Requires-Dist: requests==2.33.1
|
|
18
|
+
Requires-Dist: prettytable==3.17.0
|
|
19
|
+
Requires-Dist: oci-lexer-parser==0.1.2
|
|
20
|
+
Requires-Dist: pandas==2.3.3; python_version < "3.11"
|
|
21
|
+
Requires-Dist: pandas==3.0.1; python_version >= "3.11"
|
|
22
|
+
Requires-Dist: xlsxwriter==3.2.9
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# OCInferno
|
|
28
|
+
|
|
29
|
+
[](https://github.com/NetSPI/OCInferno/actions/workflows/ci.yml)
|
|
30
|
+
[](https://pypi.org/project/ocinferno/)
|
|
31
|
+
[](https://www.python.org/)
|
|
32
|
+
[](./LICENSE)
|
|
33
|
+
[](./CONTRIBUTING.md)
|
|
34
|
+
[](https://pypi.org/project/oci/)
|
|
35
|
+
|
|
36
|
+
## Overview
|
|
37
|
+
|
|
38
|
+
> In the spirit of transparency: parts of this project and documentation were developed with LLM coding assistance. Review code and behavior in your environment before operational use. Notably, the enum_config_checks module is still in-progress based off original content generated.
|
|
39
|
+
|
|
40
|
+
OCInferno (O-C-Inferno) is an OCI offensive security assessment framework for workspace-driven credential handling, service enumeration, artifact download, and graph-based attack-path analysis. It includes a module to generate a custom **OpenGraph output** which can be fed into BloodHound, as shown below, to map privilege-escalation paths.
|
|
41
|
+
|
|
42
|
+
<p align="center" style="margin: 0.35em 0 0 0;">
|
|
43
|
+
<img src="./images/README_OVERVIEW.png" alt="Sample OpenGraph output in BloodHound" />
|
|
44
|
+
</p>
|
|
45
|
+
<p align="center" style="margin: 0.15em 0 1em 0;"><em>Figure 1. Example OpenGraph/BloodHound relationship view.</em></p>
|
|
46
|
+
|
|
47
|
+
<p align="center" style="margin: 0.35em 0 0 0;">
|
|
48
|
+
<img src="./images/MODULE_RUN.png" alt="OCInferno terminal output example" />
|
|
49
|
+
</p>
|
|
50
|
+
<p align="center" style="margin: 0.15em 0 1em 0;"><em>Figure 2. Example module output in the CLI.</em></p>
|
|
51
|
+
|
|
52
|
+
## High-Level Features
|
|
53
|
+
|
|
54
|
+
- **CLI UX:** Interactive CLI with command and argument tab auto-complete and history.
|
|
55
|
+
- **Authentication:** Multiple supported auth methods:
|
|
56
|
+
- **Config Profile:** API key-backed and session-token-backed OCI profiles.
|
|
57
|
+
- **Instance Principal:** Compute-instance identity flow.
|
|
58
|
+
- **Resource Principal:** Runtime/workload identity flow.
|
|
59
|
+
- **Module Model:** Service-specific modules across OCI services, with proxy and rate-limiting support.
|
|
60
|
+
- **Mass Enumeration:** Broad OCI module coverage with `enum_all` orchestration support (implemented in `modules/everything`).
|
|
61
|
+
- **Config Audits:** `enum_config_check` findings based on enumerated/saved data (implemented in `modules/everything`).
|
|
62
|
+
- **Reporting Exports:** Resource export support for HTML, CSV, JSON, Excel, and graph image outputs.
|
|
63
|
+
- **Artifact Downloads:** Download support across many modules with `--download` and selective routing.
|
|
64
|
+
- **OpenGraph / BloodHound:** OpenGraph export for BloodHound ingestion, including:
|
|
65
|
+
- A default focused view of high-impact edges, with `--include-all` available for broader relationship output to ideally see all relationships regardless of priv escalation weight.
|
|
66
|
+
- Privilege-escalation path modeling across OCI IAM and Identity Domain app-role/grant relationships.
|
|
67
|
+
- Inheritance-aware modeling (`--expand-inherited`) and conditional evaluation (`--cond-eval`) to improve graph accuracy.
|
|
68
|
+
|
|
69
|
+
## Documentation
|
|
70
|
+
|
|
71
|
+
Documentation is maintained on the GitHub Wiki:
|
|
72
|
+
|
|
73
|
+
- https://github.com/NetSPI/OCInferno/wiki
|
|
74
|
+
|
|
75
|
+
Contributing guidance:
|
|
76
|
+
|
|
77
|
+
- `CONTRIBUTING.md`
|
|
78
|
+
|
|
79
|
+
Roadmap guidance:
|
|
80
|
+
|
|
81
|
+
- `ROADMAP.md`
|
|
82
|
+
|
|
83
|
+
Sample OpenGraph JSON:
|
|
84
|
+
|
|
85
|
+
- `opengraph_examples/example_input.json`
|
|
86
|
+
|
|
87
|
+
## Installation TLDR
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
git clone https://github.com/NetSPI/OCInferno.git
|
|
91
|
+
|
|
92
|
+
# You don't need pytests to run the tool
|
|
93
|
+
rm -r tests/
|
|
94
|
+
|
|
95
|
+
virtualenv .venv
|
|
96
|
+
source .venv/bin/activate
|
|
97
|
+
pip install -r requirements.txt
|
|
98
|
+
python -m cli.main
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Launch and Data Download TLDR
|
|
102
|
+
|
|
103
|
+
Run the tool:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
python -m cli.main
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
At startup:
|
|
110
|
+
|
|
111
|
+
1. Create/select a workspace.
|
|
112
|
+
2. Add credentials using your OCI config profile (example uses `MY_PROFILE` to add an API key):
|
|
113
|
+
```text
|
|
114
|
+
profile MY_PROFILE --filepath ~/.oci/config --profile MY_PROFILE
|
|
115
|
+
```
|
|
116
|
+
3. Start the first full run.
|
|
117
|
+
`enum_all` runs all modules. `--comp` recursively enumerates compartments to maximize coverage.
|
|
118
|
+
`--get` follows LIST calls with GET calls where supported for deeper detail.
|
|
119
|
+
`--download` downloads data where possible, choosing `--not-downloads buckets` attempts to download all content **except** bucket object content.
|
|
120
|
+
```bash
|
|
121
|
+
# Download as much as you can
|
|
122
|
+
modules run enum_all --get --comp --download
|
|
123
|
+
# Download everything EXCEPT bucket contents
|
|
124
|
+
modules run enum_all --get --comp [--not-downloads buckets]
|
|
125
|
+
```
|
|
126
|
+
4. Export a compartment tree image and Excel data output to quickly review what your current permissions can see:
|
|
127
|
+
```bash
|
|
128
|
+
(TENANT-2snxfsaa:TEST)> data export treeimage
|
|
129
|
+
[*] Compartment tree export complete -> ./ocinferno_output/1_TEST/exports/data/global/resource_reports/compartment_tree.svg (format=svg, renderer=svg-interactive, compartments=6)
|
|
130
|
+
|
|
131
|
+
(TENANT-2snxfsaa:TEST)> data export excel
|
|
132
|
+
[*] Excel export complete -> ./ocinferno_output/1_TEST/exports/data/global/sqlite_excel/sqlite_blob.xlsx (format=xlsx, databases=1, tables=173, rows=54951, single_sheet=True, condensed=True)
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
<p align="center" style="margin: 0.35em 0 0 0;">
|
|
137
|
+
<img src="./images/COMP_HIERARCHY_1.png" alt="Compartment Tree Export" />
|
|
138
|
+
</p>
|
|
139
|
+
<p align="center" style="margin: 0.15em 0 1em 0;"><em>Figure 1. Tree image export from <code>data export treeimage</code>.</em></p>
|
|
140
|
+
|
|
141
|
+
<p align="center" style="margin: 0.35em 0 0 0;">
|
|
142
|
+
<img src="./images/DATA_EXPORT_1.png" alt="Excel Data Export" />
|
|
143
|
+
</p>
|
|
144
|
+
<p align="center" style="margin: 0.15em 0 1em 0;"><em>Figure 2. Excel export output from <code>data export excel</code>.</em></p>
|
|
145
|
+
|
|
146
|
+
## OpenGraph TLDR
|
|
147
|
+
|
|
148
|
+
### Where JSON is saved
|
|
149
|
+
|
|
150
|
+
Once all data is collected, use the `enum_oracle_cloud_hound_data` module as seen in example below:
|
|
151
|
+
```bash
|
|
152
|
+
modules run enum_oracle_cloud_hound_data [--include-all] [--expand-inherited] [--cond-eval] --reset --out opengraph_output.json
|
|
153
|
+
```
|
|
154
|
+
Optional OpenGraph flags:
|
|
155
|
+
|
|
156
|
+
- `[--include-all]`: include broader non-default relationship output, not just default high-impact allowlist-focused edges.
|
|
157
|
+
- `[--expand-inherited]`: expand inherited IAM scope/location relationships.
|
|
158
|
+
- `[--cond-eval]`: evaluate IAM statement conditions (when resolvable) to improve edge accuracy.
|
|
159
|
+
- `[--reset]`: Wipes Opengraph database before creating JSON. Advised to always run this if you want a fresh generation each time else there might be legacy content from past runs.
|
|
160
|
+
|
|
161
|
+
### Import into BloodHound
|
|
162
|
+
|
|
163
|
+
1. Open BloodHound CE. Installation instructions can be found [here](https://bloodhound.specterops.io/get-started/quickstart/community-edition-quickstart)
|
|
164
|
+
2. Go to data import.
|
|
165
|
+
3. Upload `oracle_cloud_hound.json`.
|
|
166
|
+
4. Run path queries against high-impact OCI edges.
|
|
167
|
+
|
|
168
|
+
<p align="center" style="margin: 0.35em 0 0 0;">
|
|
169
|
+
<img src="./images/BLOODHOUND_UPLOAD.png" alt="BloodHound upload workflow for oracle_cloud_hound.json" />
|
|
170
|
+
</p>
|
|
171
|
+
<p align="center" style="margin: 0.15em 0 1em 0;"><em>Figure 3. Uploading OpenGraph JSON into BloodHound CE.</em></p>
|
|
172
|
+
|
|
173
|
+
### Sample Cypher Queries
|
|
174
|
+
|
|
175
|
+
```cypher
|
|
176
|
+
// 0) See All nodes and edges
|
|
177
|
+
MATCH (n)
|
|
178
|
+
OPTIONAL MATCH (n)-[r]-(m)
|
|
179
|
+
RETURN n, r, m
|
|
180
|
+
|
|
181
|
+
// 1) Find all users not in any group
|
|
182
|
+
MATCH (u:OCIUser)
|
|
183
|
+
WHERE NOT (u)-[:OCI_GROUP_MEMBER]->(:OCIGroup)
|
|
184
|
+
RETURN u
|
|
185
|
+
ORDER BY coalesce(u.name, u.id);
|
|
186
|
+
|
|
187
|
+
// 2a) Find standard groups with no members
|
|
188
|
+
MATCH (g:OCIGroup)
|
|
189
|
+
WHERE NOT (:OCIUser)-[:OCI_GROUP_MEMBER]->(g)
|
|
190
|
+
RETURN g
|
|
191
|
+
ORDER BY coalesce(g.name, g.id);
|
|
192
|
+
|
|
193
|
+
// 2b) Find dynamic groups with no matched members
|
|
194
|
+
MATCH (dg:OCIDynamicGroup)
|
|
195
|
+
WHERE NOT ()-[:OCI_DYNAMIC_GROUP_MEMBER]->(dg)
|
|
196
|
+
RETURN dg
|
|
197
|
+
ORDER BY coalesce(dg.name, dg.id);
|
|
198
|
+
|
|
199
|
+
// 3) Find all paths from all principals to all-resources scopes (depth 1..6)
|
|
200
|
+
MATCH (p0)
|
|
201
|
+
WHERE p0:OCIUser OR p0:OCIGroup OR p0:OCIDynamicGroup
|
|
202
|
+
MATCH p = (p0)-[*1..6]->(r:OCIAllResources)
|
|
203
|
+
RETURN p
|
|
204
|
+
LIMIT 500;
|
|
205
|
+
|
|
206
|
+
// 4) Find all paths to all-resources scopes regardless of start node type (depth 1..6)
|
|
207
|
+
MATCH p = (s)-[*1..6]->(r:OCIAllResources)
|
|
208
|
+
RETURN p
|
|
209
|
+
LIMIT 500;
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Add a custom allowlist edge (TLDR)
|
|
213
|
+
|
|
214
|
+
If you want to add your own default OpenGraph edge (for example tie `GROUP_INSPECT` to an edge), add a rule in:
|
|
215
|
+
|
|
216
|
+
- `modules/opengraph/utilities/helpers/data/static_constants.json` under `ALLOW_RULE_DEFS`
|
|
217
|
+
|
|
218
|
+
Example:
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
{
|
|
222
|
+
"id": "GROUP_INSPECT",
|
|
223
|
+
"match": {
|
|
224
|
+
"resource_tokens": ["groups"],
|
|
225
|
+
"permissions_all": ["GROUP_INSPECT"]
|
|
226
|
+
},
|
|
227
|
+
"edge": {
|
|
228
|
+
"label": "OCI_GROUP_INSPECT",
|
|
229
|
+
"description": "Inspect IAM groups in scope."
|
|
230
|
+
},
|
|
231
|
+
"destination": {
|
|
232
|
+
"token": "groups",
|
|
233
|
+
"node_type": "OCIResourceGroup",
|
|
234
|
+
"allow_specific": true
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Field quick reference:
|
|
240
|
+
|
|
241
|
+
- `id`: internal rule identifier used by the builder/tests.
|
|
242
|
+
- `match.resource_tokens`: OCI policy resource token(s) the statement must target (for example `groups`).
|
|
243
|
+
- `match.permissions_all`: permission(s) that must all be present in the same statement to trigger the edge.
|
|
244
|
+
- `edge.label`: relationship kind written into OpenGraph.
|
|
245
|
+
- `edge.description`: human-readable explanation stored on the edge.
|
|
246
|
+
- `destination.token`: logical destination scope/resource token represented in the graph.
|
|
247
|
+
- `destination.node_type`: node class to emit for the destination (commonly `OCIResourceGroup`).
|
|
248
|
+
- `destination.allow_specific`: when `true`, conditionals can resolve to specific resources (for example a specific group) instead of only generic scope nodes.
|
|
249
|
+
|
|
250
|
+
Then rerun `enum_oracle_cloud_hound_data` and update tests/golden outputs if behavior changed.
|
|
251
|
+
|
|
252
|
+
## Dependency Inventory
|
|
253
|
+
|
|
254
|
+
| Dependency | Where Used | Purpose |
|
|
255
|
+
| --- | --- | --- |
|
|
256
|
+
| `oci==2.169.0` | Core modules | OCI SDK clients/auth/providers for enumeration and actions. |
|
|
257
|
+
| `requests==2.33.1` | HTTP helpers/integrations | HTTP operations and API helper requests. |
|
|
258
|
+
| `PyYAML>=6.0.3` | Config/parsing layers | YAML config and mapping parsing. |
|
|
259
|
+
| `prettytable==3.17.0` | CLI output | Terminal table rendering. |
|
|
260
|
+
| `oci-lexer-parser==0.1.2` | Policy/OpenGraph logic | OCI IAM policy lexing/parsing support. |
|
|
261
|
+
| `pandas>=2.2.0` | Data export | Excel export pipeline. |
|
|
262
|
+
| `xlsxwriter>=3.2.0` | Data export | `.xlsx` writer engine for exports. |
|
|
263
|
+
| `pytest>=8.0`* | Unit tests | Test framework (`tests/unit`). |
|
|
264
|
+
|
|
265
|
+
*Dev/test-scoped dependency.
|
|
266
|
+
|
|
267
|
+
## Repository Layout
|
|
268
|
+
|
|
269
|
+
- `cli/`: interactive command processor and startup flow.
|
|
270
|
+
- `core/`: session, config, data, utility, and logging controllers.
|
|
271
|
+
- `modules/`: service modules plus `Everything` (`enum_all`, `enum_config_check`).
|
|
272
|
+
- `utils/`: shared module utilities and exports.
|
|
273
|
+
- `tests/`: unit tests run in CI.
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# OCInferno
|
|
2
|
+
|
|
3
|
+
[](https://github.com/NetSPI/OCInferno/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/ocinferno/)
|
|
5
|
+
[](https://www.python.org/)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
[](./CONTRIBUTING.md)
|
|
8
|
+
[](https://pypi.org/project/oci/)
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
> In the spirit of transparency: parts of this project and documentation were developed with LLM coding assistance. Review code and behavior in your environment before operational use. Notably, the enum_config_checks module is still in-progress based off original content generated.
|
|
13
|
+
|
|
14
|
+
OCInferno (O-C-Inferno) is an OCI offensive security assessment framework for workspace-driven credential handling, service enumeration, artifact download, and graph-based attack-path analysis. It includes a module to generate a custom **OpenGraph output** which can be fed into BloodHound, as shown below, to map privilege-escalation paths.
|
|
15
|
+
|
|
16
|
+
<p align="center" style="margin: 0.35em 0 0 0;">
|
|
17
|
+
<img src="./images/README_OVERVIEW.png" alt="Sample OpenGraph output in BloodHound" />
|
|
18
|
+
</p>
|
|
19
|
+
<p align="center" style="margin: 0.15em 0 1em 0;"><em>Figure 1. Example OpenGraph/BloodHound relationship view.</em></p>
|
|
20
|
+
|
|
21
|
+
<p align="center" style="margin: 0.35em 0 0 0;">
|
|
22
|
+
<img src="./images/MODULE_RUN.png" alt="OCInferno terminal output example" />
|
|
23
|
+
</p>
|
|
24
|
+
<p align="center" style="margin: 0.15em 0 1em 0;"><em>Figure 2. Example module output in the CLI.</em></p>
|
|
25
|
+
|
|
26
|
+
## High-Level Features
|
|
27
|
+
|
|
28
|
+
- **CLI UX:** Interactive CLI with command and argument tab auto-complete and history.
|
|
29
|
+
- **Authentication:** Multiple supported auth methods:
|
|
30
|
+
- **Config Profile:** API key-backed and session-token-backed OCI profiles.
|
|
31
|
+
- **Instance Principal:** Compute-instance identity flow.
|
|
32
|
+
- **Resource Principal:** Runtime/workload identity flow.
|
|
33
|
+
- **Module Model:** Service-specific modules across OCI services, with proxy and rate-limiting support.
|
|
34
|
+
- **Mass Enumeration:** Broad OCI module coverage with `enum_all` orchestration support (implemented in `modules/everything`).
|
|
35
|
+
- **Config Audits:** `enum_config_check` findings based on enumerated/saved data (implemented in `modules/everything`).
|
|
36
|
+
- **Reporting Exports:** Resource export support for HTML, CSV, JSON, Excel, and graph image outputs.
|
|
37
|
+
- **Artifact Downloads:** Download support across many modules with `--download` and selective routing.
|
|
38
|
+
- **OpenGraph / BloodHound:** OpenGraph export for BloodHound ingestion, including:
|
|
39
|
+
- A default focused view of high-impact edges, with `--include-all` available for broader relationship output to ideally see all relationships regardless of priv escalation weight.
|
|
40
|
+
- Privilege-escalation path modeling across OCI IAM and Identity Domain app-role/grant relationships.
|
|
41
|
+
- Inheritance-aware modeling (`--expand-inherited`) and conditional evaluation (`--cond-eval`) to improve graph accuracy.
|
|
42
|
+
|
|
43
|
+
## Documentation
|
|
44
|
+
|
|
45
|
+
Documentation is maintained on the GitHub Wiki:
|
|
46
|
+
|
|
47
|
+
- https://github.com/NetSPI/OCInferno/wiki
|
|
48
|
+
|
|
49
|
+
Contributing guidance:
|
|
50
|
+
|
|
51
|
+
- `CONTRIBUTING.md`
|
|
52
|
+
|
|
53
|
+
Roadmap guidance:
|
|
54
|
+
|
|
55
|
+
- `ROADMAP.md`
|
|
56
|
+
|
|
57
|
+
Sample OpenGraph JSON:
|
|
58
|
+
|
|
59
|
+
- `opengraph_examples/example_input.json`
|
|
60
|
+
|
|
61
|
+
## Installation TLDR
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
git clone https://github.com/NetSPI/OCInferno.git
|
|
65
|
+
|
|
66
|
+
# You don't need pytests to run the tool
|
|
67
|
+
rm -r tests/
|
|
68
|
+
|
|
69
|
+
virtualenv .venv
|
|
70
|
+
source .venv/bin/activate
|
|
71
|
+
pip install -r requirements.txt
|
|
72
|
+
python -m cli.main
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Launch and Data Download TLDR
|
|
76
|
+
|
|
77
|
+
Run the tool:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
python -m cli.main
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
At startup:
|
|
84
|
+
|
|
85
|
+
1. Create/select a workspace.
|
|
86
|
+
2. Add credentials using your OCI config profile (example uses `MY_PROFILE` to add an API key):
|
|
87
|
+
```text
|
|
88
|
+
profile MY_PROFILE --filepath ~/.oci/config --profile MY_PROFILE
|
|
89
|
+
```
|
|
90
|
+
3. Start the first full run.
|
|
91
|
+
`enum_all` runs all modules. `--comp` recursively enumerates compartments to maximize coverage.
|
|
92
|
+
`--get` follows LIST calls with GET calls where supported for deeper detail.
|
|
93
|
+
`--download` downloads data where possible, choosing `--not-downloads buckets` attempts to download all content **except** bucket object content.
|
|
94
|
+
```bash
|
|
95
|
+
# Download as much as you can
|
|
96
|
+
modules run enum_all --get --comp --download
|
|
97
|
+
# Download everything EXCEPT bucket contents
|
|
98
|
+
modules run enum_all --get --comp [--not-downloads buckets]
|
|
99
|
+
```
|
|
100
|
+
4. Export a compartment tree image and Excel data output to quickly review what your current permissions can see:
|
|
101
|
+
```bash
|
|
102
|
+
(TENANT-2snxfsaa:TEST)> data export treeimage
|
|
103
|
+
[*] Compartment tree export complete -> ./ocinferno_output/1_TEST/exports/data/global/resource_reports/compartment_tree.svg (format=svg, renderer=svg-interactive, compartments=6)
|
|
104
|
+
|
|
105
|
+
(TENANT-2snxfsaa:TEST)> data export excel
|
|
106
|
+
[*] Excel export complete -> ./ocinferno_output/1_TEST/exports/data/global/sqlite_excel/sqlite_blob.xlsx (format=xlsx, databases=1, tables=173, rows=54951, single_sheet=True, condensed=True)
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
<p align="center" style="margin: 0.35em 0 0 0;">
|
|
111
|
+
<img src="./images/COMP_HIERARCHY_1.png" alt="Compartment Tree Export" />
|
|
112
|
+
</p>
|
|
113
|
+
<p align="center" style="margin: 0.15em 0 1em 0;"><em>Figure 1. Tree image export from <code>data export treeimage</code>.</em></p>
|
|
114
|
+
|
|
115
|
+
<p align="center" style="margin: 0.35em 0 0 0;">
|
|
116
|
+
<img src="./images/DATA_EXPORT_1.png" alt="Excel Data Export" />
|
|
117
|
+
</p>
|
|
118
|
+
<p align="center" style="margin: 0.15em 0 1em 0;"><em>Figure 2. Excel export output from <code>data export excel</code>.</em></p>
|
|
119
|
+
|
|
120
|
+
## OpenGraph TLDR
|
|
121
|
+
|
|
122
|
+
### Where JSON is saved
|
|
123
|
+
|
|
124
|
+
Once all data is collected, use the `enum_oracle_cloud_hound_data` module as seen in example below:
|
|
125
|
+
```bash
|
|
126
|
+
modules run enum_oracle_cloud_hound_data [--include-all] [--expand-inherited] [--cond-eval] --reset --out opengraph_output.json
|
|
127
|
+
```
|
|
128
|
+
Optional OpenGraph flags:
|
|
129
|
+
|
|
130
|
+
- `[--include-all]`: include broader non-default relationship output, not just default high-impact allowlist-focused edges.
|
|
131
|
+
- `[--expand-inherited]`: expand inherited IAM scope/location relationships.
|
|
132
|
+
- `[--cond-eval]`: evaluate IAM statement conditions (when resolvable) to improve edge accuracy.
|
|
133
|
+
- `[--reset]`: Wipes Opengraph database before creating JSON. Advised to always run this if you want a fresh generation each time else there might be legacy content from past runs.
|
|
134
|
+
|
|
135
|
+
### Import into BloodHound
|
|
136
|
+
|
|
137
|
+
1. Open BloodHound CE. Installation instructions can be found [here](https://bloodhound.specterops.io/get-started/quickstart/community-edition-quickstart)
|
|
138
|
+
2. Go to data import.
|
|
139
|
+
3. Upload `oracle_cloud_hound.json`.
|
|
140
|
+
4. Run path queries against high-impact OCI edges.
|
|
141
|
+
|
|
142
|
+
<p align="center" style="margin: 0.35em 0 0 0;">
|
|
143
|
+
<img src="./images/BLOODHOUND_UPLOAD.png" alt="BloodHound upload workflow for oracle_cloud_hound.json" />
|
|
144
|
+
</p>
|
|
145
|
+
<p align="center" style="margin: 0.15em 0 1em 0;"><em>Figure 3. Uploading OpenGraph JSON into BloodHound CE.</em></p>
|
|
146
|
+
|
|
147
|
+
### Sample Cypher Queries
|
|
148
|
+
|
|
149
|
+
```cypher
|
|
150
|
+
// 0) See All nodes and edges
|
|
151
|
+
MATCH (n)
|
|
152
|
+
OPTIONAL MATCH (n)-[r]-(m)
|
|
153
|
+
RETURN n, r, m
|
|
154
|
+
|
|
155
|
+
// 1) Find all users not in any group
|
|
156
|
+
MATCH (u:OCIUser)
|
|
157
|
+
WHERE NOT (u)-[:OCI_GROUP_MEMBER]->(:OCIGroup)
|
|
158
|
+
RETURN u
|
|
159
|
+
ORDER BY coalesce(u.name, u.id);
|
|
160
|
+
|
|
161
|
+
// 2a) Find standard groups with no members
|
|
162
|
+
MATCH (g:OCIGroup)
|
|
163
|
+
WHERE NOT (:OCIUser)-[:OCI_GROUP_MEMBER]->(g)
|
|
164
|
+
RETURN g
|
|
165
|
+
ORDER BY coalesce(g.name, g.id);
|
|
166
|
+
|
|
167
|
+
// 2b) Find dynamic groups with no matched members
|
|
168
|
+
MATCH (dg:OCIDynamicGroup)
|
|
169
|
+
WHERE NOT ()-[:OCI_DYNAMIC_GROUP_MEMBER]->(dg)
|
|
170
|
+
RETURN dg
|
|
171
|
+
ORDER BY coalesce(dg.name, dg.id);
|
|
172
|
+
|
|
173
|
+
// 3) Find all paths from all principals to all-resources scopes (depth 1..6)
|
|
174
|
+
MATCH (p0)
|
|
175
|
+
WHERE p0:OCIUser OR p0:OCIGroup OR p0:OCIDynamicGroup
|
|
176
|
+
MATCH p = (p0)-[*1..6]->(r:OCIAllResources)
|
|
177
|
+
RETURN p
|
|
178
|
+
LIMIT 500;
|
|
179
|
+
|
|
180
|
+
// 4) Find all paths to all-resources scopes regardless of start node type (depth 1..6)
|
|
181
|
+
MATCH p = (s)-[*1..6]->(r:OCIAllResources)
|
|
182
|
+
RETURN p
|
|
183
|
+
LIMIT 500;
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Add a custom allowlist edge (TLDR)
|
|
187
|
+
|
|
188
|
+
If you want to add your own default OpenGraph edge (for example tie `GROUP_INSPECT` to an edge), add a rule in:
|
|
189
|
+
|
|
190
|
+
- `modules/opengraph/utilities/helpers/data/static_constants.json` under `ALLOW_RULE_DEFS`
|
|
191
|
+
|
|
192
|
+
Example:
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"id": "GROUP_INSPECT",
|
|
197
|
+
"match": {
|
|
198
|
+
"resource_tokens": ["groups"],
|
|
199
|
+
"permissions_all": ["GROUP_INSPECT"]
|
|
200
|
+
},
|
|
201
|
+
"edge": {
|
|
202
|
+
"label": "OCI_GROUP_INSPECT",
|
|
203
|
+
"description": "Inspect IAM groups in scope."
|
|
204
|
+
},
|
|
205
|
+
"destination": {
|
|
206
|
+
"token": "groups",
|
|
207
|
+
"node_type": "OCIResourceGroup",
|
|
208
|
+
"allow_specific": true
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Field quick reference:
|
|
214
|
+
|
|
215
|
+
- `id`: internal rule identifier used by the builder/tests.
|
|
216
|
+
- `match.resource_tokens`: OCI policy resource token(s) the statement must target (for example `groups`).
|
|
217
|
+
- `match.permissions_all`: permission(s) that must all be present in the same statement to trigger the edge.
|
|
218
|
+
- `edge.label`: relationship kind written into OpenGraph.
|
|
219
|
+
- `edge.description`: human-readable explanation stored on the edge.
|
|
220
|
+
- `destination.token`: logical destination scope/resource token represented in the graph.
|
|
221
|
+
- `destination.node_type`: node class to emit for the destination (commonly `OCIResourceGroup`).
|
|
222
|
+
- `destination.allow_specific`: when `true`, conditionals can resolve to specific resources (for example a specific group) instead of only generic scope nodes.
|
|
223
|
+
|
|
224
|
+
Then rerun `enum_oracle_cloud_hound_data` and update tests/golden outputs if behavior changed.
|
|
225
|
+
|
|
226
|
+
## Dependency Inventory
|
|
227
|
+
|
|
228
|
+
| Dependency | Where Used | Purpose |
|
|
229
|
+
| --- | --- | --- |
|
|
230
|
+
| `oci==2.169.0` | Core modules | OCI SDK clients/auth/providers for enumeration and actions. |
|
|
231
|
+
| `requests==2.33.1` | HTTP helpers/integrations | HTTP operations and API helper requests. |
|
|
232
|
+
| `PyYAML>=6.0.3` | Config/parsing layers | YAML config and mapping parsing. |
|
|
233
|
+
| `prettytable==3.17.0` | CLI output | Terminal table rendering. |
|
|
234
|
+
| `oci-lexer-parser==0.1.2` | Policy/OpenGraph logic | OCI IAM policy lexing/parsing support. |
|
|
235
|
+
| `pandas>=2.2.0` | Data export | Excel export pipeline. |
|
|
236
|
+
| `xlsxwriter>=3.2.0` | Data export | `.xlsx` writer engine for exports. |
|
|
237
|
+
| `pytest>=8.0`* | Unit tests | Test framework (`tests/unit`). |
|
|
238
|
+
|
|
239
|
+
*Dev/test-scoped dependency.
|
|
240
|
+
|
|
241
|
+
## Repository Layout
|
|
242
|
+
|
|
243
|
+
- `cli/`: interactive command processor and startup flow.
|
|
244
|
+
- `core/`: session, config, data, utility, and logging controllers.
|
|
245
|
+
- `modules/`: service modules plus `Everything` (`enum_all`, `enum_config_check`).
|
|
246
|
+
- `utils/`: shared module utilities and exports.
|
|
247
|
+
- `tests/`: unit tests run in CI.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from typing import List, Tuple, Optional
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from cli.workspace_instructions import workspace_instructions
|
|
6
|
+
from core.db import DataController
|
|
7
|
+
from core.console import UtilityTools
|
|
8
|
+
|
|
9
|
+
def create_workspace(dc: DataController, workspace_name: str) -> Optional[int]:
|
|
10
|
+
workspace_name = (workspace_name or "").strip()
|
|
11
|
+
if workspace_name.isdigit():
|
|
12
|
+
print(
|
|
13
|
+
f"{UtilityTools.RED}{UtilityTools.BOLD}[X] Workspace name cannot be numeric-only."
|
|
14
|
+
f" Use a descriptive name (for example: TEST, PROD, LAB).{UtilityTools.RESET}"
|
|
15
|
+
)
|
|
16
|
+
return None
|
|
17
|
+
|
|
18
|
+
existing_names = dc.fetch_all_workspace_names()
|
|
19
|
+
if workspace_name in existing_names:
|
|
20
|
+
print(f"{UtilityTools.RED}{UtilityTools.BOLD}[X] A workspace with that name already exists.{UtilityTools.RESET}")
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
starting_config_data = {
|
|
24
|
+
"proxy": None,
|
|
25
|
+
"current_default_region": "",
|
|
26
|
+
"module_auto_save": True,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
starting_config_data_json_blob = json.dumps(starting_config_data)
|
|
30
|
+
|
|
31
|
+
workspace_id = dc.insert_workspace(workspace_name, starting_config_data_json_blob)
|
|
32
|
+
if workspace_id:
|
|
33
|
+
print(f"{UtilityTools.GREEN}{UtilityTools.BOLD}[*] Workspace '{workspace_name}' created.{UtilityTools.RESET}")
|
|
34
|
+
return workspace_id
|
|
35
|
+
|
|
36
|
+
print(f"{UtilityTools.RED}{UtilityTools.BOLD}[X] Failed to create workspace.{UtilityTools.RESET}")
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
def prompt_new_workspace(dc: DataController) -> Tuple[str, int]:
|
|
40
|
+
while True:
|
|
41
|
+
name = input("> New workspace name: ").strip()
|
|
42
|
+
if 1 <= len(name) <= 80:
|
|
43
|
+
workspace_id = create_workspace(dc, name)
|
|
44
|
+
if workspace_id:
|
|
45
|
+
return name, workspace_id
|
|
46
|
+
else:
|
|
47
|
+
print(f"{UtilityTools.RED}{UtilityTools.BOLD}[X] Name must be between 1 and 80 characters.{UtilityTools.RESET}")
|
|
48
|
+
|
|
49
|
+
def list_workspaces(workspaces: List[Tuple[int, str]]) -> None:
|
|
50
|
+
print("[*] Found existing sessions:")
|
|
51
|
+
print(" [0] Create new workspace")
|
|
52
|
+
for idx, name in workspaces:
|
|
53
|
+
print(f" [{idx}] {name}")
|
|
54
|
+
print(f" [{len(workspaces)+1}] Exit")
|
|
55
|
+
|
|
56
|
+
def choose_workspace(
|
|
57
|
+
workspaces: List[Tuple[int, str]],
|
|
58
|
+
dc: DataController,
|
|
59
|
+
startup_auth_proxy: Optional[str] = None,
|
|
60
|
+
startup_silent: bool = False,
|
|
61
|
+
) -> None:
|
|
62
|
+
workspace_map = {idx: name for idx, name in workspaces}
|
|
63
|
+
|
|
64
|
+
while True:
|
|
65
|
+
try:
|
|
66
|
+
choice = int(input("Choose an option: ").strip())
|
|
67
|
+
break
|
|
68
|
+
except ValueError:
|
|
69
|
+
print("Please enter a valid number.")
|
|
70
|
+
|
|
71
|
+
if choice == 0:
|
|
72
|
+
name, workspace_id = prompt_new_workspace(dc)
|
|
73
|
+
workspace_instructions(
|
|
74
|
+
workspace_id,
|
|
75
|
+
name,
|
|
76
|
+
startup_auth_proxy=startup_auth_proxy,
|
|
77
|
+
startup_silent=startup_silent,
|
|
78
|
+
)
|
|
79
|
+
elif choice == len(workspaces) + 1:
|
|
80
|
+
exit()
|
|
81
|
+
elif choice in workspace_map:
|
|
82
|
+
workspace_instructions(
|
|
83
|
+
choice,
|
|
84
|
+
workspace_map[choice],
|
|
85
|
+
startup_auth_proxy=startup_auth_proxy,
|
|
86
|
+
startup_silent=startup_silent,
|
|
87
|
+
)
|
|
88
|
+
else:
|
|
89
|
+
print(f"{UtilityTools.RED}{UtilityTools.BOLD}[X] Invalid workspace selected. Quitting...{UtilityTools.RESET}")
|
|
90
|
+
exit()
|
|
91
|
+
|
|
92
|
+
def main() -> None:
|
|
93
|
+
parser = argparse.ArgumentParser(add_help=True)
|
|
94
|
+
parser.add_argument(
|
|
95
|
+
"--auth-proxy",
|
|
96
|
+
dest="auth_proxy",
|
|
97
|
+
default=None,
|
|
98
|
+
help=(
|
|
99
|
+
"Startup-only proxy for credential auth exchanges during add/load at launch "
|
|
100
|
+
"(does not apply to module API traffic, set that in configs or per proxy). "
|
|
101
|
+
"Format: host:port or http(s)://host:port."
|
|
102
|
+
),
|
|
103
|
+
)
|
|
104
|
+
parser.add_argument(
|
|
105
|
+
"--silent",
|
|
106
|
+
action="store_true",
|
|
107
|
+
help="Start OCInferno without printing the initial help banner.",
|
|
108
|
+
)
|
|
109
|
+
args = parser.parse_args()
|
|
110
|
+
|
|
111
|
+
dc = DataController()
|
|
112
|
+
workspaces = dc.get_workspaces()
|
|
113
|
+
|
|
114
|
+
# If we have no existing workspaces prompt for a new one
|
|
115
|
+
if not workspaces:
|
|
116
|
+
print("[*] No workspaces detected. Please create your first workspace.")
|
|
117
|
+
name, workspace_id = prompt_new_workspace(dc)
|
|
118
|
+
workspace_instructions(
|
|
119
|
+
workspace_id,
|
|
120
|
+
name,
|
|
121
|
+
startup_auth_proxy=args.auth_proxy,
|
|
122
|
+
startup_silent=args.silent,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# If workspaces exist presetn options and have user choose one
|
|
126
|
+
else:
|
|
127
|
+
list_workspaces(workspaces)
|
|
128
|
+
choose_workspace(
|
|
129
|
+
workspaces,
|
|
130
|
+
dc,
|
|
131
|
+
startup_auth_proxy=args.auth_proxy,
|
|
132
|
+
startup_silent=args.silent,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
if __name__ == "__main__":
|
|
136
|
+
try:
|
|
137
|
+
main()
|
|
138
|
+
except KeyboardInterrupt:
|
|
139
|
+
print("\n[*] Interrupted. Exiting.")
|
|
140
|
+
raise SystemExit(130)
|