mirror of
https://github.com/dmcxblue/ANIMO
synced 2026-08-04 16:10:26 +00:00
Pre-release cleanup: cross-platform installers, remove env leaks
- Install-AllModules.ps1: rewrite as additive + cross-platform. No longer force-uninstalls existing Azure modules or clones external repos into the repo. Windows-only modules (AzureAD, winget tools) guarded by platform. - install-dependencies.sh: drop unused qt6-websockets-dev, add libqt6svg6, replace snap-only pwsh install with a .deb-first fallback, stop wiping the apt cache. - Remove hardcoded operator environment: absolute home path in WHfBAttackWindow, LAN IPs + password in gdb-attach-server.sh, and LAN IPs baked into GrabTokenAzureAD/webhook helper defaults (now loopback/placeholder). - .gitignore: exclude loot/ and repo-root runtime state. - README: Windows build steps, platform column on module table, Related Tooling list for the previously auto-cloned repos.
This commit is contained in:
@@ -33,6 +33,14 @@ error.log.*
|
||||
*.animosession
|
||||
*.animosessions
|
||||
|
||||
# Engagement output - never commit captured data
|
||||
loot/
|
||||
|
||||
# Runtime state that lands at the repo root depending on the launch dir
|
||||
saved_sessions.dat
|
||||
device_certs.dat
|
||||
history_*.txt
|
||||
|
||||
# IDE / Editor
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
+223
-230
@@ -1,230 +1,223 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Write-Host "`n--- Azure Tool Setup ---`n"
|
||||
|
||||
function Remove-AllAzureModules {
|
||||
Write-Host "`n--- Starting removal of all Azure-related modules ---`n"
|
||||
|
||||
Write-Host "Removing AzureRM modules..."
|
||||
Get-Module -ListAvailable AzureRM* | ForEach-Object {
|
||||
Write-Host "Removing: $($_.Name)"
|
||||
Uninstall-Module -Name $_.Name -AllVersions -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-Host "`nRemoving Az modules..."
|
||||
Get-Module -ListAvailable Az* | ForEach-Object {
|
||||
Write-Host "Removing: $($_.Name)"
|
||||
Uninstall-Module -Name $_.Name -AllVersions -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-Host "`nRemoving any modules starting with 'Azure'..."
|
||||
Get-Module -ListAvailable Azure* | ForEach-Object {
|
||||
Write-Host "Removing: $($_.Name)"
|
||||
Uninstall-Module -Name $_.Name -AllVersions -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-Host "`nRemoving AzureAD modules..."
|
||||
Get-Module -ListAvailable AzureAD | ForEach-Object {
|
||||
Write-Host "Removing: AzureAD"
|
||||
Uninstall-Module -Name AzureAD -AllVersions -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-Host "`nRemoving Microsoft Graph modules..."
|
||||
Get-Module -ListAvailable Microsoft.Graph | ForEach-Object {
|
||||
Write-Host "Removing: $($_.Name)"
|
||||
Uninstall-Module -Name $_.Name -AllVersions -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-Host "`nRemoving AADInternals modules..."
|
||||
Get-Module -ListAvailable AADInternals | ForEach-Object {
|
||||
Write-Host "Removing: $($_.Name)"
|
||||
Uninstall-Module -Name $_.Name -AllVersions -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-Host "`n--- All specified Azure modules have been removed ---`n"
|
||||
}
|
||||
|
||||
Remove-AllAzureModules
|
||||
|
||||
Start-Sleep -Seconds 2
|
||||
|
||||
# Verify module removals
|
||||
Write-Host "`n--- Verifying module removals ---`n"
|
||||
$modulesToCheck = @("AzureRM", "Az", "AzureAD", "AzureADPreview", "Microsoft.Graph")
|
||||
foreach ($mod in $modulesToCheck) {
|
||||
if (Get-Module -ListAvailable $mod) {
|
||||
Write-Warning "$mod modules still exist."
|
||||
} else {
|
||||
Write-Host "$mod modules removed."
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "`n--- Reinstalling stable Azure modules ---`n"
|
||||
# Install stable Az and AzureAD modules
|
||||
Install-Module -Name Az -Scope CurrentUser -Force
|
||||
Install-Module -Name AzureAD -Scope CurrentUser -Force
|
||||
|
||||
# AADInternals
|
||||
|
||||
# Install the module
|
||||
Install-Module -Name "AADInternals" -Scope CurrentUser -Force
|
||||
Install-Module -Name "AADInternals-Endpoints" -Scope CurrentUser -Force
|
||||
|
||||
# Import modules
|
||||
Import-Module -Name "AADInternals"
|
||||
Import-Module -Name "AADInternals-Endpoints"
|
||||
|
||||
# SQL
|
||||
|
||||
Install-Module -Name SqlServer -Scope CurrentUser -Force
|
||||
Import-Module -Name SqlServer
|
||||
|
||||
# AzTable I needed it for PWNDLabs
|
||||
|
||||
Install-Module AzTable -Scope CurrentUser -Force
|
||||
|
||||
# Microsoft Graph
|
||||
|
||||
Install-Module -Name Microsoft.Graph -Scope CurrentUser -Force
|
||||
|
||||
# New function: Ensure additional Azure sub-modules (including Microsoft Graph) are installed.
|
||||
function Ensure-AzureModules {
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string[]]$ModuleNames
|
||||
)
|
||||
foreach ($mod in $ModuleNames) {
|
||||
if (-not (Get-Module -ListAvailable $mod)) {
|
||||
Write-Host "$mod not found. Installing..."
|
||||
Install-Module -Name $mod -Scope CurrentUser -Force -ErrorAction Stop
|
||||
Write-Host "$mod installed successfully."
|
||||
} else {
|
||||
Write-Host "$mod is already installed."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# List of additional Azure sub-modules to ensure are installed (Microsoft.Graph included)
|
||||
$additionalModules = @(
|
||||
"Az.Accounts",
|
||||
"Az.Compute",
|
||||
"Az.Network",
|
||||
"Az.Resources",
|
||||
"Az.Storage",
|
||||
"Az.KeyVault",
|
||||
"Az.Monitor",
|
||||
"Az.Security",
|
||||
"Az.Automation",
|
||||
"Az.Functions",
|
||||
"Microsoft.Graph"
|
||||
)
|
||||
|
||||
Ensure-AzureModules -ModuleNames $additionalModules
|
||||
|
||||
# Function to ensure that winget (Windows Package Manager) is installed
|
||||
function Ensure-Winget {
|
||||
if (-not (Get-Command winget -ErrorAction SilentlyContinue)) {
|
||||
Write-Host "winget not found. Downloading and installing the official winget MSIX bundle..."
|
||||
# Download the official winget MSIX bundle from Microsoft's GitHub release page.
|
||||
$installerUrl = "https://github.com/microsoft/winget-cli/releases/latest/download/Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle"
|
||||
$installerPath = "$env:TEMP\winget.msixbundle"
|
||||
Invoke-WebRequest -Uri $installerUrl -OutFile $installerPath
|
||||
Write-Host "Installing winget package..."
|
||||
# Install the downloaded MSIX bundle. Note that Add-AppxPackage may require administrator privileges.
|
||||
Add-AppxPackage -Path $installerPath
|
||||
Remove-Item $installerPath
|
||||
}
|
||||
else {
|
||||
Write-Host "winget is already installed."
|
||||
}
|
||||
}
|
||||
|
||||
Ensure-Winget
|
||||
|
||||
# Azure CLI check/install function using winget
|
||||
function Ensure-AzCLI {
|
||||
if (-not (Get-Command az -ErrorAction SilentlyContinue)) {
|
||||
Write-Host "Azure CLI not found. Installing via winget..."
|
||||
winget install -e --id Microsoft.AzureCLI --accept-source-agreements --accept-package-agreements
|
||||
} else {
|
||||
Write-Host "Azure CLI is already installed. Updating via winget..."
|
||||
winget upgrade -e --id Microsoft.AzureCLI --accept-source-agreements --accept-package-agreements
|
||||
}
|
||||
}
|
||||
|
||||
# Azure Function Core Tools check/install function using winget
|
||||
function Ensure-FuncTools {
|
||||
if (-not (Get-Command func -ErrorAction SilentlyContinue)) {
|
||||
Write-Host "Azure Function Core Tools not found. Installing via winget..."
|
||||
winget install -e --id Microsoft.Azure.FunctionsCoreTools --accept-source-agreements --accept-package-agreements
|
||||
} else {
|
||||
Write-Host "Azure Function Core Tools already installed. Updating via winget..."
|
||||
winget upgrade -e --id Microsoft.Azure.FunctionsCoreTools --accept-source-agreements --accept-package-agreements
|
||||
}
|
||||
}
|
||||
|
||||
#Install Git Module from winget
|
||||
function Ensure-Git {
|
||||
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
|
||||
Write-Host "Git not found. Installing via winget..."
|
||||
winget install --id Git.Git --accept-source-agreements --accept-package-agreements
|
||||
} else {
|
||||
Write-Host "Git is already installed. Updating via winget..."
|
||||
winget upgrade -e --id Git.Git --accept-source-agreements --accept-package-agreements
|
||||
}
|
||||
}
|
||||
|
||||
# Call installers for CLI and Function Tools
|
||||
Ensure-AzCLI
|
||||
Ensure-FuncTools
|
||||
Ensure-Git
|
||||
|
||||
|
||||
# Function to clone GitHub repositories
|
||||
function Clone-GitHubTools {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string[]]$RepoURLs,
|
||||
[string]$DestinationPath = "."
|
||||
)
|
||||
|
||||
# Create destination folder if it doesn't exist
|
||||
if (-not (Test-Path $DestinationPath)) {
|
||||
New-Item -ItemType Directory -Path $DestinationPath | Out-Null
|
||||
}
|
||||
|
||||
foreach ($url in $RepoURLs) {
|
||||
Write-Host "Cloning repository from $url"
|
||||
# Extract repository name from URL (assumes URL ends with .git)
|
||||
$repoName = ($url.Split("/")[-1]).Replace(".git", "")
|
||||
$targetFolder = Join-Path $DestinationPath $repoName
|
||||
if (Test-Path $targetFolder) {
|
||||
Write-Host "Repository '$repoName' already exists in $DestinationPath, skipping clone."
|
||||
} else {
|
||||
git clone $url $targetFolder
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host "Successfully cloned '$repoName'."
|
||||
} else {
|
||||
Write-Warning "Failed to clone '$repoName' from $url."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Clone the specified GitHub repositories
|
||||
$repos = @(
|
||||
"https://github.com/LuemmelSec/APEX.git",
|
||||
"https://github.com/hausec/PowerZure.git",
|
||||
"https://github.com/f-bader/TokenTacticsV2.git",
|
||||
"https://github.com/dirkjanm/ROADtools.git",
|
||||
"https://github.com/Azure/Stormspotter.git",
|
||||
"https://github.com/NetSPI/MicroBurst.git",
|
||||
"https://gist.github.com/xpn/f12b145dba16c2eebdd1c6829267b90c",
|
||||
"https://github.com/dafthack/MFASweep.git",
|
||||
"https://github.com/dafthack/MSOLSpray.git",
|
||||
"https://github.com/Gerenios/AADInternals.git",
|
||||
"https://github.com/YasserREED/Office365Hacker.git",
|
||||
"https://github.com/lutzenfried/OffensiveCloud.git"
|
||||
)
|
||||
Clone-GitHubTools -RepoURLs $repos -DestinationPath "."
|
||||
|
||||
Write-Host "`n Environment cleaned, Azure modules (including Microsoft Graph) reinstalled, sub-modules ensured, and tools updated successfully.`n" -ForegroundColor Green
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Installs the PowerShell modules and CLI tools ANIMO drives.
|
||||
|
||||
.DESCRIPTION
|
||||
Runs on Windows PowerShell 5.1, and on PowerShell 7 on Windows, Linux and
|
||||
macOS. Modules that only exist on Windows (AzureAD, and the winget-installed
|
||||
CLI tools) are skipped with a message on other platforms rather than failing.
|
||||
|
||||
This script is additive. It never uninstalls modules you already have.
|
||||
|
||||
.EXAMPLE
|
||||
pwsh -File Install-AllModules.ps1
|
||||
|
||||
.EXAMPLE
|
||||
# Skip the ~2 GB umbrella Az module and install only the submodules ANIMO uses
|
||||
pwsh -File Install-AllModules.ps1 -SkipUmbrellaAz
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
# Install only the Az.* submodules ANIMO calls, not the full Az meta-module.
|
||||
[switch]$SkipUmbrellaAz
|
||||
)
|
||||
|
||||
# Continue, not Stop: one unavailable module should not abort the whole install.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
|
||||
# $IsWindows is a PowerShell 6+ automatic variable. On Windows PowerShell 5.1 it
|
||||
# is undefined, and that edition only ever runs on Windows.
|
||||
$onWindows = $IsWindows -or ($PSVersionTable.PSEdition -eq 'Desktop')
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "--- ANIMO module setup ---" -ForegroundColor Cyan
|
||||
Write-Host "PowerShell $($PSVersionTable.PSVersion) ($($PSVersionTable.PSEdition)) on $(if ($onWindows) { 'Windows' } else { 'Unix' })"
|
||||
Write-Host ""
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Gallery bootstrap
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function Initialize-Gallery {
|
||||
# Windows PowerShell 5.1 ships without the NuGet provider and defaults to
|
||||
# TLS 1.0, both of which break Install-Module against the gallery.
|
||||
if ($PSVersionTable.PSEdition -eq 'Desktop') {
|
||||
try {
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
} catch {
|
||||
Write-Warning "Could not force TLS 1.2: $($_.Exception.Message)"
|
||||
}
|
||||
if (-not (Get-PackageProvider -Name NuGet -ListAvailable -ErrorAction SilentlyContinue)) {
|
||||
Write-Host "Installing NuGet package provider..."
|
||||
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
$gallery = Get-PSRepository -Name PSGallery -ErrorAction SilentlyContinue
|
||||
if ($gallery -and $gallery.InstallationPolicy -ne 'Trusted') {
|
||||
Write-Host "Trusting PSGallery for this user..."
|
||||
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Module installation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function Ensure-Module {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$Name,
|
||||
[string]$Note
|
||||
)
|
||||
|
||||
if (Get-Module -ListAvailable -Name $Name -ErrorAction SilentlyContinue) {
|
||||
Write-Host (" {0,-26} already installed" -f $Name) -ForegroundColor DarkGray
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host (" {0,-26} installing..." -f $Name)
|
||||
try {
|
||||
# -AllowClobber: Az command names collide with leftover AzureRM installs.
|
||||
Install-Module -Name $Name -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop
|
||||
Write-Host (" {0,-26} OK" -f $Name) -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Warning ("{0}: {1}" -f $Name, $_.Exception.Message)
|
||||
if ($Note) { Write-Host " $Note" -ForegroundColor Yellow }
|
||||
}
|
||||
}
|
||||
|
||||
Initialize-Gallery
|
||||
|
||||
# Az submodules ANIMO actually calls. Installing these individually is far
|
||||
# smaller than the umbrella Az module and covers every Az cmdlet in the codebase.
|
||||
$azSubmodules = @(
|
||||
'Az.Accounts' # Connect-AzAccount, Get-AzAccessToken, Get-AzContext
|
||||
'Az.Resources' # Get-AzResource, Get-AzRoleAssignment, Get-AzADServicePrincipal
|
||||
'Az.Compute' # Get-AzVM, Get-AzVMImage, Get-AzVMSize
|
||||
'Az.Network' # Get-AzVirtualNetwork, Get-AzNetworkSecurityGroup
|
||||
'Az.Storage' # Get-AzStorageAccount, Get-AzStorageBlob, SAS tokens
|
||||
'Az.KeyVault' # Get-AzKeyVaultSecret, Get-AzKeyVaultKey
|
||||
'Az.Monitor'
|
||||
)
|
||||
|
||||
# Modules that install and load on every platform.
|
||||
$crossPlatform = @(
|
||||
@{ Name = 'Microsoft.Graph' }
|
||||
@{ Name = 'SqlServer' } # Invoke-SqlCmd, used by SqlDatabaseWindow
|
||||
@{ Name = 'AADInternals'; Note = 'Some AADInternals cmdlets are Windows-only.' }
|
||||
@{ Name = 'AADInternals-Endpoints'; Note = 'Some cmdlets are Windows-only.' }
|
||||
@{ Name = 'AzTable' }
|
||||
)
|
||||
|
||||
Write-Host "Az submodules:" -ForegroundColor Cyan
|
||||
foreach ($m in $azSubmodules) { Ensure-Module -Name $m }
|
||||
|
||||
if (-not $SkipUmbrellaAz) {
|
||||
Write-Host ""
|
||||
Write-Host "Umbrella Az module (large - use -SkipUmbrellaAz to omit):" -ForegroundColor Cyan
|
||||
Ensure-Module -Name 'Az'
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Cross-platform modules:" -ForegroundColor Cyan
|
||||
foreach ($m in $crossPlatform) { Ensure-Module -Name $m.Name -Note $m.Note }
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Windows-only: AzureAD module and the winget-installed CLI tools
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function Ensure-Winget {
|
||||
if (Get-Command winget -ErrorAction SilentlyContinue) {
|
||||
Write-Host " winget already installed" -ForegroundColor DarkGray
|
||||
return $true
|
||||
}
|
||||
|
||||
Write-Host " winget installing App Installer bundle..."
|
||||
try {
|
||||
$installerUrl = 'https://github.com/microsoft/winget-cli/releases/latest/download/Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle'
|
||||
$installerPath = Join-Path $env:TEMP 'winget.msixbundle'
|
||||
Invoke-WebRequest -Uri $installerUrl -OutFile $installerPath -ErrorAction Stop
|
||||
# May require elevation.
|
||||
Add-AppxPackage -Path $installerPath -ErrorAction Stop
|
||||
Remove-Item $installerPath -ErrorAction SilentlyContinue
|
||||
return [bool](Get-Command winget -ErrorAction SilentlyContinue)
|
||||
} catch {
|
||||
Write-Warning "winget install failed: $($_.Exception.Message)"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
function Ensure-WingetPackage {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$Id,
|
||||
[Parameter(Mandatory)][string]$Command,
|
||||
[Parameter(Mandatory)][string]$Label
|
||||
)
|
||||
|
||||
if (Get-Command $Command -ErrorAction SilentlyContinue) {
|
||||
Write-Host (" {0,-26} already installed" -f $Label) -ForegroundColor DarkGray
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host (" {0,-26} installing via winget..." -f $Label)
|
||||
winget install -e --id $Id --accept-source-agreements --accept-package-agreements
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Warning "$Label install returned exit code $LASTEXITCODE."
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
if ($onWindows) {
|
||||
Write-Host "Windows-only modules:" -ForegroundColor Cyan
|
||||
# AzureAD targets .NET Framework and cannot be imported by PowerShell 7.
|
||||
# It installs fine, but only Windows PowerShell 5.1 can load it.
|
||||
Ensure-Module -Name 'AzureAD' `
|
||||
-Note 'AzureAD loads only under Windows PowerShell 5.1, not pwsh 7.'
|
||||
if ($PSVersionTable.PSEdition -ne 'Desktop') {
|
||||
Write-Host " Note: import AzureAD from Windows PowerShell 5.1, or use" -ForegroundColor Yellow
|
||||
Write-Host " Import-Module AzureAD -UseWindowsPowerShell from pwsh 7." -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Windows CLI tools:" -ForegroundColor Cyan
|
||||
if (Ensure-Winget) {
|
||||
Ensure-WingetPackage -Id 'Microsoft.AzureCLI' -Command 'az' -Label 'Azure CLI'
|
||||
Ensure-WingetPackage -Id 'Microsoft.Azure.FunctionsCoreTools' -Command 'func' -Label 'Functions Core Tools'
|
||||
Ensure-WingetPackage -Id 'Git.Git' -Command 'git' -Label 'Git'
|
||||
} else {
|
||||
Write-Host " winget unavailable - install az, func and git manually." -ForegroundColor Yellow
|
||||
}
|
||||
} else {
|
||||
Write-Host "Skipping Windows-only components on this platform:" -ForegroundColor Cyan
|
||||
Write-Host " AzureAD - .NET Framework only; cannot load on pwsh 7."
|
||||
Write-Host " ANIMO panels that need it degrade rather than break."
|
||||
Write-Host " winget - Windows package manager."
|
||||
Write-Host " az / func / git - install via ./install-dependencies.sh or your"
|
||||
Write-Host " distro package manager."
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Summary
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "--- Verifying ---" -ForegroundColor Cyan
|
||||
$check = $azSubmodules + $crossPlatform.Name
|
||||
if (-not $SkipUmbrellaAz) { $check += 'Az' }
|
||||
if ($onWindows) { $check += 'AzureAD' }
|
||||
|
||||
foreach ($m in $check) {
|
||||
$found = Get-Module -ListAvailable -Name $m -ErrorAction SilentlyContinue
|
||||
if ($found) {
|
||||
$ver = ($found.Version | Select-Object -Unique | Sort-Object -Descending | Select-Object -First 1)
|
||||
Write-Host (" {0,-26} {1}" -f $m, $ver) -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host (" {0,-26} MISSING" -f $m) -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Done. No modules were removed and nothing was cloned into this directory." -ForegroundColor Green
|
||||
Write-Host "Third-party tooling ANIMO pairs well with is listed under 'Related Tooling'"
|
||||
Write-Host "in README.md - clone those outside this repository."
|
||||
Write-Host ""
|
||||
|
||||
@@ -56,21 +56,21 @@ ANIMO is a comprehensive Azure AD / Entra ID assessment platform that combines *
|
||||
|
||||
| Requirement | Version |
|
||||
|:------------|:--------|
|
||||
| OS | Linux (Kali recommended), macOS, or Windows with WSL2 |
|
||||
| Qt6 | 6.2+ (Widgets, Network, WebEngineWidgets, Sql) |
|
||||
| OS | Linux (Kali recommended, build verified), macOS, or Windows — natively or via WSL2 |
|
||||
| Qt6 | 6.2+ (Widgets, Network, WebEngineWidgets, Sql; plus the Svg image plugin for the app icon) |
|
||||
| CMake | 3.16+ |
|
||||
| PowerShell | 7.x (`pwsh`) |
|
||||
| Azure CLI | 2.x (`az`) — optional but recommended for parity |
|
||||
| Python | 3.8+ with `msal`, `requests` |
|
||||
|
||||
### Install & Build
|
||||
### Install & Build (Linux)
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/dmcxblue/ANIMO.git
|
||||
cd ANIMO
|
||||
|
||||
# Install system dependencies (Linux/Kali)
|
||||
# Install system dependencies (Debian / Ubuntu / Kali)
|
||||
./install-dependencies.sh
|
||||
|
||||
# Install PowerShell modules
|
||||
@@ -80,6 +80,34 @@ pwsh -File Install-AllModules.ps1
|
||||
./build.sh
|
||||
```
|
||||
|
||||
### Build on Windows
|
||||
|
||||
The CMake project is cross-platform and `Install-AllModules.ps1` runs on both
|
||||
platforms, but the build itself is currently only verified on Linux. On Windows,
|
||||
install the prerequisites yourself and invoke CMake directly:
|
||||
|
||||
```powershell
|
||||
# Prerequisites (via winget, or the Qt Online Installer for Qt itself)
|
||||
winget install -e --id Kitware.CMake
|
||||
winget install -e --id Ninja-build.Ninja
|
||||
winget install -e --id ShiningLight.OpenSSL.Light
|
||||
# Qt 6.2+ with the Qt WebEngine and Qt SQL modules, plus MSVC (Visual Studio
|
||||
# Build Tools with the C++ workload). Note the Qt install prefix.
|
||||
|
||||
# PowerShell modules
|
||||
pwsh -File Install-AllModules.ps1
|
||||
|
||||
# Configure and build - point CMAKE_PREFIX_PATH at your Qt6 install
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release `
|
||||
-DCMAKE_PREFIX_PATH="C:/Qt/6.7.2/msvc2019_64"
|
||||
cmake --build build
|
||||
```
|
||||
|
||||
Binaries land in `build\server\AnimoServer.exe` and `build\client\AnimoClient.exe`.
|
||||
The hardening flags in the top-level `CMakeLists.txt` are GCC/Clang-only and are
|
||||
skipped under MSVC. `build.sh` and `clean.sh` are bash scripts — use the CMake
|
||||
commands above, or run them from WSL2.
|
||||
|
||||
### Launch
|
||||
|
||||
```bash
|
||||
@@ -540,16 +568,52 @@ AnimoServer [options]
|
||||
|
||||
### Required PowerShell Modules
|
||||
|
||||
Installed automatically by `Install-AllModules.ps1`:
|
||||
Installed by `Install-AllModules.ps1`. The script is additive — it never removes
|
||||
modules you already have — and skips Windows-only components on Linux and macOS
|
||||
with a message rather than failing.
|
||||
|
||||
| Module | Purpose |
|
||||
|:-------|:--------|
|
||||
| `Az` | Azure Resource Manager |
|
||||
| `AzureAD` | Azure Active Directory |
|
||||
| `AADInternals` | Azure AD internals & token operations |
|
||||
| `Microsoft.Graph` | Microsoft Graph SDK |
|
||||
| `SqlServer` | Azure SQL operations |
|
||||
| `AzTable` | Azure Table Storage |
|
||||
| Module | Purpose | Platform |
|
||||
|:-------|:--------|:---------|
|
||||
| `Az.Accounts`, `Az.Resources`, `Az.Compute`, `Az.Network`, `Az.Storage`, `Az.KeyVault`, `Az.Monitor` | The Az cmdlets ANIMO actually calls | Any |
|
||||
| `Az` | Umbrella meta-module (~2 GB; skip with `-SkipUmbrellaAz`) | Any |
|
||||
| `Microsoft.Graph` | Microsoft Graph SDK | Any |
|
||||
| `SqlServer` | `Invoke-SqlCmd` for the SQL Database module | Any |
|
||||
| `AADInternals` | Azure AD internals & token operations | Any (some cmdlets Windows-only) |
|
||||
| `AADInternals-Endpoints` | AADInternals endpoint helpers | Any (some cmdlets Windows-only) |
|
||||
| `AzTable` | Azure Table Storage | Any |
|
||||
| `AzureAD` | Azure Active Directory (legacy) | **Windows only** |
|
||||
|
||||
`AzureAD` targets .NET Framework and cannot be imported by PowerShell 7 — use
|
||||
Windows PowerShell 5.1, or `Import-Module AzureAD -UseWindowsPowerShell` from
|
||||
pwsh 7 on Windows. On Linux the `Get-AzureAD*` terminal autocompletions remain
|
||||
available but the cmdlets will not resolve; every ANIMO panel has an Az or Graph
|
||||
code path, so nothing depends on `AzureAD` being present.
|
||||
|
||||
On Windows the script also installs `az`, `func`, and `git` via winget. On Linux,
|
||||
`az` comes from `install-dependencies.sh`.
|
||||
|
||||
### Related Tooling
|
||||
|
||||
ANIMO pairs well with these projects. Clone them **outside** this repository:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/tools && cd ~/tools
|
||||
```
|
||||
|
||||
| Project | Purpose |
|
||||
|:--------|:--------|
|
||||
| [AADInternals](https://github.com/Gerenios/AADInternals) | Azure AD internals research toolkit |
|
||||
| [ROADtools](https://github.com/dirkjanm/ROADtools) | Azure AD exploration and enumeration |
|
||||
| [TokenTacticsV2](https://github.com/f-bader/TokenTacticsV2) | Token manipulation and family refresh abuse |
|
||||
| [APEX](https://github.com/LuemmelSec/APEX) | Azure privilege escalation toolkit |
|
||||
| [PowerZure](https://github.com/hausec/PowerZure) | Azure post-exploitation framework |
|
||||
| [MicroBurst](https://github.com/NetSPI/MicroBurst) | Azure enumeration and privesc scripts |
|
||||
| [Stormspotter](https://github.com/Azure/Stormspotter) | Azure attack-graph visualisation |
|
||||
| [MFASweep](https://github.com/dafthack/MFASweep) | MFA coverage gap discovery |
|
||||
| [MSOLSpray](https://github.com/dafthack/MSOLSpray) | Password spraying against Microsoft Online |
|
||||
| [Office365Hacker](https://github.com/YasserREED/Office365Hacker) | Office 365 attack tooling |
|
||||
| [OffensiveCloud](https://github.com/lutzenfried/OffensiveCloud) | Multi-cloud offensive references |
|
||||
| [ROADtoken gist](https://gist.github.com/xpn/f12b145dba16c2eebdd1c6829267b90c) | PRT cookie retrieval via browsercore |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -167,16 +167,22 @@ void WHfBAttackWindow::setLoading(bool loading) {
|
||||
}
|
||||
|
||||
QString WHfBAttackWindow::findPythonScript() {
|
||||
QString appDir = QCoreApplication::applicationDirPath();
|
||||
QStringList searchPaths = {
|
||||
QCoreApplication::applicationDirPath() + "/../helpers/DeviceCode2WFH.py",
|
||||
QCoreApplication::applicationDirPath() + "/../../helpers/DeviceCode2WFH.py",
|
||||
appDir + "/../../helpers/DeviceCode2WFH.py",
|
||||
appDir + "/../helpers/DeviceCode2WFH.py",
|
||||
appDir + "/helpers/DeviceCode2WFH.py",
|
||||
appDir + "/DeviceCode2WFH.py",
|
||||
QDir::currentPath() + "/helpers/DeviceCode2WFH.py",
|
||||
"/home/dmcxblue/Documents/OffensiveTools/Azure/ANIMO/helpers/DeviceCode2WFH.py"
|
||||
"../../helpers/DeviceCode2WFH.py",
|
||||
"../helpers/DeviceCode2WFH.py",
|
||||
"helpers/DeviceCode2WFH.py",
|
||||
"DeviceCode2WFH.py"
|
||||
};
|
||||
|
||||
for (const QString &path : searchPaths) {
|
||||
QFileInfo fi(path);
|
||||
if (fi.exists()) {
|
||||
if (fi.exists() && fi.isFile()) {
|
||||
return fi.absoluteFilePath();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ function Get-PRTToken {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$WebhookUrl # Example: http://192.168.1.61:8000/
|
||||
[string]$WebhookUrl # Example: http://<LISTENER_IP>:8000/
|
||||
)
|
||||
|
||||
process {
|
||||
|
||||
@@ -2,7 +2,7 @@ function Get-UserPRTToken {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$WebhookUrl # Example: http://192.168.1.61:8000/
|
||||
[string]$WebhookUrl # Example: http://<LISTENER_IP>:8000/
|
||||
)
|
||||
|
||||
process {
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace GrabTokenAzureAD
|
||||
class CombinedGrabber
|
||||
{
|
||||
// ========== HARDCODED CONFIGURATION ==========
|
||||
private const string CALLBACK_URL = "http://192.168.1.21:8000/capture";
|
||||
private const string CALLBACK_URL = "http://127.0.0.1:8000/capture";
|
||||
private const string CLIENT_ID = "1950a258-227b-4e31-a9cf-717495945fc2"; // Azure PowerShell
|
||||
private const string TENANT = "common";
|
||||
private const string REDIRECT_URI = "http://localhost:8400";
|
||||
|
||||
@@ -137,7 +137,7 @@ Options:
|
||||
Default: http://localhost:8400
|
||||
|
||||
-u, --callback <url> ANIMO webhook URL to send captured tokens
|
||||
Default: http://192.168.1.21:8000/capture
|
||||
Default: http://127.0.0.1:8000/capture
|
||||
|
||||
--no-callback Disable sending tokens to callback URL
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace GrabTokenAzureAD
|
||||
public string RedirectUri { get; set; } = "http://localhost:8400";
|
||||
public string GraphScope { get; set; } = "offline_access https://graph.microsoft.com/.default";
|
||||
public string ManagementScope { get; set; } = "https://management.azure.com/.default";
|
||||
public string CallbackUrl { get; set; } = "http://192.168.1.21:8000/capture";
|
||||
public string CallbackUrl { get; set; } = "http://127.0.0.1:8000/capture";
|
||||
public int ListenerTimeoutSeconds { get; set; } = 300;
|
||||
|
||||
public string Authority => $"https://login.microsoftonline.com/{Tenant}/oauth2/v2.0";
|
||||
|
||||
@@ -213,7 +213,7 @@ Examples:
|
||||
python3 webhook_capture.py -p 9000
|
||||
|
||||
# Start server on specific interface
|
||||
python3 webhook_capture.py -i 192.168.1.61 -p 8000
|
||||
python3 webhook_capture.py -i <LISTENER_IP> -p 8000
|
||||
|
||||
# Auto-save captured data
|
||||
python3 webhook_capture.py -o captured_prt.json
|
||||
|
||||
+92
-19
@@ -1,39 +1,112 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# install-dependencies.sh - Debian/Ubuntu/Kali build and runtime dependencies.
|
||||
#
|
||||
# Installs the Qt6 + CMake toolchain ANIMO builds against, PowerShell 7, and
|
||||
# (optionally) the Azure CLI. For the PowerShell modules themselves, run
|
||||
# `pwsh -File Install-AllModules.ps1` afterwards.
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Update package lists
|
||||
echo "[*] Updating package lists..."
|
||||
sudo apt-get update
|
||||
|
||||
# Install toolchain + Qt6 deps
|
||||
echo "[*] Installing toolchain and Qt6 development packages..."
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
cmake \
|
||||
ninja-build \
|
||||
curl \
|
||||
ca-certificates \
|
||||
qt6-base-dev \
|
||||
qt6-tools-dev \
|
||||
qt6-tools-dev-tools \
|
||||
qt6-base-dev-tools \
|
||||
qt6-webengine-dev \
|
||||
qt6-webengine-dev-tools \
|
||||
qt6-websockets-dev \
|
||||
libqt6svg6 \
|
||||
libqt6sql6 \
|
||||
libqt6sql6-sqlite \
|
||||
libssl-dev \
|
||||
snapd
|
||||
libssl-dev
|
||||
|
||||
sudo snap install powershell --classic
|
||||
# ---------------------------------------------------------------------------
|
||||
# PowerShell 7
|
||||
#
|
||||
# Kali does not ship snapd and enabling it there is unreliable, so prefer the
|
||||
# .deb Microsoft publishes on GitHub and fall back to snap only if that fails.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Azure CLI - login scripts opportunistically call `az login` alongside
|
||||
# Connect-AzAccount so operators can run `az` one-liners in the session
|
||||
# terminal. If az isn't on PATH the login scripts skip it cleanly, so
|
||||
# this install is optional but recommended.
|
||||
if ! command -v az >/dev/null 2>&1; then
|
||||
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash || {
|
||||
echo "[!] az cli install failed - continuing without it. Login scripts will skip az login."
|
||||
}
|
||||
install_powershell_deb() {
|
||||
local arch deb_arch url tmp
|
||||
arch="$(dpkg --print-architecture)"
|
||||
case "$arch" in
|
||||
amd64) deb_arch="x64" ;;
|
||||
arm64) deb_arch="arm64" ;;
|
||||
*) echo "[!] No PowerShell .deb for architecture '$arch'."; return 1 ;;
|
||||
esac
|
||||
|
||||
echo "[*] Resolving latest PowerShell release..."
|
||||
local version
|
||||
version="$(curl -fsSL https://api.github.com/repos/PowerShell/PowerShell/releases/latest \
|
||||
| grep -oP '"tag_name":\s*"v\K[^"]+' || true)"
|
||||
if [ -z "$version" ]; then
|
||||
echo "[!] Could not determine the latest PowerShell version."
|
||||
return 1
|
||||
fi
|
||||
|
||||
url="https://github.com/PowerShell/PowerShell/releases/download/v${version}/powershell_${version}-1.deb_${deb_arch}.deb"
|
||||
tmp="$(mktemp -d)"
|
||||
echo "[*] Downloading PowerShell ${version} (${deb_arch})..."
|
||||
if ! curl -fsSL -o "$tmp/powershell.deb" "$url"; then
|
||||
echo "[!] Download failed: $url"
|
||||
rm -rf "$tmp"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# apt-get install on a local .deb pulls its dependencies; dpkg alone would not.
|
||||
sudo apt-get install -y "$tmp/powershell.deb"
|
||||
rm -rf "$tmp"
|
||||
}
|
||||
|
||||
install_powershell_snap() {
|
||||
command -v snap >/dev/null 2>&1 || sudo apt-get install -y snapd
|
||||
sudo snap install powershell --classic
|
||||
}
|
||||
|
||||
if command -v pwsh >/dev/null 2>&1; then
|
||||
echo "[+] PowerShell already installed: $(pwsh --version)"
|
||||
else
|
||||
echo "[*] Installing PowerShell 7..."
|
||||
if install_powershell_deb; then
|
||||
echo "[+] PowerShell installed from .deb."
|
||||
elif install_powershell_snap; then
|
||||
echo "[+] PowerShell installed from snap."
|
||||
else
|
||||
echo "[!] PowerShell install failed. ANIMO needs pwsh for its session"
|
||||
echo " terminal - install it manually before running the server:"
|
||||
echo " https://learn.microsoft.com/powershell/scripting/install/installing-powershell-on-linux"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Optional: reduce layer size in containers/CI
|
||||
sudo apt-get clean
|
||||
sudo rm -rf /var/lib/apt/lists/*
|
||||
# ---------------------------------------------------------------------------
|
||||
# Azure CLI (optional)
|
||||
#
|
||||
# The login scripts opportunistically call `az login` alongside Connect-AzAccount
|
||||
# so operators can run `az` one-liners in the session terminal. If az is missing
|
||||
# the login scripts skip it cleanly, so this is recommended but not required.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
if command -v az >/dev/null 2>&1; then
|
||||
echo "[+] Azure CLI already installed."
|
||||
else
|
||||
echo "[*] Installing Azure CLI..."
|
||||
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash || {
|
||||
echo "[!] az cli install failed - continuing without it."
|
||||
echo " Login scripts will skip az login."
|
||||
}
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "[+] Dependencies installed."
|
||||
echo " Next: pwsh -File Install-AllModules.ps1"
|
||||
echo " Then: ./build.sh"
|
||||
|
||||
@@ -10,10 +10,11 @@
|
||||
# 2) If AnimoServer is not running, spawns it under gdb in batch mode with
|
||||
# the passed CLI args. Any crash dumps full backtrace before gdb exits.
|
||||
#
|
||||
# Server args:
|
||||
# Server args (spawn mode only):
|
||||
# Pass everything after `--` and it's forwarded verbatim to AnimoServer.
|
||||
# Or set env: ANIMO_IP, ANIMO_PORT, ANIMO_PASSWORD.
|
||||
# Defaults: -i 192.168.1.27 -p 50500 -P rt2025
|
||||
# Defaults: -i 127.0.0.1 -p 7777. ANIMO_PASSWORD has no default - either
|
||||
# export it or pass -P yourself after `--`.
|
||||
#
|
||||
# Output goes to:
|
||||
# /tmp/animo-srv-gdb.log (this script's snapshot / crash output)
|
||||
@@ -32,10 +33,11 @@ REPO_ROOT="$(cd "$SELF_DIR/.." && pwd)"
|
||||
SERVER_BIN="$REPO_ROOT/build/server/AnimoServer"
|
||||
LOG="/tmp/animo-srv-gdb.log"
|
||||
|
||||
# Defaults, overridable via env or CLI passthrough.
|
||||
IP="${ANIMO_IP:-192.168.1.27}"
|
||||
PORT="${ANIMO_PORT:-50500}"
|
||||
PASSWORD="${ANIMO_PASSWORD:-rt2025}"
|
||||
# Defaults, overridable via env or CLI passthrough. No password default on
|
||||
# purpose - it would end up committed and reused across engagements.
|
||||
IP="${ANIMO_IP:-127.0.0.1}"
|
||||
PORT="${ANIMO_PORT:-7777}"
|
||||
PASSWORD="${ANIMO_PASSWORD:-}"
|
||||
|
||||
MODE="auto"
|
||||
PASSTHROUGH=()
|
||||
@@ -49,11 +51,6 @@ while [[ $# -gt 0 ]]; do
|
||||
esac
|
||||
done
|
||||
|
||||
# If nothing passed after `--`, build default arg list.
|
||||
if [ ${#PASSTHROUGH[@]} -eq 0 ]; then
|
||||
PASSTHROUGH=(-i "$IP" -p "$PORT" -P "$PASSWORD")
|
||||
fi
|
||||
|
||||
require_gdb() {
|
||||
command -v gdb >/dev/null || { echo "gdb not on PATH"; exit 1; }
|
||||
}
|
||||
@@ -83,6 +80,16 @@ snapshot() {
|
||||
|
||||
spawn_under_gdb() {
|
||||
[ -x "$SERVER_BIN" ] || { echo "Server binary not found: $SERVER_BIN"; exit 1; }
|
||||
# Only spawn mode needs server args; snapshot mode attaches to a live process.
|
||||
if [ ${#PASSTHROUGH[@]} -eq 0 ]; then
|
||||
if [ -z "$PASSWORD" ]; then
|
||||
echo "No server password set. Either:" >&2
|
||||
echo " export ANIMO_PASSWORD=<pass>" >&2
|
||||
echo " or: $0 --spawn -- -i $IP -p $PORT -P <pass>" >&2
|
||||
exit 1
|
||||
fi
|
||||
PASSTHROUGH=(-i "$IP" -p "$PORT" -P "$PASSWORD")
|
||||
fi
|
||||
# Kill only the server matching our port; leave unrelated instances alone.
|
||||
local existing
|
||||
existing=$(find_running)
|
||||
|
||||
Reference in New Issue
Block a user