HackTheBox Classic, yet complicated! Writeup
Explore the basics of cybersecurity in the Classic, yet complicated! Challenge on Hack The Box. This easy-level Challenge introduces encryption reversal and file handling concepts in a clear and accessible way, perfect for beginners.
https://app.hackthebox.com/challenges/22
Description
Find the plaintext, the key is your flag! <br>Flag format : HTB{key in lowercase}
Exploitation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#!/usr/bin/python3
import string
from collections import Counter
def get_english_frequencies():
return {
'A': 0.0817, 'B': 0.0150, 'C': 0.0278, 'D': 0.0425, 'E': 0.1270,
'F': 0.0223, 'G': 0.0202, 'H': 0.0609, 'I': 0.0697, 'J': 0.0015,
'K': 0.0077, 'L': 0.0403, 'M': 0.0241, 'N': 0.0675, 'O': 0.0751,
'P': 0.0193, 'Q': 0.0010, 'R': 0.0599, 'S': 0.0633, 'T': 0.0906,
'U': 0.0276, 'V': 0.0098, 'W': 0.0236, 'X': 0.0015, 'Y': 0.0197,
'Z': 0.0007
}
def clean_text(text):
return ''.join(c.upper() for c in text if c.isalpha())
def get_letter_frequencies(text):
total = len(text)
return {char: count/total for char, count in Counter(text).items()}
def index_of_coincidence(text):
n = len(text)
if n <= 1:
return 0
freqs = Counter(text)
numerator = sum(freq * (freq - 1) for freq in freqs.values())
denominator = n * (n - 1)
return numerator / denominator if denominator != 0 else 0
def find_key_length(ciphertext, min_len=2, max_len=20):
best_ioc = 0
best_length = 0
for length in range(min_len, max_len + 1):
sequences = [''] * length
for i, char in enumerate(ciphertext):
sequences[i % length] += char
avg_ioc = sum(index_of_coincidence(seq) for seq in sequences) / length
if avg_ioc > best_ioc:
best_ioc = avg_ioc
best_length = length
return best_length
def find_repeating_pattern(text):
n = len(text)
for length in range(1, n + 1):
if text == text[:length] * (n // length) + text[:n % length]:
return text[:length]
return text
def decrypt_with_key(text, key):
decrypted = ''
key = key.upper()
key_length = len(key)
i = 0
for char in text:
if char.isalpha():
char_num = ord(char.upper()) - ord('A')
key_num = ord(key[i % key_length]) - ord('A')
decrypted_num = (char_num - key_num) % 26
decrypted_char = chr(decrypted_num + ord('A'))
if char.islower():
decrypted_char = decrypted_char.lower()
decrypted += decrypted_char
i += 1
else:
decrypted += char
return decrypted
def crack_vigenere(ciphertext, min_key_len=2, max_key_len=20):
cleaned_text = clean_text(ciphertext)
key_length = find_key_length(cleaned_text, min_key_len, max_key_len)
def get_column(text, key_len, offset):
return ''.join(char for i, char in enumerate(text) if i % key_len == offset)
key = ''
eng_freqs = get_english_frequencies()
for i in range(key_length):
column = get_column(cleaned_text, key_length, i)
best_shift = 0
best_score = float('inf')
for shift in range(26):
shifted = ''.join(chr((ord(c) - ord('A') - shift) % 26 + ord('A')) for c in column)
freq = get_letter_frequencies(shifted)
score = sum((freq.get(c, 0) - eng_freqs[c]) ** 2 for c in eng_freqs)
if score < best_score:
best_score = score
best_shift = shift
key += chr(best_shift + ord('A'))
actual_key = find_repeating_pattern(key)
return actual_key, decrypt_with_key(ciphertext, actual_key)
ciphertext = """alp gwcsepul gtavaf, nlv prgpbpsu mb h jcpbyvdlq, ipltga rv glniypfa we ekl 16xs nsjhlcb. px td o lccjdstslpahzn fptspf xstlxzi te iosj ezv sc xcns ttsoic lzlvrmhaw ez sjqijsa xsp rwhr. tq vxspf sciov, alp wsphvcv pr ess rwxpqlvp nwlvvc dyi dswbhvo ef htqtafvyw hqzfbpg, ezutewwm zcep xzmyr o scio ry tscoos rd woi pyqnmgelvr vpm . qbctnl xsp akbflowllmspwt nlwlpcg, lccjdstslpahzn fptspfo oip qvx dfgysgelipp ec bfvbxlrnj ojocjvpw, ld akfv ekhr zys hskehy my eva dclluxpih yoe mh yiacsoseehk fj l gebxwh sieesn we ekl iynfudktru. xsp yam zd woi qwoc."""
key, decrypted = crack_vigenere(ciphertext)
print(f"Found key: {key} Flag: HTB{{{key}}}")
print(f"{decrypted}")
By the way, you can also resolve the flag using frequency analysis with the English alphabet by manually replacing letters based on their frequency. Alternatively, you can use an online brute-force tool like:
Summary
The Classic, yet complicated! Challenge on Hack The Box involves breaking a Vigenère cipher to uncover a hidden plaintext and determine the key, which serves as the flag. Participants employ techniques such as frequency analysis, index of coincidence, and modular arithmetic to crack the cipher. This challenge provides an excellent opportunity to deepen your understanding of classical cryptography and algorithmic problem-solving. The decrypted key is formatted as HTB{key in lowercase}
.