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 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)