password mangling custom

agnusdei·2025년 9월 9일

CTF

목록 보기
95/185

1. 대안 도구들

1.1 Hashcat (가장 추천)

# 설치
apt install hashcat

# 기본 사용법 (룰 파일이 훨씬 직관적)
hashcat -m 0 -a 0 hashes.txt wordlist.txt -r rules/best64.rule

# 커스텀 룰 파일 생성 (rules/custom.rule)
echo ": # 원본
c      # 첫글자 대문자  
$1     # 뒤에 1 추가
$123   # 뒤에 123 추가
^1     # 앞에 1 추가
sa@    # a를 @로 치환" > rules/custom.rule

# 실행
hashcat -m 0 -a 0 hashes.txt wordlist.txt -r rules/custom.rule

1.2 Mentalist (GUI 기반)

# GitHub에서 다운로드
git clone https://github.com/sc0tfree/mentalist.git
cd mentalist
pip install -r requirements.txt
python mentalist.py

# GUI에서 직관적으로 룰 설정 가능
# - 드래그 앤 드롭으로 규칙 조합
# - 실시간 미리보기
# - 다양한 출력 형식

1.3 Crunch (패턴 기반 생성)

# 설치
apt install crunch

# 사용법
crunch 8 8 -t pass%%%% > wordlist.txt  # pass + 4자리 숫자
crunch 6 10 abcdefghijklmnopqrstuvwxyz0123456789 > wordlist.txt

2. Python 스크립트 (추천)

2.1 기본 맹글링 스크립트

#!/usr/bin/env python3
import sys
import itertools

def basic_mangle(word):
    """기본적인 맹글링 변형"""
    variations = []
    
    # 원본
    variations.append(word)
    
    # 대소문자 변형
    variations.append(word.lower())
    variations.append(word.upper())
    variations.append(word.capitalize())
    variations.append(word.swapcase())
    
    # 숫자 추가
    for num in ['1', '12', '123', '1234', '01', '2024', '2023']:
        variations.append(word + num)
        variations.append(word.capitalize() + num)
    
    # 특수문자 추가
    for char in ['!', '@', '#', '$', '!@', '@!']:
        variations.append(word + char)
        variations.append(word.capitalize() + char)
    
    # 문자 치환
    substitutions = {
        'a': '@', 'e': '3', 'i': '1', 'o': '0', 's': '$', 't': '+'
    }
    
    temp_word = word
    for old, new in substitutions.items():
        temp_word = temp_word.replace(old, new)
    variations.append(temp_word)
    variations.append(temp_word.capitalize())
    
    # 앞에 문자 추가
    for prefix in ['P', '1', '@']:
        variations.append(prefix + word)
    
    return list(set(variations))  # 중복 제거

def advanced_mangle(word):
    """고급 맹글링"""
    variations = []
    
    # 연도 패턴
    years = ['2020', '2021', '2022', '2023', '2024', '2025']
    for year in years:
        variations.append(word + year)
        variations.append(word.capitalize() + year)
        variations.append(year + word)
    
    # 계절 패턴
    seasons = ['Spring', 'Summer', 'Fall', 'Winter']
    for season in seasons:
        variations.append(word + season)
        variations.append(season + word)
    
    # 복합 치환
    leetspeak = {
        'a': '@', 'e': '3', 'i': '1', 'o': '0', 
        's': '$', 't': '+', 'l': '1', 'g': '9'
    }
    
    # 부분 치환 (모든 문자를 치환하지 않고 일부만)
    for i in range(len(word)):
        char = word[i].lower()
        if char in leetspeak:
            new_word = word[:i] + leetspeak[char] + word[i+1:]
            variations.append(new_word)
            variations.append(new_word.capitalize())
    
    return list(set(variations))

def korean_corporate_mangle(word):
    """한국 기업 환경 특화"""
    variations = []
    
    # 한국 기업 패턴
    corp_suffixes = ['2024!', '2023!', 'Corp', 'Ltd', 'Inc', 'Co']
    for suffix in corp_suffixes:
        variations.append(word + suffix)
    
    # 부서명 패턴
    departments = ['IT', 'HR', 'Sales', 'Dev', 'Admin']
    for dept in departments:
        variations.append(word + dept)
        variations.append(dept + word)
    
    # 한국식 숫자 패턴
    korean_nums = ['01', '02', '010', '1004', '7777', '8888']
    for num in korean_nums:
        variations.append(word + num)
    
    return list(set(variations))

