Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
gaelmuller committed Feb 2, 2017
1 parent 244419c commit 6afea2f
Show file tree
Hide file tree
Showing 58 changed files with 2,668 additions and 0 deletions.
52 changes: 52 additions & 0 deletions .gitignore
@@ -0,0 +1,52 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover

# Translations
*.mo
*.pot

# Django stuff:
*.log

Empty file added __init__.py
Empty file.
Empty file added antivirus/__init__.py
Empty file.
79 changes: 79 additions & 0 deletions antivirus/mail_submission.py
@@ -0,0 +1,79 @@
import subprocess
from os import path, remove
from uuid import uuid4
from distutils.spawn import find_executable

from fame.common.email_utils import EmailMixin, EmailServer
from fame.common.config import fame_config
from fame.core.module import AntivirusModule, ModuleInitializationError


class MailSubmission(EmailMixin, AntivirusModule):
"""Abstract class for Antivirus submission modules based on emails
Inherit from this class to easily create email-based antivirus submission
modules. Using this module as the parent class enables the creation of
modules as simple as::
class McAfee(MailSubmission):
name = "McAfee"
mail_submission = "virus_research@mcafee.com"
Only two attributes are mandatory: the classical :attr:`name`, and
:attr:`mail_submission`.
This module will automatically create a password protected zip file and
send it by email to the antivirus vendor.
Attributes:
mail_submission (string): email address to send the sample to.
password (string): password to use when creating the encrypted zip file.
Default value: ``infected``.
mail_subject (string): subject to use in the email sent to the vendor.
Default value: ``Sample submitted for analysis``.
"""

password = "infected"
mail_subject = "Sample submitted for analysis"

config = [
{
'name': 'mail_template',
'type': 'text',
'default': """Hello,
We have detected a new sample which is not detected by your engine. Therefore, as a customer we are sending you the suspected binary.
The binary is attached to this mail within a zip file encrypted with the password '{}'.
The threat is considered as an emergency for us, so we would appreciate if you could analyze the binary as soon as possible.
In the case your analysis would lead to the conclusion that the file is truly a malware, we would really appreciate you to add its signature to your database.
Thank you for your kind help.
Best regards""",
'description': 'Content of the email that will be sent to the antivirus vendor. You can include "{}" that will be replaced by the encryption password.'
},
]

def initialize(self):
if find_executable("7z") is None:
raise ModuleInitializationError(self, "Missing dependency: 7z")

return True

def submit(self, file):
archive_name = "sample_{}.zip".format(uuid4())
archive_file = path.join(fame_config.temp_path, archive_name)
subprocess.call(["7z", "a", "-tzip", "-p{}".format(self.password), archive_file, file])

server = EmailServer()
msg = server.new_message(self.mail_subject, self.mail_template.format(self.password))
msg.add_attachment(archive_file, archive_name)
msg.send([self.mail_submission])

remove(archive_file)

return True
8 changes: 8 additions & 0 deletions antivirus/mcafee.py
@@ -0,0 +1,8 @@
from .mail_submission import MailSubmission


class McAfee(MailSubmission):
name = "McAfee"
description = "Submit the file to McAfee for inclusion in detections."

mail_submission = "virus_research@mcafee.com"
9 changes: 9 additions & 0 deletions antivirus/sophos.py
@@ -0,0 +1,9 @@
from .mail_submission import MailSubmission


class Sophos(MailSubmission):
name = "Sophos"
description = "Submit the file to Sophos for inclusion in detections."

mail_submission = "samples@sophos.com"
mail_subject = "Sample submitted for analysis - Reply needed"
75 changes: 75 additions & 0 deletions antivirus/symantec.py
@@ -0,0 +1,75 @@
import os
import mimetypes

from fame.core.module import AntivirusModule, ModuleInitializationError


try:
import requests
HAVE_REQUESTS = True
except ImportError:
HAVE_REQUESTS = False


class Symantec(AntivirusModule):
name = "Symantec"
description = "Submit the file to Symantec for inclusion in detections."

submission_url = "https://submit.symantec.com/websubmit/bcs.cgi"

config = [
{
'name': 'support_id',
'type': 'str',
'description': 'Symantec support ID'
},
{
'name': 'email_address',
'type': 'str',
'description': 'Your email address'
},
{
'name': 'first_name',
'type': 'str',
'description': 'Your first name'
},
{
'name': 'last_name',
'type': 'str',
'description': 'Your last name'
},
{
'name': 'company',
'type': 'str',
'description': 'The name of your company'
},
]

def initialize(self):
if not HAVE_REQUESTS:
raise ModuleInitializationError(self, "Missing dependency: requests")

return True

def submit(self, file):
s = requests.Session()
s.get(self.submission_url)
filename = os.path.basename(file)

with open(file, 'r') as f:
params = {
'mode': "2",
'fname': self.first_name,
'lname': self.last_name,
'cname': self.company,
'email': self.email,
'email2': self.email,
'pin': self.support_id,
'stype': "upfile",
'comments': None
}
files = {
'upfile': (filename, f, mimetypes.guess_type(filename)[0] or 'application/octet-stream')
}

