PageRenderTime 35ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/cuckoo/core/startup.py

https://gitlab.com/hounge.mobile/cuckoo-android
Python | 342 lines | 224 code | 67 blank | 51 comment | 58 complexity | c8d97822b3d391e923c1f76e45d3f2ca MD5 | raw file
  1. # Copyright (C) 2010-2015 Cuckoo Foundation.
  2. # This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
  3. # See the file 'docs/LICENSE' for copying permission.
  4. import os
  5. import shutil
  6. import sys
  7. import copy
  8. import json
  9. import urllib
  10. import urllib2
  11. import logging
  12. import logging.handlers
  13. import pwd
  14. import modules.auxiliary
  15. import modules.processing
  16. import modules.signatures
  17. import modules.reporting
  18. from lib.cuckoo.common.colors import red, green, yellow, cyan
  19. from lib.cuckoo.common.config import Config
  20. from lib.cuckoo.common.constants import CUCKOO_ROOT, CUCKOO_VERSION
  21. from lib.cuckoo.common.exceptions import CuckooStartupError
  22. from lib.cuckoo.common.exceptions import CuckooOperationalError
  23. from lib.cuckoo.common.utils import create_folders
  24. from lib.cuckoo.core.database import Database, TASK_RUNNING, TASK_FAILED_ANALYSIS
  25. from lib.cuckoo.core.plugins import import_plugin, import_package, list_plugins
  26. log = logging.getLogger()
  27. def check_python_version():
  28. """Checks if Python version is supported by Cuckoo.
  29. @raise CuckooStartupError: if version is not supported.
  30. """
  31. if sys.version_info[:2] != (2, 7):
  32. raise CuckooStartupError("You are running an incompatible version "
  33. "of Python, please use 2.7")
  34. def check_working_directory():
  35. """Checks if working directories are ready.
  36. @raise CuckooStartupError: if directories are not properly configured.
  37. """
  38. if not os.path.exists(CUCKOO_ROOT):
  39. raise CuckooStartupError("You specified a non-existing root "
  40. "directory: {0}".format(CUCKOO_ROOT))
  41. cwd = os.path.join(os.getcwd(), "cuckoo.py")
  42. if not os.path.exists(cwd):
  43. raise CuckooStartupError("You are not running Cuckoo from it's "
  44. "root directory")
  45. def check_configs():
  46. """Checks if config files exist.
  47. @raise CuckooStartupError: if config files do not exist.
  48. """
  49. configs = [os.path.join(CUCKOO_ROOT, "conf", "cuckoo.conf"),
  50. os.path.join(CUCKOO_ROOT, "conf", "reporting.conf"),
  51. os.path.join(CUCKOO_ROOT, "conf", "auxiliary.conf")]
  52. for config in configs:
  53. if not os.path.exists(config):
  54. raise CuckooStartupError("Config file does not exist at "
  55. "path: {0}".format(config))
  56. return True
  57. def create_structure():
  58. """Creates Cuckoo directories."""
  59. folders = [
  60. "log",
  61. "storage",
  62. os.path.join("storage", "analyses"),
  63. os.path.join("storage", "binaries")
  64. ]
  65. try:
  66. create_folders(root=CUCKOO_ROOT, folders=folders)
  67. except CuckooOperationalError as e:
  68. raise CuckooStartupError(e)
  69. def check_version():
  70. """Checks version of Cuckoo."""
  71. cfg = Config()
  72. if not cfg.cuckoo.version_check:
  73. return
  74. print(" Checking for updates...")
  75. url = "http://api.cuckoosandbox.org/checkversion.php"
  76. data = urllib.urlencode({"version": CUCKOO_VERSION})
  77. try:
  78. request = urllib2.Request(url, data)
  79. response = urllib2.urlopen(request)
  80. except (urllib2.URLError, urllib2.HTTPError):
  81. print(red(" Failed! ") + "Unable to establish connection.\n")
  82. return
  83. try:
  84. response_data = json.loads(response.read())
  85. except ValueError:
  86. print(red(" Failed! ") + "Invalid response.\n")
  87. return
  88. if not response_data["error"]:
  89. if response_data["response"] == "NEW_VERSION":
  90. msg = "Cuckoo Sandbox version {0} is available " \
  91. "now.\n".format(response_data["current"])
  92. print(red(" Outdated! ") + msg)
  93. else:
  94. print(green(" Good! ") + "You have the latest version "
  95. "available.\n")
  96. class DatabaseHandler(logging.Handler):
  97. """Logging to database handler.
  98. Used to log errors related to tasks in database.
  99. """
  100. def emit(self, record):
  101. if hasattr(record, "task_id"):
  102. db = Database()
  103. db.add_error(record.msg, int(record.task_id))
  104. class ConsoleHandler(logging.StreamHandler):
  105. """Logging to console handler."""
  106. def emit(self, record):
  107. colored = copy.copy(record)
  108. if record.levelname == "WARNING":
  109. colored.msg = yellow(record.msg)
  110. elif record.levelname == "ERROR" or record.levelname == "CRITICAL":
  111. colored.msg = red(record.msg)
  112. else:
  113. if "analysis procedure completed" in record.msg:
  114. colored.msg = cyan(record.msg)
  115. else:
  116. colored.msg = record.msg
  117. logging.StreamHandler.emit(self, colored)
  118. def init_logging():
  119. """Initializes logging."""
  120. formatter = logging.Formatter("%(asctime)s [%(name)s] %(levelname)s: %(message)s")
  121. fh = logging.handlers.WatchedFileHandler(os.path.join(CUCKOO_ROOT, "log", "cuckoo.log"))
  122. fh.setFormatter(formatter)
  123. log.addHandler(fh)
  124. ch = ConsoleHandler()
  125. ch.setFormatter(formatter)
  126. log.addHandler(ch)
  127. dh = DatabaseHandler()
  128. dh.setLevel(logging.ERROR)
  129. log.addHandler(dh)
  130. log.setLevel(logging.INFO)
  131. def init_console_logging():
  132. """Initializes logging only to console."""
  133. formatter = logging.Formatter("%(asctime)s [%(name)s] %(levelname)s: %(message)s")
  134. ch = ConsoleHandler()
  135. ch.setFormatter(formatter)
  136. log.addHandler(ch)
  137. log.setLevel(logging.INFO)
  138. def init_tasks():
  139. """Check tasks and reschedule uncompleted ones."""
  140. db = Database()
  141. cfg = Config()
  142. log.debug("Checking for locked tasks...")
  143. tasks = db.list_tasks(status=TASK_RUNNING)
  144. for task in tasks:
  145. if cfg.cuckoo.reschedule:
  146. db.reschedule(task.id)
  147. log.info("Rescheduled task with ID {0} and "
  148. "target {1}".format(task.id, task.target))
  149. else:
  150. db.set_status(task.id, TASK_FAILED_ANALYSIS)
  151. log.info("Updated running task ID {0} status to failed_analysis".format(task.id))
  152. def init_modules():
  153. """Initializes plugins."""
  154. log.debug("Importing modules...")
  155. # Import all auxiliary modules.
  156. import_package(modules.auxiliary)
  157. # Import all processing modules.
  158. import_package(modules.processing)
  159. # Import all signatures.
  160. import_package(modules.signatures)
  161. # Import all reporting modules.
  162. import_package(modules.reporting)
  163. # Import machine manager.
  164. import_plugin("modules.machinery." + Config().cuckoo.machinery)
  165. for category, entries in list_plugins().items():
  166. log.debug("Imported \"%s\" modules:", category)
  167. for entry in entries:
  168. if entry == entries[-1]:
  169. log.debug("\t `-- %s", entry.__name__)
  170. else:
  171. log.debug("\t |-- %s", entry.__name__)
  172. def init_yara():
  173. """Generates index for yara signatures."""
  174. def find_signatures(root):
  175. signatures = []
  176. for entry in os.listdir(root):
  177. if entry.endswith(".yara") or entry.endswith(".yar"):
  178. signatures.append(os.path.join(root, entry))
  179. return signatures
  180. log.debug("Initializing Yara...")
  181. # Generate root directory for yara rules.
  182. yara_root = os.path.join(CUCKOO_ROOT, "data", "yara")
  183. # We divide yara rules in three categories.
  184. categories = ["binaries", "urls", "memory"]
  185. generated = []
  186. # Loop through all categories.
  187. for category in categories:
  188. # Check if there is a directory for the given category.
  189. category_root = os.path.join(yara_root, category)
  190. if not os.path.exists(category_root):
  191. continue
  192. # Check if the directory contains any rules.
  193. signatures = []
  194. for entry in os.listdir(category_root):
  195. if entry.endswith(".yara") or entry.endswith(".yar"):
  196. signatures.append(os.path.join(category_root, entry))
  197. if not signatures:
  198. continue
  199. # Generate path for the category's index file.
  200. index_name = "index_{0}.yar".format(category)
  201. index_path = os.path.join(yara_root, index_name)
  202. # Create index file and populate it.
  203. with open(index_path, "w") as index_handle:
  204. for signature in signatures:
  205. index_handle.write("include \"{0}\"\n".format(signature))
  206. generated.append(index_name)
  207. for entry in generated:
  208. if entry == generated[-1]:
  209. log.debug("\t `-- %s", entry)
  210. else:
  211. log.debug("\t |-- %s", entry)
  212. def cuckoo_clean():
  213. """Clean up cuckoo setup.
  214. It deletes logs, all stored data from file system and configured databases (SQL
  215. and MongoDB.
  216. """
  217. # Init logging.
  218. # This need to init a console logger handler, because the standard
  219. # logger (init_logging()) logs to a file which will be deleted.
  220. create_structure()
  221. init_console_logging()
  222. # Initialize the database connection.
  223. db = Database()
  224. # Drop all tables.
  225. db.drop()
  226. # Check if MongoDB reporting is enabled and drop that if it is.
  227. cfg = Config("reporting")
  228. if cfg.mongodb and cfg.mongodb.enabled:
  229. from pymongo import MongoClient
  230. host = cfg.mongodb.get("host", "127.0.0.1")
  231. port = cfg.mongodb.get("port", 27017)
  232. mdb = cfg.mongodb.get("db", "cuckoo")
  233. try:
  234. conn = MongoClient(host, port)
  235. conn.drop_database(mdb)
  236. conn.close()
  237. except:
  238. log.warning("Unable to drop MongoDB database: %s", mdb)
  239. # Paths to clean.
  240. paths = [
  241. os.path.join(CUCKOO_ROOT, "db"),
  242. os.path.join(CUCKOO_ROOT, "log"),
  243. os.path.join(CUCKOO_ROOT, "storage"),
  244. ]
  245. # Delete various directories.
  246. for path in paths:
  247. if os.path.isdir(path):
  248. try:
  249. shutil.rmtree(path)
  250. except (IOError, OSError) as e:
  251. log.warning("Error removing directory %s: %s", path, e)
  252. # Delete all compiled Python objects ("*.pyc").
  253. for dirpath, dirnames, filenames in os.walk(CUCKOO_ROOT):
  254. for fname in filenames:
  255. if not fname.endswith(".pyc"):
  256. continue
  257. path = os.path.join(CUCKOO_ROOT, dirpath, fname)
  258. try:
  259. os.unlink(path)
  260. except (IOError, OSError) as e:
  261. log.warning("Error removing file %s: %s", path, e)
  262. def drop_privileges(username):
  263. """Drops privileges to selected user.
  264. @param username: drop privileges to this username
  265. """
  266. try:
  267. user = pwd.getpwnam(username)
  268. os.setgroups((user.pw_gid,))
  269. os.setgid(user.pw_gid)
  270. os.setuid(user.pw_uid)
  271. os.putenv("HOME", user.pw_dir)
  272. except KeyError:
  273. sys.exit("Invalid user specified to drop privileges to: %s" % user)
  274. except OSError as e:
  275. sys.exit("Failed to drop privileges to %s: %s" % (username, e))