def main():
    if len(sys.argv) != 2:
        print("Usage: python3 mangle.py wordlist.txt")
        sys.exit(1)
    
    input_file = sys.argv[1]
    
    try:
        with open(input_file, 'r', encoding='utf-8') as f:
            words = [line.strip() for line in f if line.strip()]
        
        for word in words:
            # 기본 맹글링
            basic_vars = basic_mangle(word)
            for var in basic_vars:
                print(var)
            
            # 고급 맹글링 (선택적)
            advanced_vars = advanced_mangle(word)
            for var in advanced_vars:
                print(var)
            
            # 한국 특화 (선택적)
            korean_vars = korean_corporate_mangle(word)
            for var in korean_vars:
                print(var)
                
    except FileNotFoundError:
        print(f"Error: File '{input_file}' not found")
        sys.exit(1)

if __name__ == "__main__":
    main()

2.2 고급 Python 스크립트 (객체지향)

#!/usr/bin/env python3
import sys
import argparse
from itertools import product

class PasswordMangler:
    def __init__(self):
        self.substitutions = {
            'a': ['@', '4'], 'e': ['3'], 'i': ['1', '!'], 
            'o': ['0'], 's': ['$', '5'], 't': ['+', '7'],
            'l': ['1'], 'g': ['9', '6']
        }
        
        self.common_suffixes = [
            '1', '12', '123', '1234', '01', 
            '!', '@', '#', '$', '!@', '@!',
            '2024', '2023', '2022', '2021'
        ]
        
        self.common_prefixes = ['1', 'P', 'A', '@']
        
    def case_variations(self, word):
        """대소문자 변형"""
        return [
            word, word.lower(), word.upper(), 
            word.capitalize(), word.swapcase()
        ]
    
    def leet_speak(self, word, max_subs=3):
        """리트스피크 변형"""
        variations = []
        word_lower = word.lower()
        
        # 치환 가능한 위치 찾기
        sub_positions = []
        for i, char in enumerate(word_lower):
            if char in self.substitutions:
                sub_positions.append((i, char))
        
        # 부분 치환 (최대 max_subs개)
        for r in range(1, min(len(sub_positions), max_subs) + 1):
            for positions in product(*[self.substitutions[char] for pos, char in sub_positions[:r]]):
                new_word = list(word)
                for i, (pos, _) in enumerate(sub_positions[:r]):
                    new_word[pos] = positions[i]
                variations.append(''.join(new_word))
        
        return variations
    
    def add_suffixes(self, word):
        """접미사 추가"""
        return [word + suffix for suffix in self.common_suffixes]
    
    def add_prefixes(self, word):
        """접두사 추가"""
        return [prefix + word for prefix in self.common_prefixes]
    
    def mangle_word(self, word):
        """단어 맹글링"""
        all_variations = set()
        
        # 기본 케이스 변형
        case_vars = self.case_variations(word)
        all_variations.update(case_vars)
        
        # 각 케이스에 대해 추가 변형
        for base_word in case_vars:
            # 리트스피크
            leet_vars = self.leet_speak(base_word)
            all_variations.update(leet_vars)
            
            # 접미사 추가
            suffix_vars = self.add_suffixes(base_word)
            all_variations.update(suffix_vars)
            
            # 접두사 추가  
            prefix_vars = self.add_prefixes(base_word)
            all_variations.update(prefix_vars)
            
            # 리트스피크 + 접미사 조합
            for leet_word in leet_vars:
                suffix_leet_vars = self.add_suffixes(leet_word)
                all_variations.update(suffix_leet_vars)
        
        return sorted(all_variations)

def main():
    parser = argparse.ArgumentParser(description='Advanced Password Mangler')
    parser.add_argument('wordlist', help='Input wordlist file')
    parser.add_argument('-o', '--output', help='Output file (default: stdout)')
    parser.add_argument('--max-variations', type=int, default=100, 
                       help='Maximum variations per word')
    
    args = parser.parse_args()
    
    mangler = PasswordMangler()
    
    try:
        with open(args.wordlist, 'r', encoding='utf-8') as f:
            words = [line.strip() for line in f if line.strip()]
        
        output_file = open(args.output, 'w', encoding='utf-8') if args.output else sys.stdout
        
        for word in words:
            variations = mangler.mangle_word(word)
            for i, variation in enumerate(variations):
                if i >= args.max_variations:
                    break
                print(variation, file=output_file)
        
        if args.output:
            output_file.close()
            
    except FileNotFoundError:
        print(f"Error: File '{args.wordlist}' not found")
        sys.exit(1)

