반응형

파이썬에서는 multiprocessing 모듈을 이용해서 여러 코어에서 동시에 조합을 시도할 수 있습니다. 다만, 비밀번호 크래킹을 단순 brute-force(완전 탐색)로만 하면 경우의 수가 기하급수적으로 커져서 사실상 현실적으로 풀기 힘들어집니다. 그래서 실제 보안 연구나 해킹 실무에서는 여러 현대식 접근 방식이 사용되는데, 아래 코드 하단에 정리합니다.

 

2. 멀티코어 병렬처리, 비밀번호 찾기 (ZIP + MS Office)

 
import itertools
import zipfile
import msoffcrypto
import io
import os
import multiprocessing as mp

class PasswordCracker:
    """
    다양한 조합으로 비밀번호를 추측하여 ZIP, DOCX, XLSX, PPTX 파일의
    암호를 해독하는 클래스입니다.
    멀티프로세싱을 사용하여 속도를 높입니다.
    """
    def __init__(self, passwd_string, min_len, max_len, processes=None):
        """
        클래스를 초기화합니다.
       
        Args:
            passwd_string (str): 비밀번호에 사용될 문자열(예: '0123456789abc').
            min_len (int): 추측할 비밀번호의 최소 길이.
            max_len (int): 추측할 비밀번호의 최대 길이.
            processes (int, optional): 사용할 프로세스(코어)의 수. 지정하지 않으면 CPU 코어 수만큼 사용.
        """
        self.passwd_string = passwd_string
        self.min_len = min_len
        self.max_len = max_len
        self.processes = processes or mp.cpu_count()

    def crack(self, file_path):
        """
        주어진 파일의 암호를 해독하는 메인 함수입니다.
        파일 확장자에 따라 적절한 해독 메서드를 호출합니다.

        Args:
            file_path (str): 암호를 해독할 파일의 경로.

        Returns:
            str or None: 비밀번호를 찾으면 비밀번호를, 찾지 못하면 None을 반환.
        """
        file_ext = os.path.splitext(file_path)[1].lower()

        if file_ext == ".zip":
            return self._crack_zip(file_path)
        elif file_ext in [".pptx", ".docx", ".xlsx"]:
            return self._crack_office(file_path)
        else:
            print(f"[!] {file_ext} 형식은 지원하지 않습니다. (zip, office만 지원)")
            return None

    # --- ZIP 파일 해독 관련 메서드 ---
    def _try_zip_password(self, args):
        """
        하나의 비밀번호로 ZIP 파일의 압축을 해제해 보는 함수입니다.
        성공하면 비밀번호를 반환하고, 실패하면 None을 반환합니다.

        Args:
            args (tuple): (파일 경로, 비밀번호) 튜플.

        Returns:
            str or None: 성공 시 비밀번호, 실패 시 None.
        """
        file_path, password = args
        try:
            zFile = zipfile.ZipFile(file_path)
            zFile.extractall(pwd=password.encode('utf-8'))
            return password
        except:
            return None

    def _crack_zip(self, file_path):
        """
        멀티프로세싱을 사용하여 ZIP 파일의 암호를 해독하는 함수입니다.
        각 비밀번호 추측 시도마다 터미널에 진행 상황을 표시합니다.

        Args:
            file_path (str): ZIP 파일의 경로.
           
        Returns:
            str or None: 비밀번호를 찾으면 비밀번호를, 찾지 못하면 None을 반환.
        """
        print(f"[*] ZIP 파일 '{file_path}' 암호 해독 시작...")
        with mp.Pool(self.processes) as pool:
            for length in range(self.min_len, self.max_len + 1):
                attempts = ("".join(p) for p in itertools.product(self.passwd_string, repeat=length))
                args_gen = ((file_path, pwd) for pwd in attempts)
               
                # imap_unordered를 사용하여 결과를 비동기적으로 처리
                for result in pool.imap_unordered(self._try_zip_password, args_gen, chunksize=1000):
                    # 현재 시도하고 있는 비밀번호를 터미널에 표시
                    print(f"[*] 시도 중: '{result if result else result, ' 실패한 추측'}'", end='\r')
                    if result:
                        print(f"\n[+] 비밀번호는 '{result}' 입니다.(zip)")
                        pool.terminate() # 비밀번호를 찾으면 모든 프로세스 종료
                        return result
       
        print("\n[-] 비밀번호를 찾지 못했습니다.(zip)")
        return None

    # --- MS Office 파일 해독 관련 메서드 ---
    def _try_office_password(self, args):
        """
        하나의 비밀번호로 MS Office 파일의 암호를 해제해 보는 함수입니다.
        성공하면 비밀번호를 반환하고, 실패하면 None을 반환합니다.

        Args:
            args (tuple): (파일 경로, 비밀번호) 튜플.

        Returns:
            str or None: 성공 시 비밀번호, 실패 시 None.
        """
        file_path, password = args
        try:
            with open(file_path, "rb") as f:
                office_file = msoffcrypto.OfficeFile(f)
                office_file.load_key(password=password)
                decrypted = io.BytesIO()
                office_file.decrypt(decrypted)
                return password
        except:
            return None

    def _crack_office(self, file_path):
        """
        멀티프로세싱을 사용하여 MS Office 파일의 암호를 해독하는 함수입니다.
        각 비밀번호 추측 시도마다 터미널에 진행 상황을 표시합니다.

        Args:
            file_path (str): MS Office 파일의 경로.

        Returns:
            str or None: 비밀번호를 찾으면 비밀번호를, 찾지 못하면 None을 반환.
        """
        print(f"[*] MS Office 파일 '{file_path}' 암호 해독 시작...")
        with mp.Pool(self.processes) as pool:
            for length in range(self.min_len, self.max_len + 1):
                attempts = ("".join(p) for p in itertools.product(self.passwd_string, repeat=length))
                args_gen = ((file_path, pwd) for pwd in attempts)
               
                # imap_unordered를 사용하여 결과를 비동기적으로 처리
                for result in pool.imap_unordered(self._try_office_password, args_gen, chunksize=500):
                    # 현재 시도하고 있는 비밀번호를 터미널에 표시
                    print(f"[*] 시도 중: '{result if result else result, ' 실패한 추측'}'", end='\r')
                    if result:
                        print(f"\n[+] 비밀번호는 '{result}' 입니다.(office)")
                        pool.terminate() # 비밀번호를 찾으면 모든 프로세스 종료
                        return result
       
        print("\n[-] 비밀번호를 찾지 못했습니다.(office)")
        return None

