/* TODO: act proper if interface is promisc (only reply to packets send to local host */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BLEN 512 const char *payloads[] = { // redirect using javascript with Meta-Refresh as fallback (works // only with browsers, sends dangerous HTTP 200 independent of the // request): "HTTP/1.0 200 OK\r\n" "Refresh: 1; url=https://%s/forcessl_nojs\r\n" "Content-type: text/html\r\n" "\r\n" "" "" "enabling crypto..." "", // redirect using 301 (closer to standart, friendly to non-browser // HTTP clients): "HTTP/1.0 301 Not enough crypto to process request\r\n" "Location: https://%s/forcessl_nojs\r\n" "Content-type: text/html" "\r\n" "" "enabling crypto..." "" }; #define AP0(x) if (!(x)) { perror(NULL); exit(EXIT_FAILURE); } #define APN(x) if ((x) < 0) { perror(NULL); exit(EXIT_FAILURE); } unsigned chksum(unsigned short *buf, int length) { unsigned sum = 0; for (; length > 1; length -= 2) sum += *buf++; return sum; } unsigned short ipchksum(struct iphdr *ip) { unsigned sum = chksum((unsigned short *) ip, sizeof(struct iphdr)); sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum >> 16); return ~sum; } unsigned short tcpchksum(struct iphdr *ip, struct tcphdr *tcp, int length) { unsigned sum = chksum((unsigned short*) &(ip->saddr), sizeof(ip->saddr)) + chksum((unsigned short*) &(ip->daddr), sizeof(ip->daddr)) + ((length + ip->protocol) << 8) + chksum((unsigned short*) tcp, length); if (length % 2) sum += *(((char*) tcp) + length - 1); sum = (sum >> 16) + (sum & 0xFFFF); sum += (sum >> 16); return ~sum; } void printopts(char **argv) { printf("Usage: %s -i interface [-3|-j] [-p port] [-h target-host]\n" "\t-i interface to listen on\n" "\t-3 use HTTP 301 reponses for redirection\n" "\t-j use Javascript with Meta-Refresh as fallback for redirection (default)\n" "\t-p port to listen (default: 80)\n" "\t-h hostname of the redirection target; if unspecified the request destination IP is used\n", argv[0]); exit(EXIT_FAILURE); } int main(int argc, char **argv) { // parse cmd line args int opt, port = htons(80); char *target_host = NULL, *output_device = NULL; const char *raw_payload = payloads[0]; while ((opt = getopt(argc, argv, "3jp:h:i:")) != -1) { switch (opt) { case 'p': port = htons(atoi(optarg)); break; case 'h': AP0(target_host = strdup(optarg)); break; case 'i': AP0(output_device = strdup(optarg)); break; case '3': raw_payload = payloads[1]; break; case 'j': raw_payload = payloads[0]; break; default: printopts(argv); } } if (!output_device) printopts(argv); // prepare input buffer char *ibuf = malloc(BLEN); AP0(ibuf); struct ethhdr *i_eth = (struct ethhdr*) ibuf; struct iphdr *i_ip = (struct iphdr*) (i_eth + 1); struct tcphdr *i_tcp = (struct tcphdr*) (i_ip + 1); //char *i_pld = (char*) (i_tcp + 1); // prepare output buffer const size_t os = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct tcphdr) + strlen(raw_payload) - 1 + (target_host ? strlen(target_host) : 15); char *obuf = malloc(os); AP0(obuf); struct ethhdr *o_eth = (struct ethhdr*) obuf; struct iphdr *o_ip = (struct iphdr*) (o_eth + 1); struct tcphdr *o_tcp = (struct tcphdr*) (o_ip + 1); char *o_pld = (char*) (o_tcp + 1); if (os - sizeof(struct ethhdr) > 512) fprintf(stderr, "warning: resulting packet size %ld exceeds MTU 512\n", os - sizeof(struct ethhdr)); // setup sock int fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP)); APN(fd); // retrieve interface idx (for send()) int ifidx; { struct ifreq req; strcpy((char*) &(req.ifr_name), output_device); APN(ioctl(fd, SIOCGIFINDEX, &req)); ifidx = req.ifr_ifindex; } // bind to interface struct sockaddr_ll sall; sall.sll_family = AF_PACKET; sall.sll_ifindex = ifidx; sall.sll_halen = 6; memcpy(&(sall.sll_addr), &(o_eth->h_dest), sall.sll_halen); sall.sll_protocol = sall.sll_hatype = sall.sll_pkttype = 0; APN(bind(fd, (struct sockaddr*) &sall, sizeof(sall))); // prefill static output o_eth->h_proto = htons(ETH_P_IP); o_ip->ihl = 5; o_ip->version = 4; o_ip->tos = 0; o_ip->id = 0; o_ip->frag_off = 0; o_ip->ttl = 255; o_ip->protocol = 6; o_tcp->source = port; o_tcp->res1 = 0; o_tcp->doff = 5; o_tcp->ack = 1; o_tcp->rst = 0; o_tcp->psh = 1; o_tcp->urg = 0; o_tcp->ece = 0; o_tcp->cwr = 0; o_tcp->urg_ptr = 0; o_tcp->window = htons(4); // first 4 byte are harmless (and we won't ack them) if (target_host) snprintf(o_pld, strlen(raw_payload) + strlen(target_host) - 1, raw_payload, target_host); // read packets forever while (1) { int is = recv(fd, ibuf, BLEN, 0); APN(is); if ((is >= sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct tcphdr)) && // packet long enough? (i_eth->h_proto == htons(ETH_P_IP)) && // ip protocol? (i_ip->ihl == 5) && // no IP options (i_ip->protocol == 6) && // tcp protocol? (i_tcp->dest == port)) { // port 80? int size = 0; // eth & IP are common for all responses memcpy(&(o_eth->h_dest), &(i_eth->h_source), ETH_ALEN); memcpy(&(o_eth->h_source), &(i_eth->h_dest), ETH_ALEN); o_ip->check = 0; memcpy(&(o_ip->saddr), &(i_ip->daddr), sizeof(o_ip->saddr)); memcpy(&(o_ip->daddr), &(i_ip->saddr), sizeof(o_ip->daddr)); o_tcp->dest = i_tcp->source; o_tcp->check = 0; if (i_tcp->syn && !i_tcp->ack) { // first packet of a handshake? size = os - strlen(o_pld); o_tcp->seq = i_tcp->seq; o_tcp->ack_seq = htonl(ntohl(i_tcp->seq) + 1); o_tcp->fin = 0; o_tcp->syn = 1; }else if (i_tcp->ack) { size = os; o_tcp->seq = htonl(ntohl(i_tcp->ack_seq)); o_tcp->ack_seq = htonl(ntohl(i_tcp->ack_seq)); o_tcp->fin = 1; o_tcp->syn = 0; if (!target_host) { struct in_addr ba; ba.s_addr = i_ip->daddr; char *sa = inet_ntoa(ba); snprintf(o_pld, os - (o_pld - (char*) o_eth), raw_payload, sa); size = size - 15 + strlen(sa); } } if (size > 0) { o_ip->tot_len = htons(size - sizeof(struct ethhdr)); o_ip->check = ipchksum(o_ip); o_tcp->check = tcpchksum(o_ip, o_tcp, size - sizeof(struct ethhdr) - sizeof(struct iphdr)); if (sendto(fd, o_eth, size, 0, (struct sockaddr*) &sall, sizeof(struct sockaddr_ll)) != size) { perror(NULL); exit(EXIT_FAILURE); } } } } }