portsentry_beta/0040755000076400001440000000000007663462005014052 5ustar crowlandusersportsentry_beta/portsentry.c0100644000076400001440000015210607663462005016451 0ustar crowlandusers/************************************************************************/ /* */ /* PortSentry */ /* */ /* Created: 10-12-1997 */ /* Modified: 05-23-2003 */ /* */ /* Send all changes/modifications/bugfixes to: */ /* craigrowland at users dot sourceforge dot net */ /* */ /* */ /* This software is Copyright(c) 1997-2003 Craig Rowland */ /* */ /* This software is covered under the Common Public License v1.0 */ /* See the enclosed LICENSE file for more information. */ /* $Id: portsentry.c,v 1.40 2003/05/23 17:41:25 crowland Exp crowland $ */ /************************************************************************/ #include "portsentry.h" #include "portsentry_io.h" #include "portsentry_util.h" /* Global variables */ char gblScanDetectHost[MAXSTATE][IPMAXBUF]; char gblKillRoute[MAXBUF]; char gblKillHostsDeny[MAXBUF]; char gblKillRunCmd[MAXBUF]; char gblBlockedFile[MAXBUF]; char gblHistoryFile[MAXBUF]; char gblIgnoreFile[MAXBUF]; char gblDetectionType[MAXBUF]; int gblScanDetectCount = 0; int gblBlockTCP = 0; int gblBlockUDP = 0; int gblRunCmdFirst = 0; int gblResolveHost = 0; int gblConfigTriggerCount = 0; int main (int argc, char *argv[]) { if (argc != 2) { Usage (); Exit (ERROR); } if ((geteuid ()) && (getuid ()) != 0) { printf ("You need to be root to run this.\n"); Exit (ERROR); } /* Cheesy arg parser. Some systems don't support getopt and I don't want to port it. */ if ((strcmp (argv[1], "-tcp")) && (strcmp (argv[1], "-udp")) && (strcmp (argv[1], "-stcp")) && (strcmp (argv[1], "-atcp")) && (strcmp (argv[1], "-sudp")) && (strcmp (argv[1], "-audp")) != 0) { Usage (); Exit (ERROR); } else { Start (); /* This copies the startup type to a global for later use */ if ((SafeStrncpy (gblDetectionType, strstr (argv[1], "-") + 1, MAXBUF)) == NULL) { Log("adminalert: ERROR: Error setting internal scan detection type.\n"); printf ("ERROR: Error setting internal scan detection type.\n"); printf ("ERROR: PortSentry is shutting down!\n"); Exit (ERROR); } else if (CheckConfig () == FALSE) { Log ("adminalert: ERROR: Configuration files are missing/corrupted. Shutting down.\n"); printf ("ERROR: Configuration files are missing/corrupted.\n"); printf ("ERROR: Check your syslog for a more detailed error message.\n"); printf ("ERROR: PortSentry is shutting down!\n"); Exit (ERROR); } else if (InitConfig () == FALSE) { Log ("adminalert: ERROR: Your config file is corrupted/missing mandatory option! Shutting down.\n"); printf ("ERROR: Your config file is corrupted/missing mandatory option!\n"); printf ("ERROR: Check your syslog for a more detailed error message.\n"); printf ("ERROR: PortSentry is shutting down!\n"); Exit (ERROR); } #ifndef NODAEMON else if (DaemonSeed () == ERROR) { Log ("adminalert: ERROR: could not go into daemon mode. Shutting down.\n"); printf ("ERROR: could not go into daemon mode. Shutting down.\n"); Exit (ERROR); } #endif } if (strcmp (argv[1], "-tcp") == 0) { if (PortSentryModeTCP () == ERROR) { Log ("adminalert: ERROR: could not go into PortSentry mode. Shutting down.\n"); Exit (ERROR); } } #ifdef SUPPORT_STEALTH else if (strcmp (argv[1], "-stcp") == 0) { if (PortSentryStealthModeTCP () == ERROR) { Log ("adminalert: ERROR: could not go into PortSentry mode. Shutting down.\n"); Exit (ERROR); } } else if (strcmp (argv[1], "-atcp") == 0) { if (PortSentryAdvancedStealthModeTCP () == ERROR) { Log ("adminalert: ERROR: could not go into PortSentry mode. Shutting down.\n"); Exit (ERROR); } } else if (strcmp (argv[1], "-sudp") == 0) { if (PortSentryStealthModeUDP () == ERROR) { Log ("adminalert: ERROR: could not go into PortSentry mode. Shutting down.\n"); Exit (ERROR); } } else if (strcmp (argv[1], "-audp") == 0) { if (PortSentryAdvancedStealthModeUDP () == ERROR) { Log ("adminalert: ERROR: could not go into PortSentry mode. Shutting down.\n"); Exit (ERROR); } } #endif else if (strcmp (argv[1], "-udp") == 0) { if (PortSentryModeUDP () == ERROR) { Log ("adminalert: ERROR: could not go into PortSentry mode. Shutting down.\n"); Exit (ERROR); } } Exit (TRUE); /* shuts up compiler warning */ return (0); } /****************************************************************/ /* Reads generic config options into global variables */ /****************************************************************/ int InitConfig (void) { FILE *input; char configToken[MAXBUF]; gblBlockTCP = CheckFlag ("BLOCK_TCP"); gblBlockUDP = CheckFlag ("BLOCK_UDP"); gblResolveHost = CheckFlag ("RESOLVE_HOST"); memset (gblKillRoute, '\0', MAXBUF); memset (gblKillHostsDeny, '\0', MAXBUF); memset (gblKillRunCmd, '\0', MAXBUF); if ((ConfigTokenRetrieve ("SCAN_TRIGGER", configToken)) == FALSE) { Log ("adminalert: ERROR: Could not read SCAN_TRIGGER option from config file. Disabling SCAN DETECTION TRIGGER"); gblConfigTriggerCount = 0; } else { #ifdef DEBUG Log ("debug: InitConfig: retrieved SCAN_TRIGGER option: %s \n", configToken); #endif gblConfigTriggerCount = atoi (configToken); } if ((ConfigTokenRetrieve ("KILL_ROUTE", gblKillRoute)) == TRUE) { #ifdef DEBUG Log ("debug: InitConfig: retrieved KILL_ROUTE option: %s \n", gblKillRoute); #endif } else { #ifdef DEBUG Log ("debug: InitConfig: KILL_ROUTE option NOT FOUND.\n"); #endif } if ((ConfigTokenRetrieve ("KILL_HOSTS_DENY", gblKillHostsDeny)) == TRUE) { #ifdef DEBUG Log ("debug: InitConfig: retrieved KILL_HOSTS_DENY option: %s \n", gblKillHostsDeny); #endif } else { #ifdef DEBUG Log ("debug: InitConfig: KILL_HOSTS_DENY option NOT FOUND.\n"); #endif } if ((ConfigTokenRetrieve ("KILL_RUN_CMD", gblKillRunCmd)) == TRUE) { #ifdef DEBUG Log ("debug: InitConfig: retrieved KILL_RUN_CMD option: %s \n", gblKillRunCmd); #endif /* Check the order we should run the KILL_RUN_CMD */ /* Default is to run the command after blocking */ gblRunCmdFirst = CheckFlag ("KILL_RUN_CMD_FIRST"); } else { #ifdef DEBUG Log ("debug: InitConfig: KILL_RUN_CMD option NOT FOUND.\n"); #endif } if ((ConfigTokenRetrieve ("BLOCKED_FILE", gblBlockedFile)) == TRUE) { if (strlen (gblBlockedFile) < MAXBUF - 5) { strncat (gblBlockedFile, ".", 1); strncat (gblBlockedFile, gblDetectionType, 4); } else { Log ("adminalert: ERROR: Blocked filename is too long to append detection type file extension: %s.\n", gblBlockedFile); return (FALSE); } #ifdef DEBUG Log ("debug: InitConfig: retrieved BLOCKED_FILE option: %s \n", gblBlockedFile); Log ("debug: CheckConfig: Removing old block file: %s \n", gblBlockedFile); #endif if ((input = fopen (gblBlockedFile, "w")) == NULL) { Log ("adminalert: ERROR: Cannot delete blocked file on startup: %s.\n", gblBlockedFile); return (FALSE); } else fclose (input); } else { Log ("InitConfig: Cannot retrieve BLOCKED_FILE option! Aborting\n"); return (FALSE); } if ((ConfigTokenRetrieve ("HISTORY_FILE", gblHistoryFile)) == TRUE) { #ifdef DEBUG Log ("debug: InitConfig: retrieved HISTORY_FILE option: %s \n", gblHistoryFile); #endif } else { Log ("InitConfig: Cannot retrieve HISTORY_FILE option! Aborting\n"); return (FALSE); } if ((ConfigTokenRetrieve ("IGNORE_FILE", gblIgnoreFile)) == TRUE) { #ifdef DEBUG Log ("debug: InitConfig: retrieved IGNORE_FILE option: %s \n", gblIgnoreFile); #endif } else { Log ("InitConfig: Cannot retrieve IGNORE_FILE option! Aborting\n"); return (FALSE); } return (TRUE); } #ifdef SUPPORT_STEALTH /* Read in a TCP packet taking into account IP options and other */ /* errors */ int PacketReadTCP (int socket, struct iphdr *ipPtr, struct tcphdr *tcpPtr) { char packetBuffer[TCPPACKETLEN]; struct in_addr addr; bzero (ipPtr, sizeof (struct iphdr)); bzero (tcpPtr, sizeof (struct tcphdr)); if(read (socket, packetBuffer, TCPPACKETLEN) == ERROR) return(ERROR); memcpy (ipPtr, (struct iphdr *) packetBuffer, sizeof (struct iphdr)); if ((ipPtr->ihl < 5) || (ipPtr->ihl > 15)) { addr.s_addr = (u_int) ipPtr->saddr; Log ("attackalert: Illegal IP header length detected in TCP packet: %d from (possible) host: %s\n", ipPtr->ihl, inet_ntoa (addr)); return (FALSE); } else { memcpy (tcpPtr, (struct tcphdr *) (packetBuffer + ((ipPtr->ihl) * 4)), sizeof (struct tcphdr)); return (TRUE); } } /* Read in a UDP packet taking into account IP options and other */ /* errors */ int PacketReadUDP (int socket, struct iphdr *ipPtr, struct udphdr *udpPtr) { char packetBuffer[UDPPACKETLEN]; struct in_addr addr; bzero (ipPtr, sizeof (struct iphdr)); bzero (udpPtr, sizeof (struct udphdr)); if(read (socket, packetBuffer, UDPPACKETLEN) == ERROR) return(ERROR); memcpy (ipPtr, (struct iphdr *) packetBuffer, sizeof (struct iphdr)); if ((ipPtr->ihl < 5) || (ipPtr->ihl > 15)) { addr.s_addr = (u_int) ipPtr->saddr; Log ("attackalert: Illegal IP header length detected in UDP packet: %d from (possible) host: %s\n", ipPtr->ihl, inet_ntoa (addr)); return (FALSE); } else { memcpy (udpPtr, (struct udphdr *) (packetBuffer + ((ipPtr->ihl) * 4)), sizeof (struct udphdr)); return (TRUE); } } /****************************************************************/ /* Stealth scan detection Mode One */ /* */ /* This mode will read in a list of ports to monitor and will */ /* then open a raw socket to look for packets matching the port. */ /* */ /****************************************************************/ int PortSentryStealthModeTCP (void) { struct sockaddr_in client, server; int portCount = 0, portCount2 = 0, ports[MAXSOCKS], ports2[MAXSOCKS]; int count = 0, scanDetectTrigger = TRUE, gotBound = FALSE, result = TRUE; int openSockfd = 0, incomingPort = 0; char *temp, target[IPMAXBUF], configToken[MAXBUF]; char resolvedHost[DNSMAXBUF], *packetType; struct in_addr addr; struct iphdr ip; struct tcphdr tcp; if ((ConfigTokenRetrieve ("TCP_PORTS", configToken)) == FALSE) { Log ("adminalert: ERROR: Could not read TCP_PORTS option from config file"); return (ERROR); } /* break out the ports */ if ((temp = (char *) strtok (configToken, ",")) != NULL) { ports[0] = atoi (temp); for (count = 1; count < MAXSOCKS; count++) { if ((temp = (char *) strtok (NULL, ",")) != NULL) ports[count] = atoi (temp); else break; } portCount = count; } else { Log ("adminalert: ERROR: No TCP ports supplied in config file. Aborting"); return (ERROR); } /* ok, now check if they have a network daemon on the socket already, if they do */ /* then skip that port because it will cause false alarms */ for (count = 0; count < portCount; count++) { Log ("adminalert: Going into stealth listen mode on TCP port: %d\n", ports[count]); if ((openSockfd = OpenTCPSocket ()) == ERROR) { Log ("adminalert: ERROR: could not open TCP socket. Aborting.\n"); return (ERROR); } if (BindSocket (openSockfd, client, server, ports[count]) == ERROR) Log ("adminalert: ERROR: Socket %d is in use and will not be monitored. Attempting to continue\n", ports[count]); else /* well we at least bound to one socket so we'll continue */ { gotBound = TRUE; ports2[portCount2++] = ports[count]; } close (openSockfd); } /* if we didn't bind to anything then abort */ if (gotBound == FALSE) { Log ("adminalert: ERROR: All supplied TCP sockets are in use and will not be listened to. Shutting down.\n"); return (ERROR); } /* Open our raw socket for network IO */ if ((openSockfd = OpenRAWTCPSocket ()) == ERROR) { Log ("adminalert: ERROR: could not open RAW TCP socket. Aborting.\n"); return (ERROR); } Log ("adminalert: PortSentry is now active and listening.\n"); /* main detection loop */ for (;;) { if (PacketReadTCP (openSockfd, &ip, &tcp) != TRUE) continue; incomingPort = ntohs (tcp.dest); /* check for an ACK/RST to weed out established connections in case the user */ /* is monitoring high ephemeral port numbers */ if ((tcp.ack != 1) && (tcp.rst != 1)) { /* this iterates the list of ports looking for a match */ for (count = 0; count < portCount; count++) { if (incomingPort == ports2[count]) { if (SmartVerifyTCP (client, server, incomingPort) == TRUE) break; /* copy the clients address into our buffer for nuking */ addr.s_addr = (u_int) ip.saddr; SafeStrncpy (target, (char *) inet_ntoa (addr), IPMAXBUF); /* check if we should ignore this IP */ result = NeverBlock (target, gblIgnoreFile); if (result == ERROR) { Log ("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n"); result = FALSE; } if (result == FALSE) { /* check if they've visited before */ scanDetectTrigger = CheckStateEngine (target); if (scanDetectTrigger == TRUE) { if (gblResolveHost) /* Do they want DNS resolution? */ { if(CleanAndResolve(resolvedHost, target) != TRUE) { Log ("attackalert: ERROR: Error resolving host. \ resolving disabled for this host.\n"); snprintf (resolvedHost, DNSMAXBUF, "%s", target); } } else { snprintf (resolvedHost, DNSMAXBUF, "%s", target); } packetType = ReportPacketType (tcp); Log ("attackalert: %s from host: %s/%s to TCP port: %d", packetType, resolvedHost, target, ports2[count]); /* Report on options present */ if (ip.ihl > 5) Log ("attackalert: Packet from host: %s/%s to TCP port: %d has IP options set (detection avoidance technique).", resolvedHost, target, ports2[count]); /* check if this target is already blocked */ if (IsBlocked (target, gblBlockedFile) == FALSE) { /* toast the prick */ if (DisposeTCP (target, ports2[count]) != TRUE) Log ("attackalert: ERROR: Could not block host %s/%s !!", resolvedHost, target); else WriteBlocked (target, resolvedHost, ports2[count], gblBlockedFile, gblHistoryFile, "TCP"); } /* end IsBlocked check */ else Log ("attackalert: Host: %s/%s is already blocked Ignoring", resolvedHost, target); } /* end if(scanDetectTrigger) */ } /* end if(never block) check */ break; /* get out of for(count) loop above */ } /* end if(incoming port) == protected port */ } /* end for( check for protected port loop ) loop */ } /* end if(TH_ACK) check */ } /* end for( ; ; ) loop */ } /* end PortSentryStealthModeTCP */ /****************************************************************/ /* Advanced Stealth scan detection Mode One */ /* */ /* This mode will see what ports are listening below 1024 */ /* and will then monitor all the rest. This is very sensitive */ /* and will react on any packet hitting any monitored port, */ /* regardless of TCP flags set */ /* */ /****************************************************************/ int PortSentryAdvancedStealthModeTCP (void) { struct sockaddr_in client, server; int result = TRUE, scanDetectTrigger = TRUE, hotPort = TRUE; int openSockfd = 0, incomingPort = 0, smartVerify = FALSE; unsigned int advancedPorts = 1024; unsigned int count = 0, inUsePorts[MAXSOCKS], portCount = 0; char target[IPMAXBUF], configToken[MAXBUF]; char resolvedHost[DNSMAXBUF], *temp, *packetType; struct in_addr addr; struct iphdr ip; struct tcphdr tcp; if ((ConfigTokenRetrieve ("ADVANCED_PORTS_TCP", configToken)) == FALSE) { Log ("adminalert: ERROR: Could not read ADVANCED_PORTS_TCP option from config file. Assuming 1024."); advancedPorts = 1024; } else advancedPorts = atoi (configToken); Log ("adminalert: Advanced mode will monitor first %d ports", advancedPorts); /* try to bind to all ports below 1024, any that are taken we exclude later */ for (count = 0; count < advancedPorts; count++) { if ((openSockfd = OpenTCPSocket ()) == ERROR) { Log ("adminalert: ERROR: could not open TCP socket. Aborting.\n"); return (ERROR); } if (BindSocket (openSockfd, client, server, count) == ERROR) inUsePorts[portCount++] = count; close (openSockfd); } if ((ConfigTokenRetrieve ("ADVANCED_EXCLUDE_TCP", configToken)) != FALSE) { /* break out the ports */ if ((temp = (char *) strtok (configToken, ",")) != NULL) { inUsePorts[portCount++] = atoi (temp); Log ("adminalert: Advanced mode will manually exclude port: %d ", inUsePorts[portCount - 1]); for (count = 0; count < MAXSOCKS; count++) { if ((temp = (char *) strtok (NULL, ",")) != NULL) { inUsePorts[portCount++] = atoi (temp); Log ("adminalert: Advanced mode will manually exclude port: %d ", inUsePorts[portCount - 1]); } else break; } } } else Log ("adminalert: Advanced mode will manually exclude no ports"); for (count = 0; count < portCount; count++) Log ("adminalert: Advanced Stealth scan detection mode activated. Ignored TCP port: %d\n", inUsePorts[count]); /* open raw socket for reading */ if ((openSockfd = OpenRAWTCPSocket ()) == ERROR) { Log ("adminalert: ERROR: could not open RAW TCP socket. Aborting.\n"); return (ERROR); } Log ("adminalert: PortSentry is now active and listening.\n"); /* main detection loop */ for (;;) { if (PacketReadTCP (openSockfd, &ip, &tcp) != TRUE) continue; incomingPort = ntohs (tcp.dest); /* don't monitor packets with ACK set (established) or RST */ /* This could be a hole in some cases */ if ((tcp.ack != 1) && (tcp.rst != 1)) { /* check if we should ignore this connection to this port */ for (count = 0; count < portCount; count++) { if ((incomingPort == inUsePorts[count]) || (incomingPort >= advancedPorts)) { hotPort = FALSE; break; } else hotPort = TRUE; } if (hotPort) { smartVerify = SmartVerifyTCP (client, server, incomingPort); if (smartVerify != TRUE) { addr.s_addr = (u_int) ip.saddr; SafeStrncpy (target, (char *) inet_ntoa (addr), IPMAXBUF); /* check if we should ignore this IP */ result = NeverBlock (target, gblIgnoreFile); if (result == ERROR) { Log ("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n"); result = FALSE; } if (result == FALSE) { /* check if they've visited before */ scanDetectTrigger = CheckStateEngine (target); if (scanDetectTrigger == TRUE) { if (gblResolveHost) /* Do they want DNS resolution? */ { if(CleanAndResolve(resolvedHost, target) != TRUE) { Log ("attackalert: ERROR: Error resolving host. \ resolving disabled for this host.\n"); snprintf (resolvedHost, DNSMAXBUF, "%s", target); } } else { snprintf (resolvedHost, DNSMAXBUF, "%s", target); } packetType = ReportPacketType (tcp); Log ("attackalert: %s from host: %s/%s to TCP port: %d", packetType, resolvedHost, target, incomingPort); /* Report on options present */ if (ip.ihl > 5) Log ("attackalert: Packet from host: %s/%s to TCP port: %d has IP options set (detection avoidance technique).", resolvedHost, target, incomingPort); /* check if this target is already blocked */ if (IsBlocked (target, gblBlockedFile) == FALSE) { /* toast the prick */ if (DisposeTCP (target, incomingPort) != TRUE) Log ("attackalert: ERROR: Could not block host %s/%s!!", resolvedHost, target); else WriteBlocked (target, resolvedHost, incomingPort, gblBlockedFile, gblHistoryFile, "TCP"); } /* end IsBlocked check */ else Log ("attackalert: Host: %s/%s is already blocked Ignoring", resolvedHost, target); } /* end if(scanDetectTrigger) */ } /* end if(never block) check */ } /* end if(smartVerify) */ } /* end if(hotPort) */ } /* end if(TH_ACK) */ } /* end for( ; ; ) loop */ } /* end PortSentryAdvancedStealthModeTCP */ /****************************************************************/ /* UDP "stealth" scan detection */ /* */ /* This mode will read in a list of ports to monitor and will */ /* then open a raw socket to look for packets matching the port. */ /* */ /****************************************************************/ int PortSentryStealthModeUDP (void) { struct sockaddr_in client, server; int portCount = 0, portCount2 = 0, ports[MAXSOCKS], ports2[MAXSOCKS], result = TRUE; int count = 0, scanDetectTrigger = TRUE, gotBound = FALSE; int openSockfd = 0, incomingPort = 0; char *temp, target[IPMAXBUF], configToken[MAXBUF]; char resolvedHost[DNSMAXBUF]; struct in_addr addr; struct iphdr ip; struct udphdr udp; if ((ConfigTokenRetrieve ("UDP_PORTS", configToken)) == FALSE) { Log ("adminalert: ERROR: Could not read UDP_PORTS option from config file"); return (ERROR); } /* break out the ports */ if ((temp = (char *) strtok (configToken, ",")) != NULL) { ports[0] = atoi (temp); for (count = 1; count < MAXSOCKS; count++) { if ((temp = (char *) strtok (NULL, ",")) != NULL) ports[count] = atoi (temp); else break; } portCount = count; } else { Log ("adminalert: ERROR: No UDP ports supplied in config file. Aborting"); return (ERROR); } /* ok, now check if they have a network daemon on the socket already, if they do */ /* then skip that port because it will cause false alarms */ for (count = 0; count < portCount; count++) { Log ("adminalert: Going into stealth listen mode on UDP port: %d\n", ports[count]); if ((openSockfd = OpenUDPSocket ()) == ERROR) { Log ("adminalert: ERROR: could not open UDP socket. Aborting.\n"); return (ERROR); } if (BindSocket (openSockfd, client, server, ports[count]) == ERROR) Log ("adminalert: ERROR: Socket %d is in use and will not be monitored. Attempting to continue\n", ports[count]); else { gotBound = TRUE; ports2[portCount2++] = ports[count]; } close (openSockfd); } if (gotBound == FALSE) { Log ("adminalert: ERROR: All supplied UDP sockets are in use and will not be listened to. Shutting down.\n"); return (ERROR); } if ((openSockfd = OpenRAWUDPSocket ()) == ERROR) { Log ("adminalert: ERROR: could not open RAW UDP socket. Aborting.\n"); return (ERROR); } Log ("adminalert: PortSentry is now active and listening.\n"); /* main detection loop */ for (;;) { if (PacketReadUDP (openSockfd, &ip, &udp) != TRUE) continue; incomingPort = ntohs (udp.dest); /* this iterates the list of ports looking for a match */ for (count = 0; count < portCount; count++) { if (incomingPort == ports2[count]) { if (SmartVerifyUDP (client, server, incomingPort) == TRUE) break; addr.s_addr = (u_int) ip.saddr; SafeStrncpy (target, (char *) inet_ntoa (addr), IPMAXBUF); /* check if we should ignore this IP */ result = NeverBlock (target, gblIgnoreFile); if (result == ERROR) { Log ("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n"); result = FALSE; } if (result == FALSE) { /* check if they've visited before */ scanDetectTrigger = CheckStateEngine (target); if (scanDetectTrigger == TRUE) { if (gblResolveHost) /* Do they want DNS resolution? */ { if(CleanAndResolve(resolvedHost, target) != TRUE) { Log ("attackalert: ERROR: Error resolving host. \ resolving disabled for this host.\n"); snprintf (resolvedHost, DNSMAXBUF, "%s", target); } } else { snprintf (resolvedHost, DNSMAXBUF, "%s", target); } Log ("attackalert: UDP scan from host: %s/%s to UDP port: %d", resolvedHost, target, ports2[count]); /* Report on options present */ if (ip.ihl > 5) Log ("attackalert: Packet from host: %s/%s to UDP port: %d has IP options set (detection avoidance technique).", resolvedHost, target, incomingPort); /* check if this target is already blocked */ if (IsBlocked (target, gblBlockedFile) == FALSE) { if (DisposeUDP (target, ports2[count]) != TRUE) Log ("attackalert: ERROR: Could not block host %s/%s!!", resolvedHost, target); else WriteBlocked (target, resolvedHost, ports2[count], gblBlockedFile, gblHistoryFile, "UDP"); } /* end IsBlocked check */ else { Log ("attackalert: Host: %s/%s is already blocked Ignoring", resolvedHost, target); } } /* end if(scanDetectTrigger) */ } /* end if(never block) check */ break; /* get out of for(count) loop above */ } /* end if(incoming port) == protected port */ } /* end for( check for protected port loop ) loop */ } /* end for( ; ; ) loop */ } /* end PortSentryStealthModeUDP */ /****************************************************************/ /* Advanced Stealth scan detection mode for UDP */ /* */ /* This mode will see what ports are listening below 1024 */ /* and will then monitor all the rest. This is very sensitive */ /* and will react on any packet hitting any monitored port. */ /* This is a very dangerous option and is for advanced users */ /* */ /****************************************************************/ int PortSentryAdvancedStealthModeUDP (void) { struct sockaddr_in client, server; int result = TRUE, scanDetectTrigger = TRUE, hotPort = TRUE; int openSockfd = 0, incomingPort = 0, smartVerify = FALSE; unsigned int advancedPorts = 1024; unsigned int count = 0, inUsePorts[MAXSOCKS], portCount = 0; char target[IPMAXBUF], configToken[MAXBUF]; char resolvedHost[DNSMAXBUF], *temp; struct in_addr addr; struct iphdr ip; struct udphdr udp; if ((ConfigTokenRetrieve ("ADVANCED_PORTS_UDP", configToken)) == FALSE) { Log ("adminalert: ERROR: Could not read ADVANCED_PORTS_UDP option from config file. Assuming 1024."); advancedPorts = 1024; } else advancedPorts = atoi (configToken); Log ("adminalert: Advanced mode will monitor first %d ports", advancedPorts); /* try to bind to all ports below 1024, any that are taken we exclude later */ for (count = 0; count < advancedPorts; count++) { if ((openSockfd = OpenUDPSocket ()) == ERROR) { Log ("adminalert: ERROR: could not open UDP socket. Aborting.\n"); return (ERROR); } if (BindSocket (openSockfd, client, server, count) == ERROR) inUsePorts[portCount++] = count; close (openSockfd); } if ((ConfigTokenRetrieve ("ADVANCED_EXCLUDE_UDP", configToken)) != FALSE) { /* break out the ports */ if ((temp = (char *) strtok (configToken, ",")) != NULL) { inUsePorts[portCount++] = atoi (temp); Log ("adminalert: Advanced mode will manually exclude port: %d ", inUsePorts[portCount - 1]); for (count = 0; count < MAXSOCKS; count++) { if ((temp = (char *) strtok (NULL, ",")) != NULL) { inUsePorts[portCount++] = atoi (temp); Log ("adminalert: Advanced mode will manually exclude port: %d ", inUsePorts[portCount - 1]); } else break; } } } else Log ("adminalert: Advanced mode will manually exclude no ports"); for (count = 0; count < portCount; count++) Log ("adminalert: Advanced Stealth scan detection mode activated. Ignored UDP port: %d\n", inUsePorts[count]); if ((openSockfd = OpenRAWUDPSocket ()) == ERROR) { Log ("adminalert: ERROR: could not open RAW UDP socket. Aborting.\n"); return (ERROR); } Log ("adminalert: PortSentry is now active and listening.\n"); /* main detection loop */ for (;;) { if (PacketReadUDP (openSockfd, &ip, &udp) != TRUE) continue; incomingPort = ntohs (udp.dest); /* check if we should ignore this connection to this port */ for (count = 0; count < portCount; count++) { if ((incomingPort == inUsePorts[count]) || (incomingPort >= advancedPorts)) { hotPort = FALSE; break; } else hotPort = TRUE; } if (hotPort) { smartVerify = SmartVerifyUDP (client, server, incomingPort); if (smartVerify != TRUE) { /* copy the clients address into our buffer for nuking */ addr.s_addr = (u_int) ip.saddr; SafeStrncpy (target, (char *) inet_ntoa (addr), IPMAXBUF); /* check if we should ignore this IP */ result = NeverBlock (target, gblIgnoreFile); if (result == ERROR) { Log ("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n"); result = FALSE; } if (result == FALSE) { /* check if they've visited before */ scanDetectTrigger = CheckStateEngine (target); if (scanDetectTrigger == TRUE) { if (gblResolveHost) /* Do they want DNS resolution? */ { if(CleanAndResolve(resolvedHost, target) != TRUE) { Log ("attackalert: ERROR: Error resolving host. \ resolving disabled for this host.\n"); snprintf (resolvedHost, DNSMAXBUF, "%s", target); } } else { snprintf (resolvedHost, DNSMAXBUF, "%s", target); } Log ("attackalert: UDP scan from host: %s/%s to UDP port: %d", resolvedHost, target, incomingPort); /* Report on options present */ if (ip.ihl > 5) Log ("attackalert: Packet from host: %s/%s to UDP port: %d has IP options set (detection avoidance technique).", resolvedHost, target, incomingPort); /* check if this target is already blocked */ if (IsBlocked (target, gblBlockedFile) == FALSE) { if (DisposeUDP (target, incomingPort) != TRUE) Log ("attackalert: ERROR: Could not block host %s/%s!!", resolvedHost, target); else WriteBlocked (target, resolvedHost, incomingPort, gblBlockedFile, gblHistoryFile, "UDP"); } /* end IsBlocked check */ else Log ("attackalert: Host: %s/%s is already blocked Ignoring", resolvedHost, target); } /* end if(scanDetectTrigger) */ } /* end if(never block) check */ } /* end if (smartVerify) */ } /* end if(hotPort) */ } /* end for( ; ; ) loop */ } /* end PortSentryAdvancedStealthModeUDP */ #endif /****************************************************************/ /* Classic detection Mode */ /* */ /* This mode will bind to a list of TCP sockets and wait for */ /* connections to happen. Although the least prone to false */ /* alarms, it also won't detect stealth scans */ /* */ /****************************************************************/ int PortSentryModeTCP (void) { struct sockaddr_in client, server; int length, portCount = 0, ports[MAXSOCKS]; int openSockfd[MAXSOCKS], incomingSockfd, result = TRUE; int count = 0, scanDetectTrigger = TRUE, showBanner = FALSE, boundPortCount = 0; int selectResult = 0; char *temp, target[IPMAXBUF], bannerBuffer[MAXBUF], configToken[MAXBUF]; char resolvedHost[DNSMAXBUF]; fd_set selectFds; if ((ConfigTokenRetrieve ("TCP_PORTS", configToken)) == FALSE) { Log ("adminalert: ERROR: Could not read TCP_PORTS option from config file"); return (ERROR); } /* break out the ports */ if ((temp = (char *) strtok (configToken, ",")) != NULL) { ports[0] = atoi (temp); for (count = 1; count < MAXSOCKS; count++) { if ((temp = (char *) strtok (NULL, ",")) != NULL) ports[count] = atoi (temp); else break; } portCount = count; } else { Log ("adminalert: ERROR: No TCP ports supplied in config file. Aborting"); return (ERROR); } /* read in the banner if one is given */ if ((ConfigTokenRetrieve ("PORT_BANNER", configToken)) == TRUE) { showBanner = TRUE; SafeStrncpy (bannerBuffer, configToken, MAXBUF); } /* setup select call */ FD_ZERO (&selectFds); for (count = 0; count < portCount; count++) { Log ("adminalert: Going into listen mode on TCP port: %d\n", ports[count]); if ((openSockfd[boundPortCount] = OpenTCPSocket ()) == ERROR) { Log ("adminalert: ERROR: could not open TCP socket. Aborting.\n"); return (ERROR); } if (BindSocket (openSockfd[boundPortCount], client, server, ports[count]) == ERROR) { Log ("adminalert: ERROR: could not bind TCP socket: %d. Attempting to continue\n", ports[count]); } else /* well we at least bound to one socket so we'll continue */ boundPortCount++; } /* if we didn't bind to anything then abort */ if (boundPortCount == 0) { Log ("adminalert: ERROR: could not bind ANY TCP sockets. Shutting down.\n"); return (ERROR); } length = sizeof (client); Log ("adminalert: PortSentry is now active and listening.\n"); /* main loop for multiplexing/resetting */ for (;;) { /* set up select call */ for (count = 0; count < boundPortCount; count++) FD_SET (openSockfd[count], &selectFds); selectResult = select (MAXSOCKS, &selectFds, NULL, NULL, (struct timeval *) NULL); /* something blew up */ if (selectResult < 0) { Log ("adminalert: ERROR: select call failed. Shutting down.\n"); return (ERROR); } else if (selectResult == 0) { #ifdef DEBUG Log ("Select timeout"); #endif } /* select is reporting a waiting socket. Poll them all to find out which */ else if (selectResult > 0) { for (count = 0; count < boundPortCount; count++) { if (FD_ISSET (openSockfd[count], &selectFds)) { incomingSockfd = accept (openSockfd[count], (struct sockaddr *) &client, &length); if (incomingSockfd < 0) { Log ("attackalert: Possible stealth scan from unknown host to TCP port: %d (accept failed)", ports[count]); break; } /* copy the clients address into our buffer for nuking */ SafeStrncpy (target, (char *) inet_ntoa (client.sin_addr), IPMAXBUF); /* check if we should ignore this IP */ result = NeverBlock (target, gblIgnoreFile); if (result == ERROR) { Log ("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n"); result = FALSE; } if (result == FALSE) { /* check if they've visited before */ scanDetectTrigger = CheckStateEngine (target); if (scanDetectTrigger == TRUE) { /* show the banner if one was selected */ if (showBanner == TRUE) write (incomingSockfd, bannerBuffer, strlen (bannerBuffer)); /* we don't need the bonehead anymore */ close (incomingSockfd); if (gblResolveHost) /* Do they want DNS resolution? */ { if(CleanAndResolve(resolvedHost, target) != TRUE) { Log ("attackalert: ERROR: Error resolving host. \ resolving disabled for this host.\n"); snprintf (resolvedHost, DNSMAXBUF, "%s", target); } } else { snprintf (resolvedHost, DNSMAXBUF, "%s", target); } Log ("attackalert: Connect from host: %s/%s to TCP port: %d", resolvedHost, target, ports[count]); /* check if this target is already blocked */ if (IsBlocked (target, gblBlockedFile) == FALSE) { if (DisposeTCP (target, ports[count]) != TRUE) Log ("attackalert: ERROR: Could not block host %s !!", target); else WriteBlocked (target, resolvedHost, ports[count], gblBlockedFile, gblHistoryFile, "TCP"); } else Log ("attackalert: Host: %s is already blocked. Ignoring", target); } } close (incomingSockfd); break; } /* end if(FD_ISSET) */ } /* end for() */ } /* end else (selectResult > 0) */ } /* end main for(; ; ) loop */ /* not reached */ close (incomingSockfd); } /****************************************************************/ /* Classic detection Mode */ /* */ /* This mode will bind to a list of UDP sockets and wait for */ /* connections to happen. Stealth scanning really doesn't apply */ /* here. */ /* */ /****************************************************************/ int PortSentryModeUDP (void) { struct sockaddr_in client, server; int length, ports[MAXSOCKS], openSockfd[MAXSOCKS], result = TRUE; int count = 0, portCount = 0, selectResult = 0, scanDetectTrigger = 0; int boundPortCount = 0, showBanner = FALSE; char *temp, target[IPMAXBUF], bannerBuffer[MAXBUF], configToken[MAXBUF]; char buffer[MAXBUF]; char resolvedHost[DNSMAXBUF]; fd_set selectFds; if ((ConfigTokenRetrieve ("UDP_PORTS", configToken)) == FALSE) { Log ("adminalert: ERROR: Could not read UDP_PORTS option from config file"); return (ERROR); } /* break out the ports */ if ((temp = (char *) strtok (configToken, ",")) != NULL) { ports[0] = atoi (temp); for (count = 1; count < MAXSOCKS; count++) { if ((temp = (char *) strtok (NULL, ",")) != NULL) ports[count] = atoi (temp); else break; } portCount = count; } else { Log ("adminalert: ERROR: No UDP ports supplied in config file. Aborting"); return (ERROR); } /* read in the banner if one is given */ if ((ConfigTokenRetrieve ("PORT_BANNER", configToken)) == TRUE) { showBanner = TRUE; SafeStrncpy (bannerBuffer, configToken, MAXBUF); } /* setup select call */ FD_ZERO (&selectFds); for (count = 0; count < portCount; count++) { Log ("adminalert: Going into listen mode on UDP port: %d\n", ports[count]); if ((openSockfd[boundPortCount] = OpenUDPSocket ()) == ERROR) { Log ("adminalert: ERROR: could not open UDP socket. Aborting\n"); return (ERROR); } if (BindSocket (openSockfd[boundPortCount], client, server, ports[count]) == ERROR) { Log ("adminalert: ERROR: could not bind UDP socket: %d. Attempting to continue\n", ports[count]); } else /* well we at least bound to one socket so we'll continue */ boundPortCount++; } /* if we didn't bind to anything then abort */ if (boundPortCount == 0) { Log ("adminalert: ERROR: could not bind ANY UDP sockets. Shutting down.\n"); return (ERROR); } length = sizeof (client); Log ("adminalert: PortSentry is now active and listening.\n"); /* main loop for multiplexing/resetting */ for (;;) { /* set up select call */ for (count = 0; count < boundPortCount; count++) FD_SET (openSockfd[count], &selectFds); /* setup the select multiplexing (blocking mode) */ selectResult = select (MAXSOCKS, &selectFds, NULL, NULL, (struct timeval *) NULL); if (selectResult < 0) { Log ("adminalert: ERROR: select call failed. Shutting down.\n"); return (ERROR); } else if (selectResult == 0) { #ifdef DEBUG Log ("Select timeout"); #endif } /* select is reporting a waiting socket. Poll them all to find out which */ else if (selectResult > 0) { for (count = 0; count < portCount; count++) { if (FD_ISSET (openSockfd[count], &selectFds)) { /* here just read in one byte from the UDP socket, that's all we need to */ /* know that this person is a jerk */ if (recvfrom (openSockfd[count], buffer, 1, 0, (struct sockaddr *) &client, &length) < 0) { Log ("adminalert: ERROR: could not accept incoming socket for UDP port: %d\n", ports[count]); break; } /* copy the clients address into our buffer for nuking */ SafeStrncpy (target, (char *) inet_ntoa (client.sin_addr), IPMAXBUF); #ifdef DEBUG Log ("debug: PortSentryModeUDP: accepted UDP connection from: %s\n", target); #endif /* check if we should ignore this IP */ result = NeverBlock (target, gblIgnoreFile); if (result == ERROR) { Log ("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n"); result = FALSE; } if (result == FALSE) { /* check if they've visited before */ scanDetectTrigger = CheckStateEngine (target); if (scanDetectTrigger == TRUE) { /* show the banner if one was selected */ if (showBanner == TRUE) sendto (openSockfd[count], bannerBuffer, strlen (bannerBuffer), 0, (struct sockaddr *) &client, length); if (gblResolveHost) /* Do they want DNS resolution? */ { if(CleanAndResolve(resolvedHost, target) != TRUE) { Log ("attackalert: ERROR: Error resolving host. \ resolving disabled for this host.\n"); snprintf (resolvedHost, DNSMAXBUF, "%s", target); } } else { snprintf (resolvedHost, DNSMAXBUF, "%s", target); } Log ("attackalert: Connect from host: %s/%s to UDP port: %d", resolvedHost, target, ports[count]); /* check if this target is already blocked */ if (IsBlocked (target, gblBlockedFile) == FALSE) { if (DisposeUDP (target, ports[count]) != TRUE) Log ("attackalert: ERROR: Could not block host %s !!", target); else WriteBlocked (target, resolvedHost, ports[count], gblBlockedFile, gblHistoryFile, "UDP"); } else Log ("attackalert: Host: %s is already blocked. Ignoring", target); } } break; } /* end if(FD_ISSET) */ } /* end for() */ } /* end else (selectResult > 0) */ } /* end main for(; ; ) loop */ } /* end UDP PortSentry */ /* kill the TCP connection depending on config option */ int DisposeTCP (char *target, int port) { int status = TRUE; #ifdef DEBUG Log ("debug: DisposeTCP: disposing of host %s on port %d with option: %d", target, port, gblBlockTCP); Log ("debug: DisposeTCP: killRunCmd: %s", gblKillRunCmd); Log ("debug: DisposeTCP: gblRunCmdFirst: %d", gblRunCmdFirst); Log ("debug: DisposeTCP: killHostsDeny: %s", gblKillHostsDeny); Log ("debug: DisposeTCP: killRoute: %s %d", gblKillRoute, strlen (gblKillRoute)); #endif /* Should we ignore TCP from active response? */ if (gblBlockTCP == 1) { /* run external command first, hosts.deny second, dead route last */ if (gblRunCmdFirst) { if (strlen (gblKillRunCmd) > 0) if (KillRunCmd (target, port, gblKillRunCmd, gblDetectionType) != TRUE) status = FALSE; if (strlen (gblKillHostsDeny) > 0) if (KillHostsDeny (target, port, gblKillHostsDeny, gblDetectionType) != TRUE) status = FALSE; if (strlen (gblKillRoute) > 0) if (KillRoute (target, port, gblKillRoute, gblDetectionType) != TRUE) status = FALSE; } /* run hosts.deny first, dead route second, external command last */ else { if (strlen (gblKillHostsDeny) > 0) if (KillHostsDeny (target, port, gblKillHostsDeny, gblDetectionType) != TRUE) status = FALSE; if (strlen (gblKillRoute) > 0) if (KillRoute (target, port, gblKillRoute, gblDetectionType) != TRUE) status = FALSE; if (strlen (gblKillRunCmd) > 0) if (KillRunCmd (target, port, gblKillRunCmd, gblDetectionType) != TRUE) status = FALSE; } } else if (gblBlockTCP == 2) { /* run external command only */ if (strlen (gblKillRunCmd) > 0) if (KillRunCmd (target, port, gblKillRunCmd, gblDetectionType) != TRUE) status = FALSE; } else Log ("attackalert: Ignoring TCP response per configuration file setting."); return (status); } /* kill the UDP connection depending on config option */ int DisposeUDP (char *target, int port) { int status = TRUE; #ifdef DEBUG Log ("debug: DisposeUDP: disposing of host %s on port %d with option: %d", target, port, gblBlockUDP); Log ("debug: DisposeUDP: killRunCmd: %d", gblKillRunCmd); Log ("debug: DisposeUDP: gblRunCmdFirst: %s", gblRunCmdFirst); Log ("debug: DisposeUDP: killHostsDeny: %s", gblKillHostsDeny); Log ("debug: DisposeUDP: killRoute: %s %d", gblKillRoute, strlen (gblKillRoute)); #endif /* Should we ignore TCP from active response? */ if (gblBlockUDP == 1) { /* run external command first, hosts.deny second, dead route last */ if (gblRunCmdFirst) { if (strlen (gblKillRunCmd) > 0) if (KillRunCmd (target, port, gblKillRunCmd, gblDetectionType) != TRUE) status = FALSE; if (strlen (gblKillHostsDeny) > 0) if (KillHostsDeny (target, port, gblKillHostsDeny, gblDetectionType) != TRUE) status = FALSE; if (strlen (gblKillRoute) > 0) if (KillRoute (target, port, gblKillRoute, gblDetectionType) != TRUE) status = FALSE; } /* run hosts.deny first, dead route second, external command last */ else { if (strlen (gblKillHostsDeny) > 0) if (KillHostsDeny (target, port, gblKillHostsDeny, gblDetectionType) != TRUE) status = FALSE; if (strlen (gblKillRoute) > 0) if (KillRoute (target, port, gblKillRoute, gblDetectionType) != TRUE) status = FALSE; if (strlen (gblKillRunCmd) > 0) if (KillRunCmd (target, port, gblKillRunCmd, gblDetectionType) != TRUE) status = FALSE; } } else if (gblBlockUDP == 2) { /* run external command only */ if (strlen (gblKillRunCmd) > 0) if (KillRunCmd (target, port, gblKillRunCmd, gblDetectionType) != TRUE) status = FALSE; } else Log ("attackalert: Ignoring UDP response per configuration file setting."); return (status); } /* duh */ void Usage (void) { printf ("PortSentry - Port Scan Detector.\n"); printf ("Copyright 1997-2003 Craig H. Rowland \n"); printf ("Licensing restrictions apply. Please see documentation\n"); printf ("Version: %s\n\n", VERSION); #ifdef SUPPORT_STEALTH printf ("usage: portsentry [-tcp -udp -stcp -atcp -sudp -audp]\n\n"); #else printf ("Stealth scan detection not supported on this platform\n"); printf ("usage: portsentry [-tcp -udp]\n\n"); #endif printf ("*** PLEASE READ THE DOCS BEFORE USING *** \n\n"); } /* our cheesy state engine to monitor who has connected here before */ int CheckStateEngine (char *target) { int count = 0, scanDetectTrigger = TRUE; int gotOne = 0; /* This is the rather basic scan state engine. It maintains */ /* an array of past hosts who triggered a connection on a port */ /* when a new host arrives it is compared against the array */ /* if it is found in the array it increments a state counter by */ /* one and checks the remainder of the array. It does this until */ /* the end is reached or the trigger value has been exceeded */ /* This would probably be better as a linked list/hash table, */ /* but for the number of hosts we are tracking this is just as good. */ /* This will probably change in the future */ gotOne = 1; /* our flag counter if we get a match */ scanDetectTrigger = TRUE; /* set to TRUE until set otherwise */ if (gblConfigTriggerCount > 0) { for (count = 0; count < MAXSTATE; count++) { /* if the array has the IP address then increment the gotOne counter and */ /* check the trigger value. If it is exceeded break out of the loop and */ /* set the detecttrigger to TRUE */ if (strcmp (gblScanDetectHost[count], target) == 0 ) { /* compare the number of matches to the configured trigger value */ /* if we've exceeded we can stop this noise */ if (++gotOne >= gblConfigTriggerCount) { scanDetectTrigger = TRUE; #ifdef DEBUG Log ("debug: CheckStateEngine: host: %s has exceeded trigger value: %d\n", gblScanDetectHost[count], gblConfigTriggerCount); #endif break; } } else scanDetectTrigger = FALSE; } /* now add the fresh meat into the state engine */ /* if our array is still less than MAXSTATE large add it to the end */ if (gblScanDetectCount < MAXSTATE) { SafeStrncpy (gblScanDetectHost[gblScanDetectCount], target, IPMAXBUF); gblScanDetectCount++; } else { /* otherwise tack it to the beginning and start overwriting older ones */ gblScanDetectCount = 0; SafeStrncpy (gblScanDetectHost[gblScanDetectCount], target, IPMAXBUF); gblScanDetectCount++; } #ifdef DEBUG for (count = 0; count < MAXSTATE; count++) Log ("debug: CheckStateEngine: state engine host: %s -> position: %d Detected: %d\n", gblScanDetectHost[count], count, scanDetectTrigger); #endif /* end catch to set state if gblConfigTriggerCount == 0 */ if (gotOne >= gblConfigTriggerCount) scanDetectTrigger = TRUE; } if (gblConfigTriggerCount > MAXSTATE) { Log ("securityalert: WARNING: Trigger value %d is larger than state engine capacity of %d.\n", gblConfigTriggerCount); Log ("Adjust the value lower or recompile with a larger state engine value.\n", MAXSTATE); Log ("securityalert: Blocking host anyway because of invalid trigger value"); scanDetectTrigger = TRUE; } return (scanDetectTrigger); } #ifdef SUPPORT_STEALTH /* This takes a tcp packet and reports what type of scan it is */ char * ReportPacketType (struct tcphdr tcpPkt) { static char packetDesc[MAXBUF]; static char *packetDescPtr = packetDesc; if ((tcpPkt.syn == 0) && (tcpPkt.fin == 0) && (tcpPkt.ack == 0) \ && (tcpPkt.psh == 0) && (tcpPkt.rst == 0) && (tcpPkt.urg == 0)) snprintf (packetDesc, MAXBUF, " TCP NULL scan"); else if ((tcpPkt.fin == 1) && (tcpPkt.urg == 1) && (tcpPkt.psh == 1)) snprintf (packetDesc, MAXBUF, "TCP XMAS scan"); else if ((tcpPkt.fin == 1) && (tcpPkt.syn != 1) && (tcpPkt.ack != 1) \ &&(tcpPkt.psh != 1) && (tcpPkt.rst != 1) && (tcpPkt.urg != 1)) snprintf (packetDesc, MAXBUF, "TCP FIN scan"); else if ((tcpPkt.syn == 1) && (tcpPkt.fin != 1) && (tcpPkt.ack != 1) \ &&(tcpPkt.psh != 1) && (tcpPkt.rst != 1) && (tcpPkt.urg != 1)) snprintf (packetDesc, MAXBUF, "TCP SYN/Normal scan"); else snprintf (packetDesc, MAXBUF, "Unknown Type: TCP Packet Flags: SYN: %d FIN: %d ACK: %d PSH: %d URG: %d RST: %d", tcpPkt.syn, tcpPkt.fin, tcpPkt.ack, tcpPkt.psh, tcpPkt.urg, tcpPkt.rst); return (packetDescPtr); } int SmartVerifyTCP (struct sockaddr_in client, struct sockaddr_in server, int port) { int testSockfd; /* Ok here is where we "Smart-Verify" the socket. If the port was previously */ /* unbound, but now appears to have someone there, then we will skip responding */ /* to this inbound packet. This a basic "stateful" inspection of the */ /* the connection */ if ((testSockfd = OpenTCPSocket ()) == ERROR) { Log ("adminalert: ERROR: could not open TCP socket to smart-verify.\n"); return (FALSE); } else { if (BindSocket (testSockfd, client, server, port) == ERROR) { #ifdef DEBUG Log ("debug: SmartVerify: Smart-Verify Port In Use: %d", port); #endif close (testSockfd); return (TRUE); } } close (testSockfd); return (FALSE); } int SmartVerifyUDP (struct sockaddr_in client, struct sockaddr_in server, int port) { int testSockfd; /* Ok here is where we "Smart-Verify" the socket. If the port was previously */ /* unbound, but now appears to have someone there, then we will skip responding */ /* to this inbound packet. This essentially is a "stateful" inspection of the */ /* the connection */ if ((testSockfd = OpenUDPSocket ()) == ERROR) { Log ("adminalert: ERROR: could not open UDP socket to smart-verify.\n"); return (FALSE); } else { if (BindSocket (testSockfd, client, server, port) == ERROR) { #ifdef DEBUG Log ("debug: SmartVerify: Smart-Verify Port In Use: %d", port); #endif close (testSockfd); return (TRUE); } } close (testSockfd); return (FALSE); } #endif /* SUPPORT_STEALTH */ portsentry_beta/portsentry.h0100600000076400001440000000547407663462005016453 0ustar crowlandusers/************************************************************************/ /* */ /* PortSentry */ /* */ /* Created: 10-12-1997 */ /* Modified: 05-23-2003 */ /* */ /* Send all changes/modifications/bugfixes to: */ /* craigrowland at users dot sourceforge dot net */ /* */ /* */ /* This software is Copyright(c) 1997-2003 Craig Rowland */ /* */ /* This software is covered under the Common Public License v1.0 */ /* See the enclosed LICENSE file for more information. */ /* $Id: portsentry.h,v 1.32 2003/05/23 17:50:20 crowland Exp crowland $ */ /************************************************************************/ #define VERSION "1.2" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef _LINUX_C_LIB_VERSION #include #include #include #endif #include #include "portsentry_config.h" #include "portsentry_io.h" #include "portsentry_util.h" #ifdef SUPPORT_STEALTH #ifdef LINUX #include "portsentry_tcpip.h" #include #endif #define TCPPACKETLEN 80 #define UDPPACKETLEN 68 #endif /* SUPPORT_STEALTH */ #ifdef NEXT #include #endif #define ERROR -1 #define TRUE 1 #define FALSE 0 #define MAXBUF 1024 /* max size of an IP address plus NULL */ #define IPMAXBUF 16 /* max sockets we can open */ #define MAXSOCKS 64 /* Really is about 1025, but we don't need the length for our purposes */ #define DNSMAXBUF 255 /* prototypes */ int PortSentryModeTCP (void); int PortSentryModeUDP (void); int DisposeUDP (char *, int); int DisposeTCP (char *, int); int CheckStateEngine (char *); int InitConfig(void); void Usage (void); int SmartVerifyTCP(struct sockaddr_in, struct sockaddr_in, int); int SmartVerifyUDP(struct sockaddr_in, struct sockaddr_in, int); #ifdef SUPPORT_STEALTH int PortSentryStealthModeTCP (void); int PortSentryAdvancedStealthModeTCP (void); int PortSentryStealthModeUDP (void); int PortSentryAdvancedStealthModeUDP (void); char * ReportPacketType(struct tcphdr ); int PacketReadTCP(int, struct iphdr *, struct tcphdr *); int PacketReadUDP(int, struct iphdr *, struct udphdr *); #endif portsentry_beta/portsentry_io.c0100600000076400001440000005104307663462005017126 0ustar crowlandusers/************************************************************************/ /* */ /* PortSentry */ /* */ /* Created: 10-12-1997 */ /* Modified: 05-23-2003 */ /* */ /* Send all changes/modifications/bugfixes to: */ /* craigrowland at users dot sourceforge dot net */ /* */ /* */ /* This software is Copyright(c) 1997-2003 Craig Rowland */ /* */ /* This software is covered under the Common Public License v1.0 */ /* See the enclosed LICENSE file for more information. */ /* $Id: portsentry_io.c,v 1.36 2003/05/23 17:41:40 crowland Exp crowland $ */ /************************************************************************/ #include "portsentry.h" #include "portsentry_io.h" #include "portsentry_util.h" /* Main logging function to surrogate syslog */ void Log (char *logentry, ...) { char logbuffer[MAXBUF]; va_list argsPtr; va_start (argsPtr, logentry); vsnprintf (logbuffer, MAXBUF, logentry, argsPtr); va_end(argsPtr); openlog ("portsentry", LOG_PID, SYSLOG_FACILITY); syslog (SYSLOG_LEVEL, "%s", logbuffer); closelog (); } void Exit (int status) { Log ("securityalert: PortSentry is shutting down\n"); Log ("adminalert: PortSentry is shutting down\n"); exit (status); } void Start (void) { Log ("adminalert: PortSentry %s is starting.\n", VERSION); #ifdef DEBUG printf("Compiled: " __DATE__ " at " __TIME__ "\n"); #endif } /* The daemonizing code copied from Advanced Programming */ /* in the UNIX Environment by W. Richard Stevens with minor changes */ int DaemonSeed (void) { int childpid; signal (SIGALRM, SIG_IGN); signal (SIGHUP, SIG_IGN); signal (SIGPIPE, SIG_IGN); signal (SIGTERM, Exit); signal (SIGABRT, Exit); signal (SIGURG, Exit); signal (SIGKILL, Exit); if ((childpid = fork ()) < 0) return (ERROR); else if (childpid > 0) exit (0); setsid (); chdir ("/"); umask (077); /* close stdout, stdin, stderr */ close(0); close(1); close(2); return (TRUE); } /* Compares an IP address against a listed address and its netmask*/ int CompareIPs(char *target, char *ignoreAddr, int ignoreNetmaskBits) { unsigned long int netmaskAddr, ipAddr, targetAddr; ipAddr = inet_addr(ignoreAddr); targetAddr = inet_addr(target); netmaskAddr = htonl (0xFFFFFFFF << (32 - ignoreNetmaskBits)); #ifdef DEBUG Log ("debug: target %s\n", target); Log ("debug: ignoreAddr %s\n", ignoreAddr); Log ("debug: ignoreNetmaskBits %d\n", ignoreNetmaskBits); Log ("debug: ipAddr %lu\n", ipAddr); Log ("debug: targetAddr %lu\n", targetAddr); Log ("debug: netmask %x\n", netmaskAddr); Log ("debug: mix ipAddr %lu\n", (ipAddr & netmaskAddr)); Log ("debug: mix target %lu\n", (targetAddr & netmaskAddr)); #endif /* Network portion mask & op and return */ if ((ipAddr & netmaskAddr) == (targetAddr & netmaskAddr)) return(TRUE); else return(FALSE); } /* check hosts that should never be blocked */ int NeverBlock (char *target, char *filename) { FILE *input; char buffer[MAXBUF], tempBuffer[MAXBUF], netmaskBuffer[MAXBUF]; char *slashPos; int count = 0, dest = 0, netmaskBits = 0; #ifdef DEBUG Log ("debug: NeverBlock: Opening ignore file: %s \n", filename); #endif if ((input = fopen (filename, "r")) == NULL) return (ERROR); #ifdef DEBUG Log ("debug: NeverBlock: Doing lookup for host: %s \n", target); #endif while (fgets (buffer, MAXBUF, input) != NULL) { /* Reset destination counter */ dest = 0; if ((buffer[0] == '#') || (buffer[0] == '\n')) continue; for(count = 0; count < strlen(buffer); count++) { /* Parse out digits, colons, and slashes. Everything else rejected */ if((isdigit(buffer[count])) || (buffer[count] == '.') || (buffer[count] == ':') || (buffer[count] == '/')) { tempBuffer[dest++] = buffer[count]; } else { tempBuffer[dest] = '\0'; break; } } /* Return pointer to slash if it exists and copy data to buffer */ slashPos = strchr(tempBuffer, '/'); if (slashPos) { SafeStrncpy(netmaskBuffer, slashPos + 1, MAXBUF); /* Terminate tempBuffer string at delimeter for later use */ *slashPos = '\0'; } else /* Copy in a 32 bit netmask if none given */ SafeStrncpy(netmaskBuffer, "32", MAXBUF); /* Convert netmaskBuffer to bits in netmask */ netmaskBits = atoi(netmaskBuffer); if ((netmaskBits < 0) || (netmaskBits > 32)) { Log ("adminalert: Invalid netmask in config file: %s Ignoring entry.\n", buffer); continue; } if (CompareIPs(target, tempBuffer, netmaskBits)) { #ifdef DEBUG Log ("debug: NeverBlock: Host: %s found in ignore file with netmask %s\n", target, netmaskBuffer); #endif fclose (input); return (TRUE); } } /* end while() */ #ifdef DEBUG Log ("debug: NeverBlock: Host: %s NOT found in ignore file\n", target); #endif fclose (input); return (FALSE); } /* Make sure the config file is available */ int CheckConfig (void) { FILE *input; if ((input = fopen (CONFIG_FILE, "r")) == NULL) { Log ("adminalert: Cannot open config file: %s. Exiting\n", CONFIG_FILE); return(FALSE); } else fclose (input); return(TRUE); } /* This writes out blocked hosts to the blocked file. It adds the hostname */ /* time stamp, and port connection that was acted on */ int WriteBlocked (char *target, char *resolvedHost, int port, char *blockedFilename, char *historyFilename, char *portType) { FILE *output; int blockedStatus = TRUE, historyStatus = TRUE; struct tm *tmptr; time_t current_time; current_time = time (0); tmptr = localtime (¤t_time); #ifdef DEBUG Log ("debug: WriteBlocked: Opening block file: %s \n", blockedFilename); #endif if ((output = fopen (blockedFilename, "a")) == NULL) { Log ("adminalert: ERROR: Cannot open blocked file: %s.\n", blockedFilename); blockedStatus = FALSE; } else { fprintf (output, "%ld - %02d/%02d/%04d %02d:%02d:%02d Host: %s/%s Port: %d %s Blocked\n", current_time, tmptr->tm_mon + 1, tmptr->tm_mday, tmptr->tm_year + 1900, tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, resolvedHost, target, port, portType); fclose (output); blockedStatus = TRUE; } #ifdef DEBUG Log ("debug: WriteBlocked: Opening history file: %s \n", historyFilename); #endif if ((output = fopen (historyFilename, "a")) == NULL) { Log ("adminalert: ERROR: Cannot open history file: %s.\n", historyFilename); historyStatus = FALSE; } else { fprintf (output, "%ld - %02d/%02d/%04d %02d:%02d:%02d Host: %s/%s Port: %d %s Blocked\n", current_time, tmptr->tm_mon + 1, tmptr->tm_mday, tmptr->tm_year + 1900, tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, resolvedHost, target, port, portType); fclose (output); historyStatus = TRUE; } if (historyStatus || blockedStatus == FALSE) return (FALSE); else return (TRUE); } /* This reads a token from the config file up to the "=" and returns the string */ /* up to the first space or NULL */ int ConfigTokenRetrieve (char *token, char *configToken) { FILE *config; char buffer[MAXBUF], tokenBuffer[MAXBUF]; int count = 0; if ((config = fopen (CONFIG_FILE, "r")) == NULL) { Log ("adminalert: ERROR: Cannot open config file: %s.\n", CONFIG_FILE); return (ERROR); } else { #ifdef DEBUG Log ("debug: ConfigTokenRetrieve: checking for token %s", token); #endif while ((fgets (buffer, MAXBUF, config)) != NULL) { /* this skips comments */ if (buffer[0] != '#') { #ifdef DEBUG Log ("debug: ConfigTokenRetrieve: data: %s", buffer); #endif /* search for the token and make sure the trailing character */ /* is a " " or "=" to make sure the entire token was found */ if ((strstr (buffer, token) != (char) NULL) && ((buffer[strlen(token)] == '=') || (buffer[strlen(token)] == ' '))) { /* cut off the '=' and send it back */ if (strstr (buffer, "\"") == (char) NULL) { Log ("adminalert: Quotes missing from %s token. Option skipped\n", token); fclose (config); return (FALSE); } SafeStrncpy (tokenBuffer, strstr (buffer, "\"") + 1, MAXBUF); /* strip off unprintables/linefeeds (if any) */ count = 0; while (count < MAXBUF - 1) { if ((isprint (tokenBuffer[count])) && tokenBuffer[count] != '"') configToken[count] = tokenBuffer[count]; else { configToken[count] = '\0'; break; } count++; } #ifdef DEBUG Log ("debug: ConfigTokenRetrieved token: %s\n", configToken); #endif configToken[MAXBUF - 1] = '\0'; fclose (config); return (TRUE); } } } fclose (config); return (FALSE); } } /* This will bind a socket to a port. It works for UDP/TCP */ int BindSocket (int sockfd, struct sockaddr_in client, struct sockaddr_in server, int port) { #ifdef DEBUG Log ("debug: BindSocket: Binding to port: %d\n", port); #endif bzero ((char *) &server, sizeof (server)); server.sin_family = AF_INET; server.sin_addr.s_addr = htonl (INADDR_ANY); server.sin_port = htons (port); if (bind (sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) { #ifdef DEBUG Log ("debug: BindSocket: Binding failed\n"); #endif return (ERROR); } else { #ifdef DEBUG Log ("debug: BindSocket: Binding successful. Doing listen\n"); #endif listen (sockfd, 5); return (TRUE); } } /* Open a TCP Socket */ int OpenTCPSocket (void) { int sockfd; #ifdef DEBUG Log ("debug: OpenTCPSocket: opening TCP socket\n"); #endif if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) return (ERROR); else return (sockfd); } /* Open a UDP Socket */ int OpenUDPSocket (void) { int sockfd; #ifdef DEBUG Log ("debug: openUDPSocket opening UDP socket\n"); #endif if ((sockfd = socket (AF_INET, SOCK_DGRAM, 0)) < 0) return (ERROR); else return (sockfd); } #ifdef SUPPORT_STEALTH /* Open a RAW TCPSocket */ int OpenRAWTCPSocket (void) { int sockfd; #ifdef DEBUG Log ("debug: OpenRAWTCPSocket: opening RAW TCP socket\n"); #endif if ((sockfd = socket (AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) return (ERROR); else return (sockfd); } /* Open a RAW UDP Socket */ int OpenRAWUDPSocket (void) { int sockfd; #ifdef DEBUG Log ("debug: OpenRAWUDPSocket: opening RAW UDP socket\n"); #endif if ((sockfd = socket (AF_INET, SOCK_RAW, IPPROTO_UDP)) < 0) return (ERROR); else return (sockfd); } #endif /* This will use a system() call to change the route of the target host to */ /* a dead IP address on your LOCAL SUBNET. */ int KillRoute (char *target, int port, char *killString, char *detectionType) { char cleanAddr[MAXBUF], commandStringTemp[MAXBUF]; char commandStringTemp2[MAXBUF],commandStringFinal[MAXBUF]; char portString[MAXBUF]; int killStatus = ERROR, substStatus = ERROR; CleanIpAddr (cleanAddr, target); snprintf(portString, MAXBUF, "%d", port); substStatus = SubstString (cleanAddr, "$TARGET$", killString, commandStringTemp); if (substStatus == 0) { Log ("adminalert: No target variable specified in KILL_ROUTE option. Skipping.\n"); return (ERROR); } else if (substStatus == ERROR) { Log ("adminalert: Error trying to parse $TARGET$ Token for KILL_ROUTE. Skipping.\n"); return (ERROR); } if(SubstString (portString, "$PORT$", commandStringTemp, commandStringTemp2) == ERROR) { Log ("adminalert: Error trying to parse $PORT$ Token for KILL_ROUTE. Skipping.\n"); return (ERROR); } if(SubstString (detectionType, "$MODE$", commandStringTemp2, commandStringFinal) == ERROR) { Log ("adminalert: Error trying to parse $MODE$ Token for KILL_ROUTE. Skipping.\n"); return (ERROR); } #ifdef DEBUG Log ("debug: KillRoute: running route command: %s\n", commandStringFinal); #endif /* Kill the bastard and report a status */ killStatus = system (commandStringFinal); if (killStatus == 127) { Log ("adminalert: ERROR: There was an error trying to block host (exec fail) %s", target); return (ERROR); } else if (killStatus < 0) { Log ("adminalert: ERROR: There was an error trying to block host (system fail) %s", target); return (ERROR); } else { Log ("attackalert: Host %s has been blocked via dropped route using command: \"%s\"", target, commandStringFinal); return (TRUE); } } /* This will run a specified command with TARGET as the option if one is given. */ int KillRunCmd (char *target, int port, char *killString, char *detectionType) { char cleanAddr[MAXBUF], commandStringTemp[MAXBUF]; char commandStringTemp2[MAXBUF], commandStringFinal[MAXBUF]; char portString[MAXBUF]; int killStatus = ERROR; CleanIpAddr (cleanAddr, target); snprintf(portString, MAXBUF, "%d", port); /* Tokens are not required, but we check for an error anyway */ if(SubstString (cleanAddr, "$TARGET$", killString, commandStringTemp) == ERROR) { Log ("adminalert: Error trying to parse $TARGET$ Token for KILL_RUN_CMD. Skipping.\n"); return (ERROR); } if(SubstString (portString, "$PORT$", commandStringTemp, commandStringTemp2) == ERROR) { Log ("adminalert: Error trying to parse $PORT$ Token for KILL_RUN_CMD. Skipping.\n"); return (ERROR); } if(SubstString (detectionType, "$MODE$", commandStringTemp2, commandStringFinal) == ERROR) { Log ("adminalert: Error trying to parse $MODE$ Token for KILL_RUN_CMD. Skipping.\n"); return (ERROR); } /* Kill the bastard and report a status */ killStatus = system (commandStringFinal); if (killStatus == 127) { Log ("adminalert: ERROR: There was an error trying to run command (exec fail) %s", target); return (ERROR); } else if (killStatus < 0) { Log ("adminalert: ERROR: There was an error trying to run command (system fail) %s", target); return (ERROR); } else { /* report success */ Log ("attackalert: External command run for host: %s using command: \"%s\"", target, commandStringFinal); return (TRUE); } } /* this function will drop the host into the TCP wrappers hosts.deny file to deny */ /* all access. The drop route metod is preferred as this stops UDP attacks as well */ /* as TCP. You may find though that host.deny will be a more permanent home.. */ int KillHostsDeny (char *target, int port, char *killString, char *detectionType) { FILE *output; char cleanAddr[MAXBUF], commandStringTemp[MAXBUF]; char commandStringTemp2[MAXBUF], commandStringFinal[MAXBUF]; char portString[MAXBUF]; int substStatus = ERROR; CleanIpAddr (cleanAddr, target); snprintf(portString, MAXBUF, "%d", port); #ifdef DEBUG Log ("debug: KillHostsDeny: parsing string for block: %s\n", killString); #endif substStatus = SubstString (cleanAddr, "$TARGET$", killString, commandStringTemp); if (substStatus == 0) { Log ("adminalert: No target variable specified in KILL_HOSTS_DENY option. Skipping.\n"); return (ERROR); } else if (substStatus == ERROR) { Log ("adminalert: Error trying to parse $TARGET$ Token for KILL_HOSTS_DENY. Skipping.\n"); return (ERROR); } if(SubstString (portString, "$PORT$", commandStringTemp, commandStringTemp2) == ERROR) { Log ("adminalert: Error trying to parse $PORT$ Token for KILL_HOSTS_DENY. Skipping.\n"); return (ERROR); } if(SubstString (detectionType, "$MODE$", commandStringTemp2, commandStringFinal) == ERROR) { Log ("adminalert: Error trying to parse $MODE$ Token for KILL_HOSTS_DENY. Skipping.\n"); return (ERROR); } #ifdef DEBUG Log ("debug: KillHostsDeny: result string for block: %s\n", commandStringFinal); #endif if ((output = fopen (WRAPPER_HOSTS_DENY, "a")) == NULL) { Log ("adminalert: cannot open hosts.deny file: %s for blocking.", WRAPPER_HOSTS_DENY); Log ("securityalert: ERROR: There was an error trying to block host %s", target); return (FALSE); } else { fprintf (output, "%s\n", commandStringFinal); fclose (output); Log ("attackalert: Host %s has been blocked via wrappers with string: \"%s\"", target, commandStringFinal); return (TRUE); } } /* check if the host is already blocked */ int IsBlocked (char *target, char *filename) { FILE *input; char buffer[MAXBUF], tempBuffer[MAXBUF]; char *ipOffset; int count; #ifdef DEBUG Log ("debug: IsBlocked: Opening block file: %s \n", filename); #endif if ((input = fopen (filename, "r")) == NULL) { Log ("adminalert: ERROR: Cannot open blocked file: %s for reading. Will create.\n", filename); return (FALSE); } while (fgets (buffer, MAXBUF, input) != NULL) { if((ipOffset = strstr(buffer, target)) != (char) NULL) { for(count = 0; count < strlen(ipOffset); count++) { if((isdigit(ipOffset[count])) || (ipOffset[count] == '.')) { tempBuffer[count] = ipOffset[count]; } else { tempBuffer[count] = '\0'; break; } } if(strcmp(target, tempBuffer) == 0) { #ifdef DEBUG Log ("debug: isBlocked: Host: %s found in blocked file\n", target); #endif fclose (input); return (TRUE); } } } #ifdef DEBUG Log ("debug: IsBlocked: Host: %s NOT found in blocked file\n", target); #endif fclose(input); return (FALSE); } /********************************************************************************* * String substitute function * * This function takes: * * 1) A token to use for replacement. * 2) A token to find. * 3) A string with the tokens in it. * 4) A string to write the replaced result. * * It returns the number of substitutions made during the operation. **********************************************************************************/ int SubstString (const char *replace, const char *find, const char *target, char *result) { int replaceCount = 0, count = 0, findCount = 0, findLen=0, numberOfSubst=0; char tempString[MAXBUF], *tempStringPtr; #ifdef DEBUG Log ("debug: SubstString: Processing string: %s %d", target, strlen(target)); Log ("debug: SubstString: Processing search text: %s %d", replace, strlen(replace)); Log ("debug: SubstString: Processing replace text: %s %d", find, strlen(find)); #endif /* string not found in target */ if (strstr (target, find) == (char) NULL) { strncpy(result, target, MAXBUF); #ifdef DEBUG Log ("debug: SubstString: Result string: %s", result); #endif return (numberOfSubst); } /* String/victim/target too long */ else if ((strlen (target)) + (strlen(replace)) + (strlen(find)) > MAXBUF) return (ERROR); memset(tempString, '\0', MAXBUF); memset(result, '\0', MAXBUF); findLen = strlen(find); tempStringPtr = tempString; for(count = 0; count < MAXBUF; count++) { if(*target == '\0') break; else if((strncmp(target, find, findLen)) != 0) *tempStringPtr++ = *target++; else { numberOfSubst++; for(replaceCount = 0; replaceCount < strlen(replace); replaceCount++) *tempStringPtr++ = replace[replaceCount]; for(findCount = 0; findCount < findLen; findCount++) target++; } } strncpy(result, tempString, MAXBUF); #ifdef DEBUG Log ("debug: SubstString: Result string: %s", result); #endif return(numberOfSubst); } /* This function checks a config variable for a numerical flag and returns it */ int CheckFlag (char *flagName) { char configToken[MAXBUF]; if ((ConfigTokenRetrieve (flagName, configToken)) == TRUE) { #ifdef DEBUG Log ("debug: CheckFlag: found %s string.\n", flagName); #endif return (atoi(configToken)); } else { #ifdef DEBUG Log ("debug: CheckFlag: %s option not found. Assuming FALSE.\n", flagName); #endif return (FALSE); } } /* snprintf for NEXTSTEP (others??) */ /* I don't know where this code came from and I don't */ /* warrant its effectiveness. CHR */ #ifdef HAS_NO_SNPRINTF int snprintf (char *str, size_t n, char const *fmt,...) { va_list ap; FILE f; if (n > MAXBUF) { n = MAXBUF; } va_start (ap, fmt); f._file = EOF; f._flag = _IOWRT | _IOSTRG; f._base = f._ptr = str; f._bufsiz = f._cnt = n ? n - 1 : 0; (void) vfprintf (&f, fmt, ap); va_end (ap); if (n) { *f._ptr = '\0'; } return (f._ptr - str); } #endif portsentry_beta/portsentry_io.h0100600000076400001440000000361507663462005017135 0ustar crowlandusers/************************************************************************/ /* */ /* PortSentry */ /* */ /* Created: 10-12-1997 */ /* Modified: 05-23-2003 */ /* */ /* Send all changes/modifications/bugfixes to: */ /* craigrowland at users dot sourceforge dot net */ /* */ /* */ /* This software is Copyright(c) 1997-2003 Craig Rowland */ /* */ /* This software is covered under the Common Public License v1.0 */ /* See the enclosed LICENSE file for more information. */ /* $Id: portsentry_io.h,v 1.17 2003/05/23 17:41:46 crowland Exp crowland $ */ /************************************************************************/ /* prototypes */ int WriteBlocked (char *, char *, int, char *, char *, char *); void Log (char *,...); void Exit (int); void Start (void); int DaemonSeed (void); int NeverBlock (char *, char *); int CheckConfig (void); int OpenTCPSocket (void); int OpenUDPSocket (void); #ifdef SUPPORT_STEALTH int OpenRAWTCPSocket (void); int OpenRAWUDPSocket (void); #endif int BindSocket (int, struct sockaddr_in, struct sockaddr_in, int); int KillRoute (char *, int, char *, char *); int KillHostsDeny (char *, int, char *, char *); int KillRunCmd (char *, int, char *, char *); int ConfigTokenRetrieve (char *, char *); int IsBlocked (char *, char *); int SubstString (const char *, const char *, const char *, char *); int CheckFlag (char *); int CompareIPs(char *, char *, int); portsentry_beta/portsentry_util.c0100644000076400001440000001101207663462005017474 0ustar crowlandusers/************************************************************************/ /* */ /* PortSentry */ /* */ /* Created: 10-12-1997 */ /* Modified: 05-23-2003 */ /* */ /* Send all changes/modifications/bugfixes to: */ /* craigrowland at users dot sourceforge dot net */ /* */ /* */ /* This software is Copyright(c) 1997-2003 Craig Rowland */ /* */ /* This software is covered under the Common Public License v1.0 */ /* See the enclosed LICENSE file for more information. */ /* $Id: portsentry_util.c,v 1.11 2003/05/23 17:41:59 crowland Exp crowland $ */ /************************************************************************/ #include "portsentry.h" #include "portsentry_io.h" #include "portsentry_util.h" /* A replacement for strncpy that covers mistakes a little better */ char * SafeStrncpy (char *dest, const char *src, size_t size) { if (!dest) { dest = NULL; return (NULL); } else if (size < 1) { dest = NULL; return (NULL); } /* Null terminate string. Why the hell strncpy doesn't do this */ /* for you is mystery to me. God I hate C. */ memset (dest, '\0', size); strncpy (dest, src, size - 1); return (dest); } /************************************************************************/ /* Generic safety function to process an IP address and remove anything */ /* that is: */ /* 1) Not a number. */ /* 2) Not a period. */ /* 3) Greater than IPMAXBUF (15) */ /************************************************************************/ char * CleanIpAddr (char *cleanAddr, const char *dirtyAddr) { int count = 0, maxdot = 0, maxoctet = 0; #ifdef DEBUG Log("debug: cleanAddr: Cleaning Ip address: %s", dirtyAddr); #endif memset (cleanAddr, '\0', IPMAXBUF); /* dirtyAddr must be valid */ if(dirtyAddr == NULL) return(cleanAddr); for (count = 0; count < IPMAXBUF - 1; count++) { if (isdigit (dirtyAddr[count])) { if (++maxoctet > 3) { cleanAddr[count] = '\0'; break; } cleanAddr[count] = dirtyAddr[count]; } else if (dirtyAddr[count] == '.') { if (++maxdot > 3) { cleanAddr[count] = '\0'; break; } maxoctet = 0; cleanAddr[count] = dirtyAddr[count]; } else { cleanAddr[count] = '\0'; break; } } #ifdef DEBUG Log("debug: cleanAddr: Cleaned IpAddress: %s Dirty IpAddress: %s", cleanAddr, dirtyAddr); #endif return (cleanAddr); } /************************************************************************/ /* Generic safety function to process an unresolved address and remove */ /* anything that is: */ /* 1) Not a number. */ /* 2) Not a period. */ /* 3) Greater than DNSMAXBUF (255) */ /* 4) Not a legal DNS character (a-z, A-Z, 0-9, - ) */ /* */ /* XXX THIS FUNCTION IS NOT COMPLETE */ /************************************************************************/ int CleanAndResolve (char *resolvedHost, const char *unresolvedHost) { struct hostent *hostPtr = NULL; struct in_addr addr; #ifdef DEBUG Log("debug: CleanAndResolv: Resolving address: %s", unresolvedHost); #endif memset (resolvedHost, '\0', DNSMAXBUF); /* unresolvedHost must be valid */ if(unresolvedHost == NULL) return(ERROR); /* Not a valid address */ if ((inet_aton(unresolvedHost, &addr)) == 0) return(ERROR); hostPtr = gethostbyaddr ((char *) &addr.s_addr, sizeof (addr.s_addr), AF_INET); if (hostPtr != NULL) snprintf (resolvedHost, DNSMAXBUF, "%s", hostPtr->h_name); else snprintf (resolvedHost, DNSMAXBUF, "%s", unresolvedHost); #ifdef DEBUG Log("debug: CleanAndResolve: Cleaned Resolved: %s Dirty Unresolved: %s", resolvedHost, unresolvedHost); #endif return (TRUE); } portsentry_beta/portsentry_util.h0100644000076400001440000000252007663462005017505 0ustar crowlandusers/************************************************************************/ /* */ /* PortSentry */ /* */ /* Created: 10-12-1997 */ /* Modified: 05-23-2003 */ /* */ /* Send all changes/modifications/bugfixes to: */ /* craigrowland at users dot sourceforge dot net */ /* */ /* */ /* This software is Copyright(c) 1997-2003 Craig Rowland */ /* */ /* This software is covered under the Common Public License v1.0 */ /* See the enclosed LICENSE file for more information. */ /* $Id: portsentry_util.h,v 1.10 2003/05/23 17:42:07 crowland Exp crowland $ */ /************************************************************************/ /* IP address length plus null */ #define IPMAXBUF 16 char * SafeStrncpy (char *, const char *, size_t ); char * CleanIpAddr (char *, const char *); int CleanAndResolve (char *, const char *); portsentry_beta/portsentry_config.h0100600000076400001440000000332207663462005017766 0ustar crowlandusers/************************************************************************/ /* */ /* PortSentry */ /* */ /* Created: 10-12-1997 */ /* Modified: 05-23-2003 */ /* */ /* Send all changes/modifications/bugfixes to: */ /* craigrowland at users dot sourceforge dot net */ /* */ /* */ /* This software is Copyright(c) 1997-2003 Craig Rowland */ /* */ /* This software is covered under the Common Public License v1.0 */ /* See the enclosed LICENSE file for more information. */ /* $Id: portsentry_config.h,v 1.5 2003/05/23 17:41:51 crowland Exp crowland $ */ /************************************************************************/ /* These are probably ok. Be sure you change the Makefile if you */ /* change the path */ #define CONFIG_FILE "/usr/local/psionic/portsentry/portsentry.conf" /* The location of Wietse Venema's TCP Wrapper hosts.deny file */ #define WRAPPER_HOSTS_DENY "/etc/hosts.deny" /* The default syslog is as daemon.notice. You can also use */ /* any of the facilities from syslog.h to send messages to (LOCAL0, etc) */ #define SYSLOG_FACILITY LOG_DAEMON #define SYSLOG_LEVEL LOG_NOTICE /* the maximum number of hosts to keep in a "previous connect" state engine*/ #define MAXSTATE 50 portsentry_beta/portsentry_tcpip.h0100600000076400001440000001112007663462005017633 0ustar crowlandusers/* Versions of Linux are not consistent in how the TCP/UDP/IP headers * defined. This file contains the Linux/BSD headers from RedHat 5.0 and * should clear up compile problems. CHR */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tcp.h 8.1 (Berkeley) 6/10/93 */ #ifndef _NETINET_TCP_H #define _NETINET_TCP_H 1 #include __BEGIN_DECLS struct tcphdr { u_int16_t source; u_int16_t dest; u_int32_t seq; u_int32_t ack_seq; #if __BYTE_ORDER == __LITTLE_ENDIAN u_int16_t res1:4; u_int16_t doff:4; u_int16_t fin:1; u_int16_t syn:1; u_int16_t rst:1; u_int16_t psh:1; u_int16_t ack:1; u_int16_t urg:1; u_int16_t res2:2; #elif __BYTE_ORDER == __BIG_ENDIAN u_int16_t doff:4; u_int16_t res1:4; u_int16_t res2:2; u_int16_t urg:1; u_int16_t ack:1; u_int16_t psh:1; u_int16_t rst:1; u_int16_t syn:1; u_int16_t fin:1; #else #error "Adjust your defines" #endif u_int16_t window; u_int16_t check; u_int16_t urg_ptr; }; #endif /* tcp.h */ #ifndef __NETINET_IP_H #define __NETINET_IP_H 1 __BEGIN_DECLS struct timestamp { u_int8_t len; u_int8_t ptr; #if __BYTE_ORDER == __LITTLE_ENDIAN u_int8_t flags:4; u_int8_t overflow:4; #elif __BYTE_ORDER == __BIG_ENDIAN u_int8_t overflow:4; u_int8_t flags:4; #else #error "Please fix " #endif u_int32_t data[9]; }; struct ip_options { u_int32_t faddr; /* Saved first hop address */ u_int8_t optlen; u_int8_t srr; u_int8_t rr; u_int8_t ts; u_int8_t is_setbyuser:1; /* Set by setsockopt? */ u_int8_t is_data:1; /* Options in __data, rather than skb */ u_int8_t is_strictroute:1; /* Strict source route */ u_int8_t srr_is_hit:1; /* Packet destination addr was our one */ u_int8_t is_changed:1; /* IP checksum more not valid */ u_int8_t rr_needaddr:1; /* Need to record addr of outgoing dev */ u_int8_t ts_needtime:1; /* Need to record timestamp */ u_int8_t ts_needaddr:1; /* Need to record addr of outgoing dev */ u_int8_t router_alert; u_int8_t __pad1; u_int8_t __pad2; u_int8_t __data[0]; }; struct iphdr { #if __BYTE_ORDER == __LITTLE_ENDIAN u_int8_t ihl:4; u_int8_t version:4; #elif __BYTE_ORDER == __BIG_ENDIAN u_int8_t version:4; u_int8_t ihl:4; #else #error "Please fix " #endif u_int8_t tos; u_int16_t tot_len; u_int16_t id; u_int16_t frag_off; u_int8_t ttl; u_int8_t protocol; u_int16_t check; u_int32_t saddr; u_int32_t daddr; /*The options start here. */ }; #endif #ifndef __NETINET_UDP_H #define __NETINET_UDP_H 1 __BEGIN_DECLS /* UDP header as specified by RFC 768, August 1980. */ struct udphdr { u_int16_t source; u_int16_t dest; u_int16_t len; u_int16_t check; }; __END_DECLS #endif /* netinet/udp.h */ portsentry_beta/portsentry.ignore0100600000076400001440000000074007663462005017476 0ustar crowlandusers# Put hosts in here you never want blocked. This includes the IP addresses # of all local interfaces on the protected host (i.e virtual host, mult-home) # Keep 127.0.0.1 and 0.0.0.0 to keep people from playing games. # # PortSentry can support full netmasks for networks as well. Format is: # # / # # Example: # # 192.168.2.0/24 # 192.168.0.0/16 # 192.168.2.1/32 # Etc. # # If you don't supply a netmask it is assumed to be 32 bits. # # 127.0.0.1/32 0.0.0.0 portsentry_beta/portsentry.conf0100600000076400001440000002567607663462005017157 0ustar crowlandusers# PortSentry Configuration # # $Id: portsentry.conf,v 1.25 2003/05/23 16:15:39 crowland Exp crowland $ # # IMPORTANT NOTE: You CAN NOT put spaces between your port arguments. # # The default ports will catch a large number of common probes # # All entries must be in quotes. ####################### # Port Configurations # ####################### # # # Some example port configs for classic and basic Stealth modes # # I like to always keep some ports at the "low" end of the spectrum. # This will detect a sequential port sweep really quickly and usually # these ports are not in use (i.e. tcpmux port 1) # # ** X-Windows Users **: If you are running X on your box, you need to be sure # you are not binding PortSentry to port 6000 (or port 2000 for OpenWindows users). # Doing so will prevent the X-client from starting properly. # # These port bindings are *ignored* for Advanced Stealth Scan Detection Mode. # # Un-comment these if you are really anal: #TCP_PORTS="1,7,9,11,15,70,79,80,109,110,111,119,138,139,143,512,513,514,515,540,635,1080,1524,2000,2001,4000,4001,5742,6000,6001,6667,12345,12346,20034,27665,30303,32771,32772,32773,32774,31337,40421,40425,49724,54320" #UDP_PORTS="1,7,9,66,67,68,69,111,137,138,161,162,474,513,517,518,635,640,641,666,700,2049,31335,27444,34555,32770,32771,32772,32773,32774,31337,54321" # # Use these if you just want to be aware: TCP_PORTS="1,11,15,79,111,119,143,540,635,1080,1524,2000,5742,6667,12345,12346,20034,27665,31337,32771,32772,32773,32774,40421,49724,54320" UDP_PORTS="1,7,9,69,161,162,513,635,640,641,700,37444,34555,31335,32770,32771,32772,32773,32774,31337,54321" # # Use these for just bare-bones #TCP_PORTS="1,11,15,110,111,143,540,635,1080,1524,2000,12345,12346,20034,32771,32772,32773,32774,49724,54320" #UDP_PORTS="1,7,9,69,161,162,513,640,700,32770,32771,32772,32773,32774,31337,54321" ########################################### # Advanced Stealth Scan Detection Options # ########################################### # # This is the number of ports you want PortSentry to monitor in Advanced mode. # Any port *below* this number will be monitored. Right now it watches # everything below 1024. # # On many Linux systems you cannot bind above port 61000. This is because # these ports are used as part of IP masquerading. I don't recommend you # bind over this number of ports. Realistically: I DON'T RECOMMEND YOU MONITOR # OVER 1024 PORTS AS YOUR FALSE ALARM RATE WILL ALMOST CERTAINLY RISE. You've been # warned! Don't write me if you have have a problem because I'll only tell # you to RTFM and don't run above the first 1024 ports. # # ADVANCED_PORTS_TCP="1024" ADVANCED_PORTS_UDP="1024" # # This field tells PortSentry what ports (besides listening daemons) to # ignore. This is helpful for services like ident that services such # as FTP, SMTP, and wrappers look for but you may not run (and probably # *shouldn't* IMHO). # # By specifying ports here PortSentry will simply not respond to # incoming requests, in effect PortSentry treats them as if they are # actual bound daemons. The default ports are ones reported as # problematic false alarms and should probably be left alone for # all but the most isolated systems/networks. # # Default TCP ident and NetBIOS service ADVANCED_EXCLUDE_TCP="113,139" # Default UDP route (RIP), NetBIOS, bootp broadcasts. ADVANCED_EXCLUDE_UDP="520,138,137,67" ###################### # Configuration Files# ###################### # # Hosts to ignore IGNORE_FILE="/usr/local/psionic/portsentry/portsentry.ignore" # Hosts that have been denied (running history) HISTORY_FILE="/usr/local/psionic/portsentry/portsentry.history" # Hosts that have been denied this session only (temporary until next restart) BLOCKED_FILE="/usr/local/psionic/portsentry/portsentry.blocked" ############################## # Misc. Configuration Options# ############################## # # DNS Name resolution - Setting this to "1" will turn on DNS lookups # for attacking hosts. Setting it to "0" (or any other value) will shut # it off. RESOLVE_HOST = "1" ################### # Response Options# ################### # Options to dispose of attacker. Each is an action that will # be run if an attack is detected. If you don't want a particular # option then comment it out and it will be skipped. # # The variable $TARGET$ will be substituted with the target attacking # host when an attack is detected. The variable $PORT$ will be substituted # with the port that was scanned. # ################## # Ignore Options # ################## # These options allow you to enable automatic response # options for UDP/TCP. This is useful if you just want # warnings for connections, but don't want to react for # a particular protocol (i.e. you want to block TCP, but # not UDP). To prevent a possible Denial of service attack # against UDP and stealth scan detection for TCP, you may # want to disable blocking, but leave the warning enabled. # I personally would wait for this to become a problem before # doing though as most attackers really aren't doing this. # The third option allows you to run just the external command # in case of a scan to have a pager script or such execute # but not drop the route. This may be useful for some admins # who want to block TCP, but only want pager/e-mail warnings # on UDP, etc. # # # 0 = Do not block UDP/TCP scans. # 1 = Block UDP/TCP scans. # 2 = Run external command only (KILL_RUN_CMD) BLOCK_UDP="1" BLOCK_TCP="1" ################### # Dropping Routes:# ################### # This command is used to drop the route or add the host into # a local filter table. # # The gateway (333.444.555.666) should ideally be a dead host on # the *local* subnet. On some hosts you can also point this at # localhost (127.0.0.1) and get the same effect. NOTE THAT # 333.444.555.66 WILL *NOT* WORK. YOU NEED TO CHANGE IT!! # # ALL KILL ROUTE OPTIONS ARE COMMENTED OUT INITIALLY. Make sure you # uncomment the correct line for your OS. If you OS is not listed # here and you have a route drop command that works then please # mail it to me so I can include it. ONLY ONE KILL_ROUTE OPTION # CAN BE USED AT A TIME SO DON'T UNCOMMENT MULTIPLE LINES. # # NOTE: The route commands are the least optimal way of blocking # and do not provide complete protection against UDP attacks and # will still generate alarms for both UDP and stealth scans. I # always recommend you use a packet filter because they are made # for this purpose. # # Generic #KILL_ROUTE="/sbin/route add $TARGET$ 333.444.555.666" # Generic Linux #KILL_ROUTE="/sbin/route add -host $TARGET$ gw 333.444.555.666" # Newer versions of Linux support the reject flag now. This # is cleaner than the above option. #KILL_ROUTE="/sbin/route add -host $TARGET$ reject" # Generic BSD (BSDI, OpenBSD, NetBSD, FreeBSD) #KILL_ROUTE="/sbin/route add $TARGET$ 333.444.555.666" # Generic Sun #KILL_ROUTE="/usr/sbin/route add $TARGET$ 333.444.555.666 1" # NEXTSTEP #KILL_ROUTE="/usr/etc/route add $TARGET$ 127.0.0.1 1" # FreeBSD #KILL_ROUTE="route add -net $TARGET$ -netmask 255.255.255.255 127.0.0.1 -blackhole" # Digital UNIX 4.0D (OSF/1 / Compaq Tru64 UNIX) #KILL_ROUTE="/sbin/route add -host -blackhole $TARGET$ 127.0.0.1" # Generic HP-UX #KILL_ROUTE="/usr/sbin/route add net $TARGET$ netmask 255.255.255.0 127.0.0.1" ## # Using a packet filter is the PREFERRED. The below lines # work well on many OS's. Remember, you can only uncomment *one* # KILL_ROUTE option. ## # ipfwadm support for Linux #KILL_ROUTE="/sbin/ipfwadm -I -i deny -S $TARGET$ -o" # # ipfwadm support for Linux (no logging of denied packets) #KILL_ROUTE="/sbin/ipfwadm -I -i deny -S $TARGET$" # # ipchain support for Linux #KILL_ROUTE="/sbin/ipchains -I input -s $TARGET$ -j DENY -l" # # ipchain support for Linux (no logging of denied packets) #KILL_ROUTE="/sbin/ipchains -I input -s $TARGET$ -j DENY" # # iptables support for Linux #KILL_ROUTE="/usr/local/bin/iptables -I INPUT -s $TARGET$ -j DROP" # # For those of you running FreeBSD (and compatible) you can # use their built in firewalling as well. # #KILL_ROUTE="/sbin/ipfw add 1 deny all from $TARGET$:255.255.255.255 to any" # # # For those running ipfilt (OpenBSD, etc.) # NOTE THAT YOU NEED TO CHANGE external_interface TO A VALID INTERFACE!! # #KILL_ROUTE="/bin/echo 'block in log on external_interface from $TARGET$/32 to any' | /sbin/ipf -f -" ############### # TCP Wrappers# ############### # This text will be dropped into the hosts.deny file for wrappers # to use. There are two formats for TCP wrappers: # # Format One: Old Style - The default when extended host processing # options are not enabled. # KILL_HOSTS_DENY="ALL: $TARGET$" # Format Two: New Style - The format used when extended option # processing is enabled. You can drop in extended processing # options, but be sure you escape all '%' symbols with a backslash # to prevent problems writing out (i.e. \%c \%h ) # #KILL_HOSTS_DENY="ALL: $TARGET$ : DENY" ################### # External Command# ################### # This is a command that is run when a host connects, it can be whatever # you want it to be (pager, etc.). This command is executed before the # route is dropped or after depending on the KILL_RUN_CMD_FIRST option below # # # I NEVER RECOMMEND YOU PUT IN RETALIATORY ACTIONS AGAINST THE HOST SCANNING # YOU! # # TCP/IP is an *unauthenticated protocol* and people can make scans appear out # of thin air. The only time it is reasonably safe (and I *never* think it is # reasonable) to run reverse probe scripts is when using the "classic" -tcp mode. # This mode requires a full connect and is very hard to spoof. # # The KILL_RUN_CMD_FIRST value should be set to "1" to force the command # to run *before* the blocking occurs and should be set to "0" to make the # command run *after* the blocking has occurred. # #KILL_RUN_CMD_FIRST = "0" # # #KILL_RUN_CMD="/some/path/here/script $TARGET$ $PORT$" ##################### # Scan trigger value# ##################### # Enter in the number of port connects you will allow before an # alarm is given. The default is 0 which will react immediately. # A value of 1 or 2 will reduce false alarms. Anything higher is # probably not necessary. This value must always be specified, but # generally can be left at 0. # # NOTE: If you are using the advanced detection option you need to # be careful that you don't make a hair trigger situation. Because # Advanced mode will react for *any* host connecting to a non-used # below your specified range, you have the opportunity to really # break things. (i.e someone innocently tries to connect to you via # SSL [TCP port 443] and you immediately block them). Some of you # may even want this though. Just be careful. # SCAN_TRIGGER="0" ###################### # Port Banner Section# ###################### # # Enter text in here you want displayed to a person tripping the PortSentry. # I *don't* recommend taunting the person as this will aggravate them. # Leave this commented out to disable the feature # # Stealth scan detection modes don't use this feature # #PORT_BANNER="** UNAUTHORIZED ACCESS PROHIBITED *** YOUR CONNECTION ATTEMPT HAS BEEN LOGGED. GO AWAY." # EOF portsentry_beta/Makefile0100600000076400001440000001377107663462005015510 0ustar crowlandusers# Makefile for PortSentry package. # # Send problems/code hacks to help@psionic.com # # # STEALTH MODE: Only works on Linux systems right now. # # The snprintf included with the package is for use with NEXTSTEP only, # (Thanks Timothy ) although it may work elsewhere. # We've not tried it under any other OS to date. It shouldn't be needed # by any modern OS. # # Others have used the snprintf from: # # http://www.ijs.si/software/snprintf/ # # We've not tried this yet but others have had good success. Our only # piece of advice for those running an OS without built in snprintf() # is to upgrade. :) # # # Generic compiler (usually linked to gcc on most platforms) CC = cc # GNU.. #CC = gcc # Normal systems flags CFLAGS = -O -Wall # Debug mode for portsentry #CFLAGS = -Wall -g -DNODAEMON -DDEBUG #CFLAGS = -Wall -g -DNODAEMON #CFLAGS = -Wall -g -DDEBUG # Profiler mode for portsentry #CFLAGS = -pg -O -Wall -DNODAEMON #LIBS = /usr/lib/libefence.a INSTALLDIR = /usr/local/psionic CHILDDIR=/portsentry all: @echo "Usage: make " @echo " is one of: linux, debian-linux, bsd, solaris, hpux, hpux-gcc," @echo "freebsd, osx, openbsd, netbsd, bsdi, aix, osf, irix, generic" @echo "" @echo "This code requires snprintf()/vsnprintf() system calls" @echo "to work. If you run a modern OS it should work on" @echo "your system with 'make generic'. If you get it to" @echo "work on an unlisted OS please write us with the" @echo "changes." @echo "" @echo "Install: make install" @echo "" @echo "NOTE: This will install the package in this" @echo " directory: $(INSTALLDIR)" @echo "" @echo "Edit the makefile if you wish to change these paths." @echo "Any existing files will be overwritten." clean: /bin/rm ./portsentry uninstall: /bin/rm $(INSTALLDIR)$(CHILDDIR)/* /bin/rmdir $(INSTALLDIR) install: @echo "Creating psionic directory $(INSTALLDIR)" @if [ ! -d $(INSTALLDIR) ]; then /bin/mkdir $(INSTALLDIR); fi @echo "Setting directory permissions" @if [ "$(INSTALLDIR)" = "/usr/local/psionic" ]; then /bin/chmod 700 $(INSTALLDIR) ; fi @echo "Creating portsentry directory $(INSTALLDIR)$(CHILDDIR)" @if [ ! -d $(INSTALLDIR)$(CHILDDIR) ]; then /bin/mkdir\ $(INSTALLDIR)$(CHILDDIR); fi @echo "Setting directory permissions" chmod 700 $(INSTALLDIR)$(CHILDDIR) @echo "Copying files" cp ./portsentry.conf $(INSTALLDIR)$(CHILDDIR) cp ./portsentry.ignore $(INSTALLDIR)$(CHILDDIR) cp ./portsentry $(INSTALLDIR)$(CHILDDIR) @echo "Setting permissions" chmod 600 $(INSTALLDIR)$(CHILDDIR)/portsentry.ignore chmod 600 $(INSTALLDIR)$(CHILDDIR)/portsentry.conf chmod 700 $(INSTALLDIR)$(CHILDDIR)/portsentry @echo "" @echo "" @echo "Edit $(INSTALLDIR)$(CHILDDIR)/portsentry.conf and change" @echo "your settings if you haven't already. (route, etc)" @echo "" @echo "" @echo "WARNING: This version and above now use a new" @echo "directory structure for storing the program" @echo "and config files ($(INSTALLDIR)$(CHILDDIR))." @echo "Please make sure you delete the old files when" @echo "the testing of this install is complete." @echo "" @echo "" linux: SYSTYPE=linux @echo "Making $(SYSTYPE)" $(CC) $(CFLAGS) -DLINUX -DSUPPORT_STEALTH -o ./portsentry ./portsentry.c \ ./portsentry_io.c ./portsentry_util.c $(LIBS) debian-linux: SYSTYPE=debian-linux @echo "Making $(SYSTYPE)" $(CC) $(CFLAGS) -DLINUX -DDEBIAN -DSUPPORT_STEALTH -o ./portsentry ./portsentry.c \ ./portsentry_io.c ./portsentry_util.c $(LIBS) bsd: SYSTYPE=bsd @echo "Making $(SYSTYPE)" $(CC) $(CFLAGS) -DBSD44 -o ./portsentry ./portsentry.c \ ./portsentry_io.c ./portsentry_util.c openbsd: SYSTYPE=openbsd @echo "Making $(SYSTYPE)" $(CC) $(CFLAGS) -DBSD44 -o ./portsentry ./portsentry.c \ ./portsentry_io.c ./portsentry_util.c freebsd: SYSTYPE=freebsd @echo "Making $(SYSTYPE)" $(CC) $(CFLAGS) -DBSD44 -o ./portsentry ./portsentry.c \ ./portsentry_io.c ./portsentry_util.c osx: SYSTYPE=osx @echo "Making $(SYSTYPE)" $(CC) $(CFLAGS) -DBSD44 -o ./portsentry ./portsentry.c \ ./portsentry_io.c ./portsentry_util.c netbsd: SYSTYPE=netbsd @echo "Making $(SYSTYPE)" $(CC) $(CFLAGS) -DBSD44 -o ./portsentry ./portsentry.c \ ./portsentry_io.c ./portsentry_util.c bsdi: SYSTYPE=bsdi @echo "Making $(SYSTYPE)" $(CC) $(CFLAGS) -DBSD44 -o ./portsentry ./portsentry.c \ ./portsentry_io.c ./portsentry_util.c generic: SYSTYPE=generic @echo "Making $(SYSTYPE)" $(CC) $(CFLAGS) -o ./portsentry ./portsentry.c ./portsentry_io.c \ ./portsentry_util.c hpux: SYSTYPE=hpux @echo "Making $(SYSTYPE)" $(CC) -Ae -DHPUX -o ./portsentry ./portsentry.c ./portsentry_io.c \ ./portsentry_util.c hpux-gcc: SYSTYPE=hpux-gcc @echo "Making $(SYSTYPE)" $(CC) $(CFLAGS) -DHPUX -o ./portsentry ./portsentry.c ./portsentry_io.c \ ./portsentry_util.c solaris: SYSTYPE=solaris @echo "Making $(SYSTYPE)" $(CC) -lnsl -lsocket -lresolv -lc -o ./portsentry ./portsentry.c ./portsentry_io.c \ ./portsentry_util.c aix: SYSTYPE=aix @echo "Making $(SYSTYPE)" $(CC) $(CFLAGS) -o ./portsentry ./portsentry.c ./portsentry_io.c \ ./portsentry_util.c osf: SYSTYPE=osf @echo "Making $(SYSTYPE)" $(CC) $(CFLAGS) -taso -ldb -o ./portsentry ./portsentry.c ./portsentry_io.c \ ./portsentry_util.c irix: SYSTYPE=irix @echo "Making $(SYSTYPE)" $(CC) $(CFLAGS) -O -n32 -mips3 -o ./portsentry ./portsentry.c ./portsentry_io.c \ ./portsentry_util.c # NeXTSTEP Users. NeXT used to work, but we changed the log function and # it now uses vsnprintf() to format strings. This means that this # version does not work under NeXTSTEP until we can find a workable # vsnprintf() call to put in the program. Sorry. If you have some good # vsnprintf() code to use under NeXT please send it to us and we'll # include it on the next update. #next: # SYSTYPE=next # @echo "Making $(SYSTYPE)" # $(CC) $(CFLAGS) -DNEXT -DHAS_NO_SNPRINTF -posix -o ./portsentry ./portsentry.c \ # ./portsentry_io.c ./portsentry_util.c portsentry_beta/README.COMPAT0100600000076400001440000000056407663462005015706 0ustar crowlandusersTested and found to work on: Linux 1.x/2.x BSDI 2.x/3.x OpenBSD 2.x FreeBSD 3.x HPUX 10.20 Solaris 2.6+ AIX SCO Digital Unix NetBSD OSX portsentry_beta/README.install0100600000076400001440000005071607663462005016375 0ustar crowlandusersPortSentry - Port scan detection and active defense. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- $Id: README.install,v 1.29 2003/05/23 18:10:02 crowland Exp crowland $ E-Mail : craigrowland at users dot sourceforge dot net Date : 05-23-2003 Version: 1.2 Introduction =-=-=-=-=-=- This is the "long" install version. You should read this file if you want to understand everything going on and the method to the madness in the program logic. Skip to Install down below if you don't care about this. More information can be obtained from http://sourceforge.net/projects/sentrytools PortSentry has a number of options to detect port scans, when it finds one it can react in the following ways: - A log indicating the incident is made via syslog() - The target host is automatically dropped into /etc/hosts.deny for TCP Wrappers - The local host is automatically re-configured to route all traffic to the target to a dead host to make the target system disappear. - The local host is automatically re-configured to drop all packets from the target via a local packet filter. The purpose of this is to give an admin a heads up that their host is being probed. There are similar programs that do this already (klaxon, etc.) We have added a little twist to the whole idea (auto-blocking), plus extensive support for stealth scan detection. PortSentry has four "stealth" scan detection modes. Method one uses a pre-defined list of ports to watch over. If someone pokes at them it activates. The second method is what is called "inverse" port binding, where every port under a range is watched *except* for those that the system has bound for network daemons when the PortSentry starts or ones that you've manually excluded. This is a very sensitive way for looking for port probes, but also the most prone to false alarms. Install =-=-=-= Step ONE: Pull the portsentry_config.h file into your editor and make sure the following are to your liking: CONFIG_FILE - The path to the PortSentry configuration file. WRAPPER_HOSTS_DENY - The path and name of TCP wrapper hosts.deny file. SYSLOG_FACILITY - The syslog facility for PortSentry to use. SYSLOG_LEVEL - The syslog level to send messages. We suggest not changing any of these options unless you know what you are doing. NOTE: For advanced users, you may wish to change the SYSLOG_FACILITY from LOG_DAEMON to LOG_LOCAL0 (or one of the other LOCAL reporting facilities). This will allow you to edit the syslog.conf file and drop PortSentry messages direcly to its own file on the system for separate monitoring. SECOND NOTE: DO NOT DELETE THE "#" SIGNS FROM THIS FILE. They are NOT comments, they are required by the C compiler to pre-process the headers. If you delete the "#" signs you will get compile errors. Step TWO: Next, pull in portsentry.conf into your editor and check/change the following options: TCP_PORTS - A comma delimited string of TCP ports you want PortSentry to listen to. This string can NOT have any spaces in it. You can put in as many sockets as you want. PortSentry will try to bind them all up until the default limit of 64. For the stealth scan detection modes, the ports are not "bound" per se, but they are monitored at the socket level for connections. For the Advanced Stealth Scan Detection (see below). This list is *ignored* UDP_PORTS - The same as above, except for UDP ports. You need to be very careful with UDP mode as an attacker can forge a port sweep and make you block any number of hosts. Use this option with caution, or not at all if your host is a well-known Internet connected system. For the Advanced Stealth Scan Detection (see below). This list is *ignored* ADVANCED_PORTS_TCP - A number indicating the highest port number to monitor down from. Any port *below* this number is then monitored. The default is 1024 (reserved port range), but can be made as large as 65535 (system max). I don't recommend going over 1024 with this option. ADVANCED_PORTS_UDP - Same as above, except for UDP. ADVANCED_EXCLUDE_TCP - A comma delimited string of TCP ports that should be manually excluded from monitoring in Advanced mode. These are normally ports that may get hit by mistake by remote clients and shouldn't cause alarms (ident, SSL, etc). ADVANCED_EXCLUDE_UDP - Same as above, except for UDP. IGNORE_FILE - The path to the file that contains IP addresses of hosts you want to always be ignored. This will be explained later. BLOCKED_FILE - The path to the file that contains the IP addresses of blocked hosts. RESOLVE_HOST - This option turns off DNS resolution for hosts. If you have a slow DNS server it may be more effective to turn off resolution. BLOCK_UDP - This option disables all automatic responses to UDP probes. Because UDP can be easily forged, it may allow an attacker to start a denial of service attack against the protected host, causing it to block all manner of hosts that should normally be left alone. Setting this option to "0" will disable all responses, although the connects are still logged. This option is mainly useful for Internet exposed hosts. For internal hosts you should leave this enabled. If someone internally is firing spoofed packets at you, then you have a much bigger problem than a denial of service. BLOCK_TCP - Same as above, but for TCP. Packet forgery is not as big a problem though because PortSentry waits for a full connect to occur and this is much harder to forge in the basic modes. Leave this enabled, even for Internet connected hosts. For stealth scan detection modes the UDP warning applies: An attacker can cause you to block hosts you don't want to through packet forgery. KILL_ROUTE - This is the command to run to drop the offending route if an attack is detected. This is the *full path* to the route command along with the necessary parameters to make the command work. The macro $TARGET$ will be substituted with the attacking host IP and is REQUIRED in this option. Your gateway should be a *dead host* on the local subnet. On some systems though you can just put in the localhost address (127.0.0.1) and this will probably work. All packets from the target host will get routed to this address so don't mess this up. More modern route commands will include a "-blackhole" or "-reject" flag. Check your man(1) pages and if your route command supports this feature you should use it (although we recommend using packet filtering instead, see below). Also be aware that this creates what is known as an "asynchronous route" which basically means packets enter your host via one route and are sent out on another (dead) route. This works OK for full TCP connect requests, but for UDP and stealth scan modes it still allows packets to activate PortSentry and you may get a series of "already blocked" alarms by PortSentry. For UDP scans this method prevents ICMP messages from returning to the attacker so all ports appear open. However, if the attacker is performing an actual exploit with UDP the drop route method will not work. The asynchronous route allows the packet to hit the system and the attacker could perform a "blind" attack with UDP if they know what the responses are going to be. By far the best method is to use the local packet filter (such as Linux ipfwadm/ipchains or *BSD ipfw). This is a much cleaner solution and is detailed in the config file. The macro $PORT$ will substitute the port that was connected to by the attacker, but this is NOT required for this option. The macro $MODE$ reports what mode the blocking occurred in (tcp, udp, stcp, sudp, atcp, audp) but is also NOT required. KILL_HOSTS_DENY - This is the format of the string to drop into the hosts.deny file that TCP wrappers uses. Again the $TARGET$ macro is expanded out to be the IP of the attacker and is required. You can also drop in any TCP wrapper escape codes here as well (%h, twist, etc). The macro $PORT$ will substitute the port that was connected to by the attacker, but this is NOT required for this option. The macro $MODE$ reports what mode the blocking occurred in (tcp, udp, stcp, sudp, atcp, audp) but is also NOT required. KILL_RUN_CMD - This is a command you want run *before* the route is dropped to the attacker. You can put in any program/script you want executed when an attack is detected. WE NEVER RECOMMEND PUTTING IN RETALIATORY ACTION AGAINST AN ATTACKING HOST. Virtually every time you're are port scanned the host doing the scanning has been compromised itself. Therefore, if you retaliate you are probably attacking an innocent(?) party. Also the goal of security is to make the person GO AWAY. You don't want to irritate them into making a personal vendetta against you. Remember, even a 13 year old can run a [insert favorite D.O.S. program here] attack against you from their Windows box to make your life miserable. As above, the $TARGET$, $PORT$, and $MODE$ macros are available to you but they are not required with this option as above. KILL_RUN_CMD_FIRST - Setting this to "0" makes the command above run before the route is dropped. Setting it to "1" makes the command run aftter the blocking has occurred. SCAN_TRIGGER - PortSentry has a state engine that will remember hosts that connected to it. Setting this value will tell PortSentry to allow X number of grace port hits before it reacts. This will detect both sequential and random port sweeps. The default is 0 which will react immediately. A setting of 1 or 2 will reduce false alarms, anything higher is probably too much as anything more than 3 hits to different ports is pretty suspicious behavior. Usually you can leave this at 0 without any consequence, with the exception of Advanced stealth scan detection modes where you may create a "hair trigger" if you aren't careful. Use your own discretion. PORT_BANNER - A text banner you want displayed to the connecting host if the PortSentry is activated. Leave this commented out if you don't want this feature. If you do use it, try not to taunt the person too badly. We recommend keeping it professional and to the point. The banner is *not* displayed when stealth scan detection modes are used. Step THREE: Pull the portsentry.ignore file into your editor and add in any host you want to have ignored if it connects to a tripwired port. This should always contain at least the localhost (127.0.0.1) and the IP's of the local interfaces. We would *not* recommend putting in every machine IP on your network, but you can use a netmask to do this. Format for this is: / 192.168.2.0/24 192.168.0.0/16 etc. We don't recommend ignoring too much. It may be important for you to see who is connecting to you, even if it is a "friendly" machine. This can help you detect internal host compromises faster. To answer your paranoia, yes this does happen and we've had cases of admins ignoring too much and getting hacked by their own machines as a result with no warning. Step FOUR: Compile. Type make and pick your system type and allow it to build and install. The default directory is /usr/local/psionic/portsentry. If you don't like this directory just edit the Makefile and make sure your portsentry.conf and portsentry_config.h files reflect the new path. Type make install after the build to have it copy files to your install directory. Step FIVE: Start up PortSentry. PortSentry has six modes of operation. ONLY ONE PROTOCOL MODE TYPE CAN BE STARTED AT A TIME (i.e. one TCP mode and one UDP mode) The available modes are: portsentry -tcp (basic port-bound TCP mode) portsentry -udp (basic port-bound UDP mode) portsentry -stcp (Stealth TCP scan detection) portsentry -atcp (Advanced TCP stealth scan detection) portsentry -sudp ("Stealth" UDP scan detection) portsentry -audp (Advanced "Stealth" UDP scan detection) -tcp - Basic port-bound TCP mode PortSentry will check the config files and then bind to all the TCP ports in the background. If you want to check the init status you should just look in your local syslog file that you are having the messages directed to. -udp - Basic port-bound UDP mode PortSentry will check the config files and then bind to all the UDP ports in the background. If you want to check the init status you should just look in your local syslog file that you are having the messages directed to. UDP/Stealth scan warnings apply (read: README.stealth). -stcp - Stealth TCP scan detection mode PortSentry will use a raw socket to monitor all incoming packets. If an incoming packet is destined for a monitored port it will react to block the host. This method will detect connect() scans, SYN/half-open scans, and FIN scans. UDP/Stealth scan warnings apply (read: README.stealth). -sudp - "Stealth" UDP scan detection mode This operates the same as the TCP stealth mode above. UDP ports need to be listed and they are then monitored. This does not bind any sockets, and while not really "stealth" scan detection (doesn't usually apply to UDP), it operates in a similar manner (reacts to *any* UDP packet). UDP/Stealth scan warnings apply (read: README.stealth). -atcp - Advanced TCP stealth scan detection mode PortSentry will start by making a list of all the ports listening in the port area under the ADVANCED_PORTS_TCP option and will then create an exclusion list based on these ports. Any host connecting to *any port* in this range that is *not excluded* (i.e not a listening network daemon [SMTP, HTTP, etc.]) is blocked. This has some very powerful implications that you should be aware of: 1) This mode is the most sensitive and the most effective of all the protection options. It reacts to port probes with lightning speed because you don't have to wait for them to hit a tripwired port. 2) Because it reacts so abruptly, you may cut off legitimate traffic. A FTP site may send an ident request at you. If you are monitoring the ident port (113 TCP) then you have just cut off the FTP site you were going to! As a result you should put in ports that fall into this situation in this list. ** Advanced Logic Mode ** - PortSentry is intelligent about how it monitors ports. For some protocols such as FTP the client actually opens up ports in the ephemeral range (1024-65535) and the server then connects *back* to you. This would normally cause the port scanner to activate. PortSentry though will look at the incoming connection and determine if it is destined for one of these "temporary" bindings. If it is, then the connection is ignored for that one time. As soon as the connection is torn down the window closes and full protection is back again. This is in fact a rudimentary stateful inspection engine. UDP/Stealth scan warnings apply (read: README.stealth). -audp - Advanced UDP "stealth" scan detection mode This is the same as above except for the UDP protocol. This is a very advanced option and you stand a good chance of causing false alarms. This is because PortSentry makes no distinction between broadcast and direct traffic. If you have a router on your local network putting out RIP broadcasts then there is a good likelihood you will block them. Use this option with extreme caution. You need to be sure you put in these exclusions into the ADVANCED_EXCLUDE_UDP line (i.e 520 [RIP]) UDP/Stealth scan warnings apply (read: README.stealth). Test the install: Tail the local log and you should see several PortSentry initialization messages. A successful startup looks like this: Oct 9 09:11:35 nemesis portsentry[1644]: adminalert: portsentry is starting. Oct 9 09:11:36 nemesis portsentry[1644]: adminalert: Going into listen mode on TCP port: 143 . . . Oct 9 09:11:37 nemesis portsentry[1644]: adminalert: PortSentry is now active and listening. ************************************************************************ ** The last line indicates the PortSentry is properly initialized, if ** ** you don't see it then something failed. ** ************************************************************************ You should see all the ports you told it to listen to in the log. If a port is in use PortSentry will give you a warning that it couldn't bind to it and will continue on until all the other ports are bound. If *none* of the ports could be bound it will exit with an error. For the Advanced stealth scan detection mode it will list the ports that it will *not* listen for. This is an inverse binding. Now you can go to another host and telnet to a booby-trapped port. DO NOT DO THIS FROM YOUR ONLY ACCESS POINT TO THE PROTECTED HOST BECAUSE YOU WILL BLOCK YOURSELF OFF COMPLETELY IF IT WORKS. You should immediately see something like: Oct 9 09:12:44 nemesis portsentry[1644]: attackalert: Connect from host: 123.345.56.78 to TCP port: 143 Oct 9 09:12:46 nemesis portsentry[1644]: attackalert: Host server.haxor.org/123.345.56.78 has been blocked via dropped route. Oct 9 09:12:46 nemesis portsentry[1644]: attackalert: Host server.haxor.org/123.345.56.78 has been blocked via wrappers. For advanced mode you can telnet to any port not excluded to trip an alarm. If you disconnect and try to telnet back again you should find that the target system is now unreachable. Congratulations you are now operational. If you are running Logcheck this will show up in the next pass and it should be screaming at you. If you do a netstat -rn you will see the suspect host pointed at the dead route you supplied (unless using a packet filter, which is what we recommend). Drop the PortSentry commands in a startup file and get back to work as you are done. How will it help? Here are some ideas: - Run as a UDP service on port 69 to catch TFTP probes. - Run as a UDP service on port 161,162 to catch SNMP probes. - Run as a UDP service in the port range 32000-33000 to catch RPC probes. - Run as a TCP service on port 143 to catch IMAP probes. - Run as a TCP service on ports 11,15 to catch netstat/systat probes. - etc. The fact is that PortSentry reacts quickly enough that a port scan of your host by an attacker will be stopped within one second after hitting any tripwired port, even faster in the Advanced TCP stealth scan detection mode. For any type of UDP scan it will prove highly irritating for the person trying to scan you as all the ports will likely appear "open." For TCP scans the attacker will simply get no response whatsoever. Safety =-=-=- If we missed anything in the program's safety considerations we would very much like to hear about it before you post it to BugTraq :). Messages =-=-=-=- To the best we could, all states/errors/successes and unknowns are written to the syslog. The following tags identify each one: adminalert: - Some message indicating the status of the PortSentry. securityalert: - A message indicating a security relevant event occurred. attackalert: - A host has tripped a sensor and an action was performed. Files =-=-= As it stands now, all hosts are dropped into the portsentry.blocked.* file when they are blocked as well as a portsentry.history file. The blocked file is erased each time PortSentry is restarted. The history file is simply appended to and can be used as a record of all hosts that have been blocked to date. The portsentry.blocked.* has an appended extension indicating what mode it applies to. I.E.: portsentry.blocked.tcp portsentry.blocked.udp portsentry.blocked.stcp portsentry.blocked.sudp portsentry.blocked.atcp portsentry.blocked.audp Each file is created depending on what startup mode is selected and is removed when PortSentry re-starts. The way the route blocking works is that it drops the *return* route to the attacking host, not the *incoming* route. If an attacker is doing a UDP scan of your host the packets will still trip the sensor, they just don't return to the host. If the blocked file did not exist the host would be written possibly hundreds or thousands of times to hosts.deny!! This is not good. A better solution is to integrate in with a real packet filter locally (ala Linux ipfwadm which is supported or any other similar package on your system). After a period of time elapses you may wish to delete the local dead route to the offender and keep them in the hosts.deny file. This is solely your choice. If you wish to re-enable blocking should the offender return, just re-start PortSentry or delete the individual entry from the blocked file. If you need to restore the route to the blocked host on most systems you can simply delete the route like so: Linux: route del reject Others: route delete Or you can simply flush your packet filters. That's about it. We highly recommend you use Logcheck to keep an eye on things in the log files as well so you can detect other problems, and see what the PortSentry is saying to you. You can find this program at: http://sourceforget.net/projects/sentrytools portsentry_beta/README.methods0100600000076400001440000001236407663462005016367 0ustar crowlandusersPortSentry Errata =-=-=-=-=-=-=-=-= $Id: README.methods,v 1.15 2003/05/23 17:42:28 crowland Exp crowland $ This is file contains some answers to questions we've been asked, or you may be wondering. 1) How did you pick the ports in the default .conf file? 2) Why should I be careful about running the PortSentry program? (PLEASE READ THIS) Why did you pick the ports in the default .conf file? =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= There are some methods to the madness in the way ports were picked. Allow me to explain: TCP Ports - Chosen for a particular interest that they pose to an attacker. These ports are generally the most likely to be abused by a person seeking to gain access to a host. Low Range - Ports: 1-20 These ports are chosen for two specific purposes: 1) Because some scanners do sequential scanning starting at 1 and working up. This will allow the PortSentry to respond rapidly to a scan before any real services are revealed. 2) Several of these services (systat, netstat) are used by attackers as recon mechanisms and are frequently looked for when they are performing a targeted non-sequential port sweep. Low-Mid Range - Ports: 20-500 These ports contain a large number of services that are very commonly looked for by attackers. Some of these services can be used for recon purposes or for full remote access. Mid Range - Ports: 500-1024 These ports contain a host of services commonly looked for by attackers (rsh, rlogin, rexec, mountd). Mid-High Range - Ports: 1024-32768 These ports contain a wealth of services such as X-Windows, IRC servers, router serial ports, MUDs and others. One particular port (31337) is sometimes used as a backdoor, and is used by the program "netcat" to facilitate UDP (yes that's UDP) scanning of the target host. High Range - Ports 32769-65535 These ports are generally not used, with the exception of a really nasty bug in Solaris in which the portmapper service (normally on port 111) would present a copy of itself listening in the 327XX range of ports. This allows an attacker to use a modified portmapper probe to hop filters and dump RPC services on the target. Some scans target this range specifically. The port 49724 is used by the scanning tool "nmap" to do UDP scanning much like netcat as well. ------------------------------------------------------------------------------- UDP Ports - These ports harbor a large number of services (largely RPC related) and have become the frequent target of "stealth" RPC scanning in which the attacker attempts to locate RPC services manually instead of using portmapper. This prevents notification of admins of unauthorized use of the portmapper, a feature found in Wietse Venema's portmapper version. Low Range - Ports: 1-20 As above, some scanners do sequential scanning starting at 1 and working up. These ports will allow the PortSentry to respond rapidly to a scan before any real services are revealed. Low-Mid Range - Ports: 20-500 TFTP(69), SNMP(161,162), and SMB(137,138) are commonly sought ports in this region for the information and compromise potential they provide. Mid Range - Ports: 500-1024 A large number of RPC services are located in this area. Some attempts to do "stealth" RPC scanning concentrate a UDP scan in the 500-700 range. This will allow admins to get notification of this activity. Mid-High Range - Ports: 1024-32768 NFS (2049) is the main problem here, along with some RPC services and Back Orifice (31337). High Range - Ports 32769-65535 These ports are generally not used, again though, Solaris has a habit of concentrating services in the 327XX range. Waiting here for connections will find people probing for these services quickly. People shouldn't be here unless portmapper sent them, and since we don't register with portmapper they shouldn't know about us unless they are up to no good. Why should I be careful about running the PortSentry program? =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Because you have the potential of presenting information to an attacker that may tempt them to return at a later date. If you show a person a load of ports open then it may pique their curiosity. If they return they may find a real problem with your system that the PortSentry cannot defend. Additionally you may do something un-professional like present a banner to them that says "You k-rad HaX0r d00d you'll NeVEr brek r00t heRe!!" and this then makes it a challenge. This is something to consider. With UDP/stealth scan detection, there is the possibility that an attacker can flood your host with bogus packets causing PortSentry to continuously activate and write out warnings to your log. This can create a denial of service attack that you should be aware of. We do not recommend running UDP/stealth scan detection on an Internet host that is high-profile and subject to frequent abuse. It is our experience though that spoofed scans are not an issue and we recommend people use auto-blocking knowing that %99.9 of the time it will block a scan. If you run into a denial of service problem on your host, just go back to "classic" TCP mode and the problem will go away. Again though, we strongly feel that the benefits of blocking hosts *far outweighs* the limited risk you take by having auto-blocking turned on. portsentry_beta/README.stealth0100600000076400001440000001621607663462005016370 0ustar crowlandusers************************************************************************* STEALTH MODE ONLY SUPPORTED FOR LINUX SYSTEMS ONLY ************************************************************************* $Id: README.stealth,v 1.9 2000/09/27 04:17:23 crowland Exp crowland $ Stealth Scans =-=-=-=-=-=-= Right now PortSentry will detect the following: - Strobe-style scans (full connect() scans) - SYN/Half open scans. - FIN scans. - NULL scans. - XMAS scans. - UDP scans (not really stealth scans per se) - Any odd-ball packet with flags not matching the above. You can test out this functionality by grabbing "nmap" Which is a popular scanner. You can get it from: http://www.insecure.org A few notes on the implementation: =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Stealth scan detection mode one ------------------------------- This mode will monitor ports just like the original version of PortSentry. The primary difference being that it does not bind to anything. An attacker still has to hit a tripwired port to activate the sensor though. To prevent other issues, this scan mode will not react if the ACK flag of the TCP packet is set. This prevents causing alarms on "established" connections that happen to take one of your high ports temporarily. Stealth scan detection mode two ------------------------------- ** Advanced Logic Mode ** - PortSentry is intelligent about how it monitors ports. For some protocols such as FTP the client actually opens up ports in the ephemeral range (1024-65535) and the server then connects *back* to you. This would normally cause the port scanner to activate. PortSentry though will look at the incoming connection and determine if it is destined for one of these "temporary" bindings. If it is, then the connection is ignored for that one time. As soon as the connection is torn down the window closes and full protection is back again. This is in fact a rudimentary stateful inspection engine. Trying it out: -------------- 1) Fire up PortSentry in any one of the stealth modes (-stcp, -atcp, -sudp, -audp). 2) Take a program such as netcat and bind up to a protected port on the host (i.e. "nc -p 143 -l"). 3) Go to a remote machine and telnet to that port, you will now connect to netcat and can see the text you type on your host. 4) Now disconnect and shut down netcat. 5) Re-Connect to the same protected port again from the remote host and now PortSentry will block you normally. This logic is built into all of the stealth modes (UDP and TCP). UDP "stealth" scan detection ---------------------------- The UDP flags are not really "stealth" scan detection in the traditional sense, but acts like the other TCP stealth scan options. Simply put, it doesn't bind ports but uses a raw socket. The same warnings apply. We don't suggest using Advanced mode UDP stealth scan detection unless you know what you are doing. It is very good if you know enough to exclude broadcast traffic (you may want to run it in non-blocking mode to see what alarms are generated and then set this up). The ports that generally cause this are route (520) and NetBIOS (137/138). General WARNINGS and CAUTIONS ----------------------------- As stated in several places, it is possible that an attacker can forge packets to appear from any host and can use this to trick PortSentry into activating against the forged host IP. This can cause a variety of problems in theory such as blocking gateways or name servers. Sometimes though theory and reality just don't mix. The reality is that not many people are using this tactic. In fact recent versions of nmap even put in a "decoy" feature which we can only assume was prompted by the release of PortSentry. This feature uses a list of forged hosts to try to conceal the real culprit. The theory being that the attacker is hidden in a list of chaff and the port scan detector is blocking everyone thereby making it ineffective. Well arguments can be made all day on the pluses and minuses of auto-blocking hosts. When the theory is examined, the reality sets in which shows through our own (informal) observations that your chances of someone doing this to you are small. In fact we think that it is small enough that if you are considering running the stealth scan detection on a small *not-well-known* host the benefits outweigh the risk. Why is this? Well: 1) The person port scanning you doesn't want to be found, that is why they are "stealth" scanning you to begin with. It is kind of silly to spray false packets at a host during the scan as this only increases the chances of being spotted and no matter what gets your host blocked anyway. 2) Spraying X number of additional packets slows your scan down by a similar amount. Most attackers are going for quantity, not quality. They want a scan to finish ASAP and with the least amount of noise. 3) Many networks now deploy anti-spoof filters which will prevent "decoy" packets from exiting the border routers due to a bogus source address not on the network. This means an attacker going through an ISP or similarly clueful network will cause many router log messages to be generated and will certainly grab attention of any aware admin from the originating network. This also means the decoy packets won't make it to your host and the real scanning host is revealed. 4) Even if the intruder is smart and uses decoy addresses from the local subnet to allow them to exit the network it still raises a red flag that a network administrator will know where to start. Despite what people think, it's not *that* hard to find out which of 10 (or whatever) possible hosts are compromised and doing a port scan. Does this mean you are risk free?? No. But we have not received a single complaint so far about people using forge scan tactics on a widespread basis (in fact we haven't received a *single* complaint of this tactic being used at all). So for the time being (as of this writing) you are probably OK if you look at all the facts. So where do we stand on the issue?? Well we use full connect mode for any external host, but this is because some of the hosts we watch are high-profile and we don't want to risk people playing games with us. For internal hosts though we will use the stealth modes because we want to know immediately of a probe; forged or not. Now the conservative side would like to add a few things. We would like to say that the initial version of this tool only did full connect TCP scan detection. This was done deliberately to prevent such attacks from possibly occurring. Because of this, we only recommend the full connect mode (-tcp) for deployment on critical hosts or hosts subject to frequent abuse... ...and the band played on... Over time we had many people write about UDP support and stealth scan support so we put them in because we thought they would be useful. What this means is that our physical-world philosophy matches our virtual-world philosophy. Basically, we accept the fact that people are capable of making their own decisions when given adequate information. Therefore, you are responsible for your own actions. We have given you all the information you should need to decide what mode to use the tool. So don't complain if something bad happens, because it is your choice. portsentry_beta/CHANGES0100600000076400001440000002131407663462005015033 0ustar crowlandusers$Id: CHANGES,v 1.50 2003/05/23 17:44:15 crowland Exp crowland $ PortSentry Changes 10-12-1997 0.01 - Project Begins. 10-13-1997 0.02 - Multiple port binding. Config file added. TCP wrapper support added. 10-14-1997 0.03 - Added state engine. 11-2-1997 0.04 - Bug fixes in state engine. Speed enhancements. 11-4-1997 0.05 - Added PORT_BANNER option 11-30-1997 0.06 - Re-vamped config file options. Consolidated variables and added $TARGET$ macro expansion. Added option to run external command. 12-04-1997 0.08 - Code cleanup and public alpha release. 12-06-1997 0.09 - Added option to skip blocking on UDP scans to prevent DOS attacks. 12-10-1997 0.10 - Added alert for possible stealth scan detection. 3-10-1998 0.50 - FINALLY got back to work on this thing. 3-11-1998 0.50 - Added/changed configuration option to not react to TCP/UDP probes (report only). Added history file to store permanent list of all blocked hosts. Changed blocked file to be truncated to 0 bytes on startup so users don't have to manually clean it out on re-start. Removed reporting of already blocked hosts for UDP to prevent possible denial of service attack. Put in limit of 64 open sockets per instance (some systems did not have FD_SETSIZE defined correctly and this caused errors). Removed logging of connections from ignored hosts (why ignore them if I still report their connection??) Began renaming from "Abacus Sentry" to just plain "sentry" to eliminate confusion. Updated docs. Minor code cleanups. 5-11-1998 0.60 Put in reverse DNS reporting on connections. Should have done this long ago.. Changed history file and blocked file to write out resolved hostname on connects 5-25-1998 0.60 Added TCP SYN stealth scan detection Added TCP FIN stealth scan detection Added "Advanced" stealth scan detection mode. 5-26-1998 0.60 Added "Smart-Verify" port profiling in all stealth modes to avoid blocking legitimate connections. 5-28-1998 0.61 Added tcp/ip headers with distribution because some versions of Linux still use the "old" style and not the BSD variant. 4-12-1999 0.80 Cleaned up lots of code. Reduced major redundant sections. Eliminated redundant config file reads and moved code to inititialize variables at startup instead of during program loops. Added ports 635TCP, 12345TCP, 12346TCP, 31337UDP to port list. Lines of code reduced by 20%. Removed hacked-in tcpip.h/ip.h header files and used proper Linux defines. Renamed package from "Abacus Sentry" to "PortSentry" 4-13-1999 0.80 Cleaned up more code. Moved packet classification to separate function. Began regression testing. 4-14-1999 0.80 Added port 1524TCP (ingreslock) after several reports of ttdbserver overflows using this as the backdoor insertion point. 4-30-1999 0.80 Fixed buggy string substitute function. It is now faster and more reliable. Removed config file reading from all sub-functions and put it into InitConfig() in main executable. This cuts down on file IO during heavy activation of the probe scanner. Lots of small code cleanups. 5-01-1999 0.89 All docs updated and code base given distribution version 0.89-BETA for release. 05-03-1999 0.89 Added $PORT$ option parsing to all kill options Added KILL_TCP/KILL_UDP option of "2" to allow for running a command, but not running other kill route/hosts.deny options Fixed another bug in the subst function that would return an empty string if nothing found instead of a string copy of the original. This was causing a string that didn't have the optional $PORT$ command to return empty. Moved check for BLOCKED_FILE into init function from the checkconfig function. Printed out corrupted config file warnings to screen and syslog instead of just syslog before aborting. Made QA checklist part of package in case people are interested. Added some more logging to kill* functions. Also added print out to log files of exact string run for route, hostsdeny, runcmd options. Changed BANNER to something a little shorter. Fixed NeXTSTEP route command entry. 05-04-1999 0.89a Added new trojan horse ports for TCP_PORTS: 20034 - NetBus Pro 5742 - Win Crash Trojan 30303 - Socket De Troye 40421 - Unknown Trojan Horse (Master's Paradise [CHR]) The above were taken from a post on comp.security.unix by: jeromexxx@club-internet.fr on 05-03-99 05-10-1999 - 0.90 QA checklist completed. Incremented version to 0.90 Updated docs/spellcheck 05-11-1999 - 0.90 Changed install dir to /usr/local/psionic/portsentry Updated docs again. Removed NeXTSTEP make rule because OS lacks vsnprintf() and I don't have a working version to put in. Re-Ordered #include<> files to prevent warnings/errors under BSD (OpenBSD) 05-12-1999 - 0.90 Found very subtle bug in SafeStrncpy that would overwrite last part of dest data with extra null outside of bounds and would intermittently corrupt data. This was a real pain and took almost six hours of testing on various platforms to track down as no debugger turned it up. I hope you people appreciate all the aggravation I go through to write this thing. ;) 5-26-1999 - 0.90a Applied patch from to get new version to compile under older Linux distributions. Added AIX build option. 6-02-1999 - 0.90b Added OSF build option. Added OSF KILL_ROUTE command in .conf file Fixed ipchains entry in .conf file. Updated conf file to warn about bogus route. Switched to native linux tcphdr/udphdr structs instead of BSD style. 6-03-1999 - 0.90c Added ignore.csh script contributed by Christopher Lindsey. 7-09-1999 - 0.91 Fixed corrupted readme.qa file 7-12-1999 - 0.98 Fixed IP parsing problems in .ignore and .blocked functions. Now correctly ignores blocked hosts and ignored hosts. Bug reported by 7-27-1999 - 0.98.1 Fixed a nasty bug where an attacker could cause Sentry to ignore a scan if you have a specific configuration setup on your host. Bug reported by Reuven Gevaryahu . 7-28-1999 - 0.99 Finally put back in the parts that correctly read IP options and added extra code to check for illegal sizes, etc. This has been a long time coming...sorry for the delay. 08-02-1999 - 0.99 WriteBlocked() now writes out packet type being blocked "TCP" or "UDP" in the log files. 10-15-1999 - 1.0 NeverBlock() function fixed so now it actually does ignore hosts correctly. I was stripping off the EOL prematurely during a strlen check and this caused the problem. Thanks to all reporters for finding this. 10-24-1999 - 1.0 Updated docs to tell users how to add LOCAL0 facility for syslog reporting. 11-14-1999 - 1.0 Y2K fix in WriteBlocked functions. Now uses four digit year in .blocked and .history files. Old format: 942286729 - 11/10/99 20:18:49 Host: 192.168.2.12/192.168.2.12 Port: 111 TCP Blocked New format: 942286729 - 11/10/1999 20:18:49 Host: 192.168.2.12/192.168.2.12 Port: 111 TCP Blocked PortSentry doesn't use dates as part of its operations, however third party scripts may use the .blocked and .history files and the two digit format would roll over to 1/1/100 on Jan 1 instead of 1/1/00 causing a potential problem. Sorry about that. :( 12-21-1999 - 1.1 Fixed typo in bare-bones TCP list where 524 was supposed to be for 1524. 03-31-2000 - 1.1 Updated .conf to add ipf blocking rule. Thanks Graham Dunn 06-08-2000 - 1.1 Fixed an error in the state engine portion that could cause an increment error under certain conditions. Thanks Peter M. Allan for finding this. 6-21-2000 - 1.1 New Features added - Added in feature to disable DNS host resolution by checking RESOLVE_HOST in conf file. - Added in feature to have external command run before or after blocking has occurred as defined in KILL_RUN_CMD_FIRST option in conf file. - Removed DoBlockTCP/UDP functions. Converted over to generic flag checker. 7-5-2000 - 1.1 - Added iptables support (thanks Scott Catterton ) - Added Makefile support for Irix - Put in ports for common DDOS ports 9-8-2000 - 1.1 - Added in netmask support 9-9-2000 - 1.1 - Finally moved resolver functions to own area. - Made CleanAndResolve to ensure DNS records returned are sanitized correctly before being passed back. 3-23-2001 - 1.1 - Fixed a bug that showed up under Linux 2.4 Kernel that would cause accept to loop. There was an error with how I used a count variable after trying to bind to ports. If the port didn't bind the count for the openSockfd would still increment and this caused the error to show up. 6-26-2001 - 1.1 - Added Mac OS X build support (Same as FreeBSD). Fixed bug for Advanced mode to properly monitor 1024 ports (it only did first 1023 before). Thanks Guido. 05-23-2003 - 1.2 - Removed references to old psionic e-mail and changed license to Common Public License. portsentry_beta/CREDITS0100600000076400001440000001560407663462005015065 0ustar crowlandusers$Id: CREDITS,v 1.31 2003/05/23 16:15:45 crowland Exp crowland $ Thanks to the following people who offered testing and bug fixes/suggestions. I apologize if I've forgotten anyone. (No particular order on this list either): Jeff Johnson - Initial tester. Recommended ipfwadm addition. Ted Serreyn - Initial tester and bug reports. Les Fenison - Reported problems with logging of ignored hosts. Dave Andersen - FreeBSD testing. ipfw syntax for configuration file. Anonymous - HPUX testing and compile flags. Superuser - Lots of suggestions and general discussions... Timothy Luoma - NeXT Step fixes/bug testing/various suggestions and a really nice NeXTStep software archive (ftp.peak.org)!! Marcus Butler - Sharing ideas, suggestions and testing. Forrest Aldrich - FreeBSD testing and comments. Drew Levandoski - HPUX testing and compile flags. Yaron Yanay - Additional UDP ports to exclude in advanced mode. Andrew Raphael - Binary and source RPMs. Samuli Karkkainen - Helped in debugging Advanced mode problem with SSH connection failures to Windows boxes. System Administrator - Slackware testing and confirmation. - Linux Alpha testing. Alex Petrov - Lots of comments concerning code clean-up and general efficiency recommendations. Dmitry Prokhorov - Patches to add $PORT$ token and fixes to install scripts. Rich Homolka - Reported problem with binding over port 61000 for advanced mode was due to ip masquerading feature in Linux kernels. Vesselin Atanasov - Patch to handle scan flooding attacks (not included in this version for other reasons, but I thank him for sending them in). Lukasz Zalubski - Mirrored tools to Europe. Matthias Lohmann - Solaris porting and testing. Don Bindner - Found typo in UDP attack logging that logged it as TCP attack. Jon Coyle - VERY helpful in testing for SCO. Also made up HTML pages and man pages. I should be paying him!! Brian D. Winters - Pointed out some compile errors on various Linux platforms and submitted fixes. Also made suggestions for areas of code cleanup. Jeremy T. Bouse - Made Debian packages of Sentry as well as Debain init scripts and install scripts (not included yet). Jeremy Hinegardner - Testing on SGI platforms. Densin Roy. - Suggested adding $PORT$ macro to command execution. - NetBSD testing. Dr. Stefan Demetrescu - Discussions concerning the smart-verify timeout (or lack thereof) and recommendations on future additions and fixes. Sean Amon - Solaris testing and fixes. Eric Hines - Solaris testing and fixes. David LaPorte - Alpha Linux testing. Sent in route drop command for new RedHat 6.0 systems (-reject flag). Joshua Chamas - Solaris testing. Tom Briglia - Solaris testing. Roger Books - Solaris testing and fixes. Steven Walker - RedHat init scripts and RPMs. Dominic J. Eidson - AIX testing. Christopher P. Lindsey - Patch to get version 0.90 and below to work in stealth modes on older Linux systems/systems with oddball tcpip.h files. Also helped in testing and debugging. Found bug in .ignore processing that wouldn't skip commented out lines. Contributed script to automatically setup ignore file on system startup by parsing ifconfig. Found bug in ignore functions that would sometimes mis-compare IP's (also lead me to find similar bug in isBlocked function). Adrian - Submitted bug report for 2.2.9 kernels with respect to possible UDP struct change (dest vs. u_dport). Thomas Molina - Made binary and source RPMs for 0.90 of PortSentry. Steve Marple - Reported bug in commenting out of .ignore file entries. Graham Allan - Sent in compile options for Digital OSF Unix. Sent in route flags for same. Ian Quick - Sent in a fix for the ipchains rule to insert blocks into filter list correctly. Reuven Gevaryahu - Found a nasty bug where if you setup to ignore either a tcp or a udp event an attacker could activate the detector, cause the blocked file to be written to and then activate the side you *wanted* the blocking to occur on and have Sentry ignore them. A big thanks for finding this! Jayakrishnan Krishnan - Reported the problem on the latest build not ignoring hosts in the .ignore file. Garth Brown - Also reported a problem with .blocked file mis-reading blocked hosts and submitted a fix. Morio Taneda - Submitted a *really cool* patch to use netlink with PortSentry. I'm sorry I didn't have time to implement this, but it is on my short list of fixes to put in after 1.0 is released. A big thanks for this!! Howard Arons - Suggested using LOCAL0 for possible syslog configuration in config.h file. Guido Guenther - Sent in bug fixes, Debian packages, comments, and many other contributions. Christophe Rolland - Wrote in to correct bug in .conf file where I watched first 1025 ports instead of first 1024. Now advanced modes will watch ports 1023 and below. Ralf Hildebrandt - Sent in route kill command for HPUX and fix for GCC under HPUX for Makefile. Graham Dunn - Sent in KILL_ROUTE string for ipf filters. Peter M. Allan - Found an increment error in the host state engine that could cause an error under certain configurations. Also contributed several fixes relating to function usage and cosmetics. Scott Catterton - Sent in new iptables support KILL_ROUTE command. Dino A Amato - Sent in Makefile strings for Irix Scott McCrory - Sent in common DDOS daemon ports Per Jönsson - Sent in changes to allow for ignoring networks/ports. Flower - Sent in request to disable DNS lookups. Paul T. Kooros - Sent in some code formatting issues that were resolved. Justin Pettit - Solaris fixes, proofreading, and lots of other stuff. portsentry_beta/LICENSE0100600000076400001440000003601207663462005015046 0ustar crowlandusers Common Public License - v 1.0 THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 1. DEFINITIONS "Contribution" means: a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and b) in the case of each subsequent Contributor: i) changes to the Program, and ii) additions to the Program; where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. "Contributor" means any person or entity that distributes the Program. "Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. "Program" means the Contributions distributed in accordance with this Agreement. "Recipient" means anyone who receives the Program under this Agreement, including all Contributors. 2. GRANT OF RIGHTS a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. 3. REQUIREMENTS A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: a) it complies with the terms and conditions of this Agreement; and b) its license agreement: i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. When the Program is made available in source code form: a) it must be made available under this Agreement; and b) a copy of this Agreement must be included with each copy of the Program. Contributors may not remove or alter any copyright notices contained within the Program. Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. 4. COMMERCIAL DISTRIBUTION Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 5. NO WARRANTY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 6. DISCLAIMER OF LIABILITY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. GENERAL If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. portsentry_beta/ignore.csh0100755000076400001440000000523307663462005016037 0ustar crowlandusers#!/bin/csh -f ######################################################################### # This script automatically adds hosts to the .ignore file # # of PortSentry. This is useful for dynamic IP hosts and # # should be run after each reboot or IP change. # # # # Author: Christopher P. Lindsey # # Date: 06-03-99 # # Note: Created script # # # # Modified: Craig H. Rowland # # Modified Date: 06-03-99 # # Modified Note: Fixed /tmp race condition. Added secure path. # # # # Modified: Christopher P. Lindsey # # Modified Date: 06-04-99 # # Modified Note: Added support for various OSs, -f flag on startup # # # # $Id: ignore.csh,v 1.5 2003/05/23 17:45:21 crowland Exp crowland $ # ######################################################################### # Choose an OS # # Acceptable values are "FreeBSD", "HPUX", "IRIX", "Linux", "OSF1", # "NeXTStep", "Solaris 2.x", "SunOS 4.x" set OS="Linux" # Known good path set path = (/bin /usr/bin /sbin /usr/sbin) if ($OS == "IRIX") then set path = ($path /usr/etc) elseif ($OS == "NeXTStep" || $OS == "SunOS 4.x") then set path = ($path /etc) endif # Safe directory set SENTRYDIR=/usr/local/psionic/portsentry set TMPFILE=portsentry.ignore.tmp if (-f $SENTRYDIR/portsentry.ignore) then head -3 $SENTRYDIR/portsentry.ignore > $SENTRYDIR/$TMPFILE else echo > $SENTRYDIR/$TMPFILE endif # This entry should always be in the file. echo '0.0.0.0' >> $SENTRYDIR/$TMPFILE if ($OS == "Linux") then foreach i ( `ifconfig -a | grep inet | awk '{print $2}' | sed 's/addr://'` ) echo $i >> $SENTRYDIR/$TMPFILE end else if ($OS == "HPUX") then foreach i (`lanscan -i`) ifconfig $i | grep inet | awk '{print $2}' >> $SENTRYDIR/$TMPFILE end else if ($OS == "Solaris 2.x" || $OS == "NeXTStep" || $OS == "FreeBSD" || \ $OS == "SunOS 4.x" || $OS == "OSF1" || $OS == "IRIX") then foreach i ( `ifconfig -a | grep inet | awk '{print $2}'` ) echo $i >> $SENTRYDIR/$TMPFILE end endif cp $SENTRYDIR/$TMPFILE $SENTRYDIR/portsentry.ignore rm $SENTRYDIR/$TMPFILE