Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ AD_Miner/sources/modules/temporary*
*node_modules*
/tests
/test

evolution_data/

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down Expand Up @@ -170,6 +170,3 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# VS Code
.vscode/
11 changes: 11 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM python:3.11-slim

# Set the working directory in the container
WORKDIR /tmp

# Install necessary system dependencies for Git
RUN apt-get update && apt-get install -y git \
&& rm -rf /var/lib/apt/lists/*

# Install AD-Miner from the Git repository
RUN pip install --no-cache-dir 'git+https://github.com/AD-Security/AD_Miner.git'
28 changes: 25 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ To run AD Miner, you first need a neo4j database which contains the Active Direc

The easier way is to do the following command using `pipx`:
```shell
pipx install 'git+https://github.com/Mazars-Tech/AD_Miner.git'
pipx install 'git+https://github.com/AD-Security/AD_Miner.git'
```

ADMiner is also available on some Linux distributions:
Expand All @@ -92,6 +92,26 @@ ADMiner is also available on some Linux distributions:
- BlackArch: `pacman -S ad-miner`
- NixOS: `nix-env -iA nixos.ad-miner`

A Docker image is available to build. Build the image with the following commmand:

```sh
docker build -t ad-miner .
```

To run this on Windows with the BloodHound Community Edition data, use the commands below:

```sh
docker run -v ${PWD}:/tmp ad-miner AD-miner -b bolt://host.docker.internal:7687 -u neo4j -p mypassword -cf YOUR_PREFIX
```

To run this on Linux with the BloodHound Community Edition data, use the commands below:

```sh
docker run -v ${PWD}:/tmp --network host ad-miner AD-miner -b bolt://localhost:7687 -u neo4j -p mypassword -cf YOUR_PREFIX
```

Note that mounting the volume with `-v` is critical to get the output of the data. This assumes that the BHCE server is running on the Docker host with default settings.

## Usage ##

Run the tool:
Expand Down Expand Up @@ -154,13 +174,15 @@ In the graph pages, you can right-click on the graph nodes to cluster them or to

If you have multiple AD-Miner reports over time, you can easily track the evolution with the `--evolution` argument: each AD-Miner report generates a JSON data file alongside the `index.html` file. You just need to gather these different JSON files into a single folder and specify the path to that folder after the `--evolution` argument.

A tab called 'Evolution over time' then appears on the main page.
AD-miner -c -cf My_Report -b bolt://server:7687 -u neo4j -p mypassword -r 180 --evolution evolution_folder/

An 'Evolution over time' tab appears on the main page, providing evolution graphs for each category (Permissions, Passwords, Kerberos, and Misc).

<p align="center">
<img src="doc/img/evolution2.png" style="height:400px">
</p>

Also, views by categories 'permissions,' 'passwords,' 'kerberos' also allow you to track changes over time.
Detailed evolution for each control is also available and can be accessed via the “Show evolution” button for each category. A logarithmic scale is available to better highlight subtle variations over time.

<p align="center">
<img src="doc/img/evol.gif" style="height:200px">
Expand Down
51 changes: 51 additions & 0 deletions ad_miner/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
import traceback
import signal
import sys
import subprocess
import requests
from importlib.metadata import version, PackageNotFoundError

# Local library imports
from ad_miner.sources.modules import logger, utils, generic_formating, main_page
Expand Down Expand Up @@ -142,6 +145,48 @@ def prepare_render(arguments) -> None:
shutil.copy2(js_file, folder_name / "js")


def get_version_and_commit():
# Either using pip(x) you get the version number or in dev environment the git commit
try:
ver = version("ad-miner")
except PackageNotFoundError:
ver = "unknown"

commit = ""
try:
root = Path(__file__).resolve().parent
commit = subprocess.check_output(
["git", "rev-parse", "--short", "HEAD"], cwd=root, stderr=subprocess.DEVNULL, text=True
).strip()
except Exception:
commit = "unknown"
return ver, commit


def get_last_version():
try:
r = requests.get("https://www.ad-miner.com/version.json", timeout=0.5)
data = r.json()
return data["lastversion"]
except Exception:
return "unreachable"


def check_version(lastversion, currentversion):
try:
last = tuple(map(int, lastversion.lstrip("v").split(".")))
current = tuple(map(int, currentversion.lstrip("v").split(".")))

if last > current:
logger.print_error(f"New AD Miner version {lastversion} available.")
logger.print_error(
"Update with pipx or manually on https://github.com/AD-Security/AD_Miner"
)
return
except Exception:
return


def main() -> None:
"""Main execution function for the script."""
start = time.time()
Expand All @@ -163,6 +208,12 @@ def main() -> None:

prepare_render(arguments)

AD_miner_version, AD_miner_commit = get_version_and_commit()
arguments.version = AD_miner_version
arguments.commit = AD_miner_commit

check_version(get_last_version(), AD_miner_version)

neo4j_version, extract_date, total_objects, number_relations, boolean_azure = pre_request(
arguments
)
Expand Down
5 changes: 5 additions & 0 deletions ad_miner/sources/html/bootstrap/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -389,4 +389,9 @@ a:hover{
.percentage-evolution {
font-size: small;
font-weight: 800;
}

.ag-cell-value i.bi {
margin-right: 4px;
vertical-align: middle;
}
17 changes: 15 additions & 2 deletions ad_miner/sources/html/components/grid/grid_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,18 @@
rowData2.push(new_dico);
}
}
else if (window.location.href.includes('computers_with_administrators_details')) {
if (rowData[i]['Computer'] == parameter) {
var new_dico = {};
new_dico['Users'] = rowData[i]['Users'];
new_dico['Distinguished Name'] = rowData[i]['Distinguished Name'];
rowData2.push(new_dico);
}
}
else if (window.location.href.includes('users_rdp_access') || window.location.href.includes('computers_list_of_rdp_users')) {

if (rowData[i][keys[0]].includes(parameter)) {
const startSubstring = "<p style='visibility:hidden;'>";
const startSubstring = "<p style='display: none;'>";
const endSubstring = "</p>";

const startIndex = rowData[i][keys[1]].indexOf(startSubstring) + startSubstring.length;
Expand Down Expand Up @@ -130,7 +138,12 @@
}
}
}
columnDefs2 = [{field: parameter}];
if (window.location.href.includes('computers_with_administrators_details')) {
columnDefs2 = [{field: 'Users'},{field: 'Distinguished Name'}]
}
else {
columnDefs2 = [{field: parameter}];
}
return [rowData2, columnDefs2];
}

Expand Down
4 changes: 2 additions & 2 deletions ad_miner/sources/html/templates/main_header.html
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ <h6 class="card-subtitle mb-2 text-muted">
<div class="card main-card-category shadow mb-3" id="global-rating-azure" style="display:none">
<div class="card-body">
<h5 class="card-title">
Azure Risk rating
Entra ID Risk rating
<i class="bi bi-info-circle" data-bs-toggle="tooltip"
data-bs-title="This rating is an overall evaluation of the security level of your Active Directory based on the estimated risk of each indicator of exposure"></i>
</h5>
Expand Down Expand Up @@ -931,7 +931,7 @@ <h5 class="card-title chart-details-title">
<div class="col-graph-details">
<div>
<canvas id="chart-details-2-azure" class="chart-details"></canvas>
<h5 class="card-title chart-details-title">Azure AD Connect</h5>
<h5 class="card-title chart-details-title">Microsoft Entra Connect</h5>
</div>
<div>{{azure|ad_connect_graph_summary}}</div>
</div>
Expand Down
28 changes: 19 additions & 9 deletions ad_miner/sources/js/graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,18 +386,28 @@ function getAllEndNodes() {
}
return ends;
}
function reachableNodesFrom(selectedNode, visited = new Set()) {
// Get all reachable nodes from on start node
if (visited.has(selectedNode)) return [];
visited.add(selectedNode);

function display_only_path_from_node(node_parameter) {
// Get all nodes leading to all ends from a specific node
ends = getAllEndNodes();

allPaths = [];
if (dico_nodes[selectedNode].group.includes('end')) {
return [selectedNode];
}

for (var i = 0; i < ends.length; i++) {
allPaths = allPaths.concat(
shortestpathToChosenEnd(node_parameter, ends[i]),
);
var reachableNodes = [selectedNode];
var connectedNodes = dico_edges_children[selectedNode];

for (let i = 0; i < connectedNodes.length; i++) {
reachableNodes = reachableNodes.concat(reachableNodesFrom(connectedNodes[i], visited))
}
return reachableNodes;
}

function display_only_path_from_node(node_parameter) {
// Get all nodes leading from a specific node

allPaths = reachableNodesFrom(node_parameter);

selected_nodes_id = [...new Set(allPaths)];

Expand Down
Loading