Saturday, December 1, 2018

Simple Monitor / Scoring Engine in Python3

The below script was built to monitor IP Addresses and Ports that are open on a network.  Below is more of the dialogue of why it was created and what it does.

Almost 2 years ago I was asked to create a simple script that would monitor if a set of ports were open or closed.  Then if the ports were open provide points for them that would add up for a team score.  This script was used for the LDSBC High School Cyber Defense competition to determine if a team had maintained there systems online as they protected them from a red team and hardened them. Here is what I created located on my github.

I was asked for the same script that I used 2 years ago and found out they have used it a couple of times since that time.  The below script is a much improved scoring engine which the LDSBC is going to use in the High School Cyber Defense competition coming up December 7th.

After running the script I found the following use for it.  I have been in situations where a system administrator or myself has turned off a monitoring system like Nagios due to the amount of alarms that are firing during an outage.  The below script could be used during a timeframe like that to monitor systems that you are working to fix or verify they are working as you are in the middle of an outage.

The script is built to allow you to launch it, configure teams or vlans.  Then after configuring notes about a team or a vlan, configure the IPs and ports you would like monitored.  After launching it provides a status every 10 minutes of the IPs and ports that are UP or DOWN.  The score could be evaluated as the consistency of a system being up.  An audit log is also generated that could be viewed to determine the UP or DOWN time of systems.

Currently it is tuned to allow up to 6 teams/vlans with up-to 20 IPs/Ports per team or a total of 120 IPs/Ports to be monitored within a 10 minute period of time.  This also saves a file that could be restored every 10 minutes during the monitoring process.  This script also allows you to save a configuration that you create.




#!/usr/bin/python3
# Created Nov. 30, 2018
# Updated Dec. 11, 2018


import socket
import datetime
import time

class Teams:
    def __init__(self):
        self.teamName = []

    def addTeam(self, name):
        self.teamName.append(name)
        return self

class Team:
    def __init__(self, name, description, location, score):
        self.name = name
        self.description = description
        self.location = location
        self.score = int(score)



class Ports:
    def __init__(self):
        self.monitoredPorts = []

    def addPort(self, portInfo):
        self.monitoredPorts.append(portInfo)
        return self

class Port:
    def __init__(self, teamName, ip, port, description):
        self.teamName = teamName
        self.ip = ip
        self.port = port
        self.description = description


def modifyPort(poInfo, teInfo):
    while True:
        print('')
        print('')
        print('')
        print('Port Modification')
        print('***************************************************************************************')
        tInfo = "Name:" + str(teInfo.name)
        tInfo += "\tDesc: " + str(teInfo.description)
        tInfo += "\tLocation: " + str(teInfo.location)
        tInfo += "\tScore: " + str(teInfo.score)
        print(tInfo)
        print("")
        pInfo = "Port Desc:" + str(poInfo.description)
        pInfo += "\tIP:" + str(poInfo.ip)
        pInfo += "\tPort:" + str(poInfo.port)
        print('***************************************************************************************')
        print("")
        print("D. Modify IP/Port Description")
        print("I. Modify IP")
        print("P. Modify Port")
        print("")
        print("R. Refresh")
        print("Q. Quit to IP/Port Setup")
        print("")
        selectMod = input("> ")
        if selectMod == 'D' or selectPort == 'd':
            newDesc = input("Change IP/Port description to: ")
            poInfo.description = newDesc.strip().replace('\n','').replace('\r','')
        elif selectMod == 'I' or selectMod == 'i':
            newIP = input("Change IP to: ")
            poInfo.ip = newIP.strip().replace('\n','').replace('\r','')
        elif selectMod == 'P' or selectMod == 'p':
            newPort = input("Change Port to: ")
            poInfo.port = int(newPort.strip().replace('\n','').replace('\r',''))
        elif selectPort == 'Q' or selectPort == 'q':
            return
        else:
            continue
    return


