Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'testing', bumped release to 0.4.1

  • Loading branch information...
commit c0a9fefd40d9c1b135c3bb85932e0fd54d9ca675 2 parents 2d4b140 + 852c5b1
@jekil jekil authored
Showing with 1,184 additions and 228 deletions.
  1. +20 −8 agent/agent.py
  2. +4 −4 analyzer/windows/analyzer.py
  3. BIN  analyzer/windows/dll/cuckoomon.dll
  4. +5 −6 cuckoo.py
  5. +18 −0 data/html/sections/file.html
  6. +12 −0 docs/CHANGELOG
  7. +2 −2 docs/book/src/conf.py
  8. +1 −1  docs/book/src/customization/reporting.rst
  9. +14 −0 docs/book/src/development/code_style.rst
  10. +28 −0 docs/book/src/finalremarks/index.rst
  11. +2 −2 docs/book/src/installation/host/requirements.rst
  12. +2 −1  docs/book/src/introduction/index.rst
  13. +1 −0  docs/book/src/usage/index.rst
  14. +58 −0 docs/book/src/usage/utilities.rst
  15. +36 −17 lib/cuckoo/common/abstracts.py
  16. +7 −1 lib/cuckoo/common/config.py
  17. +1 −1  lib/cuckoo/common/constants.py
  18. +19 −13 lib/cuckoo/common/utils.py
  19. +13 −10 lib/cuckoo/core/database.py
  20. +24 −22 lib/cuckoo/core/guest.py
  21. +87 −34 lib/cuckoo/core/processor.py
  22. +31 −16 lib/cuckoo/core/reporter.py
  23. +19 −16 lib/cuckoo/core/scheduler.py
  24. +5 −1 lib/cuckoo/core/sniffer.py
  25. +6 −3 lib/cuckoo/core/startup.py
  26. +40 −40 lib/maec/maec11.py
  27. +4 −4 modules/machinemanagers/virtualbox.py
  28. +44 −12 modules/processing/behavior.py
  29. +1 −1  modules/processing/virustotal.py
  30. +1 −1  modules/reporting/hpfclient.py
  31. +1 −1  modules/reporting/jsondump.py
  32. +1 −1  modules/reporting/maec11.py
  33. +1 −1  modules/reporting/metadata.py
  34. +2 −2 modules/reporting/mongodb.py
  35. +1 −1  modules/reporting/pickled.py
  36. +2 −2 modules/reporting/reporthtml.py
  37. +2 −1  modules/signatures/creates_exe.py
  38. +163 −0 tests/abstracts_tests.py
  39. +12 −0 tests/colors_tests.py
  40. +55 −0 tests/config_tests.py
  41. +105 −0 tests/database_tests.py
  42. +76 −0 tests/processor_tests.py
  43. +52 −0 tests/reporter_tests.py
  44. +58 −0 tests/scheduler_tests.py
  45. +17 −0 tests/sniffer_tests.py
  46. +128 −0 tests/utils_tests.py
  47. +1 −1  utils/submit.py
  48. +1 −1  utils/testreport.py
  49. +1 −1  utils/testsignatures.py
