1. 대안 도구들
1.1 Hashcat (가장 추천)
apt install hashcat
hashcat -m 0 -a 0 hashes.txt wordlist.txt -r rules/best64.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 기반)
git clone https://github.com/sc0tfree/mentalist.git
cd mentalist
pip install -r requirements.txt
python mentalist.py
1.3 Crunch (패턴 기반 생성)
apt install crunch
crunch 8 8 -t pass%%%% > wordlist.txt
crunch 6 10 abcdefghijklmnopqrstuvwxyz0123456789 > wordlist.txt
2. Python 스크립트 (추천)
2.1 기본 맹글링 스크립트
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 스크립트 (객체지향)
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))
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
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
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 성능 비교
time python3 mangle.py wordlist.txt > output.txt
time bash mangle.sh wordlist.txt > output.txt
time hashcat --stdout -a 0 wordlist.txt -r rules/custom.rule > output.txt
4.2 추천 워크플로우
python3 simple_mangle.py small_wordlist.txt
hashcat --stdout -a 0 large_wordlist.txt -r custom.rule > final_wordlist.txt
hashcat -m 0 -a 0 hashes.txt final_wordlist.txt
5. 실무 팁
5.1 효율적인 개발
- Python으로 빠른 프로토타입 개발
- 복잡한 로직은 Python이 유리
- 단순 변형은 Bash도 충분
- 대용량 처리는 Hashcat 활용
5.2 성능 최적화
- 중복 제거를 마지막에 한 번만
- 메모리 사용량 고려 (대용량 wordlist)
- 병렬 처리 활용 (multiprocessing)