This commit is contained in:
Luftmensch
2026-08-26 14:11:37 +07:00
commit e528419f9a
135 changed files with 413970 additions and 0 deletions
+286
View File
@@ -0,0 +1,286 @@
"""
Windows Audit Hardening Tool - v2.0
Usage: AuditTool.exe -p audit_cis_windows.ps1
AuditTool.exe (uses bundled .ps1 file)
Build: python -m PyInstaller --onefile --console -n AuditTool ^
--add-data "audit_cis_windows.ps1;." ^
windows_audit.py
Environment: Python 3.8+ (3.8 for Windows Server 2012 compatibility)
"""
import argparse
import base64
import os
import socket
import subprocess
import sys
import tempfile
import traceback
from Crypto.Cipher import PKCS1_OAEP, AES
from Crypto.PublicKey import RSA
from Crypto.Util.Padding import pad
from colorama import Fore, init as colorama_init
from unidecode import unidecode
# ----------------------------------------------------------
# Globals
# ----------------------------------------------------------
colorama_init(strip=False, autoreset=True)
HOSTNAME = socket.gethostname()
try:
IP_ADDR = socket.gethostbyname(HOSTNAME)
except Exception:
IP_ADDR = "127.0.0.1"
BUNDLED_SCRIPT = "audit_cis_windows.ps1"
PUBLIC_KEY_FILE = "public_key.pem"
BANNER = r"""
_ _ _____ _____ _______ _ _ _____ _____ ______ _ _ _____ _ _ _____
/\ | | | | __ \_ _|__ __| | | | | /\ | __ \| __ \| ____| \ | |_ _| \ | |/ ____|
/ \ | | | | | | || | | | ______ | |__| | / \ | |__) | | | | |__ | \| | | | | \| | | __
/ /\ \| | | | | | || | | | |______| | __ | / /\ \ | _ /| | | | __| | . ` | | | | . ` | | |_ |
/ ____ \ |__| | |__| || |_ | | | | | |/ ____ \| | \ \| |__| | |____| |\ |_| |_| |\ | |__| |
/_/ \_\____/|_____/_____| |_| |_| |_/_/ \_\_| \_\_____/|______|_| \_|_____|_| \_|\_____|
"""
# ----------------------------------------------------------
# Path resolution (supports PyInstaller bundle)
# ----------------------------------------------------------
def resource_path(relative_path):
"""Get absolute path to resource, works for dev and PyInstaller."""
if getattr(sys, "frozen", False):
base = sys._MEIPASS
else:
base = os.path.dirname(os.path.abspath(__file__))
return os.path.join(base, relative_path)
def resolve_script_path(user_path=None):
"""
Resolve the .ps1 script path.
Priority: user-supplied arg > bundled file > same-dir file.
Returns absolute path or None.
"""
if user_path:
if os.path.isfile(user_path):
return os.path.abspath(user_path)
print(Fore.YELLOW + "[WARNING] Provided path not found: {}".format(user_path) + Fore.RESET)
# Try PyInstaller bundled location
bundled = resource_path(BUNDLED_SCRIPT)
if os.path.isfile(bundled):
return bundled
# Try same directory
same_dir = os.path.join(os.getcwd(), BUNDLED_SCRIPT)
if os.path.isfile(same_dir):
return same_dir
return None
def resolve_public_key():
"""Find public_key.pem - bundled, same dir, or current dir."""
for loc in [
resource_path(PUBLIC_KEY_FILE),
os.path.join(os.getcwd(), PUBLIC_KEY_FILE),
]:
if os.path.isfile(loc):
return loc
return None
# ----------------------------------------------------------
# Crypto helpers
# ----------------------------------------------------------
def encrypt_output_rsa(content):
"""Encrypt output with Hybrid AES-256-CBC + RSA (HYBRID_V1 format)."""
pk_path = resolve_public_key()
if pk_path is None:
return "PLAINTEXT:" + content
try:
raw = content.encode("utf-8")
aes_key = os.urandom(32)
iv = os.urandom(16)
padded = pad(raw, AES.block_size, style="pkcs7")
cipher_aes = AES.new(aes_key, AES.MODE_CBC, iv)
ciphertext = cipher_aes.encrypt(padded)
pub_key = RSA.import_key(open(pk_path, "rb").read())
cipher_rsa = PKCS1_OAEP.new(pub_key)
enc_key = cipher_rsa.encrypt(aes_key)
b64_enc_key = base64.b64encode(enc_key).decode("utf-8")
b64_iv = base64.b64encode(iv).decode("utf-8")
b64_ct = base64.b64encode(ciphertext).decode("utf-8")
return f"HYBRID_V1:{b64_enc_key}:{b64_iv}:{b64_ct}"
except Exception as e:
print(Fore.YELLOW + "[WARNING] RSA encrypt failed: {}".format(e) + Fore.RESET)
return "PLAINTEXT:" + content
# ----------------------------------------------------------
# PowerShell runner
# ----------------------------------------------------------
def run_powershell_script(script_content, timeout_sec=600):
"""
Write script to temp .ps1 file, execute via 'powershell -File',
return (stdout, stderr, returncode).
Handles PowerShell's UTF-16LE output encoding correctly.
"""
tmp_path = None
try:
fd, tmp_path = tempfile.mkstemp(suffix=".ps1", prefix="audit_")
with os.fdopen(fd, "w", encoding="utf-8-sig") as fh:
fh.write(script_content)
proc = subprocess.run(
[
"powershell.exe",
"-NoProfile",
"-ExecutionPolicy", "Bypass",
"-File", tmp_path,
],
capture_output=True,
timeout=timeout_sec,
)
# PowerShell outputs UTF-16LE by default
stdout = proc.stdout.decode("utf-16-le", errors="replace") if proc.stdout else ""
stderr = proc.stderr.decode("utf-16-le", errors="replace") if proc.stderr else ""
return stdout, stderr, proc.returncode
except subprocess.TimeoutExpired:
return "", "PowerShell execution timed out ({}s)".format(timeout_sec), -1
except Exception:
return "", traceback.format_exc(), -1
finally:
if tmp_path and os.path.exists(tmp_path):
try:
os.remove(tmp_path)
except Exception:
pass
# ----------------------------------------------------------
# Output parsing
# ----------------------------------------------------------
def extract_audit_info(output):
"""Extract hostname + timestamp from audit output lines."""
hostname = HOSTNAME
timestamp = "unknown"
for line in output.splitlines():
if line.startswith("Hostname:"):
val = line.replace("Hostname:", "").strip()
hostname = val if val else HOSTNAME
elif line.startswith("Time:"):
val = line.replace("Time:", "").strip()
timestamp = val.replace("-", "_").replace(":", "_").replace(" ", "-") if val else "unknown"
return hostname, timestamp
def count_pass_fail(output):
"""Count PASSED/FAILED from JSON lines."""
passed = failed = 0
for line in output.splitlines():
line = line.strip()
if line.startswith("{") and line.endswith("}"):
if '"PASSED"' in line or "'PASSED'" in line:
passed += 1
elif '"FAILED"' in line or "'FAILED'" in line:
failed += 1
return passed, failed
# ----------------------------------------------------------
# Main audit flow
# ----------------------------------------------------------
def run_audit(script_path=None):
"""Read .ps1, run PowerShell audit, encrypt+save results."""
print(Fore.BLUE + BANNER + Fore.RESET)
print(Fore.RED + "Running Audit Hardening v2.0" + Fore.RESET)
print(" Hostname : {}".format(HOSTNAME))
print(" IP : {}".format(IP_ADDR))
print("-" * 70)
# Step 0 Resolve input script
script_path = resolve_script_path(script_path)
if script_path is None:
print(Fore.RED + "ERROR: No .ps1 file found.")
print(" Provide path: AuditTool.exe -p audit_cis_windows.ps1")
print(" Or place {} in the same directory.".format(BUNDLED_SCRIPT) + Fore.RESET)
return
print(" Source : {}".format(script_path))
# Step 1 Read PowerShell script
print(" [1/4] Reading script...")
try:
with open(script_path, "r", encoding="utf-8") as fh:
ps_content = fh.read()
except Exception as e:
print(Fore.RED + " FAILED: {}".format(e) + Fore.RESET)
return
print(" [1/4] OK - {} bytes".format(len(ps_content)))
# Step 2 Execute PowerShell
print(" [2/4] Running PowerShell...")
stdout, stderr, rc = run_powershell_script(ps_content)
if rc != 0:
print(Fore.YELLOW + " [2/4] PowerShell rc={}: {}".format(rc, (stderr or "")[:200]) + Fore.RESET)
else:
print(" [2/4] OK - {} output lines".format(len(stdout.splitlines())))
# Step 3 Combine
full_output = stdout
if stderr:
full_output += "\n[STDERR]\n" + stderr
if not full_output.strip():
print(Fore.RED + "ERROR: No output from PowerShell" + Fore.RESET)
return
passed, failed = count_pass_fail(full_output)
print(" [3/4] Audit: {} PASSED / {} FAILED".format(passed, failed))
# Step 4 Encrypt & save
hostname_out, timestamp = extract_audit_info(stdout)
out_filename = "{}_{}.txt.enc".format(hostname_out, timestamp)
print(" [4/4] Encrypting -> {}".format(out_filename))
encrypted = encrypt_output_rsa(full_output)
with open(out_filename, "w", encoding="utf-8") as fh:
fh.write(encrypted)
if os.path.isfile(out_filename):
print(Fore.GREEN + "=" * 70 + Fore.RESET)
print(Fore.GREEN + " SUCCESS: {} | {} PASS / {} FAIL".format(
out_filename, passed, failed) + Fore.RESET)
print(Fore.GREEN + "=" * 70 + Fore.RESET)
else:
print(Fore.RED + " FAILED: Could not write output file" + Fore.RESET)
# ----------------------------------------------------------
# CLI
# ----------------------------------------------------------
def main():
parser = argparse.ArgumentParser(description="Audit Hardening v2.0")
parser.add_argument(
"-p", "--path",
help="Path to PowerShell .ps1 script (optional if bundled or in same dir)",
default=None,
)
return parser.parse_args()
if __name__ == "__main__":
args = main()
run_audit(args.path)