if __name__ == "__main__":
    # 비밀번호에 사용될 문자와 최소/최대 길이 설정
    passwd_string = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    cracker = PasswordCracker(passwd_string, min_len=3, max_len=4)

    # 파일 경로 지정
    file_path = "워드암호파일_123.docx"
   
    cracker.crack(file_path)
 

 

  • 멀티프로세싱 사용 (multiprocessing.Pool)
    • 여러 CPU 코어에서 동시에 비밀번호 대입 시도
    • imap_unordered → 결과를 비동기적으로 받아서 성공 즉시 종료
  • 성능 최적화
    • chunksize 조절로 워커별 처리량 최적화
    • 작은 chunksize → 빠른 응답 (즉시 종료 가능)

 

[ Brute-force 이외의 현대적 비밀번호 찾는 방안 정리 ]

1. Dictionary Attack (사전 공격)

  • 미리 만들어둔 비밀번호 후보 사전을 활용해 시도.
  • 예: password123, qwerty, iloveyou, 12345678 등
  • 장점: 실제 사람들이 자주 쓰는 패턴을 빠르게 맞출 수 있음.
  • 단점: 사전에 없는 랜덤 비밀번호는 못 잡음.

2. Hybrid Attack (혼합 공격)

  • 사전 단어 + 규칙 변형
  • 예: password → Password1!, pa$$w0rd, password2025
  • 보통 John the Ripper 나 Hashcat 같은 툴이 이 방식을 자동 지원합니다.

3. Mask Attack (패턴 기반 공격)

  • 비밀번호가 규칙적이라는 전제에서 범위를 줄임.
  • 예: 은행·회사 시스템에서 많이 쓰는 패턴
    • 숫자 6자리 (예: 123456, 202308)
    • 첫 글자 대문자 + 영문 + 숫자 2자리 (예: Abcd12)
  • 범위를 줄여서 brute-force보다 훨씬 빠르게 시도 가능.

4. Rainbow Table Attack

  • 해시된 비밀번호(예: MD5, SHA1 등)에 대해 미리 계산된 해시-평문 매핑 테이블을 이용.
  • 비밀번호 해시만 있으면 빠르게 역추적 가능.
  • 단점: Salt(난수) 기법이 적용된 경우 무력화됨.

