# Copyright (C) 2010-2012 Cuckoo Sandbox Developers. # This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org # See the file 'docs/LICENSE' for copying permission. import os import re import sys import socket import logging from urlparse import urlunparse from lib.cuckoo.common.utils import convert_to_printable from lib.cuckoo.common.abstracts import Processing try: import dpkt IS_DPKT = True except ImportError, why: IS_DPKT = False class Pcap: """ Network PCAP. """ def __init__(self, filepath): """ Creates a new instance. @param filepath: path to PCAP file """ self.filepath = filepath # List containing all IP addresses involved in the analysis. self.unique_hosts = [] # List containing all TCP packets. self.tcp_connections = [] # List containing all UDP packets. self.udp_connections = [] # List containing all HTTP requests. self.http_requests = [] # List containing all DNS requests. self.dns_requests = [] # List used to track already added DNS request processing. # It's used to avoid processing and resolving the same domains # multiple times. self.dns_performed = [] # Dictionary containing all the results of this processing. self.results = {} def _add_hosts(self, connection): """ Add IPs to unique list. @param connection: connection data """ try: if connection["src"] not in self.unique_hosts: self.unique_hosts.append(convert_to_printable(connection["src"])) if connection["dst"] not in self.unique_hosts: self.unique_hosts.append(convert_to_printable(connection["dst"])) except Exception, why: return False return True def _check_http(self, tcpdata): """ Checks for HTTP traffic. @param tcpdata: tcp data flow """ try: dpkt.http.Request(tcpdata) except dpkt.dpkt.UnpackError: return False return True def _add_http(self, tcpdata, dport): """ Adds an HTTP flow. @param tcpdata: TCP data in flow @param dport: destination port """ http = dpkt.http.Request(tcpdata) try: entry = {} if "host" in http.headers: entry["host"] = convert_to_printable(http.headers['host']) else: entry["host"] = "" entry["port"] = dport entry["data"] = convert_to_printable(tcpdata) if entry["port"] != 80: host = "%s:%d" % (entry["host"], entry["port"]) else: host = entry["host"] entry["uri"] = convert_to_printable(urlunparse(("http", host, http.uri, None, None, None))) entry["body"] = convert_to_printable(http.body) entry["path"] = convert_to_printable(http.uri) if "user-agent" in http.headers: entry["user-agent"] = convert_to_printable(http.headers["user-agent"]) entry["version"] = convert_to_printable(http.version) entry["method"] = convert_to_printable(http.method) self.http_requests.append(entry) except Exception, why: return False return True def _check_dns(self, udpdata): """ Checks for DNS traffic. @param udpdata: UDP data flow """ try: dpkt.dns.DNS(udpdata) except: return False return True def _add_dns(self, udpdata): """ Adds a DNS data flow. @param udpdata: data inside flow """ dns = dpkt.dns.DNS(udpdata) name = dns.qd[0].name if name not in self.dns_performed: if re.search("in-addr.arpa", name): return False # This is generated by time-sync of the virtual machine. if name.strip() == "time.windows.com": return False entry = {} entry["hostname"] = name try: socket.setdefaulttimeout(10) ip = socket.gethostbyname(name) except socket.gaierror: ip = "" entry["ip"] = ip self.dns_requests.append(entry) self.dns_performed.append(name) return True return False def run(self): """ Process PCAP. @return: dict with network analysis data """ log = logging.getLogger("Processing.Pcap") if not IS_DPKT: log.error("Python DPKT is not installed, aborting PCAP analysis.") return None if not os.path.exists(self.filepath): log.warning("The PCAP file does not exist at path \"%s\"." % self.filepath) return None if os.path.getsize(self.filepath) == 0: log.error("The PCAP file at path \"%s\" is empty." % self.filepath) return None file = open(self.filepath, "rb") try: pcap = dpkt.pcap.Reader(file) except dpkt.dpkt.NeedData: log.error("Unable to read PCAP file at path \"%s\"." % self.filepath) return None for ts, buf in pcap: try: eth = dpkt.ethernet.Ethernet(buf) ip = eth.data connection = {} if isinstance(ip, dpkt.ip.IP): connection["src"] = socket.inet_ntoa(ip.src) connection["dst"] = socket.inet_ntoa(ip.dst) elif isinstance(ip, dpkt.ip6.IP6): connection["src"] = socket.inet_ntop(socket.AF_INET6, ip.src) connection["dst"] = socket.inet_ntop(socket.AF_INET6, ip.dst) self._add_hosts(connection) if ip.p == dpkt.ip.IP_PROTO_TCP: tcp = ip.data if len(tcp.data) > 0: if self._check_http(tcp.data): self._add_http(tcp.data, tcp.dport) connection["sport"] = tcp.sport connection["dport"] = tcp.dport self.tcp_connections.append(connection) else: continue elif ip.p == dpkt.ip.IP_PROTO_UDP: udp = ip.data if len(udp.data) > 0: if udp.dport == 53: if self._check_dns(udp.data): self._add_dns(udp.data) connection["sport"] = udp.sport connection["dport"] = udp.dport self.udp_connections.append(connection) #elif ip.p == dpkt.ip.IP_PROTO_ICMP: #icmp = ip.data except AttributeError, why: continue except dpkt.dpkt.NeedData, why: continue file.close() self.results["hosts"] = self.unique_hosts self.results["tcp"] = self.tcp_connections self.results["udp"] = self.udp_connections self.results["http"] = self.http_requests self.results["dns"] = self.dns_requests return self.results class NetworkAnalysis(Processing): def run(self): self.key = "network" results = Pcap(self.pcap_path).run() return results .