def setPorts(teamInfo, nPorts):
    while True:
        print('')
        print('')
        print('')
        print('IP/Port Setup')
        print('***************************************************************************************')
        tInfo = "Name:" + str(teamInfo.name)
        tInfo += "\tDesc: " + str(teamInfo.description)
        tInfo += "\tLocation: " + str(teamInfo.location)
        tInfo += "\tScore: " + str(teamInfo.score)
        print(tInfo)
        print('***************************************************************************************')
        portNumber = 1
        portNumberSet = []
        for port in nPorts.monitoredPorts:
            if port.teamName == teamInfo.name:
                # Check if IP is in correct format
                # Check if Port is in correct format
                print(str(portNumber) + ". " + str(port.description) + "\t\tIP:" + str(port.ip) + "\t\tPort:" + str(port.port))
                portNumberSet.append(portNumber)
            portNumber += 1
        if len(nPorts.monitoredPorts) == 0:
            print("No ports have been configured.")
        if len(nPorts.monitoredPorts) > 0:
            print("** To modify a monitored port above select the number in the front.")
        print("")
        print('A. Add Port')
        print('')
        print('N. Modify Team Name')
        print('D. Modify Team Description')
        print('L. Modify Team Location')
        print('S. Modify Team Score')
        print('')
        print('Q. Quit to Main')
        print
        selectPort = input('> ')
        try:
            numbPort = int(selectPort)
            if len(nPorts.monitoredPorts) >= numbPort and numbPort in portNumberSet:
                modifyPort(nPorts.monitoredPorts[numb-1], teamInfo)
        except:
            if selectPort == 'A' or selectPort == 'a':
                portDescription = input("Input Description of Monitored Port: ")
                portDescription = portDescription.strip().replace('\n','').replace('\r','')
                portIP = input("Input IP to Monitor for " + str(teamInfo.name) + ": ")
                portIP = portIP.strip().replace('\n','').replace('\r','')
                portPort = input("Input Port to Monitor: ")
                portPort = int(portPort.strip().replace('\n','').replace('\r',''))
                nPorts.addPort(Port(teamInfo.name, portIP, portPort, portDescription))
            elif selectPort == 'N' or selectPort == 'n':
                newName = input("Change team name to: ")
                newName = newName.strip().replace('\n','').replace('\r','')
                teamInfo.name = newName
            elif selectPort == 'D' or selectPort == 'd':
                newDesc = input("Change team description to: ")
                newDesc = newDesc.strip().replace('\n','').replace('\r','')
                teamInfo.description = newDesc
            elif selectPort == 'L' or selectPort == 'l':
                newLoc = input("Change team location to: ")
                newLoc = newLoc.strip().replace('\n','').replace('\r','')
                teamInfo.location = newLoc
            elif selectPort == 'S' or selectPort == 's':
                newScore = input("Change team score from " + str(teamInfo.score) + " to: ")
                newScore = newScore.strip().replace('\n','').replace('\r','')
                teamInfo.score = int(newScore)
            elif selectPort == 'Q' or selectPort == 'q':
                main()
            else:
                continue
    return




def saveTeam(nTeams, nPorts):
    print('')
    fileName = input('Input file to save to, contents will be overwritten (i.e. savedTeam.txt): ')
    fileName = fileName.strip().replace('\n','').replace('\r','')
    if len(fileName) < 4:
        print('Invalid file name, using savedTeam.txt')
        fileName = 'savedTeam.txt'
    f = open(fileName, 'w')
    for team in nTeams.teamName:
        outputTeam = "TEAM" + "|" + str(team.name) + "|" + str(team.description) + "|" + str(team.location) + "|" + str(team.score) + "\r\n"
        f.write(outputTeam)
    for port in nPorts.monitoredPorts:
        outputPort = "PORT" + "|" + str(port.teamName) + "|" + str(port.ip) + "|" + str(port.port) + "|" + str(port.description) + "\r\n"
        f.write(outputPort)
    f.close()
    return



def saveTeamMonitoring(nTeams, nPorts):
    now = datetime.datetime.now()
    timeInfo = now.strftime("_%m_%d_%YT%H_%M")
    fileName = 'monitoringSaveTeam' + timeInfo + '.txt'
    f = open(fileName, 'w')
    for team in nTeams.teamName:
        outputTeam = "TEAM" + "|" + str(team.name) + "|" + str(team.description) + "|" + str(team.location) + "|" + str(team.score) + "\r\n"
        f.write(outputTeam)
    for port in nPorts.monitoredPorts:
        outputPort = "PORT" + "|" + str(port.teamName) + "|" + str(port.ip) + "|" + str(port.port) + "|" + str(port.description) + "\r\n"
        f.write(outputPort)
    f.close()
    print('Saved current team information to the file "' + fileName + '"')
    print('If you stop the program you can restore this file with the scores at this point-in-time.')
    return






def loadTeam(nTeams, nPorts):
    print('')
    fileName = input('Input file name to load: ')
    fileName = fileName.strip().replace('\n','').replace('\r','')
    if len(fileName) < 4:
        print('Invalid file name, trying savedTeam.txt')
        fileName = 'savedTeam.txt'
    try:
        f = open(fileName, 'r')
        for line in f:
            column = line.split('|')
            if column[0] == 'TEAM':
                nTeams.addTeam(Team(column[1], column[2], column[3], column[4].strip()))
            elif column[0] == 'PORT':
                nPorts.addPort(Port(column[1], column[2], column[3], column[4].strip()))
            else:
                print('Invalid line format found in ' + fileName + '. You will need to manually correct it.')
        f.close()
        print("")
        print("** Note: The score was loaded with the restored information...")
        print("")
    except:
        print("")
        print('Sorry either the file does not exist or problems loading the file exist.')
    print("")
    return