5. Rule-based Attack

  • 특정 규칙 기반 변형 (ex. 첫 글자 대문자, 뒤에 ! 붙임).
  • 사전 공격과 결합해 확장.

6. AI / ML 기반 공격 (최신 연구 분야)

  • 최근에는 머신러닝으로 사람들의 비밀번호 생성 습관을 학습한 뒤,
    • 실제로 자주 쓰이는 비밀번호를 확률적으로 예측
    • brute-force보다 훨씬 빠르게 "사람이 쓸 법한" 후보를 뽑아냄
  • 예: PassGAN, OMEN, DeepCrack 같은 연구 프로젝트

7. GPU 가속 (Hashcat, CUDA/OpenCL 활용)

  • CPU 대신 GPU를 이용해 수억~수십억 번/s 시도 가능.
  • 일반 PC에서 brute-force로 8자리 영문/숫자 탐색은 현실적으로 불가하지만,
    • GPU 1대로는 수일
    • GPU 클러스터로는 몇 시간 안에 가능

[ 정리 ]

  • Brute-force는 최후의 방법 (시간 너무 오래 걸림)
  • 현실적인 접근은:
    1. 사전 + 하이브리드 먼저
    2. 특정 패턴/마스크 기반
    3. 그래도 안 되면 GPU 병렬 brute-force

 

반응형
반응형

본인이 작성한 오피스(워드, 엑셀, 파워포인트)나 집파일에 암호를 걸어두고, 시간이 지나 비밀번호가 생각나지 않을때가 종종 있어 난처한 경우가 발생합니다. 이때, 시간 여유가 많이 있고 또한 아주 간단한 암호를 설정했다면 '단순 brute-force(무차별 대입) 방식'을 시도해 볼 수 있습니다. (*암호화된 파일을 분실한 경우, 개발사에서도 암호를 복구해 주지 않는 것으로 알고 있습니다. 사본/이전본을 찾는 게 현실적인 해법입니다. 추가로, 본문 하단에 암호분실을 대비한 예방팁을 정리하였습니다.)


  • ZIP 파일
    • zipfile 모듈로 열 수 있고, extractall(pwd=...) 같은 방식으로 비밀번호 검증 가능.
  • MS Office (ppt, pptx, doc, docx, xls, xlsx 등)
    • pptx/docx/xlsx는 사실상 ZIP 압축 기반 구조지만, 내부에 MS가 정한 암호화 헤더가 포함돼 있음.
    • 단, 전용 라이브러리 필요.
  • 아래한글(HWP)
    • HWP 5.0 이후는 OLE Compound File 포맷 (마이크로소프트 OLE 구조).
    • 암호화는 자체 알고리즘으로 되어 있어 파이썬에서 직접 풀 수 있는 공개 라이브러리는 사실상 없음.
    • → olefile + 한글 암호화 해석 구현이 필요 (공식 라이브러리 없음).
    • 암호화된 HWP는 한글에서 AES, SHA1 기반 암호화를 쓰는데, 비밀번호 없이는 복호화 불가.
    • 가능하다면 “암호 해제 프로그램” (상용 소프트웨어) 사용이 현실적이나, 현재까지 그러한 프로그램은 없다고 알고 있음.

1. 통합 코드 (ZIP + MS Office 파일 비밀번호 찾기) 

 
# pip install msoffcrypto-tool

import itertools
import zipfile
import msoffcrypto
import io
import os