s.post(self.submission_url, data=params, files=files)
Empty file added processing/__init__.py
Empty file.
Empty file added processing/apk/__init__.py
Empty file.
46 changes: 46 additions & 0 deletions processing/apk/apk.py
@@ -0,0 +1,46 @@
from fame.core.module import ProcessingModule
from fame.common.exceptions import ModuleInitializationError
from .apk_plugins import *


try:
from androguard.misc import AnalyzeAPK, AnalyzeDex
HAVE_ANDROGUARD = True
except ImportError:
HAVE_ANDROGUARD = False


class APK(ProcessingModule):
name = "apk"
description = "Perform static analysis on APK/DEX files. Will also run static analysis modules trying to extract configuration from known Android malware."
acts_on = ["apk", "dex"]

def initialize(self):
if not HAVE_ANDROGUARD:
raise ModuleInitializationError(self, "Missing dependency: androguard")

def each(self, target):
self.results = dict()

try:
apk, vm, vm_analysis = AnalyzeAPK(target)

# First, get basic information about the APK
self.results['name'] = apk.get_app_name()
self.results['package'] = apk.get_package()
self.results['permissions'] = apk.get_permissions()
self.results['main_activity'] = apk.get_main_activity()
self.results['receivers'] = apk.get_receivers()
self.results['services'] = apk.get_services()
self.results['main_activity_content'] = vm.get_class("L{};".format(self.results['main_activity']).replace('.', '/')).get_source()
except:
apk = None
vm, vm_analysis = AnalyzeDex(target)
self.results['dex'] = True

# Then, run all the APK Plugins in order to see if this is a known malware
for plugin in APKPlugin.__subclasses__():
plugin = plugin(target, apk, vm, vm_analysis)
plugin.apply(self)

return True
59 changes: 59 additions & 0 deletions processing/apk/apk_plugins/__init__.py
@@ -0,0 +1,59 @@
import os
import glob
from zipfile import ZipFile


modules = glob.glob(os.path.dirname(__file__) + "/*.py")
__all__ = [os.path.basename(f)[:-3] for f in modules] + ['APKPlugin']


class APKPlugin(object):
"""Base class for plugins that try to perform extractions with Androguard
All plugins that inherits from this class and are located in the same
directory (`fame/modules/processing/apk_plugins`) will automatically be
executed by the `apk` processing module.
Plugins should define the
:func:`fame.modules.processing.apk_plugins.APKPlugin.run`
method.
Plugins can access instance variables ``self.apk``, ``self.vm`` and
``self.vm_analysis`` that are the result of Androguard's ``AnalyzeAPK``.
``self.zipfile`` is also available, containing the APK's ZipFile object.
Attributes:
name (string): Name of the plugin.
extraction (string): Label for the extraction if the module was
successful.
probable_name (string): Probable name of the malware if the module was
successful.
"""
name = None
extraction = None
probable_name = None

def __init__(self, target, apk, vm, vm_analysis):
self.apk = apk
self.vm = vm
self.vm_analysis = vm_analysis
if self.apk:
self.zipfile = ZipFile(target)
else:
self.zipfile = None

def apply(self, module):
extraction = self.run(module)
if extraction:
module.add_tag(self.name)
module.add_probable_name(self.probable_name)
module.add_extraction(self.extraction, extraction)

def run(self, module):
"""To implement. Perform some static analysis on the APK.
Returns:
Should return the extraction content if the module was successful,
``False`` otherwise."""
raise NotImplementedError
50 changes: 50 additions & 0 deletions processing/apk/apk_plugins/androrat.py
@@ -0,0 +1,50 @@
import json
from . import APKPlugin


class AndroRAT(APKPlugin):
name = "androrat"
extraction = "AndroRAT Configuration"
probable_name = "AndroRAT"

def run(self, module):
for cls in self.vm.get_classes():
if 'Lmy/app/client/ProcessCommand;'.lower() in cls.get_name().lower():
self.process_class = cls
break
else:
return None

c2Found = False
portFound = False
c2 = ""
port = ""
string = None
for method in self.process_class.get_methods():
if method.name == 'loadPreferences':
for inst in method.get_instructions():
if inst.get_name() == 'const-string':
string = inst.get_output().split(',')[-1].strip(" '")
if c2Found:
c2 = string
c2Found = False
if string == 'ip':
c2Found = True
if string == 'port':
portFound = True
if inst.get_name() == 'const/16':
if portFound:
string = inst.get_output().split(',')[-1].strip(" '")
port = string
if c2 and port:
break

server = ""
if port:
server = "{0}:{1}".format(c2, str(port))
else:
server = c2

module.add_ioc(server, ['androdat', 'c2'])

return json.dumps({'c2': server}, indent=2)

0 comments on commit 6afea2f

Please sign in to comment.