diff --git a/REQUIREMENTS b/REQUIREMENTS
index 6cda15a..b4e5ebc 100644
--- a/REQUIREMENTS
+++ b/REQUIREMENTS
@@ -19,3 +19,4 @@ pytesseract
SPARQLWrapper
domaintools_api
pygeoip
+bs4
diff --git a/misp_modules/modules/import_mod/__init__.py b/misp_modules/modules/import_mod/__init__.py
index fd5d539..6beeaa2 100644
--- a/misp_modules/modules/import_mod/__init__.py
+++ b/misp_modules/modules/import_mod/__init__.py
@@ -1,3 +1,4 @@
from . import _vmray
-__all__ = ['vmray_import', 'testimport', 'ocr', 'stiximport', 'cuckooimport', 'email_import', 'mispjson']
+__all__ = ['vmray_import', 'testimport', 'ocr', 'stiximport', 'cuckooimport',
+ 'email_import', 'mispjson', 'openiocimport']
diff --git a/misp_modules/modules/import_mod/openiocimport.py b/misp_modules/modules/import_mod/openiocimport.py
new file mode 100755
index 0000000..27ef3f9
--- /dev/null
+++ b/misp_modules/modules/import_mod/openiocimport.py
@@ -0,0 +1,58 @@
+import json
+import base64
+
+from pymisp.tools import openioc
+
+misperrors = {'error': 'Error'}
+userConfig = {}
+inputSource = ['file']
+
+moduleinfo = {'version': '0.1', 'author': 'Raphaƫl Vinot',
+ 'description': 'Import OpenIOC package',
+ 'module-type': ['import']}
+
+moduleconfig = []
+
+
+def handler(q=False):
+ # Just in case we have no data
+ if q is False:
+ return False
+
+ # The return value
+ r = {'results': []}
+
+ # Load up that JSON
+ q = json.loads(q)
+
+ # It's b64 encoded, so decode that stuff
+ package = base64.b64decode(q.get("data")).decode('utf-8')
+
+ # If something really weird happened
+ if not package:
+ return json.dumps({"success": 0})
+
+ pkg = openioc.load_openioc(package)
+ for attrib in pkg.attributes:
+ r["results"].append({"values": [attrib.value], "types": [attrib.type], "categories": [attrib.category]})
+ return r
+
+
+def introspection():
+ modulesetup = {}
+ try:
+ userConfig
+ modulesetup['userConfig'] = userConfig
+ except NameError:
+ pass
+ try:
+ inputSource
+ modulesetup['inputSource'] = inputSource
+ except NameError:
+ pass
+ return modulesetup
+
+
+def version():
+ moduleinfo['config'] = moduleconfig
+ return moduleinfo
diff --git a/tests/openioc.xml b/tests/openioc.xml
new file mode 100644
index 0000000..dc7858c
--- /dev/null
+++ b/tests/openioc.xml
@@ -0,0 +1,91 @@
+
+
+ STUXNET VIRUS (METHODOLOGY)
+ Generic indicator for the stuxnet virus. When loaded, stuxnet spawns lsass.exe in a suspended state. The malware then maps in its own executable section and fixes up the CONTEXT to point to the newly mapped in section. This is a common task performed by malware and allows the malware to execute under the pretense of a known and trusted process.
+ methodology
+ Mandiant
+ 0001-01-01T00:00:00
+
+
+
+
+
+ .stub
+
+
+
+ mdmcpq3.PNF
+
+
+
+ mdmeric3.PNF
+
+
+
+ oem6C.PNF
+
+
+
+ oem7A.PNF
+
+
+
+
+ fs_rec.sys
+
+
+
+ mrxsmb.sys
+
+
+
+ sr.sys
+
+
+
+ fastfat.sys
+
+
+
+
+
+ mrxcls.sys
+
+
+
+ Realtek Semiconductor Corp
+
+
+
+
+
+ mrxnet.sys
+
+
+
+ Realtek Semiconductor Corp
+
+
+
+
+
+ HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\MRxCls\ImagePath
+
+
+
+ mrxcls.sys
+
+
+
+
+
+ HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\MRxNet\ImagePath
+
+
+
+ mrxnet.sys
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/test.py b/tests/test.py
index a94bbdf..156846e 100644
--- a/tests/test.py
+++ b/tests/test.py
@@ -41,6 +41,20 @@ class TestModules(unittest.TestCase):
print(response.json())
response.connection.close()
+ def test_openioc(self):
+ with open("tests/openioc.xml", "rb") as f:
+ content = base64.b64encode(f.read())
+ data = json.dumps({"module": "openiocimport",
+ "data": content.decode(),
+ })
+ response = requests.post(self.url + "query", data=data).json()
+ print(response)
+
+ print("OpenIOC :: {}".format(response))
+ values = [x["values"][0] for x in response["results"]]
+ assert("mrxcls.sys" in values)
+ assert("mdmcpq3.PNF" in values)
+
def test_stix(self):
with open("tests/stix.xml", "rb") as f:
content = base64.b64encode(f.read())
@@ -57,7 +71,7 @@ class TestModules(unittest.TestCase):
assert("eu-society.com" in values)
def test_email_headers(self):
- query = {"module":"email_import"}
+ query = {"module": "email_import"}
query["config"] = {"unzip_attachments": None,
"guess_zip_attachment_passwords": None,
"extract_urls": None}
@@ -105,7 +119,7 @@ class TestModules(unittest.TestCase):
self.assertIn("", values)
def test_email_attachment_basic(self):
- query = {"module":"email_import"}
+ query = {"module": "email_import"}
query["config"] = {"unzip_attachments": None,
"guess_zip_attachment_passwords": None,
"extract_urls": None}
@@ -128,9 +142,8 @@ class TestModules(unittest.TestCase):
attch_data = base64.b64decode(i["data"])
self.assertEqual(attch_data, b'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-')
-
def test_email_attachment_unpack(self):
- query = {"module":"email_import"}
+ query = {"module": "email_import"}
query["config"] = {"unzip_attachments": "true",
"guess_zip_attachment_passwords": None,
"extract_urls": None}
@@ -162,7 +175,7 @@ class TestModules(unittest.TestCase):
def test_email_dont_unpack_compressed_doc_attachments(self):
"""Ensures that compressed
"""
- query = {"module":"email_import"}
+ query = {"module": "email_import"}
query["config"] = {"unzip_attachments": "true",
"guess_zip_attachment_passwords": None,
"extract_urls": None}
@@ -192,9 +205,8 @@ class TestModules(unittest.TestCase):
self.assertEqual(filesum.hexdigest(),
'098da5381a90d4a51e6b844c18a0fecf2e364813c2f8b317cfdc51c21f2506a5')
-
def test_email_attachment_unpack_with_password(self):
- query = {"module":"email_import"}
+ query = {"module": "email_import"}
query["config"] = {"unzip_attachments": "true",
"guess_zip_attachment_passwords": 'true',
"extract_urls": None}
@@ -221,9 +233,8 @@ class TestModules(unittest.TestCase):
self.assertEqual(attch_data,
b'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-')
-
def test_email_attachment_password_in_body(self):
- query = {"module":"email_import"}
+ query = {"module": "email_import"}
query["config"] = {"unzip_attachments": "true",
"guess_zip_attachment_passwords": 'true',
"extract_urls": None}
@@ -246,7 +257,7 @@ class TestModules(unittest.TestCase):
'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-')
def test_email_attachment_password_in_body_quotes(self):
- query = {"module":"email_import"}
+ query = {"module": "email_import"}
query["config"] = {"unzip_attachments": "true",
"guess_zip_attachment_passwords": 'true',
"extract_urls": None}
@@ -274,7 +285,7 @@ class TestModules(unittest.TestCase):
'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-')
def test_email_attachment_password_in_html_body(self):
- query = {"module":"email_import"}
+ query = {"module": "email_import"}
query["config"] = {"unzip_attachments": "true",
"guess_zip_attachment_passwords": 'true',
"extract_urls": None}
@@ -304,7 +315,7 @@ class TestModules(unittest.TestCase):
query['data'] = decode_email(message)
data = json.dumps(query)
response = requests.post(self.url + "query", data=data)
- #print(response.json())
+ # print(response.json())
values = [x["values"] for x in response.json()["results"]]
self.assertIn('EICAR.com', values)
for i in response.json()['results']:
@@ -315,7 +326,7 @@ class TestModules(unittest.TestCase):
'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-')
def test_email_attachment_password_in_subject(self):
- query = {"module":"email_import"}
+ query = {"module": "email_import"}
query["config"] = {"unzip_attachments": "true",
"guess_zip_attachment_passwords": 'true',
"extract_urls": None}
@@ -344,9 +355,8 @@ class TestModules(unittest.TestCase):
self.assertEqual(attch_data,
'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-')
-
def test_email_extract_html_body_urls(self):
- query = {"module":"email_import"}
+ query = {"module": "email_import"}
query["config"] = {"unzip_attachments": None,
"guess_zip_attachment_passwords": None,
"extract_urls": "true"}
@@ -374,12 +384,12 @@ without modifying core components. The API is available via a simple REST API wh
query['data'] = decode_email(message)
data = json.dumps(query)
response = requests.post(self.url + "query", data=data)
- #print(response.json())
+ # print(response.json())
values = [x["values"] for x in response.json()["results"]]
self.assertIn("https://github.com/MISP/MISP", values)
self.assertIn("https://www.circl.lu/assets/files/misp-training/3.1-MISP-modules.pdf", values)
- #def test_domaintools(self):
+ # def test_domaintools(self):
# query = {'config': {'username': 'test_user', 'api_key': 'test_key'}, 'module': 'domaintools', 'domain': 'domaintools.com'}
# try:
# response = requests.post(self.url + "query", data=json.dumps(query)).json()
@@ -388,33 +398,34 @@ without modifying core components. The API is available via a simple REST API wh
# response = requests.post(self.url + "query", data=json.dumps(query)).json()
# print(response)
+
def decode_email(message):
message64 = base64.b64encode(message.as_bytes()).decode()
return message64
def get_base_email():
- headers = {"Received":"via dmail-2008.19 for +INBOX; Tue, 3 Feb 2009 19:29:12 -0600 (CST)",
- "Received":"from abc.luxsci.com ([10.10.10.10]) by xyz.luxsci.com (8.13.7/8.13.7) with ESMTP id n141TCa7022588 for ; Tue, 3 Feb 2009 19:29:12 -0600",
- "Received":"from [192.168.0.3] (verizon.net [44.44.44.44]) (user=test@sender.com mech=PLAIN bits=2) by abc.luxsci.com (8.13.7/8.13.7) with ESMTP id n141SAfo021855 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NOT) for ; Tue, 3 Feb 2009 19:28:10 -0600",
- "X-Received":"by 192.168.0.45 with SMTP id q4mr156123401yw1g.911.1912342394963; Tue, 3 Feb 2009 19:32:15 -0600 (PST)",
- "Message-ID":"<4988EF2D.40804@example.com>",
- "Date":"Tue, 03 Feb 2009 20:28:13 -0500",
- "From":'"Innocent Person" ',
- "User-Agent":'Thunderbird 2.0.0.19 (Windows/20081209)',
- "Sender":'"Malicious MailAgent" ',
- "References":"",
- "In-Reply-To":"",
- "Accept-Language":'en-US',
- "X-Mailer":'mlx 5.1.7',
+ headers = {"Received": "via dmail-2008.19 for +INBOX; Tue, 3 Feb 2009 19:29:12 -0600 (CST)",
+ "Received": "from abc.luxsci.com ([10.10.10.10]) by xyz.luxsci.com (8.13.7/8.13.7) with ESMTP id n141TCa7022588 for ; Tue, 3 Feb 2009 19:29:12 -0600",
+ "Received": "from [192.168.0.3] (verizon.net [44.44.44.44]) (user=test@sender.com mech=PLAIN bits=2) by abc.luxsci.com (8.13.7/8.13.7) with ESMTP id n141SAfo021855 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NOT) for ; Tue, 3 Feb 2009 19:28:10 -0600",
+ "X-Received": "by 192.168.0.45 with SMTP id q4mr156123401yw1g.911.1912342394963; Tue, 3 Feb 2009 19:32:15 -0600 (PST)",
+ "Message-ID": "<4988EF2D.40804@example.com>",
+ "Date": "Tue, 03 Feb 2009 20:28:13 -0500",
+ "From": '"Innocent Person" ',
+ "User-Agent": 'Thunderbird 2.0.0.19 (Windows/20081209)',
+ "Sender": '"Malicious MailAgent" ',
+ "References": "",
+ "In-Reply-To": "",
+ "Accept-Language": 'en-US',
+ "X-Mailer": 'mlx 5.1.7',
"Return-Path": "evil_spoofer@example.com",
- "Thread-Topic":'This is a thread.',
- "Thread-Index":'AQHSR8Us3H3SoaY1oUy9AAwZfMF922bnA9GAgAAi9s4AAGvxAA==',
- "Content-Language":'en-US',
- "To":'"Testy Testerson" ',
- "Cc":'"Second Person" , "Other Friend" , "Last One" ',
- "Subject":'Example Message',
- "MIME-Version":'1.0'}
+ "Thread-Topic": 'This is a thread.',
+ "Thread-Index": 'AQHSR8Us3H3SoaY1oUy9AAwZfMF922bnA9GAgAAi9s4AAGvxAA==',
+ "Content-Language": 'en-US',
+ "To": '"Testy Testerson" ',
+ "Cc": '"Second Person" , "Other Friend" , "Last One" ',
+ "Subject": 'Example Message',
+ "MIME-Version": '1.0'}
msg = MIMEMultipart()
for key, val in headers.items():
msg.add_header(key, val)