📄 spp_portscan.c
字号:
ScanList *newList = (ScanList*)malloc(sizeof(ScanList));
newList->listHead = NULL;
newList->lastSource = NULL;
newList->numberOfSources = 0;
return(newList);
}
void PortscanPreprocFunction(Packet *p)
{
/* The main loop. Whenever this is called, first we expire connections so we
don't get false positives from stale connections. Then we add the new
connection information and check if the latest connection has passed the
threshold. If it has or if a stealth technique was used, we immediately
report the scan. Then we go through the list and any host that has been
flagged as doing a portscan and has passed the necessary amount of time
between reports and has connections stored are reported and cleared. Any
host that has been flagged as doing a portscan and has passed the necessary
amount of time between reports and does not have connections stored has the
portscan flag cleared and will automatically be flushed at next call.
*/
SourceInfo *currentSource;
ScanType scanType;
struct spp_timeval currTime;
char logMessage[180];
int numPorts;
/* Only do processing on IP Packets */
if (p->iph == NULL)
{
return;
}
/* Here we check if it is a protocol we are watching and if it is
a destination we are watching. If either fails, we return abruptly.
*/
switch (p->iph->ip_proto)
{
case IPPROTO_TCP:
if (p->tcph == NULL)
{
/* ZDNOTE Fragmented packets have IPH set to NULL so `nmap -f` defeats
SPP, at least until reassembly or the header pointer is fixed.
*/
return;
}
scanType = CheckTCPFlags(p->tcph->th_flags);
break;
case IPPROTO_UDP:
/* We no longer check for NULL UDP headers here, because it really doesn't matter
anymore. We don't access it. We just use p->[sd]p instead.
*/
scanType = sUDP;
break;
default:
/* The packet isn't a protocol we watch, so get out of here. */
return; /*** RETURN ***/
break;
}
/* For speed, we're going to drop out right now if this packet is not any type of scan.
My assumption is most packets on the network are not going to be any type of scan
packet (not even SYN or UDP), so this extra check will be faster in the long run.
*/
if (!scanType) return;
/* The checks above are faster, so now that we know this packet is interesting we'll
check the address.
*/
if (!CheckAddrPort(homeNet, homeNetMask, 0, 0, p, ANY_DST_PORT, CHECK_DST)) return;
/* If we ignore SYN and UDP scans from this host (presumably because it's a server),
clear out those flags so we don't get false alarms.
If it's a server, we also need to make sure there are no reserved bits set
because otherwise "2*S*****" shows as UNKNOWN instead of as SYN w/ RESERVEDBITS.
The beast below makes sure we are actually watching for RB scans. The previous
version would have let servers be left as SYN scans if a reserved bit was set.
*/
if (IsServer(p) && !(scanType & sRESERVEDBITS & scansToWatch)) scanType &= ~(sSYN | sUDP);
if (scanType & scansToWatch)
{
currTime.tv_sec = p->pkth->ts.tv_sec;
currTime.tv_usec = p->pkth->ts.tv_usec;
ExpireConnections(scanList, maxTime, currTime);
/* If more than maxPorts connections made or if stealth scan technique
was used, mark this as a portscan.
*/
numPorts = NewScan(scanList, p, scanType);
/* Timestamp info for statistics */
scanList->lastSource->lastPacketTime = currTime;
if ((numPorts > maxPorts) || (scanType & ~(sSYN | sUDP)))
{
if (scanType & ~(sSYN | sUDP))
{
scanList->lastSource->stealthScanUsed = 1;
scanList->lastSource->reportStealth = 1;
}
if (!scanList->lastSource->scanDetected)
{
if (scanList->lastSource->stealthScanUsed)
{
sprintf(logMessage, MODNAME ": PORTSCAN DETECTED from %s (STEALTH)", inet_ntoa(scanList->lastSource->saddr));
}
else
{
sprintf(logMessage, MODNAME ": PORTSCAN DETECTED from %s (THRESHOLD %ld connections exceeded in %ld seconds)",
inet_ntoa(scanList->lastSource->saddr), maxPorts,
(long int)(currTime.tv_sec - scanList->lastSource->firstPacketTime.tv_sec));
}
(*AlertFunc)(NULL, logMessage);
scanList->lastSource->scanDetected = 1;
scanList->lastSource->reportTime = currTime;
}
}
/* See if there's anyone we can snitch on. ;) */
currentSource = scanList->listHead;
while (currentSource)
{
if (currentSource->scanDetected)
{
if (currentSource->reportTime.tv_sec + maxTime.tv_sec < currTime.tv_sec)
{
if (currentSource->numberOfConnections == 0)
{
/* Portscan stopped. Clear flag. */
sprintf(logMessage, MODNAME ": End of portscan from %s: TOTAL time(%lds) hosts(%d) TCP(%d) UDP(%d)%s",
inet_ntoa(currentSource->saddr),
(long int)(currentSource->lastPacketTime.tv_sec - currentSource->firstPacketTime.tv_sec),
currentSource->totalNumberOfDestinations,
currentSource->totalNumberOfTCPConnections,
currentSource->totalNumberOfUDPConnections,
(currentSource->reportStealth) ? " STEALTH" : "");
(*AlertFunc)(NULL, logMessage);
currentSource->scanDetected = 0;
}
else
{
/* This is where we do the real logging */
if (logLevel & lFILE) LogScanInfoToSeparateFile(currentSource);
if (logLevel & lEXTENDED) AlertIntermediateInfo(currentSource);
currentSource->totalNumberOfTCPConnections += currentSource->numberOfTCPConnections;
currentSource->totalNumberOfUDPConnections += currentSource->numberOfUDPConnections;
ClearConnectionInfoFromSource(currentSource);
currentSource->stealthScanUsed = 0;
currentSource->reportTime = currTime;
}
}
}
currentSource = currentSource->nextNode;
}
}
}
void ClearConnectionInfoFromSource(SourceInfo *currentSource)
{
DestinationInfo *currentDestination, *tmpDestination;
ConnectionInfo *currentConnection, *tmpConnection;
currentDestination = currentSource->destinationsList;
while (currentDestination)
{
currentConnection = currentDestination->connectionsList;
while (currentConnection)
{
tmpConnection = currentConnection;
currentConnection = currentConnection->nextNode;
if (tmpConnection->scanType == sUDP)
{
currentSource->numberOfUDPConnections--;
#ifdef DEBUG
printf(MODNAME": ClearConnectionInfoFromSource(): %s->numberOfUDPConnections-- (%d)\n",
inet_ntoa(currentSource->saddr), currentSource->numberOfUDPConnections);
#endif
}
else
{
currentSource->numberOfTCPConnections--;
#ifdef DEBUG
printf(MODNAME": ClearConnectionInfoFromSource(): %s->numberOfTCPConnections-- (%d)\n",
inet_ntoa(currentSource->saddr), currentSource->numberOfTCPConnections);
#endif
}
RemoveConnection(tmpConnection);
currentDestination->numberOfConnections--;
currentSource->numberOfConnections--;
}
tmpDestination = currentDestination;
currentDestination = currentDestination->nextNode;
RemoveDestination(tmpDestination);
currentSource->numberOfDestinations--;
}
currentSource->destinationsList = NULL;
}
void SetupPortscan(void)
{
RegisterPreprocessor("portscan", PortscanInit);
}
void PortscanInit(u_char *args)
{
ParsePortscanArgs(args);
scanList = CreateScanList();
/* We set serverList to NULL here so if portscan-ignorehosts is used it must be set
after portscan. This is necessary to make sure we don't check an empty list.
*/
serverList = NULL;
AddFuncToPreprocList(PortscanPreprocFunction);
}
ScanType CheckTCPFlags(u_char th_flags)
{
u_char th_flags_cleaned;
ScanType scan = sNONE;
/* Strip off the reserved bits for the testing, but flag
that a scan is being done.
*/
th_flags_cleaned = th_flags & ~(R_RES1 | R_RES2);
if (th_flags != th_flags_cleaned)
{
scan = sRESERVEDBITS;
}
/* Most TCP packets will have the ACK bit set, so split that out
quickly. Any scans which use ACK (like Full-XMAS) must be
added to this part. Otherwise, it goes in the !ACK section.
In addition, anything that is !ACK && !SYN eventually gets
flagged as something. This is to hopefully detect new stealth scan types.
*/
if (th_flags_cleaned & R_ACK)
{
/* This is from ipt_unclean.c from the netfilter package. We are allowing
packets which are valid and flagging the rest as INVALIDACK, if it's
not already listed as some other scan.
*/
switch (th_flags_cleaned)
{
case (R_ACK):
case (R_SYN | R_ACK):
case (R_FIN | R_ACK):
case (R_RST | R_ACK):
case (R_ACK | R_PSH):
case (R_ACK | R_URG):
case (R_ACK | R_URG | R_PSH):
case (R_FIN | R_ACK | R_PSH):
case (R_FIN | R_ACK | R_URG):
case (R_FIN | R_ACK | R_URG | R_PSH):
case (R_RST | R_ACK | R_PSH): /* Found through numerous false alerts. */
/* Nothing. This is legitimate traffic. */
break;
case (R_SYN | R_RST | R_ACK | R_FIN | R_PSH | R_URG):
scan |= sFULLXMAS;
break;
case (R_SYN | R_PSH | R_ACK | R_URG):
scan |= sSPAU;
break;
default:
scan |= sINVALIDACK;
break;
}
}
else
{
/* ZDNOTE This could/should be optimized, but just by having the check
for SYN or RST being first makes this faster. (Anything else is a scan
and shouldn't be hit often.
*/
switch (th_flags_cleaned)
{
case R_SYN:
scan |= sSYN;
break;
case R_RST:
/* Nothing. This is legitimately tearing down a connection. */
break;
case R_FIN:
scan |= sFIN;
break;
case (R_SYN | R_FIN):
scan |= sSYNFIN;
break;
case 0:
scan |= sNULL;
break;
case (R_FIN | R_PSH | R_URG):
scan |= sXMAS;
break;
case R_URG:
case R_PSH:
case (R_URG | R_FIN):
case (R_PSH | R_FIN):
case (R_URG | R_PSH):
scan |= sVECNA;
break;
case (R_SYN | R_FIN | R_PSH | R_URG):
scan |= sNMAPID;
break;
default:
/* We assume that anything down here w/out an ACK flag is some sort of a
scan or something. Anyway, we'll flag it because if it doesn't have
an ACK it should have been only a SYN or RST and be detected above.
*/
scan |= sNOACK;
break;
}
}
return(scan);
}
void ParsePortscanArgs(u_char *args)
{
char **toks;
int numToks;
char *logFileName;
#ifdef DEBUG
printf(MODNAME": ParsePortscanArgs(): %s\n", args);
#endif
logLevel = lNONE;
if (!args)
{
FatalError(MODNAME": ERROR: %s (%d) => portscan configuration format: address/mask ports seconds [logfile]\n", file_name, file_line);
}
toks = mSplit(args, " ", 4, &numToks, '\\'); /*ZDNOTE What does the '\\' do? */
if ((numToks < 3) || (numToks > 4))
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -