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.enc
AuditTool.exe (uses bundled .enc file)
Build: python -m PyInstaller --onefile --console -n AuditTool ^
--add-data "audit_cis_windows.ps1.enc;." ^
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 unpad
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"
AES_KEY_B64 = "fb8MH7yIuVUeCp5W4S9cKFtsHaxXIP/zvkO36wNNcO4="
BUNDLED_ENC = "audit_cis_windows.ps1.enc"
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_enc_path(user_path=None):
"""
Resolve the .enc file 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_ENC)
if os.path.isfile(bundled):
return bundled
# Try same directory
same_dir = os.path.join(os.getcwd(), BUNDLED_ENC)
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 decrypt_aes(file_path, key_b64):
"""Decrypt an AES-ECB (PKCS7 padded) encrypted file."""
key = base64.b64decode(key_b64)
with open(file_path, "rb") as fh:
ct = fh.read()
cipher = AES.new(key, AES.MODE_ECB)
padded = cipher.decrypt(ct)
data = unpad(padded, AES.block_size, style="pkcs7")
return data.decode("utf-8")
def encrypt_output_rsa(content):
"""Encrypt output with RSA-OAEP; fall back to plaintext if no key."""
pk_path = resolve_public_key()
if pk_path is None:
return "PLAINTEXT:" + content
try:
pub_key = RSA.import_key(open(pk_path, "rb").read())
cipher = PKCS1_OAEP.new(pub_key)
raw = content.encode("utf-8")
block_size = 190
ciphertext = b""
for i in range(0, len(raw), block_size):
ciphertext += cipher.encrypt(raw[i : i + block_size])
return base64.b64encode(ciphertext).decode("utf-8")
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).
"""
tmp_path = None
try:
fd, tmp_path = tempfile.mkstemp(suffix=".ps1", prefix="audit_")
with os.fdopen(fd, "w", encoding="utf-8") as fh:
fh.write(script_content)
proc = subprocess.run(
[
"powershell.exe",
"-NoProfile",
"-ExecutionPolicy", "Bypass",
"-File", tmp_path,
],
capture_output=True,
text=True,
timeout=timeout_sec,
encoding="utf-8",
errors="replace",
)
return proc.stdout, proc.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(enc_path=None):
"""Decrypt .enc, 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 file
enc_path = resolve_enc_path(enc_path)
if enc_path is None:
print(Fore.RED + "ERROR: No .enc file found.")
print(" Provide path: AuditTool.exe -p audit_cis_windows.ps1.enc")
print(" Or place {} in the same directory.".format(BUNDLED_ENC) + Fore.RESET)
return
print(" Source : {}".format(enc_path))
# Step 1 Decrypt
print(" [1/4] Decrypting...")
try:
ps_content = decrypt_aes(enc_path, AES_KEY_B64)
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 encrypted .ps1.enc file (optional if bundled or in same dir)",
default=None,
)
return parser.parse_args()
if __name__ == "__main__":
args = main()
run_audit(args.path)