modules.auxiliary.sniffer

Package modules :: Package auxiliary :: Module sniffer |
[hide private]
[frames] | no frames]

# Source Code for Module modules.auxiliary.sniffer

      1  # Copyright (C) 2010-2013 Claudio Guarnieri.
      2  # Copyright (C) 2014-2016 Cuckoo Foundation.
      3  # This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
      4  # See the file 'docs/LICENSE' for copying permission.
      5
      6  import os
      7  import getpass
      8  import logging
      9  import subprocess
     10
     11  from lib.cuckoo.common.abstracts import Auxiliary
     12  from lib.cuckoo.common.constants import CUCKOO_ROOT, CUCKOO_GUEST_PORT
     13  from lib.cuckoo.common.exceptions import CuckooOperationalError
     14
     15  log = logging.getLogger(__name__)
     16
     17 -class Sniffer(Auxiliary):
     18 -    def __init__(self):
     19          Auxiliary.__init__(self)
     20          self.proc = None
     21
     22 -    def start(self):
     23          if not self.machine.interface:
     24              log.error("Network interface not defined, network capture aborted")
     25              return
     26
     27          # Handle special pcap dumping options.
     28          if "nictrace" in self.machine.options:
     29              return
     30
     31          tcpdump = self.options.get("tcpdump", "/usr/sbin/tcpdump")
     32          bpf = self.options.get("bpf", "")
     33          file_path = os.path.join(CUCKOO_ROOT, "storage", "analyses",
     34                                   "%s" % self.task.id, "dump.pcap")
     35
     36          if not os.path.exists(tcpdump):
     37              log.error("Tcpdump does not exist at path \"%s\", network "
     38                        "capture aborted", tcpdump)
     39              return
     40
     41          # TODO: this isn't working. need to fix.
     42          # mode = os.stat(tcpdump)[stat.ST_MODE]
     43          # if (mode & stat.S_ISUID) == 0:
     44          #    log.error("Tcpdump is not accessible from this user, "
     45          #              "network capture aborted")
     46          #    return
     47
     48          pargs = [
     49              tcpdump, "-U", "-q", "-s", "0", "-n",
     50              "-i", self.machine.interface,
     51          ]
     52
     53          # Trying to save pcap with the same user which cuckoo is running.
     54          try:
     55              user = getpass.getuser()
     56              pargs.extend(["-Z", user])
     57          except:
     58              pass
     59
     60          pargs.extend(["-w", file_path])
     61          pargs.extend(["host", self.machine.ip])
     62
     63          if self.task.options.get("sniffer.debug") != "1":
     64              # Do not capture Agent traffic.
     65              pargs.extend([
     66                  "and", "not", "(",
     67                  "dst", "host", self.machine.ip, "and",
     68                  "dst", "port", str(CUCKOO_GUEST_PORT),
     69                  ")", "and", "not", "(",
     70                  "src", "host", self.machine.ip, "and",
     71                  "src", "port", str(CUCKOO_GUEST_PORT),
     72                  ")",
     73              ])
     74
     75              # Do not capture ResultServer traffic.
     76              pargs.extend([
     77                  "and", "not", "(",
     78                  "dst", "host", self.machine.resultserver_ip, "and",
     79                  "dst", "port", self.machine.resultserver_port,
     80                  ")", "and", "not", "(",
     81                  "src", "host", self.machine.resultserver_ip, "and",
     82                  "src", "port", self.machine.resultserver_port,
     83                  ")",
     84              ])
     85
     86              if bpf:
     87                  pargs.extend(["and", "(", bpf, ")"])
     88
     89          try:
     90              self.proc = subprocess.Popen(
     91                  pargs, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True
     92              )
     93          except (OSError, ValueError):
     94              log.exception(
     95                  "Failed to start sniffer (interface=%s, host=%s, pcap=%s)",
     96                  self.machine.interface, self.machine.ip, file_path,
     97              )
     98              return
     99
    100          log.info(
    101              "Started sniffer with PID %d (interface=%s, host=%s, pcap=%s)",
    102              self.proc.pid, self.machine.interface, self.machine.ip, file_path,
    103          )
    104
    105 -    def _check_output(self, out, err):
    106          if out:
    107              raise CuckooOperationalError(
    108                  "Potential error while running tcpdump, did not expect "
    109                  "standard output, got: %r." % out
    110              )
    111
    112          err_whitelist = (
    113              "packets captured",
    114              "packets received by filter",
    115              "packets dropped by kernel",
    116          )
    117
    118          for line in err.split("\n"):
    119              if not line or line.startswith("tcpdump: listening on "):
    120                  continue
    121
    122              if line.endswith(err_whitelist):
    123                  continue
    124
    125              raise CuckooOperationalError(
    126                  "Potential error while running tcpdump, did not expect "
    127                  "the following standard error output: %r." % line
    128              )
    129
    130 -    def stop(self):
    131          """Stop sniffing.
    132          @return: operation status.
    133          """
    134          # The tcpdump process was never started in the first place.
    135          if not self.proc:
    136              return
    137
    138          # The tcpdump process has already quit, generally speaking this
    139          # indicates an error such as "permission denied".
    140          if self.proc.poll():
    141              out, err = self.proc.communicate()
    142              raise CuckooOperationalError(
    143                  "Error running tcpdump to sniff the network traffic during "
    144                  "the analysis; stdout = %r and stderr = %r. Did you enable "
    145                  "the extra capabilities to allow running tcpdump as non-root "
    146                  "user and disable AppArmor properly (only applies to Ubuntu)?"
    147                  % (out, err)
    148              )
    149
    150          try:
    151              self.proc.terminate()
    152          except:
    153              try:
    154                  if not self.proc.poll():
    155                      log.debug("Killing sniffer")
    156                      self.proc.kill()
    157              except OSError as e:
    158                  log.debug("Error killing sniffer: %s. Continue", e)
    159              except Exception as e:
    160                  log.exception("Unable to stop the sniffer with pid %d: %s",
    161                                self.proc.pid, e)
    162
    163          # Ensure expected output was received from tcpdump.
    164          out, err = self.proc.communicate()
    165          self._check_output(out, err)
    166