def monitorMode(currentTeams, currentPorts):
    while True:
        print('')
        print('')
        print('')
        print('Monitoring Mode - Will cylcle through all ports every 10 minutes...')
        print('**Note: If the cycle time is greater than 600 seconds not enough time is allowed.')
        print('***************************************************************************************')
        print('Current Team Scores')
        for team in currentTeams.teamName:
            teamInfo = str(team.name)
            teamInfo += "\tDesc: " + str(team.description)
            teamInfo += "\tLocation: " + str(team.location)
            teamInfo += "\tScore: " + str(team.score)
            print(teamInfo)
        print('***************************************************************************************')
        fAudit = open('auditLog.txt', 'a')
        timeSleep = 0
        totalSleep = 0
        beforeCycle = time.time()
        for port in currentPorts.monitoredPorts:
            ip = port.ip
            currentPort = int(port.port)
            beforeDate = datetime.datetime.now()
            beforeConnection = time.time()
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.settimeout(2)
            result = s.connect_ex((ip,currentPort))
            s.close()
            if result == 0:
                for team in currentTeams.teamName:
                    if team.name == port.teamName:
                        # For each successful connection it provides 5 points
                        team.score = team.score + 5
                        teamScore = team.score
                infoAudit = port.teamName + "|"
                infoAudit += port.ip + "|"
                infoAudit += str(port.port) + "|"
                infoAudit += "UP|"
                infoAudit += beforeDate.strftime("%m-%d-%Y %H:%M:%S") + "|"
                infoAudit += str(teamScore)
                print(infoAudit)
                infoAudit += "\r\n"
                fAudit.write(infoAudit)
            else:
                infoAudit = port.teamName + "|"
                infoAudit += port.ip + "|"
                infoAudit += str(port.port) + "|"
                infoAudit += "DOWN|"
                infoAudit += beforeDate.strftime("%m-%d-%Y %H:%M:%S") + "|"
                infoAudit += "|"
                print(infoAudit)
                infoAudit += "\r\n"
                fAudit.write(infoAudit)
            afterConnection = time.time()
            # Sleep for a total of 5 seconds per connection whether failed or successful
            timeSleep = 5 - (afterConnection - beforeConnection)
            #print("Time Elapsed: " + str(timeSleep))
            time.sleep(timeSleep)
            totalSleep += 5
        fAudit.close()
        print("")
        saveTeamMonitoring(currentTeams, currentPorts)
        # Setup to monitor all connections every 10 minutes (6 teams 20 ports = 120 ports * 5 seconds = 600 seconds)
        # 3 hours / 10 minutes = 18 cycles
        afterCycle = time.time()
        totalCycle = afterCycle - beforeCycle
        print("")
        print("Cycle took " + str(totalCycle) + " seconds.")
        totalTimeSleep = 600 - (afterCycle - beforeCycle)
        print("Sleeping for " + str(totalTimeSleep) + " seconds until the next cycle.")
        time.sleep(totalTimeSleep)
        totalSleep = 0
    return


def main(newTeams, newPorts):
    while True:
        print('')
        print('')
        print('')
        print('Main Menu')
        print('***************************************************************************************')
        teamNumber = 1
        for team in newTeams.teamName:
            portCount = 0
            for port in newPorts.monitoredPorts:
                if port.teamName == team.name:
                    portCount += 1
            teamInfo = str(teamNumber) + ". "
            teamInfo += str(team.name)
            teamInfo += "\tDesc: " + str(team.description)
            teamInfo += "\tLocation: " + str(team.location)
            teamInfo += "\tPorts Configured: " + str(portCount)
            teamInfo += "\tScore: " + str(team.score)
            print(teamInfo)
            teamNumber += 1
        if len(newTeams.teamName) == 0:
            print("No teams have been configured.")
        if len(newTeams.teamName) > 0:
            print("** To modify a team select the number in the front.")
        print('')
        print('A. Add Team')
        print('L. Load Team Info from File')
        print('S. Save Team Info to File')
        print('')
        print('Type "Monitor" to enter monitoring mode based on the configuration above')
        print('')
        print('R. Refresh')
        print('Q. Quit')
        print("")
        selection = input('> ')
        try:
            numb = int(selection)
            if len(newTeams.teamName) >= numb:
                setPorts(newTeams.teamName[numb-1], newPorts)
        except:
            if selection == 'A' or selection == 'a':
                teamName = input("Input team name: ")
                teamDescription = input("Input team description: ")
                teamLocation = input("Input team location: ")
                newTeams.addTeam(Team(teamName, teamDescription, teamLocation, 0))
            elif selection == 'L' or selection == 'l':
                loadTeam(newTeams, newPorts)
            elif selection == 'S' or selection == 's':
                saveTeam(newTeams, newPorts)
            elif selection == 'Q' or selection == 'q':
                exit()
            elif selection == 'monitor' or selection == 'Monitor' or selection == 'MONITOR':
                monitorMode(newTeams, newPorts)
            else:
                continue
    return



listTeams = Teams()
listPorts = Ports()
main(listTeams, listPorts)




No comments:

Post a Comment

Test Authentication from Linux Console using python3 pexpect

Working with the IT420 lab, you will discover that we need to discover a vulnerable user account.  The following python3 script uses the pex...