Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
244419c
commit 6afea2f
Showing
58 changed files
with
2,668 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
Oops, something went wrong.