class PasswordCracker:
    """
    다양한 조합으로 비밀번호를 추측하여 ZIP, DOCX, XLSX, PPTX 파일의
    암호를 해독하는 클래스입니다.
    """
    def __init__(self, passwd_string, min_len, max_len):
        """
        클래스를 초기화합니다.
       
        Args:
            passwd_string (str): 비밀번호에 사용될 문자열(예: '0123456789').
            min_len (int): 추측할 비밀번호의 최소 길이.
            max_len (int): 추측할 비밀번호의 최대 길이.
        """
        self.passwd_string = passwd_string
        self.min_len = min_len
        self.max_len = max_len

    def crack(self, file_path):
        """
        주어진 파일의 암호를 해독하는 메인 함수입니다.
        파일 확장자에 따라 적절한 해독 메서드를 호출합니다.

        Args:
            file_path (str): 암호를 해독할 파일의 경로.

        Returns:
            str or None: 비밀번호를 찾으면 비밀번호를, 찾지 못하면 None을 반환.
        """
        file_ext = os.path.splitext(file_path)[1].lower()
       
        if file_ext == ".zip":
            return self._crack_zip(file_path)
        elif file_ext in [".pptx", ".docx", ".xlsx"]: # 'xlsx'는 문자열로 변경
            return self._crack_office(file_path)
        else:
            print(f"[!] {file_ext} 형식은 지원하지 않습니다.(zip, office만 지원)")
            return None
   
    def _crack_zip(self, file_path):
        """
        ZIP 파일의 암호를 해독하는 함수입니다.
        비밀번호를 찾으면 즉시 해당 비밀번호를 반환하고, 함수를 종료합니다.
        """
        try:
            zFile = zipfile.ZipFile(file_path)
        except zipfile.BadZipFile:
            print("[-] 유효하지 않은 ZIP 파일입니다.")
            return None

        for length in range(self.min_len, self.max_len + 1):
            for attempt in itertools.product(self.passwd_string, repeat = length):
                passwd = "".join(attempt)
                # 시도 중인 비밀번호를 터미널에 표시 (동일 라인에 덮어쓰기)
                print(f"[*] 시도 중: '{passwd}'", end='\r')
                try:
                    zFile.extractall(pwd=passwd.encode('utf-8'))
                    print(f"\n[+] 비밀번호는 '{passwd}'입니다.(zip)")
                    return passwd # 비밀번호를 찾으면 즉시 반환
                except (RuntimeError, zipfile.BadZipFile):
                    # 비밀번호가 틀렸을 때 발생하는 예외는 무시하고 다음 시도를 계속
                    continue
       
        print("\n[-] 비밀번호를 찾지 못했습니다.(zip)")
        return None
   
    def _crack_office(self, file_path):
        """
        MS Office 파일의 암호를 해독하는 함수입니다.
        비밀번호를 찾으면 즉시 해당 비밀번호를 반환하고, 함수를 종료합니다.
        """
        for length in range(self.min_len, self.max_len + 1):
            for attempt in itertools.product(self.passwd_string, repeat=length):
                passwd = "".join(attempt)
                # 시도 중인 비밀번호를 터미널에 표시 (동일 라인에 덮어쓰기)
                print(f"[*] 시도 중: '{passwd}'", end='\r')
                try:
                    with open(file_path, "rb") as f:
                        office_file = msoffcrypto.OfficeFile(f)
                        office_file.load_key(password=passwd)
                        decrypted = io.BytesIO()
                        office_file.decrypt(decrypted)
                        print(f"\n[+] 비밀번호는 '{passwd}'입니다.(office)")
                        return passwd # 비밀번호를 찾으면 즉시 반환
                except Exception as e:
                    # 비밀번호가 틀렸을 때 발생하는 예외는 무시하고 다음 시도를 계속
                    continue
                   
        print("\n[-] 비밀번호를 찾지 못했습니다.(office)")
        return None

if __name__ == "__main__":
    # 비밀번호에 사용될 문자와 최소/최대 길이 설정
    passwd_string = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    cracker = PasswordCracker(passwd_string, min_len=3, max_len=4)

    # 파일 경로 지정
    file_path = "워드암호파일_123.docx"
   
    cracker.crack(file_path)
 

 

  • 파일 확장자 자동 구분
    • .zip → zipfile 모듈로 brute-force
    • .pptx/.docx/.xlsx → msoffcrypto-tool로 brute-force
  • 무차별 대입(brute-force)
    • min_len ~ max_len 길이의 모든 조합을 생성 (itertools.product)
    • 하나씩 대입 → 맞으면 성공, 프로그램 종료

 

[ 예방 팁 ]

  • 비밀번호 관리자 사용: 조직/개인별로 안전하게 공유·보관.
  • 버전 기록이 있는 저장소 사용: OneDrive/SharePoint/Google Drive 등.
  • 중요 문서는 이중 보호: 암호+접근권한(ACL)로 관리하고, 비상 복구 담당자/절차를 미리 지정.
  • 규칙적 백업: 로컬+클라우드 3-2-1 규칙(사본 3개, 매체 2종, 오프사이트 1개).
반응형

+ Recent posts