Post

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}.

This post is licensed under CC BY 4.0 by the author.