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
+38
View File
@@ -0,0 +1,38 @@
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['windows_audit.py'],
pathex=[],
binaries=[],
datas=[('audit_cis_windows.ps1', '.')],
hiddenimports=['Crypto.Cipher', 'Crypto.PublicKey', 'Crypto.Util.Padding'],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='AuditTool',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
+442
View File
@@ -0,0 +1,442 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import base64, os, socket, subprocess, tempfile, sys, binascii
import re
from datetime import datetime
# ---- Compatibility for CentOS 6 (Py2.6/2.7) ----
# Safe print (UTF-8, hỗ trợ stream=file)
def _p(msg, color=None, stream=None, **kwargs):
# Back-compat cho calls kiểu file=...
if stream is None and 'file' in kwargs:
stream = kwargs['file']
if stream is None:
stream = sys.stdout
try:
unicode # Py2 check
b = msg.encode('utf-8') if isinstance(msg, unicode) else msg
except NameError:
b = msg
try:
if color:
stream.write(color)
stream.write(b)
if color:
try:
stream.write(Fore.RESET)
except Exception:
pass
stream.write("\n")
except Exception:
try:
stream.write((u'' + msg).encode('utf-8') + "\n")
except Exception:
sys.stdout.write((u'' + msg).encode('utf-8') + "\n")
# optparse (có sẵn trên Py2.6)
try:
from optparse import OptionParser
except Exception:
OptionParser = None # fallback rất cũ
# Color handling (tùy chọn)
try:
from colorama import Fore, init as colorama_init
colorama_init()
except Exception:
class _NoColor(object):
def __getattr__(self, name): return ''
Fore = _NoColor()
BUNDLED_SCRIPT = "audit_cis_centos6.sh"
def resource_path(relative_path):
if getattr(sys, "frozen", False):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.dirname(os.path.abspath(__file__)), relative_path)
def resolve_script_path(user_path=None):
if user_path and os.path.isfile(user_path):
return os.path.abspath(user_path)
bundled = resource_path(BUNDLED_SCRIPT)
if os.path.isfile(bundled):
return bundled
same_dir = os.path.join(os.getcwd(), BUNDLED_SCRIPT)
if os.path.isfile(same_dir):
return same_dir
return None
def detect_os_name():
"""
Trả về chuỗi tên HĐH ngắn gọn.
Ưu tiên /etc/*-release; fallback sang uname -srm.
"""
paths = [
'/etc/centos-release',
'/etc/redhat-release',
'/etc/os-release', # có trên bản mới hơn
'/etc/lsb-release', # có thể chứa DISTRIB_DESCRIPTION=
]
for p in paths:
try:
if os.path.isfile(p):
data = open(p, 'rb').read()
try:
text = data.decode('utf-8', 'ignore')
except Exception:
text = data
# /etc/lsb-release
if 'DISTRIB_DESCRIPTION=' in text:
for line in text.splitlines():
if line.startswith('DISTRIB_DESCRIPTION='):
val = line.split('=',1)[1].strip().strip('"')
return val
# các file *release khác: dùng dòng đầu
first = text.splitlines()[0].strip()
if first:
return first
except Exception:
pass
# Fallback: uname
try:
out = subprocess.Popen("uname -srm", shell=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
try:
return out.decode('utf-8','ignore').strip()
except Exception:
return out.strip()
except Exception:
return 'Unknown-OS'
try:
hostname = socket.gethostname()
IPAddr = socket.gethostbyname(hostname)
except Exception:
hostname = 'unknown-host'
IPAddr = '127.0.0.1'
cwd = os.getcwd()
def pkcs7_unpad(data, block_size=16):
if not data:
return data
pad_len = ord(data[-1]) if isinstance(data[-1], str) else data[-1]
if pad_len < 1 or pad_len > block_size:
# Defensive: return as-is to avoid exception storms in Py2 envs
return data
return data[:-pad_len]
def write_content(content, use_oaep=True):
"""
Hybrid AES-256-CBC + RSA encryption using OpenSSL CLI.
Only 1 RSA operation for the AES key; bulk data encrypted with fast AES.
Output: HYBRID_V1:<b64_enc_key>:<b64_iv>:<b64_ciphertext>
"""
import binascii
pubkey_path = os.path.join(os.getcwd(), 'public_key.pem')
if not os.path.isfile(pubkey_path):
raise RuntimeError("public_key.pem khong ton tai tai: %s" % pubkey_path)
try:
unicode
if isinstance(content, unicode):
message = content.encode('utf-8')
else:
message = content
except NameError:
if isinstance(content, str):
message = content.encode('utf-8')
else:
message = content
aes_key = os.urandom(32)
iv = os.urandom(16)
fd_in, path_in = tempfile.mkstemp(prefix='aes_in_', dir='/tmp')
fd_out, path_out = tempfile.mkstemp(prefix='aes_out_', dir='/tmp')
os.close(fd_in)
os.close(fd_out)
try:
with open(path_in, 'wb') as f:
f.write(message)
key_hex = binascii.hexlify(aes_key)
iv_hex = binascii.hexlify(iv)
cmd_aes = [
'/usr/bin/openssl', 'enc', '-aes-256-cbc',
'-K', key_hex, '-iv', iv_hex,
'-in', path_in, '-out', path_out
]
p = subprocess.Popen(cmd_aes, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
if p.returncode != 0:
raise RuntimeError("OpenSSL AES encrypt error (rc=%d): %s" % (p.returncode, err))
with open(path_out, 'rb') as f:
ciphertext = f.read()
finally:
try: os.remove(path_in)
except: pass
try: os.remove(path_out)
except: pass
fd_key_in, path_key_in = tempfile.mkstemp(prefix='rsa_key_in_', dir='/tmp')
fd_key_out, path_key_out = tempfile.mkstemp(prefix='rsa_key_out_', dir='/tmp')
os.close(fd_key_in)
os.close(fd_key_out)
try:
with open(path_key_in, 'wb') as f:
f.write(aes_key)
cmd_rsa = [
'/usr/bin/openssl', 'rsautl', '-encrypt',
'-pubin', '-inkey', pubkey_path,
'-in', path_key_in, '-out', path_key_out
]
if use_oaep:
cmd_rsa.insert(3, '-oaep')
else:
cmd_rsa.insert(3, '-pkcs')
p = subprocess.Popen(cmd_rsa, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
if p.returncode != 0:
raise RuntimeError("OpenSSL rsautl error (rc=%d): %s" % (p.returncode, err))
with open(path_key_out, 'rb') as f:
enc_key = f.read()
finally:
try: os.remove(path_key_in)
except: pass
try: os.remove(path_key_out)
except: pass
b64_enc_key = base64.b64encode(enc_key)
b64_iv = base64.b64encode(iv)
b64_ct = base64.b64encode(ciphertext)
return b"HYBRID_V1:" + b64_enc_key + b":" + b64_iv + b":" + b64_ct
def _key_b64_to_hex(key_b64):
key = base64.b64decode(key_b64) # 16/24/32 bytes
try:
# Py2 cách cũ
return key.encode('hex')
except Exception:
# fallback portable
return binascii.b2a_hex(key)
def _key_b64_to_hex_and_cipher(key_b64):
"""
Base64 -> bytes -> (hex_key, cipher_name) theo độ dài key:
16B -> aes-128-ecb, 24B -> aes-192-ecb, 32B -> aes-256-ecb
"""
key = base64.b64decode(key_b64)
klen = len(key)
if klen == 16:
cipher = 'aes-128-ecb'
elif klen == 24:
cipher = 'aes-192-ecb'
elif klen == 32:
cipher = 'aes-256-ecb'
else:
raise RuntimeError("Độ dài key không hợp lệ: %d bytes (cần 16/24/32)" % klen)
# Py2: trả về hex dạng str
try:
hexkey = key.encode('hex')
except Exception:
hexkey = binascii.b2a_hex(key)
return hexkey, cipher
def run_bash(script_bytes):
"""Run bash script passed as BYTES via stdin; returns stdout bytes (Py2-safe)."""
if isinstance(script_bytes, unicode):
script_bytes = script_bytes.encode('utf-8')
p = subprocess.Popen("/bin/bash -s", shell=True,
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate(script_bytes)
if p.returncode != 0:
# Ghi stderr vào cuối output để tiện debug nhưng không làm hỏng bytes
out = (out or b"") + b"\n===== STDERR =====\n" + (err or b"")
return out
ANSI_RE = re.compile(r'\x1b\[[0-9;]*[A-Za-z]')
def strip_ansi(s):
try:
# Py2: s có thể là bytes; cứ để bytes, regex vẫn xử được
return ANSI_RE.sub('', s)
except Exception:
return s
def safe_slug(s, maxlen=80):
# Chuyển sang ASCII-safe cho tên file: thay khoảng trắng/ký tự lạ bằng _
if isinstance(s, unicode):
s = s.encode('utf-8')
# bỏ ANSI, rồi thay ký tự không an toàn
s = strip_ansi(s)
s = re.sub(r'[^\w\.-]+', '_', s) # chỉ giữ a-zA-Z0-9_ . -
s = s.strip('_')
if len(s) > maxlen:
s = s[:maxlen]
return s
def extract_host_time(text):
"""
Tìm Hostname và Time bằng regex, không phụ thuộc thứ tự dòng.
Kỳ vọng trong output có dạng:
Hostname: myhost
Time: 2025-11-06 13:00:00
"""
t = strip_ansi(text)
# Tách dòng để tìm
lines = t.splitlines()
hostname_out, time_out = None, None
# Regex linh hoạt (bỏ qua hoa/thường, khoảng trắng)
re_host = re.compile(r'^\s*Hostname:\s*(\S+)\s*$', re.I)
re_time = re.compile(r'^\s*Time:\s*(.+?)\s*$', re.I)
for line in lines:
if hostname_out is None:
m = re_host.match(line)
if m:
hostname_out = m.group(1)
if time_out is None:
m = re_time.match(line)
if m:
time_out = m.group(1)
if hostname_out and time_out:
break
if not hostname_out:
# fallback an toàn
try:
hostname_out = socket.gethostname()
except Exception:
hostname_out = 'unknown-host'
if not time_out:
# fallback: now
try:
time_out = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
except Exception:
time_out = 'unknown-time'
# Chuẩn hoá
hostname_out = safe_slug(hostname_out, maxlen=80)
# format lại time sang safe filename
time_safe = time_out.replace('/', '-').replace('\\', '-').replace(':', '_').replace(' ', '-')
time_safe = safe_slug(time_safe, maxlen=80)
return hostname_out, time_safe
def run_audit(file_path=None):
# Banner
print(Fore.BLUE + """
_ _ _____ _____ _______ _ _ _____ _____ ______ _ _ _____ _ _ _____
/\\ | | | | __ \\_ _|__ __| | | | | /\\ | __ \\| __ \\| ____| \\ | |_ _| \\ | |/ ____|
/ \\ | | | | | | || | | | ______ | |__| | / \\ | |__) | | | | |__ | \\| | | | | \\| | | __
/ /\\ \\| | | | | | || | | | |______| | __ | / /\\ \\ | _ /| | | | __| | . ` | | | | . ` | | |_ |
/ ____ \\ |__| | |__| || |_ | | | | | |/ ____ \\| | \\ \\| |__| | |____| |\\ |_| |_| |\\ | |__| |
/_/ \\_\\____/|_____/_____| |_| |_| |_/_/ \\_\\_| \\_\\_____/|______|_| \\_|_____|_| \\_|\\_____|
""" + Fore.RESET)
print(Fore.RED + "Running Audit Hardening..." + Fore.RESET)
script_path = resolve_script_path(file_path)
if script_path is None:
_p(u"ERROR: No audit script found. Use -p <script.sh> or bundle the script.", getattr(Fore, 'RED', None))
return
_p(u" Script: %s" % script_path)
print(" [1/3] Reading script...")
with open(script_path, 'r') as f:
script_content = f.read()
content_u = script_content
script_parts = content_u.split(
"##################################################################################################################")
output = ""
for part in script_parts:
# Chuẩn hoá: part_bytes là bytes UTF-8; so sánh shebang ở dạng bytes
try:
unicode
if isinstance(part, unicode):
part_bytes = part.encode('utf-8')
else:
part_bytes = part
except NameError:
part_bytes = part # (không dùng trên Py3 ở đây)
if b"#!/bin/bash" not in part_bytes:
bash_src = b"#!/bin/bash\n" + part_bytes
else:
bash_src = part_bytes
out = run_bash(bash_src) # out là bytes
try:
output += out.decode('utf-8', 'ignore')
except Exception:
output += out # worst-case, giữ nguyên bytes
# Extract hostname/time lines from the produced output (keep logic as original)
hostname_out, time_generate = extract_host_time(output)
os_name_raw = detect_os_name()
os_name = safe_slug(os_name_raw, maxlen=60) # ASCII-safe
file_encrypt_name = '%s_%s_%s.txt.enc' % (hostname_out, os_name, time_generate)
enc_b64 = write_content(output)
with open(file_encrypt_name, 'wb') as f:
# enc_b64 is bytes in Py2
f.write(enc_b64)
if is_file_exist(file_encrypt_name):
_p(u"THÀNH CÔNG - FILE ENCRYPT %s" % file_encrypt_name, getattr(Fore, 'GREEN', None))
else:
_p(u"THẤT BẠI!!!", getattr(Fore, 'RED', None))
def run_ubuntu_audit(file_path=None):
old_file_path = '%s.txt' % hostname
if is_file_exist(old_file_path):
try:
os.remove(old_file_path)
except Exception:
pass
run_audit(file_path)
def parse_options():
if not OptionParser:
raise SystemExit("[ERROR] optparse not available. Please use Python 2.6/2.7.")
parser = OptionParser(usage="usage: %prog [-p PATH_TO_SCRIPT]")
parser.add_option("-p", "--path", dest="path",
help="Path to audit script (optional if bundled)", metavar="FILE", action="store", default=None)
(options, args) = parser.parse_args()
return options
if __name__ == '__main__':
import sys
opts = parse_options()
run_ubuntu_audit(opts.path)
File diff suppressed because it is too large Load Diff
Binary file not shown.
+48
View File
@@ -0,0 +1,48 @@
@echo off
REM ============================================================
REM Build AuditTool.exe for Windows Server 2012+
REM Requires: Python 3.8+, pip
REM ============================================================
echo [1/3] Installing dependencies...
python -m pip install pycryptodome colorama unidecode pyinstaller
if %errorlevel% neq 0 (
echo ERROR: pip install failed. Check Python installation.
pause
exit /b 1
)
echo [2/3] Copying PowerShell audit script...
copy /Y ..\audit_check_script\audit_cis_windows.ps1 audit_cis_windows.ps1
if %errorlevel% neq 0 (
echo ERROR: audit_cis_windows.ps1 not found at ..\audit_check_script\
pause
exit /b 1
)
echo [3/3] Building AuditTool.exe with PyInstaller...
python -m PyInstaller --onefile --console --name AuditTool ^
--add-data "audit_cis_windows.ps1;." ^
--hidden-import Crypto.Cipher ^
--hidden-import Crypto.PublicKey ^
--hidden-import Crypto.Util.Padding ^
windows_audit.py
if %errorlevel% neq 0 (
echo ERROR: PyInstaller build failed
pause
exit /b 1
)
echo.
echo ========================================
echo Build complete!
echo Output: dist\AuditTool.exe
echo.
echo Deploy to Windows Server:
echo 1. Copy dist\AuditTool.exe to target server
echo 2. Copy keys\public_key.pem to same folder as AuditTool.exe
echo 3. Run: AuditTool.exe
echo ========================================
pause
+114
View File
@@ -0,0 +1,114 @@
#!/bin/bash
# ============================================================
# Build Linux audit binaries with PyInstaller
# Run this on the TARGET OS version:
# - Ubuntu binary → build on Ubuntu 20.04+
# - CentOS 7 binary → build on CentOS 7
# - CentOS 6 binary → build on CentOS 6 (Python 2.6+)
# - Oracle binary → build on Oracle Linux 7+
#
# Usage: bash build_all.sh [ubuntu|centos|oracle|centos6|all]
# ============================================================
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PARENT_DIR="$(dirname "$SCRIPT_DIR")"
CHECK_SCRIPT_DIR="${PARENT_DIR}/audit_check_script"
DIST_DIR="${SCRIPT_DIR}/dist"
mkdir -p "${DIST_DIR}"
build_ubuntu() {
echo "=== Building Ubuntu Audit Tool ==="
cp "${CHECK_SCRIPT_DIR}/audit_cis_ubuntu_v202.sh" "${SCRIPT_DIR}/"
pip install pycryptodome cryptography colorama unidecode pyinstaller
pyinstaller --onefile --console --name ubuntu_audit \
--add-data "audit_cis_ubuntu_v202.sh:." \
--hidden-import colorama \
--hidden-import unidecode \
--hidden-import Crypto.Cipher \
--hidden-import Crypto.PublicKey \
--hidden-import Crypto.Util.Padding \
--hidden-import cryptography \
--hidden-import cryptography.hazmat.primitives.ciphers \
--hidden-import cryptography.hazmat.primitives.padding \
ubuntu_audit_v2.py
echo " -> ${DIST_DIR}/ubuntu_audit"
}
build_centos() {
echo "=== Building CentOS/RHEL Audit Tool ==="
cp "${CHECK_SCRIPT_DIR}/audit_cis_centos_v202.sh" "${SCRIPT_DIR}/"
pip install pycryptodome cryptography colorama unidecode pyinstaller
pyinstaller --onefile --console --name centos_rhel_audit \
--add-data "audit_cis_centos_v202.sh:." \
--hidden-import colorama \
--hidden-import unidecode \
--hidden-import Crypto.Cipher \
--hidden-import Crypto.PublicKey \
--hidden-import Crypto.Util.Padding \
--hidden-import cryptography \
--hidden-import cryptography.hazmat.primitives.ciphers \
--hidden-import cryptography.hazmat.primitives.padding \
centos_rhel_audit_v2.py
echo " -> ${DIST_DIR}/centos_rhel_audit"
}
build_oracle() {
echo "=== Building Oracle Linux Audit Tool ==="
cp "${CHECK_SCRIPT_DIR}/audit_cis_centos_v202.sh" "${SCRIPT_DIR}/"
pip install pycryptodome cryptography colorama unidecode pyinstaller
pyinstaller --onefile --console --name oracle_linux_audit \
--add-data "audit_cis_centos_v202.sh:." \
--hidden-import colorama \
--hidden-import unidecode \
--hidden-import Crypto.Cipher \
--hidden-import Crypto.PublicKey \
--hidden-import Crypto.Util.Padding \
--hidden-import cryptography \
--hidden-import cryptography.hazmat.primitives.ciphers \
--hidden-import cryptography.hazmat.primitives.padding \
oracle_linux_audit_v2.py
echo " -> ${DIST_DIR}/oracle_linux_audit"
}
build_centos6() {
echo "=== Building CentOS 6 Audit Tool ==="
cp "${CHECK_SCRIPT_DIR}/audit_cis_centos6.sh" "${SCRIPT_DIR}/"
pip install pyinstaller
pyinstaller --onefile --console --name centos6_audit \
--add-data "audit_cis_centos6.sh:." \
audit_centos6.py
echo " -> ${DIST_DIR}/centos6_audit"
}
build_all() {
build_ubuntu
build_centos
build_oracle
build_centos6
}
case "${1:-all}" in
ubuntu) build_ubuntu ;;
centos) build_centos ;;
oracle) build_oracle ;;
centos6) build_centos6 ;;
all) build_all ;;
*)
echo "Usage: bash build_all.sh [ubuntu|centos|oracle|centos6|all]"
exit 1
;;
esac
echo ""
echo "========================================"
echo "Build complete! Output: ${DIST_DIR}/"
echo ""
echo "Deploy to target server:"
echo " 1. Copy the binary from dist/"
echo " 2. Copy public_key.pem to same folder"
echo " 3. Run: ./<binary> (script is bundled, no -p needed)"
echo " Or: ./<binary> -p custom_script.sh"
echo "========================================"
+42
View File
@@ -0,0 +1,42 @@
# -*- mode: python ; coding: utf-8 -*-
# PyInstaller spec for CentOS 6 Audit Tool (Python 2.6+)
# Build: pyinstaller centos6_audit.spec
block_cipher = None
a = Analysis(
['audit_centos6.py'],
pathex=[],
binaries=[],
datas=[('audit_cis_centos6.sh', '.')],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='centos6_audit',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
+48
View File
@@ -0,0 +1,48 @@
# -*- mode: python ; coding: utf-8 -*-
# PyInstaller spec for CentOS/RHEL Audit Tool
# Build: pyinstaller centos_rhel_audit.spec
block_cipher = None
a = Analysis(
['centos_rhel_audit_v2.py'],
pathex=[],
binaries=[],
datas=[('audit_cis_centos_v202.sh', '.')],
hiddenimports=[
'colorama',
'unidecode',
'Crypto', 'Crypto.Cipher', 'Crypto.PublicKey', 'Crypto.Util.Padding',
'cryptography', 'cryptography.hazmat', 'cryptography.hazmat.primitives',
'cryptography.hazmat.primitives.ciphers', 'cryptography.hazmat.primitives.padding',
],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='centos_rhel_audit',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
+253
View File
@@ -0,0 +1,253 @@
import argparse
import base64
import os
import socket
import subprocess
import re
import sys
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
from colorama import Fore
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
hostname = socket.gethostname()
IPAddr = socket.gethostbyname(hostname)
cwd = os.getcwd()
BUNDLED_SCRIPT = "audit_cis_centos_v202.sh"
def resource_path(relative_path):
if getattr(sys, "frozen", False):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.dirname(os.path.abspath(__file__)), relative_path)
def resolve_script_path(user_path=None):
if user_path and os.path.isfile(user_path):
return os.path.abspath(user_path)
bundled = resource_path(BUNDLED_SCRIPT)
if os.path.isfile(bundled):
return bundled
same_dir = os.path.join(os.getcwd(), BUNDLED_SCRIPT)
if os.path.isfile(same_dir):
return same_dir
return None
def print_progress(current, total):
if total == 0:
return
bar_len = 40 # độ dài thanh
filled = int(bar_len * current / total)
bar = '' * filled + '-' * (bar_len - filled)
percent = int(current * 100 / total)
sys.stdout.write(f'\rĐang xử lý... [{bar}] {percent}%')
sys.stdout.flush()
# Khi xong hết thì xuống dòng mới cho đẹp
if current == total:
sys.stdout.write('\n')
sys.stdout.flush()
def get_os_tag():
"""Trả về nhãn OS dạng 'ubuntu_22_04' hoặc 'centos_7', ..."""
try:
if os.path.isfile("/etc/os-release"):
name = ""
version = ""
with open("/etc/os-release") as f:
for line in f:
if line.startswith("NAME=") and not name:
name = line.split("=", 1)[1].strip().strip('"').lower()
elif line.startswith("VERSION_ID=") and not version:
version = line.split("=", 1)[1].strip().strip('"').lower()
tag = f"{name}_{version}" if version else name
# chuẩn hoá: thay khoảng trắng và ký tự lạ
tag = re.sub(r"[^a-z0-9]+", "_", tag).strip("_")
if tag:
return tag
except Exception:
pass
try:
if os.path.isfile("/etc/redhat-release"):
txt = open("/etc/redhat-release").read().strip().lower()
# ví dụ: "centos linux release 7.9.2009 (core)"
# lấy vendor + major.minor
m = re.search(r"(centos|red hat|rhel|rocky|almalinux).*?release\s+([0-9]+(?:\.[0-9]+)?)", txt)
if m:
vendor = m.group(1).replace(" ", "")
ver = m.group(2).replace(".", "_")
return f"{vendor}_{ver}"
# fallback rút gọn
tag = re.sub(r"[^a-z0-9]+", "_", txt).strip("_")
if tag:
return tag
except Exception:
pass
try:
import platform
sysname = platform.system().lower()
release = platform.release().lower()
tag = f"{sysname}_{release}"
tag = re.sub(r"[^a-z0-9]+", "_", tag).strip("_")
return tag or "unknown_os"
except Exception:
return "unknown_os"
def write_content(content):
message = content.encode('utf-8')
aes_key = os.urandom(32)
iv = os.urandom(16)
padder = padding.PKCS7(128).padder()
padded_data = padder.update(message) + padder.finalize()
cipher_aes = Cipher(algorithms.AES(aes_key), modes.CBC(iv))
encryptor = cipher_aes.encryptor()
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
public_key = RSA.import_key(open('public_key.pem').read())
cipher_rsa = PKCS1_OAEP.new(public_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}"
def is_file_exist(path):
isFile = os.path.isfile(path)
if isFile:
return True
else:
return False
def run_bash(command):
process = subprocess.Popen(
['/bin/bash', '-c', command],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout, stderr = process.communicate()
rc = process.returncode
out = stdout.decode(errors="ignore")
err = stderr.decode(errors="ignore")
# Luôn luôn ghép stderr (nếu có) vào log để không mất thông tin
if err.strip():
out += "\n########## STDERR BEGIN ##########\n"
out += err
out += "\n########## STDERR END ##########\n"
# Trả về tuple: (output, stderr, return_code)
return out, err, rc
def run_audit(file_path):
print(Fore.BLUE + """
_ _ _____ _____ _______ _ _ _____ _____ ______ _ _ _____ _ _ _____
/\ | | | | __ \_ _|__ __| | | | | /\ | __ \| __ \| ____| \ | |_ _| \ | |/ ____|
/ \ | | | | | | || | | | ______ | |__| | / \ | |__) | | | | |__ | \| | | | | \| | | __
/ /\ \| | | | | | || | | | |______| | __ | / /\ \ | _ /| | | | __| | . ` | | | | . ` | | |_ |
/ ____ \ |__| | |__| || |_ | | | | | |/ ____ \| | \ \| |__| | |____| |\ |_| |_| |\ | |__| |
/_/ \_\____/|_____/_____| |_| |_| |_/_/ \_\_| \_\_____/|______|_| \_|_____|_| \_|\_____|
""" + Fore.RESET)
print(Fore.RED + "Running Audit Hardening..." + Fore.RESET)
script_path = resolve_script_path(file_path)
if script_path is None:
print(Fore.RED + "ERROR: No audit script found. Use -p <script.sh> or bundle the script." + Fore.RESET)
return
print(f" Script: {script_path}")
print(" [1/3] Reading script...")
with open(script_path, 'r', encoding='utf-8') as f:
script_content = f.read()
script = script_content.split(
"##################################################################################################################")
# Lọc bỏ các block rỗng trước
script_blocks = [block.strip() for block in script if block.strip()]
total_blocks = len(script_blocks)
output = ""
errors = [] # Lưu lỗi để hiển thị sau khi loading xong
for idx, i in enumerate(script_blocks, 1):
# Hiển thị progress bar
print_progress(idx, total_blocks)
if "#!/bin/bash" not in i:
result, err, rc = run_bash("#!/bin/bash\n{0}".format(i))
else:
result, err, rc = run_bash(i)
# Lưu lỗi nếu có (exit code != 0)
if rc != 0 and err.strip():
errors.append(f"[Block {idx}] {err.strip().split(chr(10))[0]}")
# Nếu lệnh lỗi và run_bash trả về None thì bỏ qua
if result is None:
continue
output += result
# Hiển thị các lỗi sau khi loading xong
if errors:
print(Fore.YELLOW + f"\n⚠ Có {len(errors)} cảnh báo:" + Fore.RESET)
for e in errors:
print(Fore.YELLOW + f"{e}" + Fore.RESET)
# Dùng regex để tìm Hostname và Audit Time trong output
hostname_match = re.search(r"Hostname:\s*(\S+)", output)
time_match = re.search(r"Audit Time:\s*(.+)", output)
hostname = hostname_match.group(1) if hostname_match else "unknown_host"
time_generate = time_match.group(1).strip().replace("-", "_").replace(":", "_").replace(" ", "-") if time_match else "unknown_time"
os_tag = get_os_tag()
file_encrypt_name = f"{hostname}_{os_tag}_{time_generate}.txt.enc"
# file_encrypt_name = '{}_{}.txt.enc'.format(hostname, time_generate)
encrypt_result = write_content(output)
with open(file_encrypt_name, 'w') as f:
f.write(encrypt_result)
if is_file_exist(file_encrypt_name):
print(Fore.GREEN + "THÀNH CÔNG - FILE ENCRYPT {}".format(file_encrypt_name) + Fore.RESET)
else:
print(Fore.RED + "THẤT BẠI RỒI THỬ LẠI NHÁ !!!" + Fore.RESET)
def run_ubuntu_audit(file_path=None):
old_file_path = '{}.txt'.format(hostname)
old_file = is_file_exist(old_file_path)
if old_file:
os.remove(old_file_path)
run_audit(file_path)
def main():
# Parse Arguments
parser = argparse.ArgumentParser(description='Audit Hardening')
parser.add_argument('-p', '--path', help='Path to audit script (optional if bundled)', default=None)
args = parser.parse_args()
return args
if __name__ == '__main__':
args = main()
run_ubuntu_audit(args.path)
+48
View File
@@ -0,0 +1,48 @@
# -*- mode: python ; coding: utf-8 -*-
# PyInstaller spec for Oracle Linux Audit Tool
# Build: pyinstaller oracle_linux_audit.spec
block_cipher = None
a = Analysis(
['oracle_linux_audit_v2.py'],
pathex=[],
binaries=[],
datas=[('audit_cis_centos_v202.sh', '.')],
hiddenimports=[
'colorama',
'unidecode',
'Crypto', 'Crypto.Cipher', 'Crypto.PublicKey', 'Crypto.Util.Padding',
'cryptography', 'cryptography.hazmat', 'cryptography.hazmat.primitives',
'cryptography.hazmat.primitives.ciphers', 'cryptography.hazmat.primitives.padding',
],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='oracle_linux_audit',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
+272
View File
@@ -0,0 +1,272 @@
import argparse
import base64
import os
import socket
import subprocess
import re
import sys
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
from colorama import Fore
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
hostname = socket.gethostname()
IPAddr = socket.gethostbyname(hostname)
cwd = os.getcwd()
BUNDLED_SCRIPT = "audit_cis_centos_v202.sh"
def resource_path(relative_path):
if getattr(sys, "frozen", False):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.dirname(os.path.abspath(__file__)), relative_path)
def resolve_script_path(user_path=None):
if user_path and os.path.isfile(user_path):
return os.path.abspath(user_path)
bundled = resource_path(BUNDLED_SCRIPT)
if os.path.isfile(bundled):
return bundled
same_dir = os.path.join(os.getcwd(), BUNDLED_SCRIPT)
if os.path.isfile(same_dir):
return same_dir
return None
def print_progress(current, total):
if total == 0:
return
bar_len = 40 # độ dài thanh
filled = int(bar_len * current / total)
bar = '' * filled + '-' * (bar_len - filled)
percent = int(current * 100 / total)
sys.stdout.write(f'\rĐang xử lý... [{bar}] {percent}%')
sys.stdout.flush()
# Khi xong hết thì xuống dòng mới cho đẹp
if current == total:
sys.stdout.write('\n')
sys.stdout.flush()
def get_os_tag():
"""
Trả về nhãn OS dạng 'oracle_linux_8_9', 'rhel_7', 'centos_7_9', 'ubuntu_22_04', ...
Ưu tiên /etc/os-release; fallback /etc/oracle-release hoặc /etc/redhat-release; cuối cùng uname.
"""
# 1) /etc/os-release (chuẩn mới trên hầu hết distro)
try:
if os.path.isfile("/etc/os-release"):
name = ""
version = ""
with open("/etc/os-release") as f:
for line in f:
if line.startswith("NAME=") and not name:
name = line.split("=", 1)[1].strip().strip('"').lower()
elif line.startswith("VERSION_ID=") and not version:
version = line.split("=", 1)[1].strip().strip('"').lower()
tag = f"{name}_{version}" if version else name
tag = re.sub(r"[^a-z0-9]+", "_", tag).strip("_")
if tag:
return tag
except Exception:
pass
# 2) Oracle Linux đời cũ
try:
if os.path.isfile("/etc/oracle-release"):
txt = open("/etc/oracle-release").read().strip().lower()
# ví dụ: "oracle linux server release 8.9"
m = re.search(r"(oracle)\s+linux.*?release\s+([0-9]+(?:\.[0-9]+)?)", txt)
if m:
vendor = "oracle_linux"
ver = m.group(2).replace(".", "_")
return f"{vendor}_{ver}"
tag = re.sub(r"[^a-z0-9]+", "_", txt).strip("_")
if tag:
return tag
except Exception:
pass
# 3) RHEL/CentOS/Alma/Rocky đời cũ
try:
if os.path.isfile("/etc/redhat-release"):
txt = open("/etc/redhat-release").read().strip().lower()
# ví dụ: "centos linux release 7.9.2009 (core)"
m = re.search(r"(centos|red hat|rhel|rocky|almalinux).*?release\s+([0-9]+(?:\.[0-9]+)?)", txt)
if m:
vendor = m.group(1).replace(" ", "")
ver = m.group(2).replace(".", "_")
return f"{vendor}_{ver}"
tag = re.sub(r"[^a-z0-9]+", "_", txt).strip("_")
if tag:
return tag
except Exception:
pass
# 4) uname (fallback cuối)
try:
import platform
sysname = platform.system().lower()
release = platform.release().lower()
tag = re.sub(r"[^a-z0-9]+", "_", f"{sysname}_{release}").strip("_")
return tag or "unknown_os"
except Exception:
return "unknown_os"
def write_content(content):
message = content.encode('utf-8')
aes_key = os.urandom(32)
iv = os.urandom(16)
padder = padding.PKCS7(128).padder()
padded_data = padder.update(message) + padder.finalize()
cipher_aes = Cipher(algorithms.AES(aes_key), modes.CBC(iv))
encryptor = cipher_aes.encryptor()
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
public_key = RSA.import_key(open('public_key.pem').read())
cipher_rsa = PKCS1_OAEP.new(public_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}"
def is_file_exist(path):
isFile = os.path.isfile(path)
if isFile:
return True
else:
return False
def run_bash(command):
process = subprocess.Popen(
['/bin/bash', '-c', command],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout, stderr = process.communicate()
rc = process.returncode
out = stdout.decode(errors="ignore")
err = stderr.decode(errors="ignore")
# Luôn luôn ghép stderr (nếu có) vào log để không mất thông tin
if err.strip():
out += "\n########## STDERR BEGIN ##########\n"
out += err
out += "\n########## STDERR END ##########\n"
# Trả về tuple: (output, stderr, return_code)
return out, err, rc
def run_audit(file_path=None):
print(Fore.BLUE + """
_ _ _____ _____ _______ _ _ _____ _____ ______ _ _ _____ _ _ _____
/\ | | | | __ \_ _|__ __| | | | | /\ | __ \| __ \| ____| \ | |_ _| \ | |/ ____|
/ \ | | | | | | || | | | ______ | |__| | / \ | |__) | | | | |__ | \| | | | | \| | | __
/ /\ \| | | | | | || | | | |______| | __ | / /\ \ | _ /| | | | __| | . ` | | | | . ` | | |_ |
/ ____ \ |__| | |__| || |_ | | | | | |/ ____ \| | \ \| |__| | |____| |\ |_| |_| |\ | |__| |
/_/ \_\____/|_____/_____| |_| |_| |_/_/ \_\_| \_\_____/|______|_| \_|_____|_| \_|\_____|
""" + Fore.RESET)
print(Fore.RED + "Running Audit Hardening..." + Fore.RESET)
script_path = resolve_script_path(file_path)
if script_path is None:
print(Fore.RED + "ERROR: No audit script found. Use -p <script.sh> or bundle the script." + Fore.RESET)
return
print(f" Script: {script_path}")
print(" [1/3] Reading script...")
with open(script_path, 'r', encoding='utf-8') as f:
script_content = f.read()
script = script_content.split(
"##################################################################################################################")
# Lọc bỏ các block rỗng trước
script_blocks = [block.strip() for block in script if block.strip()]
total_blocks = len(script_blocks)
output = ""
errors = [] # Lưu lỗi để hiển thị sau khi loading xong
for idx, i in enumerate(script_blocks, 1):
# Hiển thị progress bar
print_progress(idx, total_blocks)
if "#!/bin/bash" not in i:
result, err, rc = run_bash("#!/bin/bash\n{0}".format(i))
else:
result, err, rc = run_bash(i)
# Lưu lỗi nếu có (exit code != 0)
if rc != 0 and err.strip():
errors.append(f"[Block {idx}] {err.strip().split(chr(10))[0]}")
# Nếu lệnh lỗi và run_bash trả về None thì bỏ qua
if result is None:
continue
output += result
# Hiển thị các lỗi sau khi loading xong
if errors:
print(Fore.YELLOW + f"\n⚠ Có {len(errors)} cảnh báo:" + Fore.RESET)
for e in errors:
print(Fore.YELLOW + f"{e}" + Fore.RESET)
# Dùng regex để tìm Hostname và Audit Time trong output
hostname_match = re.search(r"Hostname:\s*(\S+)", output)
time_match = re.search(r"Audit Time:\s*(.+)", output)
hostname = hostname_match.group(1) if hostname_match else "unknown_host"
time_generate = time_match.group(1).strip().replace("-", "_").replace(":", "_").replace(" ", "-") if time_match else "unknown_time"
os_tag = get_os_tag()
file_encrypt_name = f"{hostname}_{os_tag}_{time_generate}.txt.enc"
# file_encrypt_name = '{}_{}.txt.enc'.format(hostname, time_generate)
encrypt_result = write_content(output)
with open(file_encrypt_name, 'w') as f:
f.write(encrypt_result)
if is_file_exist(file_encrypt_name):
print(Fore.GREEN + "THÀNH CÔNG - FILE ENCRYPT {}".format(file_encrypt_name) + Fore.RESET)
else:
print(Fore.RED + "THẤT BẠI RỒI THỬ LẠI NHÁ !!!" + Fore.RESET)
def run_os_audit(file_path=None):
old_file_path = '{}.txt'.format(hostname)
old_file = is_file_exist(old_file_path)
if old_file:
os.remove(old_file_path)
run_audit(file_path)
def main():
# Parse Arguments
parser = argparse.ArgumentParser(description='Audit Hardening')
parser.add_argument('-p', '--path', help='Path to audit script (optional if bundled)', default=None)
args = parser.parse_args()
return args
if __name__ == '__main__':
args = main()
run_os_audit(args.path)
+12
View File
@@ -0,0 +1,12 @@
# Windows Audit Python Dependencies
# Target: Python 3.8 (Windows Server 2012 compatible)
#
# Setup with conda:
# conda create -n audit python=3.8 -y
# conda activate audit
# pip install -r requirements.txt
pycryptodome>=3.15,<4
colorama>=0.4
unidecode>=1.3
pyinstaller>=5.0,<6
+48
View File
@@ -0,0 +1,48 @@
# -*- mode: python ; coding: utf-8 -*-
# PyInstaller spec for Ubuntu Audit Tool
# Build: pyinstaller ubuntu_audit.spec
block_cipher = None
a = Analysis(
['ubuntu_audit_v2.py'],
pathex=[],
binaries=[],
datas=[('audit_cis_ubuntu_v202.sh', '.')],
hiddenimports=[
'colorama',
'unidecode',
'Crypto', 'Crypto.Cipher', 'Crypto.PublicKey', 'Crypto.Util.Padding',
'cryptography', 'cryptography.hazmat', 'cryptography.hazmat.primitives',
'cryptography.hazmat.primitives.ciphers', 'cryptography.hazmat.primitives.padding',
],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='ubuntu_audit',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
+252
View File
@@ -0,0 +1,252 @@
import argparse
import base64
import os
import socket
import subprocess
import re
import sys
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
from colorama import Fore
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
hostname = socket.gethostname()
IPAddr = socket.gethostbyname(hostname)
cwd = os.getcwd()
BUNDLED_SCRIPT = "audit_cis_ubuntu_v202.sh"
def resource_path(relative_path):
if getattr(sys, "frozen", False):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.dirname(os.path.abspath(__file__)), relative_path)
def resolve_script_path(user_path=None):
if user_path and os.path.isfile(user_path):
return os.path.abspath(user_path)
bundled = resource_path(BUNDLED_SCRIPT)
if os.path.isfile(bundled):
return bundled
same_dir = os.path.join(os.getcwd(), BUNDLED_SCRIPT)
if os.path.isfile(same_dir):
return same_dir
return None
def print_progress(current, total):
if total == 0:
return
bar_len = 40 # độ dài thanh
filled = int(bar_len * current / total)
bar = '' * filled + '-' * (bar_len - filled)
percent = int(current * 100 / total)
sys.stdout.write(f'\rĐang xử lý... [{bar}] {percent}%')
sys.stdout.flush()
# Khi xong hết thì xuống dòng mới cho đẹp
if current == total:
sys.stdout.write('\n')
sys.stdout.flush()
def get_os_tag():
"""Trả về nhãn OS dạng 'ubuntu_22_04' hoặc 'centos_7', ..."""
try:
if os.path.isfile("/etc/os-release"):
name = ""
version = ""
with open("/etc/os-release") as f:
for line in f:
if line.startswith("NAME=") and not name:
name = line.split("=", 1)[1].strip().strip('"').lower()
elif line.startswith("VERSION_ID=") and not version:
version = line.split("=", 1)[1].strip().strip('"').lower()
tag = f"{name}_{version}" if version else name
# chuẩn hoá: thay khoảng trắng và ký tự lạ
tag = re.sub(r"[^a-z0-9]+", "_", tag).strip("_")
if tag:
return tag
except Exception:
pass
try:
if os.path.isfile("/etc/redhat-release"):
txt = open("/etc/redhat-release").read().strip().lower()
# ví dụ: "centos linux release 7.9.2009 (core)"
# lấy vendor + major.minor
m = re.search(r"(centos|red hat|rhel|rocky|almalinux).*?release\s+([0-9]+(?:\.[0-9]+)?)", txt)
if m:
vendor = m.group(1).replace(" ", "")
ver = m.group(2).replace(".", "_")
return f"{vendor}_{ver}"
# fallback rút gọn
tag = re.sub(r"[^a-z0-9]+", "_", txt).strip("_")
if tag:
return tag
except Exception:
pass
try:
import platform
sysname = platform.system().lower()
release = platform.release().lower()
tag = f"{sysname}_{release}"
tag = re.sub(r"[^a-z0-9]+", "_", tag).strip("_")
return tag or "unknown_os"
except Exception:
return "unknown_os"
def write_content(content):
message = content.encode('utf-8')
aes_key = os.urandom(32)
iv = os.urandom(16)
padder = padding.PKCS7(128).padder()
padded_data = padder.update(message) + padder.finalize()
cipher_aes = Cipher(algorithms.AES(aes_key), modes.CBC(iv))
encryptor = cipher_aes.encryptor()
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
public_key = RSA.import_key(open('public_key.pem').read())
cipher_rsa = PKCS1_OAEP.new(public_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}"
def is_file_exist(path):
isFile = os.path.isfile(path)
if isFile:
return True
else:
return False
def run_bash(command):
process = subprocess.Popen(
['/bin/bash', '-c', command],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout, stderr = process.communicate()
rc = process.returncode
out = stdout.decode(errors="ignore")
err = stderr.decode(errors="ignore")
# Luôn luôn ghép stderr (nếu có) vào log để không mất thông tin
if err.strip():
out += "\n########## STDERR BEGIN ##########\n"
out += err
out += "\n########## STDERR END ##########\n"
# Trả về tuple: (output, stderr, return_code)
return out, err, rc
def run_audit(file_path=None):
print(Fore.BLUE + """
_ _ _____ _____ _______ _ _ _____ _____ ______ _ _ _____ _ _ _____
/\ | | | | __ \_ _|__ __| | | | | /\ | __ \| __ \| ____| \ | |_ _| \ | |/ ____|
/ \ | | | | | | || | | | ______ | |__| | / \ | |__) | | | | |__ | \| | | | | \| | | __
/ /\ \| | | | | | || | | | |______| | __ | / /\ \ | _ /| | | | __| | . ` | | | | . ` | | |_ |
/ ____ \ |__| | |__| || |_ | | | | | |/ ____ \| | \ \| |__| | |____| |\ |_| |_| |\ | |__| |
/_/ \_\____/|_____/_____| |_| |_| |_/_/ \_\_| \_\_____/|______|_| \_|_____|_| \_|\_____|
""" + Fore.RESET)
print(Fore.RED + "Running Audit Hardening..." + Fore.RESET)
script_path = resolve_script_path(file_path)
if script_path is None:
print(Fore.RED + "ERROR: No audit script found. Use -p <script.sh> or bundle the script." + Fore.RESET)
return
print(f" Script: {script_path}")
print(" [1/3] Reading script...")
with open(script_path, 'r', encoding='utf-8') as f:
script_content = f.read()
script = script_content.split(
"##################################################################################################################")
# Lọc bỏ các block rỗng trước
script_blocks = [block.strip() for block in script if block.strip()]
total_blocks = len(script_blocks)
output = ""
errors = [] # Lưu lỗi để hiển thị sau khi loading xong
for idx, i in enumerate(script_blocks, 1):
# Hiển thị progress bar
print_progress(idx, total_blocks)
if "#!/bin/bash" not in i:
result, err, rc = run_bash("#!/bin/bash\n{0}".format(i))
else:
result, err, rc = run_bash(i)
# Lưu lỗi nếu có (exit code != 0)
if rc != 0 and err.strip():
errors.append(f"[Block {idx}] {err.strip().split(chr(10))[0]}")
# Nếu lệnh lỗi và run_bash trả về None thì bỏ qua
if result is None:
continue
output += result
# Hiển thị các lỗi sau khi loading xong
if errors:
print(Fore.YELLOW + f"\n⚠ Có {len(errors)} cảnh báo:" + Fore.RESET)
for e in errors:
print(Fore.YELLOW + f"{e}" + Fore.RESET)
# Dùng regex để tìm Hostname và Audit Time trong output
hostname_match = re.search(r"Hostname:\s*(\S+)", output)
time_match = re.search(r"Audit Time:\s*(.+)", output)
hostname = hostname_match.group(1) if hostname_match else "unknown_host"
time_generate = time_match.group(1).strip().replace("-", "_").replace(":", "_").replace(" ", "-") if time_match else "unknown_time"
os_tag = get_os_tag()
file_encrypt_name = f"{hostname}_{os_tag}_{time_generate}.txt.enc"
# file_encrypt_name = '{}_{}.txt.enc'.format(hostname, time_generate)
encrypt_result = write_content(output)
with open(file_encrypt_name, 'w') as f:
f.write(encrypt_result)
if is_file_exist(file_encrypt_name):
print(Fore.GREEN + "THÀNH CÔNG - FILE ENCRYPT {}".format(file_encrypt_name) + Fore.RESET)
else:
print(Fore.RED + "THẤT BẠI RỒI THỬ LẠI NHÁ !!!" + Fore.RESET)
def run_ubuntu_audit(file_path=None):
old_file_path = '{}.txt'.format(hostname)
old_file = is_file_exist(old_file_path)
if old_file:
os.remove(old_file_path)
run_audit(file_path)
def main():
parser = argparse.ArgumentParser(description='Audit Hardening')
parser.add_argument('-p', '--path', help='Path to audit script (optional if bundled)', default=None)
args = parser.parse_args()
return args
if __name__ == '__main__':
args = main()
run_ubuntu_audit(args.path)
+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)