if __name__ == "__main__":
    main()

3. Bash 스크립트

3.1 간단한 Bash 스크립트

#!/bin/bash
# simple_mangle.sh

if [ $# -ne 1 ]; then
    echo "Usage: $0 wordlist.txt"
    exit 1
fi

WORDLIST="$1"

while IFS= read -r word; do
    # 원본
    echo "$word"
    
    # 대소문자 변형
    echo "$word" | tr '[:upper:]' '[:lower:]'
    echo "$word" | tr '[:lower:]' '[:upper:]'
    echo "${word^}"  # 첫글자만 대문자
    
    # 숫자 추가
    for num in 1 12 123 1234 01 2024 2023; do
        echo "${word}${num}"
        echo "${word^}${num}"
    done
    
    # 특수문자 추가
    for char in '!' '@' '#' '$' '!@' '@!'; do
        echo "${word}${char}"
        echo "${word^}${char}"
    done
    
    # 리트스피크
    echo "$word" | sed 's/a/@/g; s/e/3/g; s/i/1/g; s/o/0/g; s/s/$/g'
    
done < "$WORDLIST" | sort -u

3.2 고급 Bash 스크립트

#!/bin/bash
# advanced_mangle.sh

declare -A SUBSTITUTIONS=(
    ["a"]="@" ["e"]="3" ["i"]="1" ["o"]="0" 
    ["s"]="$" ["t"]="+" ["l"]="1" ["g"]="9"
)

SUFFIXES=("1" "12" "123" "1234" "!" "@" "#" "$" "2024" "2023")
PREFIXES=("P" "A" "1" "@")

mangle_word() {
    local word="$1"
    local variations=()
    
    # 기본 변형
    variations+=("$word")
    variations+=("$(echo "$word" | tr '[:upper:]' '[:lower:]')")
    variations+=("$(echo "$word" | tr '[:lower:]' '[:upper:]')")
    variations+=("${word^}")
    
    # 접미사 추가
    for suffix in "${SUFFIXES[@]}"; do
        variations+=("${word}${suffix}")
        variations+=("${word^}${suffix}")
    done
    
    # 접두사 추가
    for prefix in "${PREFIXES[@]}"; do
        variations+=("${prefix}${word}")
    done
    
    # 리트스피크
    local leet_word="$word"
    for char in "${!SUBSTITUTIONS[@]}"; do
        leet_word="${leet_word//$char/${SUBSTITUTIONS[$char]}}"
    done
    variations+=("$leet_word")
    variations+=("${leet_word^}")
    
    # 출력 (중복 제거)
    printf '%s\n' "${variations[@]}" | sort -u
}

main() {
    if [ $# -ne 1 ]; then
        echo "Usage: $0 wordlist.txt"
        exit 1
    fi
    
    local wordlist="$1"
    
    if [ ! -f "$wordlist" ]; then
        echo "Error: File '$wordlist' not found"
        exit 1
    fi
    
    while IFS= read -r word; do
        [ -n "$word" ] && mangle_word "$word"
    done < "$wordlist"
}

main "$@"

4. 사용법 및 비교

4.1 성능 비교

# Python 스크립트
time python3 mangle.py wordlist.txt > output.txt

# Bash 스크립트  
time bash mangle.sh wordlist.txt > output.txt

# Hashcat (가장 빠름)
time hashcat --stdout -a 0 wordlist.txt -r rules/custom.rule > output.txt

4.2 추천 워크플로우

# 1단계: 간단한 Python/Bash 스크립트로 프로토타입
python3 simple_mangle.py small_wordlist.txt

# 2단계: 결과 확인 후 대용량 처리는 Hashcat 사용
hashcat --stdout -a 0 large_wordlist.txt -r custom.rule > final_wordlist.txt

# 3단계: 실제 크랙킹
hashcat -m 0 -a 0 hashes.txt final_wordlist.txt

5. 실무 팁

5.1 효율적인 개발

  • Python으로 빠른 프로토타입 개발
  • 복잡한 로직은 Python이 유리
  • 단순 변형은 Bash도 충분
  • 대용량 처리는 Hashcat 활용

5.2 성능 최적화

  • 중복 제거를 마지막에 한 번만
  • 메모리 사용량 고려 (대용량 wordlist)
  • 병렬 처리 활용 (multiprocessing)
profile
DevSecOps, Pentest, Cloud(OpenStack), Develop, Data Engineering, AI-Agent

0개의 댓글