View
28 agent/agent.py
@@ -34,8 +34,8 @@ def __init__(self):
def _get_root(self, root="", container="cuckoo", create=True):
"""Get Cuckoo path.
- @param root: root folder.
- @param container: folder which will contain Cuckoo.
+ @param root: force root folder, don't detect it.
+ @param container: folder which will contain Cuckoo, not used root parameter is used.
@param create: create folder.
"""
if not root:
@@ -43,6 +43,9 @@ def _get_root(self, root="", container="cuckoo", create=True):
root = os.path.join(os.environ["SYSTEMDRIVE"] + os.sep, container)
elif self.system == "linux" or self.system == "darwin":
root = os.path.join(os.environ["HOME"], container)
+ else:
+ self.error = "Unable to detect OS system"
+ return False
if create and not os.path.exists(root):
try:
@@ -50,6 +53,10 @@ def _get_root(self, root="", container="cuckoo", create=True):
except OSError as e:
self.error = e
return False
+ else:
+ if not os.path.exists(root):
+ self.error = "Directory not found: %s" % root
+ return False
return root
@@ -58,7 +65,7 @@ def get_status(self):
@return: status.
"""
return CURRENT_STATUS
-
+
def get_error(self):
"""Get error message.
@return: error message.
@@ -112,8 +119,8 @@ def add_malware(self, data, name, iszip=False):
return True
def add_config(self, options):
- """Add configuration.
- @param options: configuration options.
+ """Creates analysis.cond file from current analysis options.
+ @param options: current configuration options, dict format.
@return: operation status.
"""
root = self._get_root()
@@ -131,8 +138,12 @@ def add_config(self, options):
config.set("analysis", key, value)
config_path = os.path.join(root, "analysis.conf")
- with open(config_path, "wb") as config_file:
- config.write(config_file)
+ try:
+ with open(config_path, "wb") as config_file:
+ config.write(config_file)
+ except OSError as e:
+ self.error = e
+ return False
return True
@@ -170,7 +181,8 @@ def execute(self):
return False
try:
- proc = subprocess.Popen([sys.executable, self.analyzer_path], cwd=os.path.dirname(self.analyzer_path))
+ proc = subprocess.Popen([sys.executable, self.analyzer_path],
+ cwd=os.path.dirname(self.analyzer_path))
self.analyzer_pid = proc.pid
except OSError as e:
self.error = e
View
8 analyzer/windows/analyzer.py
@@ -215,7 +215,7 @@ def get_options(self):
try:
key, value = field.strip().split("=")
except ValueError as e:
- log.warning("Failed parsing option (%s): %s" % (field, e.message))
+ log.warning("Failed parsing option (%s): %s" % (field, e))
continue
options[key.strip()] = value.strip()
@@ -262,7 +262,7 @@ def run(self):
try:
package_class = Package.__subclasses__()[0]
except IndexError as e:
- raise CuckooError("Unable to select package class (package=%s): %s" % (package_name, e.message))
+ raise CuckooError("Unable to select package class (package=%s): %s" % (package_name, e))
pack = package_class(self.get_options())
@@ -322,11 +322,11 @@ def run(self):
except KeyboardInterrupt:
error = "Keyboard Interrupt"
except CuckooError as e:
- error = e.message
+ error = e
if len(log.handlers) > 0:
log.critical(error)
else:
- sys.stderr.write("%s\n" % e.message)
+ sys.stderr.write("%s\n" % e)
finally:
server = xmlrpclib.Server("http://127.0.0.1:8000")
if error:
View
BIN  analyzer/windows/dll/cuckoomon.dll
Binary file not shown
View
11 cuckoo.py
@@ -54,10 +54,9 @@ def main():
try:
main()
except CuckooCriticalError as e:
- if hasattr(e, "message"):
- message = "%s: %s" % (e.__class__.__name__, e.message)
- if len(log.handlers) > 0:
- log.critical(message)
- else:
- sys.stderr.write("%s\n" % message)
+ message = "%s: %s" % (e.__class__.__name__, e)
+ if len(log.handlers) > 0:
+ log.critical(message)
+ else:
+ sys.stderr.write("%s\n" % message)
sys.exit(1)
View
18 data/html/sections/file.html
@@ -58,6 +58,24 @@
</tr>
%endif
<tr>
+ <td><strong>Yara Signatures</strong>:</td>
+ <td>
+ %if yara is not UNDEFINED:
+ %if yara:
+ <ul style="margin-bottom:0">
+ %for rule in yara:
+ <li>${rule["name"]} (${rule["meta"]["description"]})</li>
+ %endfor
+ </ul>
+ %else:
+ None matched
+ %endif
+ %else:
+ Yara signatures disabled
+ %endif
+ </td>
+ </tr>
+ <tr>
<td><strong>Antivirus Results</strong>:</td>
<td>
%if virustotal is not UNDEFINED:
View
12 docs/CHANGELOG
@@ -1,5 +1,17 @@
CHANGELOG
+Cuckoo Sandbox 0.4.1 (2012-08-09)
+=================================
+
+* Added Yara signatures to HTML report
+* Replaced pyssdeep with pydeep
+* Added support for signatures' version requirements
+* Added unit tests
+* Fixed delete_original race condition
+* Fixed reconstruction of registry keys
+* Fixed logging in cuckoomon
+* Improved exception handling
+
Cuckoo Sandbox 0.4 (2012-07-24)
===============================
View
4 docs/book/src/conf.py
@@ -47,9 +47,9 @@
# built documents.
#
# The short X.Y version.
-version = '0.4'
+version = '0.4.1'
# The full version, including alpha/beta/rc tags.
-release = '0.4'
+release = '0.4.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
View
2  docs/book/src/customization/reporting.rst
@@ -47,7 +47,7 @@ Following is an example of a working JSON reporting module:
report.write(json.dumps(results, sort_keys=False, indent=4))
report.close()
except (TypeError, IOError) as e:
- raise CuckooReportError("Failed to generate JSON report: %s" % e.message)
+ raise CuckooReportError("Failed to generate JSON report: %s" % e)
This code is very simple, it basically just receives the global container produced by the
processing modules, converts it into JSON and writes it to a file.
View
14 docs/book/src/development/code_style.rst
@@ -161,6 +161,8 @@ When catching an exception and accessing its handle, use ``as e``::
except Exception, something:
bar()
+It's a good practice use "e" instead of "e.message", as in the example above.
+
Documentation
=============
@@ -168,3 +170,15 @@ All code must be documented in docstring format, see `PEP 257 -- Docstring
Conventions <http://www.python.org/dev/peps/pep-0257/>`_.
Additional comments may be added in logical blocks will be results hard to
understand.
+
+Automated testing
+=================
+
+We belive in automated testing to provide high quality code and avoid dumb
+bugs.
+When possible, all code must be committed with proper unit tests. Particular
+attention must be placed when fixing bugs: it's good practice to write unit
+tests to reproduce the bug.
+All unit tests and fixtures are placed in the tests folder in the cuckoo
+root.
+We adopt `Nose <http://nose.readthedocs.org/en/latest/>`_ as unit testing framework.
View
28 docs/book/src/finalremarks/index.rst
@@ -27,6 +27,34 @@ official `#cuckoobox`_ channel.
.. _`official mailing list`: https://public.honeynet.org/mailman/listinfo/cuckoo
.. _`#cuckoobox`: irc://irc.freenode.net/cuckoobox
+Mailing list how to
+-------------------
+
+Cuckoo's `official mailing list`_ require registration, so you have to register
+your email address before sending mails. Please make sure you registered with
+the email address you're trying to post with.
+
+Please respect netiquette when posting, in detail:
+
+ * Before posting read the mailing list archives, read the Cuckoo blog, read
+ the documentation and Google about your issue. Stop posting questions that have
+ already been answered over and over everywhere.
+ * Posting emails saying just like "Doesn't work, help me" are completely
+ useless. If something is not working report the error, paste the logs, paste the
+ config file, paste the information on the virtual machine, paste the
+ results of the troubleshooting, give context. We are not wizards and we
+ don't have the crystal ball.
+ * Use a proper title. Stuff like "Doesn't work", "Help me", "Error" is not a
+ proper title.
+ * Tend to use `pastebin.com`_, `pastie.org`_ and similar services to paste
+ logs and configs: make the email more readable.
+ * Tend to upload your attachment to file upload services, we have a very
+ low attachment size limit.
+ * Tend to not write HTML emails.
+
+.. _`pastebin.com`: http://pastebin.com/
+.. _`pastie.org`:http://pastie.org/
+
Donations
=========
View
4 docs/book/src/installation/host/requirements.rst
@@ -25,9 +25,9 @@ We suggest you to install all of them so that you can take advantage of the
project at its full potential.
* `Magic`_ (Highly Recommended): for identifying files' formats (otherwise use "file" command line utility)
- * `Pyssdeep`_ (Recommended): for calculating ssdeep fuzzy hash of files.
* `Dpkt`_ (Highly Recommended): for extracting relevant information from PCAP files.
* `Mako`_ (Highly Recommended): for rendering the HTML reports and the web interface.
+ * `Pydeep`_ (Optional): for calculating ssdeep fuzzy hash of files.
* `Pymongo`_ (Optional): for storing the results in a MongoDB database.
* `Yara`_ and Yara Python (Optional): for matching Yara signatures.
* `Libvirt`_ (Optional): for using the KVM module.
@@ -45,7 +45,7 @@ For the rest refer to their websites.
.. _Magic: http://www.darwinsys.com/file/
.. _Dpkt: http://code.google.com/p/dpkt/
.. _Mako: http://www.makotemplates.org
-.. _Pyssdeep: http://code.google.com/p/pyssdeep/
+.. _Pydeep: https://github.com/kbandla/pydeep
.. _Pymongo: http://pypi.python.org/pypi/pymongo/
.. _Yara: http://code.google.com/p/yara-project/
.. _Libvirt: http://www.libvirt.org
View
3  docs/book/src/introduction/index.rst
@@ -4,7 +4,8 @@ Introduction
============
This is an introductory chapter to Cuckoo Sandbox.
-It explains some basic malware analysis concepts, what's Cuckoo an how it can fit in malware analysis.
+It explains some basic malware analysis concepts, what's Cuckoo an how it can fit
+in malware analysis.
.. toctree::
View
1  docs/book/src/usage/index.rst
@@ -11,3 +11,4 @@ This chapter explains how to use Cuckoo.
submit
packages
results
+ utilities
View
58 docs/book/src/usage/utilities.rst
@@ -0,0 +1,58 @@
+=========
+Utilities
+=========
+
+Cuckoo comes with a set of pre-built utilities to automatize several common
+tasks.
+You can find them in "utils" folder.
+
+Cleanup utility
+===============
+
+If you want to delete all history, analysis, data and begin again from the first
+task you need clean.sh utility.
+
+.. note::
+
+ Running clean.sh will delete:
+ * Analyses
+ * Binaries
+ * Cuckoo task's database
+ * Cuckoo logs
+
+To clean your setup, run:
+
+ $ cd utils
+ $ sh clean.sh
+
+Submission Utility
+==================
+
+Submits sample to analysis. This tool is already described in :doc:`submit`.
+
+Web Utility
+===========
+
+Cuckoo's web interface. This tool is already described in :doc:`submit`.
+
+Test Report Utility
+===================
+
+Run the reporting engine (run all reports) on an already available analysis
+folder. So you don't need to run an analysis again to generate reports.
+This is used mainly in debugging and developing Cuckoo.
+For example if you want run again the report engine for analysis number 1:
+
+ $ cd utils
+ $ python testreport.py ../storage/analyses/1/
+
+Test Signature Utility
+======================
+
+Run the signature engine (checks all signatures) on an already available
+analysis folder. So you don't need to run an analysis again.
+This is used mainly in debugging and developing Cuckoo.
+For example if you want run again the singature engine for analysis number 1:
+
+ $ cd utils
+ $ python testsignatures.py ../storage/analyses/1/
View
53 lib/cuckoo/common/abstracts.py
@@ -3,10 +3,15 @@
# See the file 'docs/LICENSE' for copying permission.
import os
+import logging
import ConfigParser
-from lib.cuckoo.common.exceptions import CuckooMachineError
+from lib.cuckoo.common.exceptions import CuckooMachineError, CuckooOperationalError, CuckooReportError
from lib.cuckoo.common.constants import CUCKOO_ROOT
+from lib.cuckoo.common.utils import create_folder
+
+log = logging.getLogger(__name__)
+
class Dictionary(dict):
"""Cuckoo custom dict."""
@@ -32,6 +37,16 @@ def set_options(self, options):
self.options = options
def initialize(self, module_name):
+ """Read and load machines configuration, try to check the configuration.
+ @param module_name: module name.
+ """
+ # Load.
+ self._initialize(module_name)
+
+ # Run initialization checks.
+ self._initialize_check()
+
+ def _initialize(self, module_name):
"""Read configuration.
@param module_name: module name.
"""
@@ -39,24 +54,24 @@ def initialize(self, module_name):
mmanager_opts = self.options.get(module_name)
for machine_id in mmanager_opts["machines"].strip().split(","):
- machine_opts = self.options.get(machine_id)
- machine = Dictionary()
- machine.id = machine_id
- machine.label = machine_opts["label"]
- machine.platform = machine_opts["platform"]
- machine.ip = machine_opts["ip"]
- machine.locked = False
- self.machines.append(machine)
-
- # Run initialization checks.
- self._initialize_check()
+ try:
+ machine_opts = self.options.get(machine_id)
+ machine = Dictionary()
+ machine.id = machine_id
+ machine.label = machine_opts["label"]
+ machine.platform = machine_opts["platform"]
+ machine.ip = machine_opts["ip"]
+ machine.locked = False
+ self.machines.append(machine)
+ except (AttributeError, CuckooOperationalError):
+ log.warning("Configuration details about machine %s are missing. Continue" % machine_id)
+ continue
def _initialize_check(self):
- """Runs all checks when a machine manager is initialized.
+ """Runs checks against virtualization software when a machine manager is initialized.
@note: in machine manager modules you may override or superclass this method.
@raise CuckooMachineError: if a misconfiguration or a unkown vm state is found.
"""
- # Checks if machines configured are really available.
try:
configured_vm = self._list()
for machine in self.machines:
@@ -110,7 +125,7 @@ def release(self, label=None):
machine.locked = False
def running(self):
- """Returns running virutal machines.
+ """Returns running virtual machines.
@return: running virtual machines list.
"""
return [m for m in self.machines if m.locked]
@@ -172,6 +187,8 @@ class Signature(object):
references = []
alert = False
enabled = True
+ minimum = None
+ maximum = None
def __init__(self):
self.data = []
@@ -199,8 +216,10 @@ def set_path(self, analysis_path):
self.conf_path = os.path.join(self.analysis_path, "analysis.conf")
self.reports_path = os.path.join(self.analysis_path, "reports")
- if not os.path.exists(self.reports_path):
- os.mkdir(self.reports_path)
+ try:
+ create_folder(folder=self.reports_path)
+ except CuckooOperationalError as e:
+ CuckooReportError(e)
def set_options(self, options):
"""Set report options.
View
8 lib/cuckoo/common/config.py
@@ -7,6 +7,8 @@
from lib.cuckoo.common.constants import CUCKOO_ROOT
from lib.cuckoo.common.abstracts import Dictionary
+from lib.cuckoo.common.exceptions import CuckooOperationalError
+
class Config:
"""Configuration file parser."""
@@ -32,6 +34,10 @@ def __init__(self, cfg=os.path.join(CUCKOO_ROOT, "conf", "cuckoo.conf")):
def get(self, section):
"""Get option.
@param section: section to fetch.
+ @raise CuckooOperationalError: if section not found.
@return: option value.
"""
- return getattr(self, section)
+ try:
+ return getattr(self, section)
+ except AttributeError as e:
+ raise CuckooOperationalError("Option %s is not found in configuration, error: %s" % (section, e))
View
2  lib/cuckoo/common/constants.py
@@ -5,7 +5,7 @@
import os
CUCKOO_ROOT = os.path.normpath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "..", ".."))
-CUCKOO_VERSION = "0.4"
+CUCKOO_VERSION = "0.4.1"
CUCKOO_GUEST_PORT = 8000
CUCKOO_GUEST_INIT = 0x001
CUCKOO_GUEST_RUNNING = 0x002
View
32 lib/cuckoo/common/utils.py
@@ -16,25 +16,38 @@
pass
try:
- import ssdeep
+ import pydeep
HAVE_SSDEEP = True
except ImportError:
HAVE_SSDEEP = False
+from lib.cuckoo.common.exceptions import CuckooOperationalError
+
+
def create_folders(root=".", folders=[]):
- """Create direcotry.
+ """Create directories.
@param root: root path.
- @param folders: folders name to be created.
+ @param folders: folders list to be created.
+ @raise CuckooOperationalError: if fails to create folder.
"""
for folder in folders:
if os.path.exists(os.path.join(root, folder)):
continue
+ else:
+ create_folder(root, folder)
+def create_folder(root=".", folder=None):
+ """Create directory.
+ @param root: root path.
+ @param folder: folder name to be created.
+ @raise CuckooOperationalError: if fails to create folder.
+ """
+ if not os.path.exists(os.path.join(root, folder)) and folder:
try:
folder_path = os.path.join(root, folder)
os.makedirs(folder_path)
except OSError as e:
- continue
+ raise CuckooOperationalError("Unable to create folder: %s" % folder_path)
def convert_char(c):
"""Escapes characters.
@@ -61,14 +74,7 @@ def datetime_to_iso(timestamp):
@param timestamp: timestamp string
@return: ISO datetime
"""
- if hasattr(datetime, 'strptime'):
- # Python 2.6
- strptime = datetime.strptime
- else:
- # Python 2.4 equivalent
- import time
- strptime = lambda date_string, format: datetime(*(time.strptime(date_string, format)[0:6]))
- return strptime(timestamp, '%Y-%m-%d %H:%M:%S').isoformat()
+ return datetime.strptime(timestamp, '%Y-%m-%d %H:%M:%S').isoformat()
class File:
"""Basic file object class with all useful utilities."""
@@ -147,7 +153,7 @@ def get_ssdeep(self):
return None
try:
- return ssdeep.ssdeep().hash_file(self.file_path)
+ return pydeep.hash_file(self.file_path)
except Exception:
return None
View
23 lib/cuckoo/core/database.py
@@ -7,10 +7,11 @@
import sqlite3
from lib.cuckoo.common.constants import CUCKOO_ROOT
-from lib.cuckoo.common.exceptions import CuckooDatabaseError
+from lib.cuckoo.common.exceptions import CuckooDatabaseError, CuckooOperationalError
from lib.cuckoo.common.abstracts import Dictionary
+from lib.cuckoo.common.utils import create_folder
-# from http://docs.python.org/library/sqlite3.html
+# From http://docs.python.org/library/sqlite3.html
def dict_factory(cursor, row):
d = Dictionary()
for idx, col in enumerate(cursor.description):
@@ -20,9 +21,12 @@ def dict_factory(cursor, row):
class Database:
"""Analysis queue database."""
- def __init__(self, root="."):
- """@param root: database path."""
- self.db_file = os.path.join(root, os.path.join(CUCKOO_ROOT, "db", "cuckoo.db"))
+ def __init__(self, db_file=None):
+ """@param db_file: database file path."""
+ if db_file:
+ self.db_file = db_file
+ else:
+ self.db_file = os.path.join(CUCKOO_ROOT, "db", "cuckoo.db")
self.generate()
self.conn = sqlite3.connect(self.db_file, timeout=60)
@@ -39,9 +43,9 @@ def generate(self):
db_dir = os.path.dirname(self.db_file)
if not os.path.exists(db_dir):
try:
- os.makedirs(db_dir)
- except OSError as e:
- return False
+ create_folder(folder=db_dir)
+ except CuckooOperationalError as e:
+ raise CuckooDatabaseError("Unable to create database directory: %s" % e)
conn = sqlite3.connect(self.db_file)
cursor = conn.cursor()
@@ -115,8 +119,7 @@ def fetch(self):
"WHERE lock = 0 " \
"AND status = 0 " \
"ORDER BY priority DESC, added_on LIMIT 1;")
- except sqlite3.OperationalError as e:
- print e
+ except sqlite3.OperationalError:
return None
row = self.cursor.fetchone()
View
46 lib/cuckoo/core/guest.py
@@ -18,12 +18,13 @@
class GuestManager:
"""Guest machine manager."""
- def __init__(self, ip, platform="windows"):
- """@param ip: IP address.
+ def __init__(self, vm_id, ip, platform="windows"):
+ """@param ip: guest IP address.
@param platform: OS type.
"""
- self.platform = platform
+ self.id = vm_id
self.ip = ip
+ self.platform = platform
self.server = xmlrpclib.Server("http://%s:%s" % (ip, CUCKOO_GUEST_PORT), allow_none=True)
def wait(self, status):
@@ -31,17 +32,17 @@ def wait(self, status):
@param status: status.
@return: always True.
"""
- log.debug("Waiting for status 0x%.04x" % status)
+ log.debug("%s: waiting for status 0x%.04x" % (self.id, status))
while True:
try:
if self.server.get_status() == status:
- log.debug("Status ready")
+ log.debug("%s: status ready" % self.id)
break
except:
pass
- log.debug("Not ready yet")
+ log.debug("%s: not ready yet" % self.id)
time.sleep(1)
return True
@@ -71,7 +72,7 @@ def upload_analyzer(self):
data = xmlrpclib.Binary(zip_data.getvalue())
zip_data.close()
- log.debug("Uploading analyzer to guest (ip=%s)" % self.ip)
+ log.debug("Uploading analyzer to guest (id=%s, ip=%s)" % (self.id, self.ip))
self.server.add_analyzer(data)
def start_analysis(self, options):
@@ -82,7 +83,7 @@ def start_analysis(self, options):
if not os.path.exists(options["file_path"]):
return False
- log.info("Starting analysis on guest (ip=%s)" % self.ip)
+ log.info("Starting analysis on guest (id=%s, ip=%s)" % (self.id, self.ip))
socket.setdefaulttimeout(180)
@@ -97,7 +98,7 @@ def start_analysis(self, options):
self.server.add_malware(data, options["file_name"])
self.server.execute()
except socket.timeout:
- raise CuckooGuestError("Guest communication timeout. Check networking or try to increase timeout")
+ raise CuckooGuestError("%s: guest communication timeout, check networking or try to increase timeout" % self.id)
def wait_for_completion(self):
"""Wai for analysis completion.
@@ -107,13 +108,13 @@ def wait_for_completion(self):
try:
status = self.server.get_status()
if status == CUCKOO_GUEST_COMPLETED:
- log.info("Analysis completed successfully")
+ log.info("%s: analysis completed successfully" % self.id)
break
elif status == CUCKOO_GUEST_FAILED:
- log.error("Analysis failed: %s" % self.server.get_error())
+ log.error("%s: analysis failed: %s" % (self.id, self.server.get_error()))
return False
else:
- log.debug("Analysis not completed yet")
+ log.debug("%s: analysis not completed yet" % self.id)
except:
pass
@@ -131,15 +132,16 @@ def save_results(self, folder):
zip_data = StringIO()
zip_data.write(data)
- with ZipFile(zip_data, "r") as archive:
- if not os.path.exists(folder):
- try:
- os.mkdir(folder)
- except OSError as e:
- log.error("Failed to create results folder: %s" % e.message)
- return False
-
- log.debug("Extracting results to %s" % folder)
- archive.extractall(folder)
+ archive = ZipFile(zip_data, "r")
+ if not os.path.exists(folder):
+ try:
+ os.mkdir(folder)
+ except (IOError, OSError) as e:
+ log.exception("Failed to create the results folder")
+ return False
+
+ log.debug("Extracting results to %s" % folder)
+ archive.extractall(folder)
+ archive.close()
return True
View
121 lib/cuckoo/core/processor.py
@@ -5,8 +5,10 @@
import copy
import logging
import pkgutil
+from distutils.version import StrictVersion
from lib.cuckoo.common.config import Config
+from lib.cuckoo.common.constants import CUCKOO_VERSION
from lib.cuckoo.common.abstracts import Processing, Signature
from lib.cuckoo.common.exceptions import CuckooProcessingError
import modules.processing as processing
@@ -15,15 +17,15 @@
log = logging.getLogger(__name__)
class Processor:
- """Analysis processor."""
+ """Analysis processing module."""
def __init__(self, analysis_path):
"""@param analysis_path: analysis folder path."""
self.analysis_path = analysis_path
- self.__populate(processing)
- self.__populate(signatures)
+ self._populate(processing)
+ self._populate(signatures)
- def __populate(self, modules):
+ def _populate(self, modules):
"""Load modules.
@param modules: modules.
"""
@@ -34,49 +36,100 @@ def __populate(self, modules):
__import__(name, globals(), locals(), ["dummy"], -1)
+ def _run_processing(self, module):
+ """Run a processing module.
+ @param module: processing module to run.
+ @param results: results dict.
+ @return: results generated by module.
+ """
+ current = module()
+ current.set_path(self.analysis_path)
+ current.cfg = Config(current.conf_path)
+
+ try:
+ data = current.run()
+ log.debug("Executed processing module \"%s\" on analysis at \"%s\"" % (current.__class__.__name__, self.analysis_path))
+ return {current.key : data}
+ except NotImplementedError:
+ log.debug("The processing module \"%s\" is not correctly implemented" % current.__classs__.__name__)
+ except CuckooProcessingError as e:
+ log.warning("The processing module \"%s\" returned the following error: %s" % (current.__class__.__name__, e))
+ except Exception as e:
+ log.exception("Failed to run the processing module \"%s\":" % (current.__class__.__name__))
+
+ return None
+
+ def _run_signature(self, signature, results):
+ """Run a signature.
+ @param signature: signature to run.
+ @param signs: signature results dict.
+ @return: matched signature.
+ """
+ current = signature()
+ log.debug("Running signature \"%s\"" % current.name)
+
+ if not current.enabled:
+ return None
+
+ version = CUCKOO_VERSION.split("-")[0]
+ if current.minimum:
+ try:
+ if StrictVersion(version) < StrictVersion(current.minimum.split("-")[0]):
+ log.debug("You are running an older incompatible version of Cuckoo, the signature \"%s\" requires minimum version %s"
+ % (current.name, current.minimum))
+ return None
+ except ValueError:
+ log.debug("Wrong minor version number in signature %s" % current.name)
+ return None
+
+ if current.maximum:
+ try:
+ if StrictVersion(version) > StrictVersion(current.maximum.split("-")[0]):
+ log.debug("You are running a newer incompatible version of Cuckoo, the signature \"%s\" requires maximum version %s"
+ % (current.name, current.maximum))
+ return None
+ except ValueError:
+ log.debug("Wrong major version number in signature %s" % current.name)
+ return None
+
+ try:
+ if current.run(copy.deepcopy(results)):
+ matched = {"name" : current.name,
+ "description" : current.description,
+ "severity" : current.severity,
+ "references" : current.references,
+ "data" : current.data,
+ "alert" : current.alert}
+ log.debug("Analysis at \"%s\" matched signature \"%s\"" % (self.analysis_path, current.name))
+ return matched
+ except NotImplementedError:
+ log.debug("The signature \"%s\" is not correctly implemented" % current.name)
+ except Exception as e:
+ log.exception("Failed to run signature \"%s\":" % (current.name))
+
+ return None
+
def run(self):
- """Run all processors.
+ """Run all processing modules and all signatures.
@return: processing results.
"""
results = {}
Processing()
for module in Processing.__subclasses__():
- current = module()
- current.set_path(self.analysis_path)
- current.cfg = Config(current.conf_path)
-
- try:
- results[current.key] = current.run()
- log.debug("Executed processing module \"%s\"" % current.__class__.__name__)
- except NotImplementedError:
- continue
- except CuckooProcessingError as e:
- log.warning("Failed to execute processing module \"%s\": %s" % (current.__class__.__name__, e.message))
+ result = self._run_processing(module)
+ if result:
+ results.update(result)
Signature()
sigs = []
for signature in Signature.__subclasses__():
- current = signature()
-
- if not current.enabled:
- continue
-
- try:
- if current.run(copy.deepcopy(results)):
- matched = {"name" : current.name,
- "description" : current.description,
- "severity" : current.severity,
- "references" : current.references,
- "data" : current.data,
- "alert" : current.alert}
- sigs.append(matched)
- log.debug("Analysis at \"%s\" matched signature \"%s\"" % (self.analysis_path, current.name))
- except NotImplementedError:
- continue
+ match = self._run_signature(signature, results)
+ if match:
+ sigs.append(match)
sigs.sort(key=lambda key: key["severity"])
results["signatures"] = sigs
- return results
+ return results
View
47 lib/cuckoo/core/reporter.py
@@ -11,7 +11,7 @@
from lib.cuckoo.common.constants import CUCKOO_ROOT
from lib.cuckoo.common.config import Config
from lib.cuckoo.common.abstracts import Report
-from lib.cuckoo.common.exceptions import CuckooDependencyError, CuckooReportError
+from lib.cuckoo.common.exceptions import CuckooDependencyError, CuckooReportError, CuckooOperationalError
import modules.reporting as plugins
log = logging.getLogger(__name__)
@@ -50,7 +50,7 @@ def __populate(self, modules):
try:
__import__(path, globals(), locals(), ["dummy"], -1)
except CuckooDependencyError as e:
- log.warning("Unable to import reporting module \"%s\": %s" % (name, e.message))
+ log.warning("Unable to import reporting module \"%s\": %s" % (name, e))
def run(self, data):
"""Generates all reports.
@@ -60,20 +60,35 @@ def run(self, data):
Report()
for plugin in Report.__subclasses__():
- current = plugin()
- current.set_path(self.analysis_path)
- current.cfg = Config(current.conf_path)
- module = inspect.getmodule(current)
+ self._run_report(plugin, data)
+
+ def _run_report(self, plugin, data):
+ """Run a single report plugin.
+ @param plugin: report plugin.
+ @param data: results data from analysis.
+ """
+ current = plugin()
+ current.set_path(self.analysis_path)
+ current.cfg = Config(current.conf_path)
+ module = inspect.getmodule(current)
+
+ if "." in module.__name__:
module_name = module.__name__.rsplit(".", 1)[1]
+ else:
+ module_name = module.__name__
+
+ try:
current.set_options(self.cfg.get(module_name))
+ except CuckooOperationalError:
+ raise CuckooReportError("Reporting module %s not found in configuration file" % module_name)
- try:
- # Run report, for each report a brand new copy of results is
- # created, to prevent a reporting module to edit global
- # result set and affect other reporting modules.
- current.run(copy.deepcopy(data))
- log.debug("Executed reporting module \"%s\"" % current.__class__.__name__)
- except NotImplementedError:
- continue
- except CuckooReportError as e:
- log.warning("Failed to execute reporting module \"%s\": %s" % (current.__class__.__name__, e.message))
+ try:
+ # Run report, for each report a brand new copy of results is
+ # created, to prevent a reporting module to edit global
+ # result set and affect other reporting modules.
+ current.run(copy.deepcopy(data))
+ log.debug("Executed reporting module \"%s\"" % current.__class__.__name__)
+ except NotImplementedError:
+ return
+ except CuckooReportError as e:
+ log.warning("Failed to execute reporting module \"%s\": %s" % (current.__class__.__name__, e))
View
35 lib/cuckoo/core/scheduler.py
@@ -9,9 +9,9 @@
import logging
from threading import Thread, Lock
-from lib.cuckoo.common.exceptions import CuckooAnalysisError, CuckooMachineError, CuckooGuestError
+from lib.cuckoo.common.exceptions import CuckooAnalysisError, CuckooMachineError, CuckooGuestError, CuckooOperationalError
from lib.cuckoo.common.abstracts import Dictionary, MachineManager
-from lib.cuckoo.common.utils import File, create_folders
+from lib.cuckoo.common.utils import File, create_folders, create_folder
from lib.cuckoo.common.config import Config
from lib.cuckoo.core.database import Database
from lib.cuckoo.core.guest import GuestManager
@@ -44,7 +44,10 @@ def init_storage(self):
if os.path.exists(self.analysis.results_folder):
raise CuckooAnalysisError("Analysis results folder already exists at path \"%s\", analysis aborted" % self.analysis.results_folder)
- os.mkdir(self.analysis.results_folder)
+ try:
+ create_folder(folder=self.analysis.results_folder)
+ except CuckooOperationalError:
+ raise CuckooAnalysisError("Unable to create analysis folder %s" % self.analysis.results_folder)
def store_file(self):
"""Store sample file.
@@ -77,7 +80,7 @@ def store_file(self):
try:
os.remove(self.task.file_path)
except OSError as e:
- log.warning("Unable to delete original file at path \"%s\": %s" % (self.task.file_path, e.message))
+ log.error("Unable to delete original file at path \"%s\": %s" % (self.task.file_path, e))
def build_options(self):
"""Get analysis options.
@@ -107,7 +110,7 @@ def launch_analysis(self):
"""Start analysis.
@raise CuckooAnalysisError: if unable to start analysis.
"""
- log.info("Starting analysis of file \"%s\"" % self.task.file_path)
+ log.info("Starting analysis of file \"%s\" (task=%s)" % (self.task.file_path, self.task.id))
if not os.path.exists(self.task.file_path):
raise CuckooAnalysisError("The file to analyze does not exist at path \"%s\", analysis aborted" % self.task.file_path)
@@ -121,10 +124,10 @@ def launch_analysis(self):
vm = mmanager.acquire(machine_id=self.task.machine, platform=self.task.platform)
machine_lock.release()
if not vm:
- log.debug("No machine available")
+ log.debug("Task #%s: no machine available" % self.task.id)
time.sleep(1)
else:
- log.info("Acquired machine %s (Label: %s)" % (vm.id, vm.label))
+ log.info("Task #%s: acquired machine %s (label=%s)" % (self.task.id, vm.id, vm.label))
break
# Initialize sniffer
@@ -138,7 +141,7 @@ def launch_analysis(self):
# Start machine
mmanager.start(vm.label)
# Initialize guest manager
- guest = GuestManager(vm.ip, vm.platform)
+ guest = GuestManager(vm.id, vm.ip, vm.platform)
# Launch analysis
guest.start_analysis(options)
# Wait for analysis to complete
@@ -148,11 +151,11 @@ def launch_analysis(self):
sniffer.stop()
if not success:
- raise CuckooAnalysisError("Analysis failed, review previous errors")
+ raise CuckooAnalysisError("Task #%s: analysis failed, review previous errors" % self.task.id)
# Save results
guest.save_results(self.analysis.results_folder)
except (CuckooMachineError, CuckooGuestError) as e:
- raise CuckooAnalysisError(e.message)
+ raise CuckooAnalysisError(e)
finally:
# Stop machine
mmanager.stop(vm.label)
@@ -162,7 +165,7 @@ def launch_analysis(self):
# Launch reports generation
Reporter(self.analysis.results_folder).run(Processor(self.analysis.results_folder).run())
- log.info("Reports generation completed (path=%s)" % self.analysis.results_folder)
+ log.info("Task #%s: reports generation completed (path=%s)" % (self.task.id, self.analysis.results_folder))
def run(self):
"""Run manager thread."""
@@ -174,10 +177,10 @@ def run(self):
try:
self.launch_analysis()
except CuckooMachineError as e:
- log.error("Please check virtual machine status: %s" % e.message)
+ log.error("Please check virtual machine status: %s" % e)
success = False
except CuckooAnalysisError as e:
- log.error(e.message)
+ log.error(e)
success = False
finally:
db.complete(self.task.id, success)
@@ -231,7 +234,7 @@ def stop(self):
try:
mmanager.stop(machine.label)
except CuckooMachineError as e:
- log.error("Unable to shutdown machine %s, please check manually. Error: %s" % (machine.label, e.message))
+ log.error("Unable to shutdown machine %s, please check manually. Error: %s" % (machine.label, e))
def start(self):
"""Start scheduler."""
@@ -243,13 +246,13 @@ def start(self):
time.sleep(1)
if mmanager.availables() == 0:
- log.debug("No machines available, try again")
+ #log.debug("No machines available, try again")
continue
task = self.db.fetch()
if not task:
- log.debug("No pending tasks, try again")
+ #log.debug("No pending tasks, try again")
continue
analysis = AnalysisManager(task)
View
6 lib/cuckoo/core/sniffer.py
@@ -27,13 +27,16 @@ def start(self, interface="eth0", host="", file_path=""):
@return: operation status.
"""
if not os.path.exists(self.tcpdump):
+ log.error("Tcpdump does not exist at path \"%s\", network capture aborted" % self.tcpdump)
return False
mode = os.stat(self.tcpdump)[stat.ST_MODE]
if mode and stat.S_ISUID != 2048:
+ log.error("Tcpdump is not accessible from this user, network capture aborted")
return False
if not interface:
+ log.error("Network interface not defined, network capture aborted")
return False
pargs = [self.tcpdump, '-U', '-q', '-i', interface, '-n', '-s', '1515']
@@ -46,7 +49,7 @@ def start(self, interface="eth0", host="", file_path=""):
try:
self.proc = subprocess.Popen(pargs, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except (OSError, ValueError) as e:
- log.error("Failed to start sniffer (interface=%s, host=%s, dump path=%s): %s" % (interface, host, file_path, e.message))
+ log.exception("Failed to start sniffer (interface=%s, host=%s, dump path=%s)" % (interface, host, file_path))
return False
log.info("Started sniffer (interface=%s, host=%s, dump path=%s)" % (interface, host, file_path))
@@ -64,6 +67,7 @@ def stop(self):
try:
self.proc.kill()
except Exception as e:
+ log.exception("Unable to stop the sniffer (interface=%s, host=%s, dump path=%s" % (interface, host, file_path))
return False
return True
View
9 lib/cuckoo/core/startup.py
@@ -7,7 +7,7 @@
import logging
from lib.cuckoo.common.constants import CUCKOO_ROOT
-from lib.cuckoo.common.exceptions import CuckooStartupError
+from lib.cuckoo.common.exceptions import CuckooStartupError, CuckooOperationalError
from lib.cuckoo.common.utils import create_folders
log = logging.getLogger()
@@ -43,7 +43,7 @@ def check_dependencies():
try:
__import__(dependency)
except ImportError as e:
- raise CuckooStartupError("Unable to import \"%s\"." % dependency)
+ raise CuckooStartupError("Unable to import \"%s\"" % dependency)
return True
@@ -68,7 +68,10 @@ def create_structure():
"storage/analyses",
"storage/binaries"]
- create_folders(root=CUCKOO_ROOT,folders=folders)
+ try:
+ create_folders(root=CUCKOO_ROOT,folders=folders)
+ except CuckooOperationalError as e:
+ raise CuckooStartupError(e)
def init_logging():
"""Initialize logging."""
View
80 lib/maec/maec11.py
@@ -490,7 +490,7 @@ def buildAttributes(self, node, attrs, already_processed):
try:
self.schema_version = float(value)
except ValueError as e:
- raise ValueError('Bad float/double attribute (schema_version): %s' % e.message)
+ raise ValueError('Bad float/double attribute (schema_version): %s' % e)
def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
if nodeName_ == 'Analyses':
obj_ = AnalysesType.factory()
@@ -947,7 +947,7 @@ def buildAttributes(self, node, attrs, already_processed):
try:
self.ordinal_position = int(value)
except ValueError as e:
- raise_parse_error(node, 'Bad integer attribute: %s' % e.message)
+ raise_parse_error(node, 'Bad integer attribute: %s' % e)
if self.ordinal_position <= 0:
raise_parse_error(node, 'Invalid PositiveInteger')
value = find_attr_value_('id', node)
@@ -1542,7 +1542,7 @@ def buildAttributes(self, node, attrs, already_processed):
try:
self.ordinal_position = int(value)
except ValueError as e:
- raise_parse_error(node, 'Bad integer attribute: %s' % e.message)
+ raise_parse_error(node, 'Bad integer attribute: %s' % e)
if self.ordinal_position <= 0:
raise_parse_error(node, 'Invalid PositiveInteger')
value = find_attr_value_('type', node)
@@ -4752,7 +4752,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Size')
self.Size = ival_
# end class PEDataDirectoryStruct
@@ -4970,7 +4970,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Number_Of_Relocations')
self.Number_Of_Relocations = ival_
elif nodeName_ == 'Number_Of_Linenumbers':
@@ -4978,7 +4978,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Number_Of_Linenumbers')
self.Number_Of_Linenumbers = ival_
elif nodeName_ == 'Characteristics':
@@ -5096,7 +5096,7 @@ def buildAttributes(self, node, attrs, already_processed):
try:
self.length = int(value)
except ValueError as e:
- raise_parse_error(node, 'Bad integer attribute: %s' % e.message)
+ raise_parse_error(node, 'Bad integer attribute: %s' % e)
value = find_attr_value_('encoding', node)
if value is not None and 'encoding' not in already_processed:
already_processed.append('encoding')
@@ -5348,7 +5348,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Ordinal')
self.Ordinal = ival_
# end class PEExportType
@@ -5509,7 +5509,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
fval_ = float(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires float or double: %s' % e.message)
+ raise_parse_error(child_, 'requires float or double: %s' % e)
fval_ = self.gds_validate_float(fval_, node, 'Entropy')
self.Entropy = fval_
elif nodeName_ == 'Virtual_Address':
@@ -5521,7 +5521,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Virtual_Size')
self.Virtual_Size = ival_
elif nodeName_ == 'Flags':
@@ -5533,7 +5533,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Relocations')
self.Relocations = ival_
# end class PESectionType
@@ -5870,7 +5870,7 @@ def buildAttributes(self, node, attrs, already_processed):
try:
self.version = float(value)
except ValueError as e:
- raise ValueError('Bad float/double attribute (version): %s' % e.message)
+ raise ValueError('Bad float/double attribute (version): %s' % e)
value = find_attr_value_('id', node)
if value is not None and 'id' not in already_processed:
already_processed.append('id')
@@ -6112,7 +6112,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'size')
self.size = ival_
elif nodeName_ == 'crc32':
@@ -6468,7 +6468,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'port')
self.port = ival_
elif nodeName_ == 'path':
@@ -6815,14 +6815,14 @@ def buildAttributes(self, node, attrs, already_processed):
try:
self.id = int(value)
except ValueError as e:
- raise_parse_error(node, 'Bad integer attribute: %s' % e.message)
+ raise_parse_error(node, 'Bad integer attribute: %s' % e)
def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
if nodeName_ == 'as-number':
sval_ = child_.text
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'as_number')
self.as_number = ival_
# end class ASNObject
@@ -7225,7 +7225,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'importance')
self.importance = ival_
self.validate_intBetween0and100(self.importance) # validate type intBetween0and100
@@ -10650,7 +10650,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Maximum_Instances')
self.Maximum_Instances = ival_
elif nodeName_ == 'Security_Attributes':
@@ -10991,7 +10991,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
fval_ = float(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires float or double: %s' % e.message)
+ raise_parse_error(child_, 'requires float or double: %s' % e)
fval_ = self.gds_validate_float(fval_, node, 'TRID_Confidence')
self.TRID_Confidence = fval_
# end class TrID_TypeType
@@ -11448,7 +11448,7 @@ def buildAttributes(self, node, attrs, already_processed):
try:
self.dll_count = int(value)
except ValueError as e:
- raise_parse_error(node, 'Bad integer attribute: %s' % e.message)
+ raise_parse_error(node, 'Bad integer attribute: %s' % e)
value = find_attr_value_('type', node)
if value is not None and 'type' not in already_processed:
already_processed.append('type')
@@ -12246,7 +12246,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
fval_ = float(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires float or double: %s' % e.message)
+ raise_parse_error(child_, 'requires float or double: %s' % e)
fval_ = self.gds_validate_float(fval_, node, 'Entropy')
self.Entropy = fval_
elif nodeName_ == 'Signature':
@@ -12509,7 +12509,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Number_Of_Symbols')
self.Number_Of_Symbols = ival_
elif nodeName_ == 'Size_Of_Optional_Header':
@@ -14272,7 +14272,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Width')
self.Width = ival_
elif nodeName_ == 'Height':
@@ -14280,7 +14280,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Height')
self.Height = ival_
elif nodeName_ == 'Window_Display_Name':
@@ -14408,7 +14408,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Thread_ID')
self.Thread_ID = ival_
elif nodeName_ == 'Start_Address':
@@ -14552,7 +14552,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'AS_Number')
self.AS_Number = ival_
# end class Internet_Object_AttributesType
@@ -15325,7 +15325,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Name')
self.Name = ival_
# end class HandleType
@@ -15553,7 +15553,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Internal_Port')
self.Internal_Port = ival_
elif nodeName_ == 'External_Port':
@@ -15561,7 +15561,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'External_Port')
self.External_Port = ival_
elif nodeName_ == 'Socket_Type':
@@ -15573,7 +15573,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Socket_ID')
self.Socket_ID = ival_
elif nodeName_ == 'Internal_IP_Address':
@@ -16993,7 +16993,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Thread_ID')
self.Thread_ID = ival_
elif nodeName_ == 'Target_PID':
@@ -17001,7 +17001,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Target_PID')
self.Target_PID = ival_
elif nodeName_ == 'Start_Address':
@@ -17147,7 +17147,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Target_PID')
self.Target_PID = ival_
# end class Process_Action_AttributesType
@@ -17291,7 +17291,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Target_PID')
self.Target_PID = ival_
elif nodeName_ == 'Requested_Address':
@@ -17515,7 +17515,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Internal_Port')
self.Internal_Port = ival_
elif nodeName_ == 'External_Port':
@@ -17523,7 +17523,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'External_Port')
self.External_Port = ival_
elif nodeName_ == 'Internal_IP_Address':
@@ -17551,7 +17551,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Buffer_Length')
self.Buffer_Length = ival_
# end class Network_Action_AttributesType
@@ -18151,7 +18151,7 @@ def buildAttributes(self, node, attrs, already_processed):
try:
self.nvd_id = int(value)
except ValueError as e:
- raise_parse_error(node, 'Bad integer attribute: %s' % e.message)
+ raise_parse_error(node, 'Bad integer attribute: %s' % e)
value = find_attr_value_('xmlns-meta', node)
if value is not None and 'xmlns-meta' not in already_processed:
already_processed.append('xmlns-meta')
@@ -18246,7 +18246,7 @@ def buildAttributes(self, node, attrs, already_processed):
try:
self.ordinal_position = int(value)
except ValueError as e:
- raise_parse_error(node, 'Bad integer attribute: %s' % e.message)
+ raise_parse_error(node, 'Bad integer attribute: %s' % e)
if self.ordinal_position <= 0:
raise_parse_error(node, 'Invalid PositiveInteger')
def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
@@ -19346,7 +19346,7 @@ def buildChildren(self, child_, node, nodeName_, fromsubclass_=False):
try:
ival_ = int(sval_)
except (TypeError, ValueError) as e:
- raise_parse_error(child_, 'requires integer: %s' % e.message)
+ raise_parse_error(child_, 'requires integer: %s' % e)
ival_ = self.gds_validate_integer(ival_, node, 'Ordinal')
self.Ordinal = ival_
# end class Imported_FunctionType
View
8 modules/machinemanagers/virtualbox.py
@@ -43,7 +43,7 @@ def start(self, label):
stderr=subprocess.PIPE)
except OSError as e:
raise CuckooMachineError("VBoxManage failed starting the machine in %s mode: %s"
- % (mode.upper(), e.message))
+ % (mode.upper(), e))
def stop(self, label):
"""Stops a virtual machine.
@@ -56,7 +56,7 @@ def stop(self, label):
stderr=subprocess.PIPE):
raise CuckooMachineError("VBoxManage exited with error powering off the machine")
except OSError as e:
- raise CuckooMachineError("VBoxManage failed powering off the machine: %s" % e.message)
+ raise CuckooMachineError("VBoxManage failed powering off the machine: %s" % e)
time.sleep(3)
@@ -66,7 +66,7 @@ def stop(self, label):
stderr=subprocess.PIPE):
raise CuckooMachineError("VBoxManage exited with error restoring the machine's snapshot")
except OSError as e:
- raise CuckooMachineError("VBoxManage failed restoring the machine: %s" % e.message)
+ raise CuckooMachineError("VBoxManage failed restoring the machine: %s" % e)
def _list(self):
"""Lists virtual machines installed.
@@ -78,7 +78,7 @@ def _list(self):
stderr=subprocess.PIPE)
output = proc.communicate()
except OSError as e:
- raise CuckooMachineError("VBoxManage error listing installed machines: %s" % e.message)
+ raise CuckooMachineError("VBoxManage error listing installed machines: %s" % e)
machines = []
for line in output[0].split("\n"):
View
56 modules/processing/behavior.py
@@ -43,7 +43,7 @@ def _parse(self, row):
status_value = row[7] # Success or Failure?
return_value = row[8] # Value returned by the function.
except IndexError as e:
- log.debug("Unable to parse process log row: %s" % e.message)
+ log.debug("Unable to parse process log row: %s" % e)
return False
if not self.process_id:
@@ -67,11 +67,11 @@ def _parse(self, row):
try:
(arg_name, arg_value) = row[index].split("->")
except ValueError as e:
- log.debug("Unable to parse analysis row argument (row=%s): %s" % (row[index], e.message))
+ log.debug("Unable to parse analysis row argument (row=%s): %s" % (row[index], e))
continue
argument["name"] = arg_name
- argument["value"] = convert_to_printable(arg_value)
+ argument["value"] = convert_to_printable(arg_value).lstrip("\\??\\")
arguments.append(argument)
call["timestamp"] = timestamp
@@ -112,7 +112,7 @@ def extract(self):
self._parse(row)
except csv.Error as e:
log.warning("Something went wrong while parsing analysis log: %s"
- % e.message)
+ % e)
return True
@@ -195,15 +195,47 @@ def _gen_keys(self):
"""
keys = []
- for entry in self.proc_results:
- for call in entry["calls"]:
- if call["category"] == "registry":
- hKey = None
- lpSubKey = None
+ def _check_registry(handles, registry, subkey, handle):
+ for known_handle in handles:
+ if handle != 0 and handle == known_handle["handle"]:
+ return
+
+ name = ""
+ if registry == 0x80000000:
+ name = "HKEY_CLASSES_ROOT\\"
+ elif registry == 0x80000001:
+ name = "HKEY_CURRENT_USER\\"
+ elif registry == 0x80000002:
+ name = "HKEY_LOCAL_MACHINE\\"
+ else:
+ for known_handle in handles:
+ if registry == known_handle["handle"]:
+ name = known_handle["name"] + "\\"
+
+ handles.append({"handle" : handle, "name" : name + subkey})
+
+ for process in self.proc_results:
+ handles = []
+
+ for call in process["calls"]:
+ if call["api"].startswith("RegOpenKeyEx"):
+ registry = 0
+ subkey = ""
+ handle = 0
+
for argument in call["arguments"]:
- if argument["name"] == "SubKey":
- if argument["value"] not in keys:
- keys.append(argument["value"])
+ if argument["name"] == "Registry":
+ registry = int(argument["value"], 16)
+ elif argument["name"] == "SubKey":
+ subkey = argument["value"]
+ elif argument["name"] == "Handle":
+ handle = int(argument["value"], 16)
+
+ _check_registry(handles, registry, subkey, handle)
+
+ for handle in handles:
+ if handle["name"] not in keys:
+ keys.append(handle["name"])
return keys
View
2  modules/processing/virustotal.py
@@ -34,7 +34,7 @@ def run(self):
try:
md5 = File(self.file_path).get_md5()
except IOError as e:
- raise CuckooProcessingError("Unable to open \"%s\": %s" % (self.file_path, e.message))
+ raise CuckooProcessingError("Unable to open \"%s\": %s" % (self.file_path, e))
data = urllib.urlencode({"resource" : md5, "apikey" : VIRUSTOTAL_KEY})
View
2  modules/reporting/hpfclient.py
@@ -25,4 +25,4 @@ def run(self, results):
hpc.publish(self.options["channel"], json.dumps(results, sort_keys=False, indent=4))
hpc.close()
except hpfeeds.FeedException as e:
- raise CuckooReportError("Failed to publish on HPFeeds channel: %s" % e.message)
+ raise CuckooReportError("Failed to publish on HPFeeds channel: %s" % e)
View
2  modules/reporting/jsondump.py
@@ -21,4 +21,4 @@ def run(self, results):
report.write(json.dumps(results, sort_keys=False, indent=4))
report.close()
except (TypeError, IOError) as e:
- raise CuckooReportError("Failed to generate JSON report: %s" % e.message)
+ raise CuckooReportError("Failed to generate JSON report: %s" % e)
View
2  modules/reporting/maec11.py
@@ -380,4 +380,4 @@ def output(self):
self.m.export(report, 0, namespace_ = '', name_ = 'MAEC_Bundle', namespacedef_ = 'xsi:schemaLocation="http://maec.mitre.org/XMLSchema/maec-core-1 file:MAEC_v1.1.xsd"')
report.close()
except (TypeError, IOError) as e:
- raise CuckooReportError("Failed to generate MAEC 1.1 report: %s" % e.message)
+ raise CuckooReportError("Failed to generate MAEC 1.1 report: %s" % e)
View
2  modules/reporting/metadata.py
@@ -244,5 +244,5 @@ def output(self):
self.m.export(report, 0, namespace_ = '', namespacedef_ = 'xmlns="http://xml/metadataSharing.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xml/metadataSharing.xsd"')
report.close()
except (TypeError, IOError) as e:
- raise CuckooReportError("Failed to generate MAEC Metadata report: %s" % e.message)
+ raise CuckooReportError("Failed to generate MAEC Metadata report: %s" % e)
View
4 modules/reporting/mongodb.py
@@ -56,7 +56,7 @@ def run(self, results):
try:
drop = open(drop_file, 'r')
except IOError as e:
- raise CuckooReportError("Failed to read file %s: %s" % (drop_file, e.message))
+ raise CuckooReportError("Failed to read file %s: %s" % (drop_file, e))
try:
drop_id = self._fs.put(drop, filename=dropped["name"])
except FileExists:
@@ -73,7 +73,7 @@ def run(self, results):
try:
shot = File(shot_path)
except IOError as e:
- raise CuckooReportError("Failed to read screenshot %s: %s" % (shot_path, e.message))
+ raise CuckooReportError("Failed to read screenshot %s: %s" % (shot_path, e))
try:
shot_id = self._fs.put(shot.get_data(), filename=shot.get_name())
View
2  modules/reporting/pickled.py
@@ -19,4 +19,4 @@ def run(self, results):
try:
pickle.dump(results, open(os.path.join(self.reports_path, "report.pickle"), "w"), 2)
except (pickle.PickleError, IOError) as e:
- raise CuckooReportError("Failed to generate Pickle report: %s" % e.message)
+ raise CuckooReportError("Failed to generate Pickle report: %s" % e)
View
4 modules/reporting/reporthtml.py
@@ -62,13 +62,13 @@ def run(self, results):
try:
html = template.render(**results)
except Exception as e:
- raise CuckooReportError("Failed to generate HTML report: %s" % e.message)
+ raise CuckooReportError("Failed to generate HTML report: %s" % e)
try:
report = open(os.path.join(self.reports_path, "report.html"), "w")
report.write(html)
report.close()
except (TypeError, IOError) as e:
- raise CuckooReportError("Failed to generate HTML report: %s" % e.message)
+ raise CuckooReportError("Failed to generate HTML report: %s" % e)
return True
View
3  modules/signatures/creates_exe.py
@@ -8,8 +8,9 @@ class CreatesExe(Signature):
name = "creates_exe"
description = "Creates a Windows executable on the filesystem"
severity = 2
- category = ["generic"]
+ categories = ["generic"]
authors = ["Cuckoo Developers"]
+ minimum = "0.4"
def run(self, results):
for file_name in results["behavior"]["summary"]["files"]:
View
163 tests/abstracts_tests.py
@@ -0,0 +1,163 @@
+# 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 tempfile
+from nose.tools import assert_equals, raises
+
+import lib.cuckoo.common.abstracts as abstracts
+from lib.cuckoo.common.config import Config
+from lib.cuckoo.common.constants import CUCKOO_ROOT
+
+
+class TestDictionary:
+ def setUp(self):
+ self.d = abstracts.Dictionary()
+
+ def test_usage(self):
+ self.d.a = "foo"
+ assert_equals("foo", self.d.a)
+ self.d.a = "bar"
+ assert_equals("bar", self.d.a)
+
+ @raises(AttributeError)
+ def test_exception(self):
+ self.d.b.a
+
+class TestMachineManager:
+
+ CONF_EXAMPLE = """
+[kvm]
+machines = cxp
+[cxp]
+label = cxp-k
+platform = windows
+ip = 192.168.122.27
+"""
+
+ CONF_EXAMPLE_MISSING_VM = """
+[kvm]
+machines = cxp, missing
+[cxp]
+label = cxp-k
+platform = windows
+ip = 192.168.122.27
+"""
+
+ def setUp(self):
+ self.file = tempfile.mkstemp()[1]
+ self.m = abstracts.MachineManager()
+ self._load_conf(self.CONF_EXAMPLE)
+ self.m._initialize("kvm")
+
+ def _load_conf(self, conf):
+ """Loads a configuration from a string.
+ @param conf: configuration string.
+ """
+ f = open(self.file, "w")
+ f.write(conf)
+ f.close()
+ self.m.set_options(Config(self.file))
+
+ @raises(NotImplementedError)
+ def test_not_implemented_start(self):
+ self.m.start()
+
+ @raises(NotImplementedError)
+ def test_not_implemented_stop(self):
+ self.m.stop()
+
+ @raises(NotImplementedError)
+ def test_not_implemented_list(self):
+ self.m._list()
+
+ def test_initialize_machines_type(self):
+ assert isinstance(self.m.machines, list)
+
+ def test_initialize_machines_items(self):
+ assert_equals(1, len(self.m.machines))
+
+ def test_initialize_machines_equals(self):
+ right = {'ip': '192.168.122.27',
+ 'platform': 'windows',
+ 'locked': False,
+ 'id':'cxp',
+ 'label': 'cxp-k'}
+ match = True
+ for keys in self.m.machines[0]:
+ if self.m.machines[0][keys] != right[keys]:
+ match = False
+ assert match
+
+ def test_initialize_missing_machine(self):
+ self.m = abstracts.MachineManager()
+ self._load_conf(self.CONF_EXAMPLE_MISSING_VM)
+ self.m._initialize("kvm")
+ assert_equals(1, len(self.m.machines))
+
+ def test_availables(self):
+ assert isinstance(self.m.availables(), int)
+ assert_equals(1, self.m.availables())
+
+ def test_acquire_by_name(self):
+ machine = self.m.acquire(machine_id="cxp")
+ assert_equals(0, self.m.availables())
+ self.m.release(machine.label)
+ assert_equals(1, self.m.availables())
+
+ def test_acquire_by_platform(self):
+ machine = self.m.acquire(platform="windows")
+ assert_equals(0, self.m.availables())
+ self.m.release(machine.label)
+ assert_equals(1, self.m.availables())
+
+ def test_running(self):
+ assert_equals(0, len(self.m.running()))
+ machine = self.m.acquire()
+ assert_equals(1, len(self.m.running()))
+ assert_equals(machine, self.m.running()[0])
+ self.m.release(machine.label)
+ assert_equals(0, len(self.m.running()))
+
+ def tearDown(self):
+ os.remove(self.file)
+
+class TestProcessing:
+ def setUp(self):
+ self.p = abstracts.Processing()
+
+ @raises(NotImplementedError)
+ def test_not_implemented_run(self):
+ self.p.run()
+
+class TestSignature(object):
+ def setUp(self):
+ self.s = abstracts.Signature()
+
+ @raises(NotImplementedError)
+ def test_not_implemented_run(self):
+ self.s.run()
+