def _createRamUsageGraph(self): logentries = parseMachineReadableLogfile(self.__class__.__name__) x_min = min([datetimeToEpoch(e.entrytime) for e in logentries]) y = [] x = [] for entry in logentries: match = re.match("ram: (\d+)", entry.message) if match: x.append(datetimeToEpoch(entry.entrytime) - x_min) y.append(int(match.group(1))) outputfile = "/tmp/botnetemulator/performance/ram_usage.svg" createLinePlot(x, "Runtime of experiment in seconds", y, "Used RAM in Megabytes", outputfile, title="RAM usage")
def testHttpconnections(self): inputfile = basedir + "/testfiles/httpconnections.pcap" actual = self.objectUnderTest.plotConnectionStatisticsFromPcap( inputfile) self.assertEqual(249, len(actual)) self.assertEqual(1469368567, datetimeToEpoch(actual[0].startTime)) self.assertEqual(12.800143, actual[8].duration.total_seconds()) self.assertEqual(("10.0.2.15", 43048), actual[141].host1) self.assertEqual(('178.255.83.1', 80), actual[141].host2) self.assertEqual( 1469368612, datetimeToEpoch(actual[248].startTime + actual[248].duration)) self.assertEqual(("83.97.42.2", 80), actual[248].host2)
def _createPlotOfLoadingTimes(self, loadingTimesDict): """Creates a pdf file that shows a plot with the relative time when a page load began on the x-axis and the time taken to complete the page load on the y-axis. Page load means that this sensor loads a web page from a web server and measures how long this takes.""" mkdir_p(self.outputdir) # Ensure outputdir exists pyplot.ioff() # Ensure that matplotlib does not try to show a gui for page in loadingTimesDict.keys(): pyplot.close() raw_x = [ datetimeToEpoch(tuple[0]) for tuple in loadingTimesDict[page] ] raw_min = min(raw_x) x = [x - raw_min for x in raw_x] y = [tuple[1] for tuple in loadingTimesDict[page]] logging.debug("len(x) = %d" % len(x)) logging.debug("len(y) = %d" % len(y)) pyplot.plot(numpy.array(x), numpy.array(y)) pyplot.xlabel("time") pyplot.ylabel('loading time') outputfile = os.path.join( self.outputdir, urlparse.urlparse(page).hostname) + ".pdf" pyplot.savefig(outputfile)
def _start(self): super(PingExperiment, self)._start() pingresult = self.mininet.pingPair() logging.debug("pingpair: %s"%pingresult) assert len(self.getNodes("victim")) >= 1 # Get a random element from a set. random.choice() does not work here. victim = random.sample(self.getNodes("victim"), 1)[0] logging.debug("IP of Victim: %s"%(victim.IP())) # Start the necessary runnables self.overlord.startRunnable("Victim", "Victim", hostlist=[h.name for h in self.getNodes("victim")]) self.overlord.startRunnable("Sensor", "Sensor", {"pagesToWatch": ["http://%s:%d/?root=1234"%(victim.IP(), PORT)]}, hostlist=[h.name for h in self.getNodes("sensor")]) for h in self.getNodes("servents"): peerlist = random.sample([peer.IP() for peer in self.getNodes("servents") if not peer == h], 3) self.overlord.startRunnable("ping.Servent", "Servent", {"peerlist": peerlist, "pauseBetweenDuties": 5}, hostlist=[h.name]) for h in self.getNodes("clients"): peerlist = random.sample([peer.IP() for peer in self.getNodes("servents") if not peer == h], 3) self.overlord.startRunnable("ping.Client", "Client", {"peerlist": peerlist, "pauseBetweenDuties": 5}, hostlist=[h.name]) victim.cmd(self.tsharkCommand%self.pcapfile) logging.debug("Runnables wurden gestartet") time.sleep(35) chosenOne = random.sample(self.getNodes("servents"), 1)[0] kwargsStr = json.dumps({"url": "http://%s:%d/ddos_me"%(victim.IP(), PORT)}) curlcmd = "timeout 60s wget -q -O - --post-data 'command=ddos_server&kwargs=%s×tamp=%d' '%s'"%( kwargsStr, datetimeToEpoch(datetime.now()), "http://%s:%d/current_command"%(chosenOne.IP(), PORT)) result = chosenOne.cmd(curlcmd, verbose=True) assert result.strip() == "OK", "Could not send the DDoS-command to the bot %s: |%s|"%(chosenOne, result)
def _createCpuLoadGraph(self): logentries = parseMachineReadableLogfile(self.__class__.__name__) x_min = min([datetimeToEpoch(e.entrytime) for e in logentries]) y = [] x = [] for entry in logentries: match = re.match("load: (\d+)", entry.message) if match: x.append(datetimeToEpoch(entry.entrytime) - x_min) y.append(int(match.group(1))) outputfile = "/tmp/botnetemulator/performance/cpu_load.svg" createLinePlot(x, "Runtime of experiment in seconds", y, "Average number of processes using the\n" "CPU simultaneously in the last minute", outputfile, title="Load average")
def _createPlotOfLoadingTimes(self, connection_statistics, filename): """Creates a pdf that shows a plot with the relative time when a page load began on the x-axis and the time taken to complete the page load on the y-axis. Page load means that this sensor loads a web page from a web server and measures how long this takes. :param connection_statistics: A list of TcpConnection objects""" x, startTime, _ = self._extractXAxis(connection_statistics) connectionsGroupedByStarttime = dict() for con in connection_statistics: key = datetimeToEpoch(con.startTime) - startTime if not connectionsGroupedByStarttime.has_key(key): connectionsGroupedByStarttime[key] = [] connectionsGroupedByStarttime[key].append(con) y = [] for second in x: if connectionsGroupedByStarttime.has_key(second): # average duration of the connections that were started during the given second avg = average([ con.duration.total_seconds() for con in connectionsGroupedByStarttime[second] ]) y.append(avg) else: y.append(0) createLinePlot(x, "experiment runtime in seconds", y, "loading time in seconds", os.path.join(self.outputdir, filename))
def executeExperiment(self): """This method implements the execution strategy of all botnet experiments. It corresponds to the execute() method from the strategy pattern.""" name = self.__class__.__name__ # Name of the subclass writeLogentry(runnable=name, message="Experiment started") logging.debug("Initialise %s"%name) self._setup() self.setNodes("nodes", frozenset(self.mininet.hosts)) # Category that includes all Mininet hosts logging.info("Starting %s"%name) self._start() writeLogentry(runnable=type(self).__name__, message="Experiment fully started") doNextStep = True currentIteration = 0 while doNextStep: writeLogentry(runnable=type(self).__name__, message="Iteration: %d %d"%(currentIteration, len(self.getNodes("bots")))) logging.info("Step %d on %d "%(currentIteration, datetimeToEpoch(datetime.now()))) doNextStep = self._executeStep(currentIteration) currentIteration += 1 logging.info("Stoping %s after %d iterations"%(name, currentIteration)) self._stop() logging.info("Produce output files") self._produceOutputFiles() writeLogentry(runnable=name, message="Experiment ended")
def executeExperiment(self): """This method implements the execution strategy of all botnet experiments. It corresponds to the execute() method from the strategy pattern.""" name = self.__class__.__name__ # Name of the subclass writeLogentry(runnable=name, message="Experiment started") logging.debug("Initialise %s" % name) self._setup() self.setNodes("nodes", frozenset( self.mininet.hosts)) # Category that includes all Mininet hosts logging.info("Starting %s" % name) self._start() writeLogentry(runnable=type(self).__name__, message="Experiment fully started") doNextStep = True currentIteration = 0 while doNextStep: writeLogentry(runnable=type(self).__name__, message="Iteration: %d %d" % (currentIteration, len(self.getNodes("bots")))) logging.info("Step %d on %d " % (currentIteration, datetimeToEpoch(datetime.now()))) doNextStep = self._executeStep(currentIteration) currentIteration += 1 logging.info("Stoping %s after %d iterations" % (name, currentIteration)) self._stop() logging.info("Produce output files") self._produceOutputFiles() writeLogentry(runnable=name, message="Experiment ended")
def _start(self): super(PingExperiment, self)._start() pingresult = self.mininet.pingPair() logging.debug("pingpair: %s" % pingresult) assert len(self.getNodes("victim")) >= 1 # Get a random element from a set. random.choice() does not work here. victim = random.sample(self.getNodes("victim"), 1)[0] logging.debug("IP of Victim: %s" % (victim.IP())) # Start the necessary runnables self.overlord.startRunnable( "Victim", "Victim", hostlist=[h.name for h in self.getNodes("victim")]) self.overlord.startRunnable( "Sensor", "Sensor", { "pagesToWatch": ["http://%s:%d/?root=1234" % (victim.IP(), PORT)] }, hostlist=[h.name for h in self.getNodes("sensor")]) for h in self.getNodes("servents"): peerlist = random.sample([ peer.IP() for peer in self.getNodes("servents") if not peer == h ], 3) self.overlord.startRunnable("ping.Servent", "Servent", { "peerlist": peerlist, "pauseBetweenDuties": 5 }, hostlist=[h.name]) for h in self.getNodes("clients"): peerlist = random.sample([ peer.IP() for peer in self.getNodes("servents") if not peer == h ], 3) self.overlord.startRunnable("ping.Client", "Client", { "peerlist": peerlist, "pauseBetweenDuties": 5 }, hostlist=[h.name]) victim.cmd(self.tsharkCommand % self.pcapfile) logging.debug("Runnables wurden gestartet") time.sleep(35) chosenOne = random.sample(self.getNodes("servents"), 1)[0] kwargsStr = json.dumps( {"url": "http://%s:%d/ddos_me" % (victim.IP(), PORT)}) curlcmd = "timeout 60s wget -q -O - --post-data 'command=ddos_server&kwargs=%s×tamp=%d' '%s'" % ( kwargsStr, datetimeToEpoch(datetime.now()), "http://%s:%d/current_command" % (chosenOne.IP(), PORT)) result = chosenOne.cmd(curlcmd, verbose=True) assert result.strip( ) == "OK", "Could not send the DDoS-command to the bot %s: |%s|" % ( chosenOne, result)
def _extractXAxis(self, connection_statistics): x_raw = { datetimeToEpoch(conn.startTime) for conn in connection_statistics } startTime = min(x_raw) endTime = max(x_raw) x = [i for i in range(endTime - startTime)] return x, startTime, endTime
def _extractConnectionStatistics(self, ttoutput): """Extracts the communicating hosts, relative time of first packet and duration for every tcp connection from the given output of tcptrace -ln. See loc testfiles/httpconnections.tcptrace for an example of what this method parses. :type ttoutput: str :param ttoutput: The output that tcptrace produced :return: A list of TcpConnection objects ordered by the connection start time.""" assert isinstance(ttoutput, str), "type ttoutput: %s" % type(ttoutput) hostRE = r"host \w+:\s*([0-9.]+):([0-9]+)" # Matches a line containing one of the hosts that are communicating here startTimeRE = r'first packet:\s*([\w\s:\.]+)\s*' # The time the first packet of this connection was seen lastPacketRE = r'last packet:\s*([\w\s:\.]+)\s*' # The time the last packet was seen completedRE = r'complete conn: (\w+)' # Whether the connection has been completed or not dateformat = "%a %b %d %H:%M:%S.%f %Y" # How the dates in the input are formatted (used to convert them to datetime) connectionSeparatorRE = "=====+" # The descriptions of the individual connections are separated by a line of = result = [] current_connection = TcpConnection() for line in ttoutput.splitlines(): if re.search(hostRE, line) and current_connection.host1 is None: # If the line contains the IP of one of the hosts match = re.search(hostRE, line) current_connection.host1 = (match.group(1), int(match.group(2))) elif re.search(hostRE, line): # If the line contains the IP of one of the hosts and the first host was already seen, it has to be the second host match = re.search(hostRE, line) current_connection.host2 = (match.group(1), int(match.group(2))) elif re.search(startTimeRE, line): match = re.search(startTimeRE, line) current_connection.startTime = datetime.strptime( match.group(1), dateformat) elif re.search(lastPacketRE, line): assert current_connection.startTime is not None match = re.search(lastPacketRE, line) current_connection.duration = datetime.strptime( match.group(1), dateformat) - current_connection.startTime assert isinstance(current_connection.duration, timedelta) elif re.search(completedRE, line): match = re.search(completedRE, line) current_connection.connection_completed = True if match.group( 1).lower() == "yes" else False elif re.search(connectionSeparatorRE, line): assert current_connection.isComplete() result.append(current_connection) current_connection = TcpConnection() result.append(current_connection) result = sorted(result, key=lambda conn: datetimeToEpoch(conn.startTime)) return result
def _createPlotOfFailedConnections(self, connection_statistics, outputdir): """Creates a pdf that shows a plot with the relative time when a page load began on the x-axis and the time taken to complete the page load on the y-axis. Page load means that this sensor loads a web page from a web server and measures how long this takes. :param connection_statistics: A list of TcpConnection objects""" assert os.path.isdir(outputdir) x, startTime, _ = self._extractXAxis(connection_statistics) completeConnectionsGroupedByStarttime = dict() failedConnectionsGroupedByStarttime = dict() for con in connection_statistics: key = datetimeToEpoch(con.startTime) - startTime insertInto = completeConnectionsGroupedByStarttime if con.connection_completed else failedConnectionsGroupedByStarttime if not insertInto.has_key(key): insertInto[key] = [] insertInto[key].append(con) y_failed = [] # Number of incomplete connections per second y_completed = [] # Number of completed connections per second for second in x: y_failed.append( len(failedConnectionsGroupedByStarttime[second]) if failedConnectionsGroupedByStarttime.has_key(second) else 0) y_completed.append( len(completeConnectionsGroupedByStarttime[second]) if completeConnectionsGroupedByStarttime.has_key(second) else 0) createLinePlot(x, "experiment runtime in seconds", y_failed, "loading time in seconds", os.path.join(outputdir, "failed_connections.pdf"), clear=False, plotlabel="failed") createLinePlot(x, "experiment runtime in seconds", y_completed, "loading time in seconds", os.path.join(outputdir, "completedVSfailed_connections.pdf"), clear=True, plotlabel="completed") createLinePlot(x, "experiment runtime in seconds", y_completed, "loading time in seconds", os.path.join(outputdir, "completed_connections.pdf"), clear=True, plotlabel="completed")
def sendDDoSCommand(hostList, victimip): if len(hostList)==0: logging.warn("Could not send ddos command to empty host list") return botToIssueCommandFrom = random.sample(hostList, 1)[0] writeLogentry(runnable="KademliaExperiment", message="Send command %s to bot %s"%("ddos_server", botToIssueCommandFrom)) kwargsStr = json.dumps({"url": "http://%s:%d/ddos_me"%(victimip, PORT)}) urlToAttack = "http://%s:%d/current_command"%(botToIssueCommandFrom.IP(), PORT) result = botToIssueCommandFrom.cmd("timeout 60s wget -q -O - --post-data 'command=ddos_server&kwargs=%s×tamp=%d' '%s'" %(kwargsStr, datetimeToEpoch(datetime.now()), urlToAttack), verbose=True) assert "OK" in result.strip(), "Could not send the DDoS-command to the bot %s: |%s|"%(botToIssueCommandFrom, result)
def sendDDoSCommand(hostList, victimip): if len(hostList) == 0: logging.warn("Could not send ddos command to empty host list") return botToIssueCommandFrom = random.sample(hostList, 1)[0] writeLogentry(runnable="KademliaExperiment", message="Send command %s to bot %s" % ("ddos_server", botToIssueCommandFrom)) kwargsStr = json.dumps({"url": "http://%s:%d/ddos_me" % (victimip, PORT)}) urlToAttack = "http://%s:%d/current_command" % (botToIssueCommandFrom.IP(), PORT) result = botToIssueCommandFrom.cmd( "timeout 60s wget -q -O - --post-data 'command=ddos_server&kwargs=%s×tamp=%d' '%s'" % (kwargsStr, datetimeToEpoch(datetime.now()), urlToAttack), verbose=True) assert "OK" in result.strip( ), "Could not send the DDoS-command to the bot %s: |%s|" % ( botToIssueCommandFrom, result)
def _createPlotOfLoadingTimes(self, loadingTimesDict): """Creates a pdf file that shows a plot with the relative time when a page load began on the x-axis and the time taken to complete the page load on the y-axis. Page load means that this sensor loads a web page from a web server and measures how long this takes.""" mkdir_p(self.outputdir) # Ensure outputdir exists pyplot.ioff() # Ensure that matplotlib does not try to show a gui for page in loadingTimesDict.keys(): pyplot.close() raw_x = [datetimeToEpoch(tuple[0]) for tuple in loadingTimesDict[page]] raw_min = min(raw_x) x = [x - raw_min for x in raw_x] y = [tuple[1] for tuple in loadingTimesDict[page]] logging.debug("len(x) = %d"%len(x)) logging.debug("len(y) = %d"%len(y)) pyplot.plot(numpy.array(x), numpy.array(y)) pyplot.xlabel("time") pyplot.ylabel('loading time') outputfile = os.path.join(self.outputdir, urlparse.urlparse(page).hostname) + ".pdf" pyplot.savefig(outputfile)
def _start(self): super(ZeusExperiment, self)._start() pingresult = self.mininet.pingPair() logging.debug("pingpair: %s"%pingresult) assert len(self.getNodes("victim")) == 1 assert len(self.getNodes("cncserver")) == 1 victim = next(iter(self.getNodes("victim"))) # Get a sets only element ... cncserver = next(iter(self.getNodes("cncserver"))) nameserver = next(iter(self.getNodes("nameserver"))) logging.debug("IP of Victim: %s; IP of CnC server: %s"%(victim.IP(), cncserver.IP())) # Start the necessary runnables self.overlord.startRunnable("Victim", "Victim", hostlist=[victim.name]) self.overlord.startRunnable("Sensor", "Sensor", {"pagesToWatch": ["http://%s:%d/?root=1234"%(victim.IP(), PORT)]}, hostlist=[h.name for h in self.getNodes("sensor")]) self.overlord.startRunnable("zeus.CnCServer", "CnCServer", {"host": cncserver.IP()}, hostlist=[h.name for h in self.getNodes("cncserver")]) for h in self.getNodes("bots"): self.overlord.startRunnable("zeus.Bot", "Bot", hostlist=[h.name], kwargs={"name": h.name, "peerlist": [nameserver.IP()], "pauseBetweenDuties": 1}) self.overlord.startRunnable("nameserver", "Nameserver", {"peerlist": [cncserver.IP()]}, hostlist=[nameserver.name]) victim.cmd(self.tsharkCommand%self.pcapfile) logging.debug("Runnables wurden gestartet") time.sleep(35) # Initiate DDoS attack kwargs = json.dumps( {"url": "http://%s:%d/ddos_me?composite=%d"%(victim.IP(), PORT, 9999123456789012345678901456780L), "timeout":10}) urlOfCnCServer = "http://%s:%d/current_command"%(cncserver.IP(), PORT) result = cncserver.cmd("timeout 60s wget -q -O - --post-data 'command=ddos_server×tamp=%d&kwargs=%s' '%s'" %(datetimeToEpoch(datetime.now()), kwargs, urlOfCnCServer), verbose=True) assert result.strip() == "OK", "Could not send the DDoS-command to the CnC server: %s"%result
is defined in BotCommands.py. This method will be executed by all clients that fetch it. All handler support plain text and json output.""" import json, logging, string, sys, time, socket, os from datetime import datetime sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir)) import tornado.web from threading import Thread from tornado.ioloop import IOLoop import tornado.httpserver from actors.AbstractBot import Runnable, CurrentCommandHandler from resources import emu_config from utils.MiscUtils import NetworkAddressSchema, datetimeToEpoch _current_command = {"command": "default_command", "timestamp": datetimeToEpoch(datetime.now()), "kwargs": dict()} class BotInformation(object): def __init__(self, botid): self.last_seen = time.time() self.botid = botid class MainHandler(tornado.web.RequestHandler): def get(self): if "json" in string.lower(self.request.headers.get("Accept")): self.set_header("Content-Type", "application/json") self.write(json.dumps(tornado.web.Application.handlers)) else: self.write("This server offers the following functions:\n" "/register where clients can register themselves so we can keep track of them."
def _start(self): super(ZeusExperiment, self)._start() pingresult = self.mininet.pingPair() logging.debug("pingpair: %s" % pingresult) assert len(self.getNodes("victim")) == 1 assert len(self.getNodes("cncserver")) == 1 victim = next(iter( self.getNodes("victim"))) # Get a sets only element ... cncserver = next(iter(self.getNodes("cncserver"))) nameserver = next(iter(self.getNodes("nameserver"))) logging.debug("IP of Victim: %s; IP of CnC server: %s" % (victim.IP(), cncserver.IP())) # Start the necessary runnables self.overlord.startRunnable("Victim", "Victim", hostlist=[victim.name]) self.overlord.startRunnable( "Sensor", "Sensor", { "pagesToWatch": ["http://%s:%d/?root=1234" % (victim.IP(), PORT)] }, hostlist=[h.name for h in self.getNodes("sensor")]) self.overlord.startRunnable( "zeus.CnCServer", "CnCServer", {"host": cncserver.IP()}, hostlist=[h.name for h in self.getNodes("cncserver")]) for h in self.getNodes("bots"): self.overlord.startRunnable("zeus.Bot", "Bot", hostlist=[h.name], kwargs={ "name": h.name, "peerlist": [nameserver.IP()], "pauseBetweenDuties": 1 }) self.overlord.startRunnable("nameserver", "Nameserver", {"peerlist": [cncserver.IP()]}, hostlist=[nameserver.name]) victim.cmd(self.tsharkCommand % self.pcapfile) logging.debug("Runnables wurden gestartet") time.sleep(35) # Initiate DDoS attack kwargs = json.dumps({ "url": "http://%s:%d/ddos_me?composite=%d" % (victim.IP(), PORT, 9999123456789012345678901456780L), "timeout": 10 }) urlOfCnCServer = "http://%s:%d/current_command" % (cncserver.IP(), PORT) result = cncserver.cmd( "timeout 60s wget -q -O - --post-data 'command=ddos_server×tamp=%d&kwargs=%s' '%s'" % (datetimeToEpoch(datetime.now()), kwargs, urlOfCnCServer), verbose=True) assert result.strip( ) == "OK", "Could not send the DDoS-command to the CnC server: %s" % result
#!/usr/bin/env python2 # coding=UTF-8 import logging, time, os from datetime import datetime from threading import Thread import tornado.web import tornado.httpserver from actors.AbstractBot import CurrentCommandHandler from resources import emu_config from actors.ping.Client import Client from utils.MiscUtils import datetimeToEpoch _current_command = { "command": "default_command", "timestamp": datetimeToEpoch(datetime.now()), "kwargs": dict() } class Servent(Client): """Defines a Servent that provides a web interface to request new commands and also requests and executes new commands in regular intervals""" def __init__(self, peerlist, name="Servent", *args, **kwargs): """:param peerlist: The list of servents where this Servent requests new commands from""" super(Servent, self).__init__(name=name, peerlist=peerlist, *args, **kwargs) app = tornado.web.Application( [("/current_command", ServentCommandHandler, {
#!/usr/bin/env python2 # coding=UTF-8 import logging, time, os from datetime import datetime from threading import Thread import tornado.web import tornado.httpserver from actors.AbstractBot import CurrentCommandHandler from resources import emu_config from actors.ping.Client import Client from utils.MiscUtils import datetimeToEpoch _current_command = {"command": "default_command", "timestamp": datetimeToEpoch(datetime.now()), "kwargs": dict()} class Servent(Client): """Defines a Servent that provides a web interface to request new commands and also requests and executes new commands in regular intervals""" def __init__(self, peerlist, name="Servent", *args, **kwargs): """:param peerlist: The list of servents where this Servent requests new commands from""" super(Servent, self).__init__(name=name, peerlist=peerlist, *args, **kwargs) app = tornado.web.Application([("/current_command", ServentCommandHandler, {"servent": self})], autoreload=False) self.httpserver = tornado.httpserver.HTTPServer(app) def start(self): """Makes the webserver listen on the configured port. Implements start() from the superclass.""" self.httpserver.listen(emu_config.PORT) logging.debug("Start the ping.Servent %s on port %d"%(self.name, emu_config.PORT)) super(Servent, self).start() # Do not execute this at the beginning, because it does not terminate