├── tmc ├── README.md ├── static │ ├── 404.jpg │ ├── TMCv1.png │ ├── glasses.png │ ├── no-data.png │ ├── custom.js │ └── style.css ├── __pycache__ │ ├── db.cpython-36.pyc │ ├── auth.cpython-36.pyc │ ├── blog.cpython-36.pyc │ ├── maps.cpython-36.pyc │ └── __init__.cpython-36.pyc ├── config.py ├── templates │ ├── maps │ │ ├── 404.html │ │ ├── no-data.html │ │ ├── completed.html │ │ ├── creation │ │ │ ├── create-subtechnique.html │ │ │ ├── create-technique.html │ │ │ ├── create-tactic.html │ │ │ ├── create-tool.html │ │ │ └── create-adversary.html │ │ ├── explore │ │ │ ├── main.html │ │ │ └── explore-element.html │ │ └── welcome.html │ ├── login.html │ ├── auth │ │ ├── login.html │ │ └── register.html │ └── index.html ├── queries │ ├── q_get_countries.py │ ├── q_insert_adversary_x_tool.py │ ├── q_insert_event_x_industry.py │ ├── q_insert_adversary_x_event.py │ ├── q_get_events.py │ ├── q_insert_tactic_x_technique.py │ ├── q_get_industries.py │ ├── q_insert_relation_into_tables.py │ ├── q_insert_into_events.py │ ├── q_insert_tool_x_techn.py │ ├── q_insert_tool_x_subtechn.py │ ├── q_get_element_id.py │ ├── q_get_adversaries_x_industry.py │ ├── q_get_techniques_per_industry.py │ ├── q_get_adversaries_x_sorigin.py │ ├── q_get_most_used_techniques.py │ ├── q_insert_into_tables.py │ ├── q_get_adversaries_sorigin.py │ ├── q_get_tactics.py │ ├── q_get_adversaries_x_event.py │ ├── q_get_tools.py │ ├── q_get_events_x_industry.py │ ├── q_get_techniques.py │ ├── q_get_adversaries_x_tool.py │ ├── q_get_adversaries.py │ ├── q_insert_adversary_into_tables.py │ ├── q_get_subtechniques.py │ ├── q_get_adversaries_x_technique.py │ ├── __init__.py │ ├── q_get_tools_techniques.py │ ├── q_get_tools_x_techniques.py │ ├── q_get_tools_x_subtechniques.py │ └── q_get_adversaries_techniques.py ├── db.py ├── __init__.py ├── auth.py ├── processor.py ├── schema.sql └── maps.py ├── MANIFEST.in ├── setup.cfg ├── requirements.txt ├── .gitignore ├── setup.py └── README.md /tmc/README.md: -------------------------------------------------------------------------------- 1 | # TMC 2 | Threat Mapping Catalogue 3 | -------------------------------------------------------------------------------- /tmc/static/404.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intelforge/tmc/HEAD/tmc/static/404.jpg -------------------------------------------------------------------------------- /tmc/static/TMCv1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intelforge/tmc/HEAD/tmc/static/TMCv1.png -------------------------------------------------------------------------------- /tmc/static/glasses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intelforge/tmc/HEAD/tmc/static/glasses.png -------------------------------------------------------------------------------- /tmc/static/no-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intelforge/tmc/HEAD/tmc/static/no-data.png -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include tmc/schema.sql 2 | graft tmc/static 3 | graft tmc/templates 4 | global-exclude *.pyc -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [tool:pytest] 2 | testpaths = tests 3 | 4 | [coverage:run] 5 | branch = True 6 | source = 7 | tmc -------------------------------------------------------------------------------- /tmc/__pycache__/db.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intelforge/tmc/HEAD/tmc/__pycache__/db.cpython-36.pyc -------------------------------------------------------------------------------- /tmc/__pycache__/auth.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intelforge/tmc/HEAD/tmc/__pycache__/auth.cpython-36.pyc -------------------------------------------------------------------------------- /tmc/__pycache__/blog.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intelforge/tmc/HEAD/tmc/__pycache__/blog.cpython-36.pyc -------------------------------------------------------------------------------- /tmc/__pycache__/maps.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intelforge/tmc/HEAD/tmc/__pycache__/maps.cpython-36.pyc -------------------------------------------------------------------------------- /tmc/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intelforge/tmc/HEAD/tmc/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask 2 | # functools 3 | click 4 | attackcti 5 | IPython 6 | six==1.15.0 7 | stix2==2.0.2 8 | taxii2-client==2.2.1 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.py[cod] 3 | *$py.class 4 | 5 | test/ 6 | 7 | instance/ 8 | *.sqlite 9 | 10 | export/ 11 | tmc/config.py -------------------------------------------------------------------------------- /tmc/config.py: -------------------------------------------------------------------------------- 1 | config_dict = { 2 | 3 | "tram": "http://localhost:9999", 4 | "navigator": "http://localhost:4200", 5 | "tmc": "http://localhost:5000" 6 | } 7 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_packages, setup 2 | 3 | setup( 4 | name='tmc', 5 | version='1.0.0', 6 | packages=find_packages(), 7 | include_package_data=True, 8 | zip_safe=False, 9 | install_requires=[ 10 | 'flask', 11 | ], 12 | ) -------------------------------------------------------------------------------- /tmc/templates/maps/404.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | 3 | {% block header %} 4 |

Sorry, we are experiencing some problems...

5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 | Error 404 10 |
11 | {% endblock %} -------------------------------------------------------------------------------- /tmc/templates/maps/no-data.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | 3 | {% block header %} 4 |

There's nothing here yet!
Try adding some data to the database first.

5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 | Nothing here yet 10 |
11 | {% endblock %} -------------------------------------------------------------------------------- /tmc/templates/maps/completed.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | 3 | {% block header %} 4 |

{% block title %}Thank you for your patience!{% endblock %}

5 |

{{ message }}

6 | {% if g.user %} 7 | 8 | 9 | {% endif %} 10 | {% endblock %} 11 | 12 | 13 | 14 | {% block content %} 15 | 16 | {% endblock %} -------------------------------------------------------------------------------- /tmc/queries/q_get_countries.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of all industries available in the database. 5 | def get_countries(): 6 | 7 | db = get_db() 8 | try: 9 | db.row_factory = lambda cursor, row: row[0] 10 | query = db.execute( 11 | 'SELECT country FROM countries ORDER BY country').fetchall() 12 | return query 13 | except TypeError: 14 | #embed() 15 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/queries/q_insert_adversary_x_tool.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db 3 | 4 | # Insert relation adversary per tool 5 | def insert_adversary_x_tool(adversary_id, tool_id): 6 | author_id = g.user['id'] 7 | 8 | g.db = get_db() 9 | query='INSERT INTO {} ({}, {}, {}) VALUES (?, ?, ?)'.format('adversaries_x_tools', 'author_id', 'adversary_id', 'tool_id') 10 | 11 | result = g.db.execute(query, (author_id, adversary_id, tool_id)) 12 | g.db.commit() 13 | element_id = result.lastrowid 14 | 15 | return element_id -------------------------------------------------------------------------------- /tmc/queries/q_insert_event_x_industry.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db 3 | 4 | # Insert relation adversary per tool 5 | def insert_event_x_industry(event_id, industry_id): 6 | author_id = g.user['id'] 7 | 8 | g.db = get_db() 9 | query='INSERT INTO {} ({}, {}, {}) VALUES (?, ?, ?)'.format('events_x_industries', 'author_id', 'event_id', 'industry_id') 10 | 11 | result = g.db.execute(query, (author_id, event_id, industry_id)) 12 | g.db.commit() 13 | element_id = result.lastrowid 14 | 15 | return element_id -------------------------------------------------------------------------------- /tmc/queries/q_insert_adversary_x_event.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db 3 | 4 | # Insert relation adversary per tool 5 | def insert_adversary_x_event(adversary_id, event_id): 6 | author_id = g.user['id'] 7 | 8 | g.db = get_db() 9 | query='INSERT INTO {} ({}, {}, {}) VALUES (?, ?, ?)'.format('adversaries_x_events', 'author_id', 'adversary_id', 'event_id') 10 | 11 | result = g.db.execute(query, (author_id, adversary_id, event_id)) 12 | g.db.commit() 13 | element_id = result.lastrowid 14 | 15 | return element_id -------------------------------------------------------------------------------- /tmc/queries/q_get_events.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of all events available in the database. 5 | def get_events(): 6 | 7 | db = get_db() 8 | try: 9 | db.row_factory = make_dicts 10 | query = db.execute( 11 | 'SELECT event_name as Event, event_description as Description, event_url as URL FROM events').fetchall() 12 | return query 13 | except TypeError: 14 | #embed() 15 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/queries/q_insert_tactic_x_technique.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db 3 | 4 | # Insert relation tactic_x_technique 5 | def insert_tactic_x_technique(tactic_id, technique_id): 6 | author_id = g.user['id'] 7 | 8 | g.db = get_db() 9 | query='INSERT INTO {} ({}, {}, {}) VALUES (?, ?, ?)'.format('tactics_x_techniques', 'author_id', 'tactic_id', 'technique_id') 10 | 11 | result = g.db.execute(query, (author_id, tactic_id, technique_id)) 12 | g.db.commit() 13 | element_id = result.lastrowid 14 | 15 | return element_id -------------------------------------------------------------------------------- /tmc/queries/q_get_industries.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of all industries available in the database. 5 | def get_industries(): 6 | 7 | db = get_db() 8 | try: 9 | db.row_factory = make_dicts 10 | query = db.execute( 11 | 'SELECT id as db_id, industry_name as Industry FROM industries ORDER BY industry_name ASC').fetchall() 12 | return query 13 | except TypeError: 14 | #embed() 15 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/queries/q_insert_relation_into_tables.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db 3 | 4 | # Isert relation into db from any table 5 | def insert_relation_into_tables(table, relation_name, element_name, related_id, element_id): 6 | 7 | author_id = g.user['id'] 8 | 9 | g.db = get_db() 10 | query='INSERT INTO {} ({}, {}, {}) VALUES (?, ?, ?)'.format(table, 'author_id', relation_name, element_name) 11 | 12 | result = g.db.execute(query, (author_id, related_id, element_id)) 13 | g.db.commit() 14 | element_id = result.lastrowid 15 | 16 | return element_id -------------------------------------------------------------------------------- /tmc/queries/q_insert_into_events.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db 3 | 4 | # Isert into db for any table 5 | def insert_into_events(event_name, event_description, event_url): 6 | 7 | author_id = g.user['id'] 8 | 9 | g.db = get_db() 10 | 11 | query='INSERT INTO events ({}, {}, {}, {}) VALUES (?, ?, ?, ?)'.format('author_id', 'event_name', 'event_description', 'event_url') 12 | 13 | result = g.db.execute(query, (author_id, event_name, event_description, event_url)) 14 | g.db.commit() 15 | element_id = result.lastrowid 16 | 17 | return element_id -------------------------------------------------------------------------------- /tmc/queries/q_insert_tool_x_techn.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db 3 | 4 | # Insert relation tool_x_technique 5 | def insert_tool_x_techn(table, tool_id, technique_id): 6 | try: 7 | author_id = g.user['id'] 8 | except (NameError, TypeError) as error: 9 | author_id = 1 10 | 11 | g.db = get_db() 12 | query='INSERT INTO {} ({}, {}, {}) VALUES (?, ?, ?)'.format(table, 'author_id', 'tool_id', 'technique_id') 13 | 14 | result = g.db.execute(query, (author_id, tool_id, technique_id)) 15 | g.db.commit() 16 | element_id = result.lastrowid 17 | 18 | return element_id 19 | -------------------------------------------------------------------------------- /tmc/queries/q_insert_tool_x_subtechn.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db 3 | 4 | # Insert relation tool_x_subtechnique 5 | def insert_tool_x_subtechn(table, tool_id, subtechnique_id): 6 | try: 7 | author_id = g.user['id'] 8 | except (NameError, TypeError) as error: 9 | author_id = 1 10 | 11 | g.db = get_db() 12 | query='INSERT INTO {} ({}, {}, {}) VALUES (?, ?, ?)'.format(table, 'author_id', 'tool_id', 'subtechnique_id') 13 | 14 | result = g.db.execute(query, (author_id, tool_id, subtechnique_id)) 15 | g.db.commit() 16 | element_id = result.lastrowid 17 | 18 | return element_id 19 | -------------------------------------------------------------------------------- /tmc/queries/q_get_element_id.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db 3 | 4 | # Get table element by ID 5 | def get_element_id(table, column, value): #FROM MOBILE, TECHNIQUE 'COMPROMISE' needs fixing 6 | 7 | value2 = value.replace('-', ' ').lower() 8 | 9 | db = get_db() 10 | try: 11 | query = db.execute( 12 | 'SELECT id FROM {} WHERE lower({}) is ?'.format(table, column), 13 | (value2,) 14 | ) 15 | result = query.fetchone() 16 | return result['id'] 17 | except TypeError: 18 | #embed() 19 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/queries/q_get_adversaries_x_industry.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of all adversaries per industry available in the database. 5 | def get_adversaries_x_industry(): 6 | 7 | db = get_db() 8 | try: 9 | db.row_factory = make_dicts 10 | #db.row_factory = lambda cursor, row: {row: row[0]} 11 | query = db.execute( 12 | 'SELECT adversary_id as ID, adversary_name as Name, adversary_identifiers as Identifiers, adversary_description as Description FROM adversaries ORDER BY Name').fetchall() 13 | return query 14 | except TypeError: 15 | #embed() 16 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/queries/q_get_techniques_per_industry.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of all techniques per industry available in the database. 5 | def get_techniques_per_industry(): 6 | 7 | db = get_db() 8 | try: 9 | db.row_factory = make_dicts 10 | #db.row_factory = lambda cursor, row: {row: row[0]} 11 | query = db.execute( 12 | 'SELECT adversary_id as ID, adversary_name as Name, adversary_identifiers as Identifiers, adversary_description as Description FROM adversaries ORDER BY Name').fetchall() 13 | return query 14 | except TypeError: 15 | #embed() 16 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/queries/q_get_adversaries_x_sorigin.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of all adversaries per suspected origin available in the database. 5 | def get_adversaries_x_sorigin(): 6 | 7 | db = get_db() 8 | try: 9 | db.row_factory = make_dicts 10 | #db.row_factory = lambda cursor, row: {row: row[0]} 11 | query = db.execute( 12 | 'SELECT adversary_id as ID, adversary_name as Name, adversary_identifiers as Identifiers, adversary_description as Description FROM adversaries ORDER BY Name').fetchall() 13 | return query 14 | except TypeError: 15 | #embed() 16 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/templates/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | 3 | {% block header %} 4 |

{% block title %}Log In{% endblock %}

5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 |
10 |
11 | 12 | 13 |
14 | 15 |
16 | 17 | 18 |
19 | 20 |
21 | 22 |
23 |
24 |
25 | {% endblock %} -------------------------------------------------------------------------------- /tmc/queries/q_get_most_used_techniques.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of most used techniques 5 | def get_most_used_techniques(): 6 | 7 | db = get_db() 8 | try: 9 | db.row_factory = make_dicts 10 | query = db.execute( 11 | 'SELECT t.technique_id as \'TechniqueID\', t.technique_name as Technique, count(*) as Hits FROM techniques t \ 12 | inner join tools_x_techniques txt on txt.technique_id=t.id \ 13 | GROUP by t.technique_name \ 14 | ORDER BY Hits Desc').fetchall() 15 | return query 16 | except TypeError: 17 | #embed() 18 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/queries/q_insert_into_tables.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db 3 | 4 | # Isert into db for any table 5 | def insert_into_tables(table, element_id, element_name, element_description): 6 | 7 | table_id = table[:-1] + '_id' 8 | table_name = table[:-1] + '_name' 9 | table_description = table[:-1] + '_description' 10 | 11 | author_id = g.user['id'] 12 | 13 | g.db = get_db() 14 | 15 | query='INSERT INTO {} ({}, {}, {}, {}) VALUES (?, ?, ?, ?)'.format(table, 'author_id', table_id, table_name, table_description) 16 | 17 | result = g.db.execute(query, (author_id, element_id, element_name, element_description)) 18 | g.db.commit() 19 | element_id = result.lastrowid 20 | 21 | return element_id -------------------------------------------------------------------------------- /tmc/templates/auth/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | 3 | {% block header %} 4 |

{% block title %}Log In{% endblock %}

5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 |
10 |
11 | 12 | 13 |
14 | 15 |
16 | 17 | 18 |
19 | 20 |
21 | 22 |
23 |
24 |
25 | {% endblock %} -------------------------------------------------------------------------------- /tmc/templates/auth/register.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | 3 | {% block header %} 4 |

{% block title %}New Usuer{% endblock %}

5 | {% endblock %} 6 | 7 | {% block content %} 8 |
9 |
10 |
11 | 12 | 13 |
14 | 15 |
16 | 17 | 18 |
19 | 20 |
21 | 22 |
23 |
24 |
25 | {% endblock %} -------------------------------------------------------------------------------- /tmc/queries/q_get_adversaries_sorigin.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of all adversaries available in the database. 5 | def get_adversaries_sorigin(): 6 | 7 | db = get_db() 8 | try: 9 | db.row_factory = make_dicts 10 | #db.row_factory = lambda cursor, row: {row: row[0]} 11 | query = db.execute( 12 | 'SELECT adversary_sorigin as \'Suspected Origin\', GROUP_CONCAT(adversary_name) as Adversary \ 13 | FROM adversaries \ 14 | where adversary_sorigin is not null \ 15 | GROUP BY adversary_sorigin;').fetchall() 16 | return query 17 | except TypeError: 18 | #embed() 19 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/queries/q_get_tactics.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of all techniques available in the database. 5 | def get_tactics(tactic=''): 6 | db = get_db() 7 | db.row_factory = make_dicts 8 | try: 9 | if not tactic: 10 | query = db.execute( 11 | 'SELECT id as \'db_id\', tactic_id as ID, tactic_name as Tactic, tactic_description as Description FROM tactics ORDER BY tactic_name ASC').fetchall() 12 | return query 13 | else: 14 | query = db.execute( 'SELECT * FROM tactics WHERE id is ?', 15 | (tactic,) 16 | ).fetchone() 17 | return query 18 | 19 | except TypeError: 20 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/queries/q_get_adversaries_x_event.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of all adversaries per event available in the database. 5 | def get_adversaries_x_event(): 6 | 7 | db = get_db() 8 | try: 9 | db.row_factory = make_dicts 10 | #db.row_factory = lambda cursor, row: {row: row[0]} 11 | query = db.execute( 12 | 'select a.adversary_id, a.adversary_name, event_name, event_description from events e \ 13 | inner join adversaries_x_events ae on ae.event_id = e.id \ 14 | inner join adversaries a on a.id = ae.adversary_id ORDER BY adversary_name').fetchall() 15 | return query 16 | except TypeError: 17 | #embed() 18 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/queries/q_get_tools.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of all tools available in the database. 5 | def get_tools(tool=''): 6 | db = get_db() 7 | db.row_factory = make_dicts 8 | try: 9 | if not tool: 10 | query = db.execute( 11 | 'SELECT id as \'db_id\', tool_id as ID, tool_name as Tool, tool_description as Description, tool_identifiers as Identifiers FROM tools ORDER BY tool_name').fetchall() 12 | return query 13 | else: 14 | query = db.execute( 'SELECT * FROM tools WHERE id is ?', 15 | (tool,) 16 | ).fetchone() 17 | return query 18 | except TypeError: 19 | #embed() 20 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/queries/q_get_events_x_industry.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of all events per industry available in the database. 5 | def get_events_x_industry(): 6 | 7 | db = get_db() 8 | db.row_factory = make_dicts 9 | try: 10 | query = db.execute( 11 | 'select a.adversary_name, i.industry_name, e.event_name from events e \ 12 | inner join events_x_industries ei on e.id = ei.event_id \ 13 | inner join industries i on i.id = ei.industry_id \ 14 | inner join adversaries_x_events ae on ae.event_id = e.id \ 15 | inner join adversaries a on a.id = ae.adversary_id').fetchall() 16 | return query 17 | except TypeError: 18 | #embed() 19 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/queries/q_get_techniques.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of all techniques available in the database. 5 | def get_techniques(technique=''): 6 | db = get_db() 7 | db.row_factory = make_dicts 8 | try: 9 | if not technique: 10 | query = db.execute( 11 | 'SELECT id as \'db_id\', technique_id as ID, technique_name as Technique, technique_description as Description FROM techniques ORDER BY technique_name ASC').fetchall() 12 | return query 13 | else: 14 | query = db.execute( 'SELECT * FROM techniques WHERE id is ?', 15 | (technique,) 16 | ).fetchone() 17 | return query 18 | 19 | except TypeError: 20 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/queries/q_get_adversaries_x_tool.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of all adversaries per tool available in the database. 5 | def get_adversaries_x_tool(): 6 | 7 | db = get_db() 8 | try: 9 | db.row_factory = make_dicts 10 | query = db.execute( 11 | "SELECT a.adversary_id As \'Adversary ID\', a.adversary_name as Adversary, t.tool_id as \'Tool ID\', t.tool_name as Tool \ 12 | FROM adversaries a \ 13 | inner join adversaries_x_tools axt on axt.adversary_id=a.id \ 14 | inner join tools t on axt.tool_id=t.id \ 15 | ORDER BY a.adversary_name").fetchall() 16 | return query 17 | except TypeError: 18 | #embed() 19 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/queries/q_get_adversaries.py: -------------------------------------------------------------------------------- 1 | from flask import ( g ) 2 | from tmc.db import get_db, make_dicts 3 | from IPython import embed 4 | 5 | # Get list of all adversaries available in the database. 6 | def get_adversaries(adversary=''): 7 | 8 | db = get_db() 9 | db.row_factory = make_dicts 10 | try: 11 | if not adversary: 12 | query = db.execute('SELECT id as db_id, adversary_id as ID, adversary_name as Adversary, adversary_identifiers as Identifiers, adversary_description as Description \ 13 | FROM adversaries ORDER BY adversary_name ASC').fetchall() 14 | return query 15 | else: 16 | query = db.execute( 17 | 'SELECT * FROM adversaries WHERE id is ?', 18 | (adversary,) 19 | ).fetchone() 20 | return query 21 | except TypeError: 22 | return False -------------------------------------------------------------------------------- /tmc/queries/q_insert_adversary_into_tables.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db 3 | 4 | # Iserta adversary into db from table 5 | def insert_adversary_into_tables(table, element_id, element_name, element_description, element_identifiers): 6 | 7 | table_id = 'adversary_id' 8 | table_name = 'adversary_name' 9 | table_description = 'adversary_description' 10 | adversary_identifiers = 'adversary_identifiers' 11 | 12 | author_id = g.user['id'] 13 | 14 | g.db = get_db() 15 | query='INSERT INTO {} ({}, {}, {}, {}, {}) VALUES (?, ?, ?, ?, ?)'.format(table, 'author_id', table_id, table_name, table_description, adversary_identifiers) 16 | result = g.db.execute(query, (author_id, element_id, element_name, element_description, element_identifiers)) 17 | g.db.commit() 18 | element_id = result.lastrowid 19 | 20 | return element_id -------------------------------------------------------------------------------- /tmc/queries/q_get_subtechniques.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of all subtechniques available in the database. 5 | def get_subtechniques(subtechnique=''): 6 | db = get_db() 7 | db.row_factory = make_dicts 8 | try: 9 | if not subtechnique: 10 | query = db.execute( 11 | 'SELECT id as \'db_id\', subtechnique_id as ID, subtechnique_name as Subtechnique, subtechnique_description as Description FROM subtechniques ORDER BY subtechnique_name ASC').fetchall() 12 | return query 13 | else: 14 | query = db.execute( 'SELECT * FROM subtechniques WHERE id is ?', 15 | (subtechnique,) 16 | ).fetchone() 17 | return query 18 | 19 | except TypeError: 20 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/db.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | import click 4 | from flask import current_app, g 5 | from flask.cli import with_appcontext 6 | 7 | 8 | def get_db(): 9 | if 'db' not in g: 10 | g.db = sqlite3.connect( 11 | current_app.config['DATABASE'], 12 | detect_types=sqlite3.PARSE_DECLTYPES 13 | ) 14 | g.db.row_factory = sqlite3.Row 15 | 16 | return g.db 17 | 18 | 19 | def close_db(e=None): 20 | db = g.pop('db', None) 21 | 22 | if db is not None: 23 | db.close() 24 | 25 | 26 | def init_db(): 27 | db = get_db() 28 | 29 | with current_app.open_resource('schema.sql') as f: 30 | db.executescript(f.read().decode('utf8')) 31 | 32 | 33 | @click.command('init-db') 34 | @with_appcontext 35 | def init_db_command(): 36 | """Clear the existing data and create new tables.""" 37 | init_db() 38 | click.echo('Initialized the database.') 39 | 40 | 41 | def init_app(app): 42 | app.teardown_appcontext(close_db) 43 | app.cli.add_command(init_db_command) 44 | 45 | def make_dicts(cursor, row): 46 | return dict((cursor.description[idx][0], value) 47 | for idx, value in enumerate(row)) -------------------------------------------------------------------------------- /tmc/queries/q_get_adversaries_x_technique.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of all adversaries per technique available in the database. 5 | def get_adversaries_x_technique(): 6 | 7 | db = get_db() 8 | try: 9 | db.row_factory = make_dicts 10 | query = db.execute( 11 | 'SELECT a.adversary_id As \'Adversary ID\', a.adversary_name as Adversary, t.technique_id as \'Technique ID\', t.technique_name as Technique, s.subtechnique_id as \'Subtechnique ID\',s.subtechnique_name as Subtechnique \ 12 | FROM adversaries a \ 13 | inner join adversaries_x_tools axt on axt.adversary_id=a.id \ 14 | inner join tools_x_techniques txt on txt.tool_id=axt.tool_id \ 15 | inner join tools_x_subtechniques txst on txst.tool_id=axt.tool_id \ 16 | inner join techniques t on t.id=txt.technique_id \ 17 | inner join subtechniques s on s.id=txst.subtechnique_id \ 18 | ORDER BY a.adversary_name ').fetchall() #LIMIT 100 OFFSET 200 19 | return query 20 | except TypeError: 21 | #embed() 22 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/queries/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["q_get_element_id", 2 | "q_insert_into_tables", 3 | "q_insert_into_events", 4 | "q_insert_adversary_x_event", 5 | "q_insert_adversary_x_tool", 6 | "q_insert_event_x_industry", 7 | "q_insert_tactic_x_technique", 8 | "q_insert_tool_x_techn", 9 | "q_get_tools_x_subtechniques", 10 | "q_insert_adversary_into_tables", 11 | "q_get_adversaries", 12 | "q_get_adversaries_sorigin", 13 | "q_get_adversaries_techniques", 14 | "q_get_tools_techniques", 15 | "q_get_countries", 16 | "q_get_industries", 17 | "q_get_events", 18 | "q_get_tools", 19 | "q_get_tactics", 20 | "q_get_techniques", 21 | "q_get_subtechniques", 22 | "q_get_adversaries_x_event", 23 | "q_get_adversaries_x_tool", 24 | "q_get_adversaries_x_technique", 25 | "q_get_adversaries_x_sorigin", 26 | "q_get_adversaries_x_industry", 27 | "q_get_tools_x_techniques", 28 | "q_get_events_x_industry", 29 | "q_get_most_used_techniques", 30 | "q_get_techniques_per_industry", 31 | "q_insert_relation_into_tables", 32 | "q_insert_tool_x_subtechn" 33 | ] -------------------------------------------------------------------------------- /tmc/queries/q_get_tools_techniques.py: -------------------------------------------------------------------------------- 1 | from flask import ( g ) 2 | from tmc.db import get_db, make_dicts 3 | from IPython import embed 4 | 5 | # Get list of adversary techniques available in the database. 6 | def get_tools_techniques(tool_id): 7 | 8 | db = get_db() 9 | db.row_factory = make_dicts 10 | try: 11 | query = db.execute('select t.tool_name as Tool, tec.technique_id as TechniqueID, \ 12 | tec.technique_name as Technique, null as SubtechniqueID, null as Subtechnique \ 13 | from tools t \ 14 | inner join tools_x_techniques tt on t.id = tt.tool_id \ 15 | inner join techniques tec on tec.id = tt.technique_id \ 16 | where t.id=? \ 17 | \ 18 | UNION ALL \ 19 | \ 20 | select t.tool_name as Tool , tec.technique_id, tec.technique_name, stec.subtechnique_id, \ 21 | stec.subtechnique_name from tools t \ 22 | inner join tools_x_subtechniques st on t.id = st.tool_id \ 23 | inner join techniques_x_subtechniques ts on st.subtechnique_id=ts.subtechnique_id \ 24 | inner join techniques tec on tec.id=ts.technique_id \ 25 | inner join subtechniques stec on stec.id = st.subtechnique_id \ 26 | where t.id=? \ 27 | ORDER BY t.tool_name, tec.technique_id, stec.subtechnique_id', 28 | (tool_id, tool_id, )) 29 | result = query.fetchall() 30 | return result 31 | except TypeError: 32 | return False -------------------------------------------------------------------------------- /tmc/queries/q_get_tools_x_techniques.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of all tools per technique available in the database. 5 | def get_tools_x_techniques(technique=''): 6 | 7 | db = get_db() 8 | db.row_factory = make_dicts 9 | try: 10 | if technique: 11 | query = db.execute( 12 | 'SELECT t.tool_id As \'ToolID\', t.tool_name as Tool, tec.technique_id as \'TechniqueID\', tec.technique_name as Technique \ 13 | FROM tools t \ 14 | inner join adversaries_x_tools axt on axt.tool_id=t.id \ 15 | inner join tools_x_techniques txt on txt.technique_id=t.id \ 16 | inner join techniques tec on tec.id=txt.technique_id \ 17 | WHERE t.id=?', (technique, )).fetchall() 18 | return query 19 | else: 20 | query = db.execute( 21 | 'SELECT t.tool_id As \'ToolID\', t.tool_name as Tool, tec.technique_id as \'TechniqueID\', tec.technique_name as Technique \ 22 | FROM tools t \ 23 | inner join adversaries_x_tools axt on axt.tool_id=t.id \ 24 | inner join tools_x_techniques txt on txt.technique_id=t.id \ 25 | inner join techniques tec on tec.id=txt.technique_id \ 26 | ORDER BY t.tool_name').fetchall() 27 | return query 28 | except TypeError: 29 | #embed() 30 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/templates/maps/creation/create-subtechnique.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | 3 | {% block header %} 4 | {% if request_subtechnique %} 5 |

Edit subtechnique

6 | {% else %} 7 |

New subtechnique

8 | {% endif %} 9 | {% endblock %} 10 | 11 | {% block content %} 12 |
13 |
14 |
15 | {% if request_subtechnique %} 16 | 17 | {% endif %} 18 | 19 | 20 | 21 |
22 | 23 |
24 | 25 | 30 |
31 | 32 |
33 | 34 | 35 |
36 | 37 |
38 | 39 |
40 |
41 |
42 | {% endblock %} -------------------------------------------------------------------------------- /tmc/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from flask import Flask 4 | 5 | 6 | def create_app(test_config=None): 7 | """Create and configure an instance of the Flask application.""" 8 | app = Flask(__name__, instance_relative_config=True) 9 | app.config.from_mapping( 10 | # a default secret that should be overridden by instance config 11 | SECRET_KEY="dev", 12 | # store the database in the instance folder 13 | DATABASE=os.path.join(app.instance_path, "tmc.sqlite"), 14 | ) 15 | 16 | if test_config is None: 17 | # load the instance config, if it exists, when not testing 18 | app.config.from_pyfile("config.py", silent=True) 19 | else: 20 | # load the test config if passed in 21 | app.config.update(test_config) 22 | 23 | # ensure the instance folder exists 24 | try: 25 | os.makedirs(app.instance_path) 26 | except OSError: 27 | pass 28 | 29 | @app.route("/hello") 30 | def hello(): 31 | return "Hello, World!" 32 | 33 | # register the database commands 34 | from tmc import db 35 | 36 | db.init_app(app) 37 | 38 | # apply the blueprints to the app 39 | from tmc import auth, maps 40 | 41 | app.register_blueprint(auth.bp) 42 | app.register_blueprint(maps.bp) 43 | 44 | # make url_for('index') == url_for('maps.index') 45 | # in another app, you might define a separate main index here with 46 | # app.route, while giving the blog blueprint a url_prefix, but for 47 | # the tutorial the blog will be the main index 48 | app.add_url_rule("/", endpoint="index") 49 | 50 | return app -------------------------------------------------------------------------------- /tmc/queries/q_get_tools_x_subtechniques.py: -------------------------------------------------------------------------------- 1 | from flask import ( g, redirect, url_for ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of all tools per technique available in the database. 5 | def get_tools_x_subtechniques(subtechnique=''): 6 | 7 | db = get_db() 8 | db.row_factory = make_dicts 9 | try: 10 | if subtechnique: 11 | query = db.execute( 12 | 'SELECT t.tool_id As \'ToolID\', t.tool_name as Tool, subtec.subtechnique_id as \'SubtechniqueID\', subtec.subtechnique_name as Subtechnique \ 13 | FROM tools t \ 14 | inner join adversaries_x_tools axt on axt.tool_id=t.id \ 15 | inner join tools_x_subtechniques txt on txt.subtechnique_id=t.id \ 16 | inner join subtechniques subtec on subtec.id=txt.subtechnique_id \ 17 | WHERE t.id=?', (subtechnique, )).fetchall() 18 | return query 19 | else: 20 | query = db.execute( 21 | 'SELECT t.tool_id As \'ToolID\', t.tool_name as Tool, subtec.subtechnique_id as \'SubtechniqueID\', subtec.subtechnique_name as Subtechnique \ 22 | FROM tools t \ 23 | inner join adversaries_x_tools axt on axt.tool_id=t.id \ 24 | inner join tools_x_subtechniques txt on txt.subtechnique_id=t.id \ 25 | inner join subtechniques subtec on subtec.id=txt.subtechnique_id \ 26 | ORDER BY t.tool_name').fetchall() 27 | return query 28 | except TypeError: 29 | #embed() 30 | return False #Change this for something more meaningful -- warning/alert -------------------------------------------------------------------------------- /tmc/queries/q_get_adversaries_techniques.py: -------------------------------------------------------------------------------- 1 | from flask import ( g ) 2 | from tmc.db import get_db, make_dicts 3 | 4 | # Get list of adversary techniques available in the database. 5 | def get_adversaries_techniques(adversary_id): 6 | 7 | db = get_db() 8 | db.row_factory = make_dicts 9 | try: 10 | query = db.execute('select t.tool_name as Tool, tec.technique_id as TechniqueID, \ 11 | tec.technique_name as Technique, null as SubtechniqueID, null as Subtechnique \ 12 | from adversaries_x_tools at \ 13 | inner join tools t on at.tool_id = t.id \ 14 | inner join tools_x_techniques tt on t.id = tt.tool_id \ 15 | inner join techniques tec on tec.id = tt.technique_id \ 16 | where at.adversary_id=? \ 17 | \ 18 | UNION ALL\ 19 | \ 20 | select t.tool_name as Tool , tec.technique_id, tec.technique_name, stec.subtechnique_id \ 21 | , stec.subtechnique_name from adversaries_x_tools at \ 22 | inner join tools t on at.tool_id = t.id \ 23 | inner join tools_x_subtechniques st on t.id = st.tool_id \ 24 | inner join techniques_x_subtechniques ts on st.subtechnique_id=ts.subtechnique_id \ 25 | inner join techniques tec on tec.id=ts.technique_id \ 26 | inner join subtechniques stec on stec.id = st.subtechnique_id \ 27 | where at.adversary_id=? \ 28 | ORDER BY t.tool_name, tec.technique_id, stec.subtechnique_id' , 29 | (adversary_id, adversary_id, )) 30 | result = query.fetchall() 31 | return result 32 | except TypeError: 33 | return False -------------------------------------------------------------------------------- /tmc/static/custom.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | // Change first column background color. 3 | $("#firstColumn").click(function(){ 4 | $("table tr td:first-child").css("background-color","#ff0000"); 5 | }); 6 | // Change second column background color. 7 | $("#secondColumn").click(function(){ 8 | $("table tr td:nth-child(2)").css("background-color","#ff0000"); 9 | }); 10 | }); 11 | 12 | $(document).ready(function() { 13 | 14 | var table = $('#explore_results').DataTable(); 15 | var anchor = document.querySelectorAll('.disabled'); 16 | 17 | var loc = location.href; 18 | 19 | $('#explore_results tbody').on( 'click', 'tr', function () { 20 | if ( $(this).hasClass('selected') ) { 21 | $(this).removeClass('selected'); 22 | for (var i = 0; i < anchor.length; i++) { 23 | anchor[i].classList.add('disabled'); 24 | } 25 | } 26 | else { 27 | table.$('tr.selected').removeClass('selected'); 28 | $(this).addClass('selected'); 29 | var column_count = table.$('tr.selected').children().length; 30 | var enables = table.$('tr.selected').children()[column_count-1].children; 31 | for (var i = 0; i < enables.length; i++) { 32 | enables[i].classList.remove('disabled'); 33 | } 34 | } 35 | } ); 36 | 37 | } ); 38 | 39 | function clickRowListener(anchor) { 40 | var table = $('#explore_results').DataTable(); 41 | var headers = document.querySelectorAll("th"); 42 | var element_value = table.$('tr.selected').find('td').eq(0).text(); 43 | var element_name = headers[2].innerText; 44 | var atb = anchor.getAttribute('title'); 45 | var link = atb + "/" + element_name.toLowerCase() + "/" + element_value.toLowerCase(); 46 | window.location.replace(link); 47 | } 48 | 49 | 50 | function open_tram() 51 | { 52 | var win=window.open('http://localhost:9999', '_blank'); 53 | win.focus(); 54 | } -------------------------------------------------------------------------------- /tmc/templates/maps/creation/create-technique.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | 3 | {% block header %} 4 | {% if request_technique %} 5 |

Edit Technique

6 | {% else %} 7 |

New Technique

8 | {% endif %} 9 | {% endblock %} 10 | 11 | {% block content %} 12 |
13 |
14 |
15 | {% if request_technique %} 16 | 17 | {% endif %} 18 | 19 | 20 | 21 |
22 | 23 |
24 | 25 | 26 |
27 | 28 |
29 | 30 | 35 |
36 | 37 |
38 | 39 | 41 |
42 | 43 | 44 |
45 | 46 |
47 |
48 |
49 | {% endblock %} -------------------------------------------------------------------------------- /tmc/static/style.css: -------------------------------------------------------------------------------- 1 | /*************** 2 | #General 3 | ****************/ 4 | 5 | html, body { 6 | background-color: #fff; 7 | color: #000; 8 | font-size: 1.05rem; 9 | } 10 | 11 | .btn-dark { 12 | background-color: #000; 13 | } 14 | 15 | label, .btn-dark { 16 | margin-top: 3%; 17 | } 18 | 19 | h2, p { 20 | color: #000; 21 | } 22 | 23 | .container:not(nav){ 24 | border: solid 1px #000; 25 | margin-bottom: 15%; 26 | } 27 | 28 | .error_bckg { 29 | width:80%; 30 | display: block; 31 | margin: 0 auto; 32 | } 33 | /*************** 34 | #Nav 35 | ****************/ 36 | 37 | nav { 38 | margin-bottom: 2%; 39 | 40 | } 41 | 42 | #logo { 43 | width: 5%; 44 | } 45 | 46 | 47 | nav h1 { 48 | margin-top: 5%; 49 | display: inline-block; 50 | vertical-align: sub; 51 | } 52 | 53 | nav ul { 54 | list-style-type: none; 55 | text-align: right; 56 | } 57 | 58 | nav ul li { 59 | display: inline-block; 60 | padding-right: 2%; 61 | } 62 | 63 | 64 | ul li a { 65 | color: #000; 66 | } 67 | 68 | a:hover, ul li a:hover { 69 | color: #FF0000; 70 | } 71 | 72 | 73 | /*************** 74 | #Body 75 | ****************/ 76 | 77 | div.container { 78 | /* background-color: rgba(1,1,45,0.6);*/ 79 | padding: 2%; 80 | } 81 | 82 | .btn:hover, .bnt-dark:hover { 83 | background-color: #424242; 84 | } 85 | 86 | /*************** 87 | #Explore 88 | ****************/ 89 | .create-menu ul, .action-list { 90 | text-decoration: none; 91 | text-align: right; 92 | } 93 | 94 | .create-menu ul li, i { 95 | display: inline-block; 96 | } 97 | 98 | .create-menu ul li { 99 | background-color: #000; 100 | border: 1px solid black; 101 | padding: 0.5% 0.9%; 102 | margin-left: 2% 103 | } 104 | 105 | .create-menu ul li a { 106 | color: #fff; 107 | font-size: 1em; 108 | } 109 | 110 | .row a, .row a.disabled { 111 | color: #000; 112 | margin-right: 3%; 113 | } 114 | 115 | #actions a { 116 | margin-right: 13%; 117 | display: block; 118 | padding-top: 15%; 119 | } 120 | 121 | .row a.disabled { 122 | cursor: not-allowed; 123 | opacity: 0.5; 124 | } 125 | 126 | table { 127 | margin-top: 3%; 128 | } 129 | -------------------------------------------------------------------------------- /tmc/templates/maps/explore/main.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | 3 | {% block header %} 4 | {% if g.user %} 5 |
6 | 13 |
14 | {% endif %} 15 | {% endblock %}\ 16 | 17 | {% block content %} 18 | 19 |

List database elements

20 | 21 | 29 | 30 |

Explore database relationships

31 | 32 | 44 | {% endblock %} -------------------------------------------------------------------------------- /tmc/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% block title %}{% endblock %}TMC 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 38 | 39 | 40 | 57 |
58 |
59 |
60 | {% block header %}{% endblock %} 61 |
62 | {% for message in get_flashed_messages() %} 63 |
{{ message }}
64 | {% endfor %} 65 | {% block content %}{% endblock %} 66 |
67 |
-------------------------------------------------------------------------------- /tmc/templates/maps/welcome.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | 3 | {% block header %} 4 | 5 |

{% block title %}Welcome!{% endblock %}

6 |

Load one or multiple URLs to carry out the mapping against TRAM.
Select which adversary, tool, and industry you want that mapping to be associated with.

7 | {% if g.user %} 8 | 12 | {% endif %} 13 | {% endblock %} 14 | 15 | 16 | {% block content %} 17 |
18 | 19 |
20 |
21 | 22 | 23 | 28 | 29 | 30 | 35 | 36 | 37 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
50 |
51 | 52 | 53 |
54 | 55 |
56 | 57 |
58 |
59 | 60 |
61 |
62 |
63 | 64 | {% endblock %} 65 | -------------------------------------------------------------------------------- /tmc/auth.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | from flask import ( 4 | Blueprint, flash, g, redirect, render_template, request, session, url_for 5 | ) 6 | from werkzeug.security import check_password_hash, generate_password_hash 7 | 8 | from tmc.db import get_db 9 | 10 | bp = Blueprint('auth', __name__, url_prefix='/auth') 11 | 12 | @bp.route('/register', methods=('GET', 'POST')) 13 | def register(): 14 | if request.method == 'POST': 15 | username = request.form['username'] 16 | password = request.form['password'] 17 | db = get_db() 18 | error = None 19 | 20 | if not username: 21 | error = 'Username is required.' 22 | elif not password: 23 | error = 'Password is required.' 24 | elif db.execute( 25 | 'SELECT id FROM users WHERE username = ?', (username,) 26 | ).fetchone() is not None: 27 | error = 'User {} is already registered.'.format(username) 28 | 29 | if error is None: 30 | db.execute( 31 | 'INSERT INTO users (username, password) VALUES (?, ?)', 32 | (username, generate_password_hash(password)) 33 | ) 34 | db.commit() 35 | return redirect(url_for('auth.login')) 36 | 37 | flash(error) 38 | 39 | return render_template('auth/register.html') 40 | 41 | 42 | @bp.route('/login', methods=('GET', 'POST')) 43 | def login(): 44 | if request.method == 'POST': 45 | username = request.form['username'] 46 | password = request.form['password'] 47 | db = get_db() 48 | error = None 49 | user = db.execute( 50 | 'SELECT * FROM users WHERE username = ?', (username,) 51 | ).fetchone() 52 | 53 | if user is None: 54 | error = 'Incorrect username.' 55 | elif not check_password_hash(user['password'], password): 56 | error = 'Incorrect password.' 57 | 58 | if error is None: 59 | session.clear() 60 | session['user_id'] = user['id'] 61 | return redirect(url_for('index')) 62 | 63 | flash(error) 64 | 65 | return render_template('auth/login.html') 66 | 67 | 68 | @bp.before_app_request 69 | def load_logged_in_user(): 70 | user_id = session.get('user_id') 71 | 72 | if user_id is None: 73 | g.user = None 74 | else: 75 | g.user = get_db().execute( 76 | 'SELECT * FROM users WHERE id = ?', (user_id,) 77 | ).fetchone() 78 | 79 | 80 | @bp.route('/logout') 81 | def logout(): 82 | session.clear() 83 | return redirect(url_for('index')) 84 | 85 | 86 | def login_required(view): 87 | @functools.wraps(view) 88 | def wrapped_view(**kwargs): 89 | if g.user is None: 90 | return redirect(url_for('auth.login')) 91 | 92 | return view(**kwargs) 93 | 94 | return wrapped_view -------------------------------------------------------------------------------- /tmc/templates/maps/explore/explore-element.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | 3 | {% block header %} 4 | {% if g.user %} 5 |
6 | 13 |
14 | {% endif %} 15 | {% endblock %} 16 | 17 | 18 | 19 | 20 | {% block content %} 21 |
22 |
23 |
24 |

{{ title }}

25 | 26 | 27 | 28 | {% for element in element_th %} 29 | {% if element == 'db_id' %} 30 | 31 | {% else %} 32 | 33 | {% endif %} 34 | {% endfor %} 35 | 36 | 37 | 38 | 39 | {% for element in paginated_element %} 40 | 41 | {% for key, value in element.items() %} 42 | {% if key == 'db_id' %} 43 | 44 | {% else %} 45 | 46 | {% endif %} 47 | {% endfor %} 48 | 56 | {% endif %} 57 | 58 | {% endfor %} 59 | 60 |
{{ element }}{{ element }}Actions
{{ value }}{{ value }} 49 | 50 | {% if title == 'Adversaries' or title == 'Tools' %} 51 | 52 | 53 | 55 |
61 |
62 |
63 |
64 | {% endblock %} 65 | -------------------------------------------------------------------------------- /tmc/templates/maps/creation/create-tactic.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | 3 | {% block header %} 4 | {% if request_tactic %} 5 |

Edit tactic

6 | {% else %} 7 |

New tactic

8 | {% endif %} 9 | {% endblock %} 10 | 11 | {% block content %} 12 |
13 |
14 |
15 | {% if request_tactic %} 16 | 17 | {% endif %} 18 | 19 | 20 | 21 |
22 | 23 |
24 | 25 | 26 |
27 | 28 |
29 | 30 | 32 |
33 | 34 |
35 | 36 | 38 |
39 | 40 |
41 | 42 | 43 |
44 |
45 |
46 | 47 | {% if request_tactic_techniques|length %} 48 | 49 | 50 | 51 | 52 | {% for element in element_th %} 53 | 54 | {% endfor %} 55 | 56 | 57 | 58 | 59 | {% for element in request_tactic_techniques %} 60 | 61 | {% for key, value in element.items() %} 62 | 63 | {% endfor %} 64 | 68 | 69 | {% endfor %} 70 | 71 |
{{ element }}Actions
{{ value }} 65 | 66 | 67 |
72 | {% endif %} 73 | 74 | {% endblock %} -------------------------------------------------------------------------------- /tmc/templates/maps/creation/create-tool.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | 3 | {% block header %} 4 | {% if request_tool %} 5 |

Edit Tool

6 | {% else %} 7 |

New Tool

8 | {% endif %} 9 | {% endblock %} 10 | 11 | {% block content %} 12 |
13 |
14 |
15 | {% if request_tool %} 16 | 17 | {% endif %} 18 | 19 | 20 | 21 |
22 | 23 |
24 | 25 | 26 |
27 | 28 |
29 | 30 | 32 |
33 | 34 |
35 | 36 | 41 |
42 | 43 |
44 | 45 | 47 |
48 | 49 |
50 | 51 | 52 |
53 |
54 |
55 | 56 | {% if request_tools_techniques|length %} 57 | 58 | 59 | 60 | 61 | {% for element in element_th %} 62 | 63 | {% endfor %} 64 | 65 | 66 | 67 | 68 | {% for element in request_tools_techniques %} 69 | 70 | {% for key, value in element.items() %} 71 | 72 | {% endfor %} 73 | 77 | 78 | {% endfor %} 79 | 80 |
{{ element }}Actions
{{ value }} 74 | 75 | 76 |
81 | {% endif %} 82 | 83 | {% endblock %} -------------------------------------------------------------------------------- /tmc/templates/maps/creation/create-adversary.html: -------------------------------------------------------------------------------- 1 | {% extends 'index.html' %} 2 | 3 | {% block header %} 4 | {% if request_adversary %} 5 |

Edit Adversary

6 | {% else %} 7 |

New Adversary

8 | {% endif %} 9 | {% endblock %} 10 | 11 | {% block content %} 12 |
13 |
14 |
15 | {% if request_adversary %} 16 | 17 | {% endif %} 18 | 19 | 20 | 21 |
22 | 23 |
24 | 25 | 26 |
27 | 28 |
29 | 30 | 32 |
33 | 34 |
35 | 36 | 41 |
42 | 43 |
44 | 45 | 47 |
48 | 49 |
50 | 51 | 52 |
53 |
54 |
55 | 56 | {% if request_adversary_techniques|length %} 57 | 58 | 59 | 60 | 61 | {% for element in element_th %} 62 | 63 | {% endfor %} 64 | 65 | 66 | 67 | 68 | {% for element in request_adversary_techniques %} 69 | {% for key, value in element.items() %} 70 | 71 | {% endfor %} 72 | 76 | 77 | {% endfor %} 78 | 79 |
{{ element }}Actions
{{ value }} 73 | 74 | 75 |
80 | {% endif %} 81 | 82 | {% endblock %} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) 2 | [![Open Source Love](https://badges.frapsoft.com/os/v1/open-source.png?v=103)](https://github.com/ellerbrock/open-source-badges/) 3 | [![stability-alpha](https://img.shields.io/badge/stability-alpha-f4d03f.svg)](https://github.com/mkenney/software-guides/blob/master/STABILITY-BADGES.md#alpha) 4 | 5 | # tmc 6 | Threat Mapping Catalogue 7 | 8 | ![](tmc/static/TMCv1.png "TMC Diagram") 9 | ​ 10 | ## Requirements 11 | - [python3](https://www.python.org/) (3.7+) 12 | - Flask 13 | - Sqlite 14 | - **TRAM adaptation** : (https://github.com/fierytermite/tram-1) [forked from fixed version by [@cyb3rR4v3n](https://twitter.com/cyb3rR4v3n) 15 | - **Attack Navigator adaptation** : (https://github.com/fierytermite/attack-navigator) 16 | 17 | For the navigator, you'll need to clone the branch 'tmc' in order to make it work: 18 | 19 | ``` 20 | git clone --branch tmc https://github.com/fierytermite/attack-navigator 21 | ``` 22 | 23 | Note!! In order to the Navigator Adaptation to work properly you would have to work around CORS blocked by policy. You can get away easily by using Chrome browser extension [Allow CORS: Access-Control-Allow-Origin](https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf?). Keep in mind that CORS blocks are implemented for security reasons, so allow them wisely. You can still use TRAM and all other functionality without it. 24 | 25 | ## Installation 26 | 27 | Clone this github repository and from its root folder install its requirements: 28 | ``` 29 | pip install -r requirements.txt 30 | ``` 31 | 32 | You can set the enviornment in which the app is running by setting the FLASK_ENV variable to ```development``` or ```production```: 33 | 34 | ``` 35 | export FLASK_ENV= 36 | ``` 37 | 38 | Then run the following commands: 39 | 40 | ``` 41 | export FLASK_APP=tmc 42 | flask init-db 43 | flask run 44 | ``` 45 | 46 | Once the server has started, access the application through localhost:5000. **You will need to register an user and log in in order to use the application.** 47 | 48 | To load the database with the data from [ATT&CK](https://attack.mitre.org/) access the following path. Please be patiente, since this operation **really takes a while** (aprox 1h 30m, depending on your internet connection): 49 | 50 | ``` 51 | localhost:5000/first-time 52 | ``` 53 | 54 | Keep in mind that the next time you want to run the application, you **don't have to** initilizate the database, since doing it will erase any content loaded in it. Repeat the previous steps without that command: 55 | 56 | ``` 57 | export FLASK_ENV= 58 | export FLASK_APP=tmc 59 | flask run 60 | ``` 61 | 62 | # Making the APP public 63 | 64 | If you prefer, you can change the port and make the app publicly available by running the following command: 65 | 66 | ``` 67 | flask run -p -h 0.0.0.0 68 | ``` 69 | 70 | # Author 71 | 72 | * Valentina Palacin [@fierytermite](https://twitter.com/fierytermite) 73 | 74 | # License: MIT License 75 | 76 | MIT License 77 | 78 | Copyright (c) 2021 Valentina Palacin 79 | 80 | Permission is hereby granted, free of charge, to any person obtaining a copy 81 | of this software and associated documentation files (the "Software"), to deal 82 | in the Software without restriction, including without limitation the rights 83 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 84 | copies of the Software, and to permit persons to whom the Software is 85 | furnished to do so, subject to the following conditions: 86 | 87 | The above copyright notice and this permission notice shall be included in all 88 | copies or substantial portions of the Software. 89 | 90 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 91 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 92 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 93 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 94 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 95 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 96 | SOFTWARE. 97 | 98 | # TO-DOs 99 | 100 | - [x] Add Industry List 101 | - [x] Add mapping from TRAM 102 | - [x] Link TMC with TRAM 103 | - [x] Link TMC with Navigator 104 | - [ ] Automatically download the mapping from Navigator as SVG 105 | - [x] Export as CSV function 106 | - [x] Edit database views 107 | - [ ] Dockerize project 108 | -------------------------------------------------------------------------------- /tmc/processor.py: -------------------------------------------------------------------------------- 1 | from flask import ( 2 | g, redirect, render_template, request, url_for 3 | ) 4 | from tmc.auth import login_required 5 | from attackcti import attack_client 6 | from tmc.db import get_db 7 | from IPython import embed 8 | import tmc.queries as q 9 | import logging 10 | logging.basicConfig(level=logging.INFO) 11 | 12 | # Global variable 13 | lift = '' 14 | lift = attack_client() 15 | 16 | # Classes 17 | 18 | class Adversary(): 19 | 20 | def __init__(self, element): 21 | super().__init__() 22 | self.related_tools = lift.get_software_used_by_group(element) 23 | self.attack_identifiers = '' 24 | self.attack_id = element['external_references'][0]['external_id'] 25 | self.adversary_name = element['name'] 26 | 27 | try: 28 | self.adversary_description = element['description'] 29 | except KeyError: 30 | self.adversary_description = '' 31 | try: 32 | aliases_list = element['aliases'] 33 | 34 | for element in aliases_list: 35 | self.attack_identifiers += element + '; ' 36 | 37 | except KeyError: 38 | self.attack_identifiers = '' 39 | 40 | self.adversary_id = insert_adversary(self.attack_id, self.adversary_name, self.adversary_description, self.attack_identifiers) 41 | 42 | 43 | class Events(): 44 | 45 | def __init__(self, element): 46 | super().__init__() 47 | 48 | 49 | class Tools(): 50 | 51 | def __init__(self, element): 52 | super().__init__() 53 | self.attack_id = element['external_references'][0]['external_id'] 54 | self.tool_name = element['name'] 55 | try: 56 | self.tool_description = element['description'] 57 | except KeyError: 58 | self.tool_description = '' 59 | try: 60 | self.attack_identifiers = element['x_mitre_aliases'] 61 | except KeyError: 62 | self.attack_identifiers = '' 63 | self.techniques_used = lift.get_techniques_used_by_software(element) 64 | self.tool_id = insert_tool(self.attack_id, self.tool_name, self.tool_description) 65 | 66 | 67 | class Tactics(): 68 | 69 | def __init__(self, element): 70 | super().__init__() 71 | self.attack_id = element['external_references'][0]['external_id'] 72 | self.tactic_name = element['name'] 73 | self.tactic_description = element['description'] 74 | self.tactic_id = insert_tactic(self.attack_id, self.tactic_name, self.tactic_description) 75 | 76 | 77 | class Techniques(): 78 | 79 | def __init__(self, element): 80 | super().__init__() 81 | self.attack_id = element['external_references'][0]['external_id'] 82 | 83 | if '.' in self.attack_id: 84 | technique_attack_id,subtechnique_attack_id = self.attack_id.split('.', 1) 85 | sub = Subtechniques(element, self.attack_id) 86 | 87 | self.technique_name = element['name'] 88 | self.technique_description = element['description'] 89 | self.related_tactic = element['kill_chain_phases'][0]['phase_name'] 90 | self.technique_id = insert_technique(self.attack_id, self.technique_name, self.technique_description) 91 | 92 | 93 | class Subtechniques(): 94 | 95 | def __init__(self, element, subtechnique_attack_id): 96 | super().__init__() 97 | self.attack_id = subtechnique_attack_id 98 | self.subtechnique_name = element['name'] 99 | self.subtechnique_description = element['description'] 100 | self.related_tactic = element['kill_chain_phases'][0]['phase_name'] 101 | self.subtechnique_id = insert_subtechnique(self.attack_id, self.subtechnique_name, self.subtechnique_description) 102 | 103 | 104 | # ATT&CK FRAMEWORK INTERACTION 105 | def get_elements(): 106 | 107 | tactics = lift.get_tactics() 108 | for element in tactics: 109 | 110 | if 'x_mitre_deprecated' in element: 111 | continue 112 | else: 113 | tac = Tactics(element) 114 | 115 | unsorted_techniques = lift.get_techniques() 116 | techniques = sorted(unsorted_techniques, key = lambda i: i['external_references'][0]['external_id']) 117 | for element in techniques: 118 | 119 | if element['revoked'] == True: 120 | continue 121 | 122 | technique_id = element['external_references'][0]['external_id'] 123 | 124 | if '.' in technique_id: 125 | subtec = Subtechniques(element, technique_id) 126 | technique_attack_id,subtechnique_attack_id = technique_id.split('.', 1) 127 | related_technique=q.q_get_element_id.get_element_id('techniques', 'technique_id', technique_attack_id) 128 | insert_tecxsubtec(related_technique, subtec.subtechnique_id) 129 | else: 130 | tec = Techniques(element) 131 | insert_tacxtec(tec.technique_id, tec.related_tactic) 132 | 133 | tools = lift.get_software() 134 | for element in tools: 135 | t = Tools(element) 136 | insert_toolxtec(t.tool_name, t.tool_id, t.techniques_used) 137 | 138 | adversaries = lift.get_groups() 139 | for element in adversaries: 140 | adv = Adversary(element) 141 | insert_advxtool(adv.adversary_id, adv.related_tools) 142 | 143 | 144 | def insert_tactic(attack_id, tactic_name, tactic_description): 145 | 146 | existence = q.q_get_element_id.get_element_id('tactics', 'tactic_name', tactic_name) 147 | if not existence: 148 | insert_into_table = q.q_insert_into_tables.insert_into_tables('tactics', attack_id, tactic_name, tactic_description) 149 | logging.info('Created tactic %s' % tactic_name) 150 | return insert_into_table 151 | else: 152 | return existence 153 | 154 | 155 | # Adding ATT&CK Techniques 156 | def insert_technique(attack_id, technique_name, technique_description): 157 | 158 | insert_into_table = q.q_insert_into_tables.insert_into_tables('techniques', attack_id, technique_name, technique_description) 159 | logging.info('Created technique %s' % technique_name) 160 | return insert_into_table 161 | 162 | 163 | #REFACTOR THIS 164 | def insert_subtechnique(attack_id, subtechnique_name, subtechnique_description): 165 | 166 | insert_into_table = q.q_insert_into_tables.insert_into_tables('subtechniques', attack_id, subtechnique_name, subtechnique_description) 167 | logging.info('Created technique %s' % subtechnique_name) 168 | return insert_into_table 169 | 170 | 171 | # Adding ATT&CK Adversaries 172 | def insert_adversary(attack_id, adversary_name, adversary_description, attack_identifiers): 173 | 174 | insert_into_table = q.q_insert_adversary_into_tables.insert_adversary_into_tables('adversaries', attack_id, adversary_name, adversary_description, attack_identifiers) 175 | logging.info('Created adversary %s' % adversary_name) 176 | return insert_into_table 177 | 178 | 179 | # Adding ATT&CK Tools 180 | def insert_tool(attack_id, tool_name, tool_description): 181 | 182 | insert_into_table = q.q_insert_into_tables.insert_into_tables('tools', attack_id, tool_name, tool_description) 183 | logging.info('Created tool %s' % tool_name) 184 | return insert_into_table 185 | 186 | 187 | # Insert Tools x Techniques ----- this is gatherin technique att&ck id, instead of technique id 188 | def insert_toolxtec(tool_name, tool_id, techniques_used): 189 | 190 | for element in techniques_used: 191 | 192 | technique_attack_id = element['external_references'][0]['external_id'] 193 | 194 | if '.' in technique_attack_id: 195 | subtechnique_id = q.q_get_element_id.get_element_id('subtechniques', 'subtechnique_id', technique_attack_id) 196 | result = q.q_insert_tool_x_subtechn.insert_tool_x_subtechn('tools_x_subtechniques', tool_id, subtechnique_id) 197 | 198 | else: 199 | technique_id = q.q_get_element_id.get_element_id('techniques', 'technique_id', technique_attack_id) 200 | result = q.q_insert_tool_x_techn.insert_tool_x_techn('tools_x_techniques', tool_id, technique_id) 201 | logging.info('Created relationship for %s' % tool_name) 202 | return result 203 | 204 | 205 | 206 | # Adding ATT&CK TacticxTechniques 207 | def insert_tacxtec(technique_id, related_tactic): 208 | 209 | error = [] 210 | tactic = related_tactic.replace('-', ' ') 211 | 212 | if 'ics' in tactic: 213 | tactic=tactic.replace(' ics', '') 214 | 215 | tactic_id = q.q_get_element_id.get_element_id('tactics', 'tactic_name', tactic) 216 | 217 | if tactic_id == 0: 218 | logging.info('Unrecognized tactic in %s: ' % technique_id) 219 | else: 220 | try: 221 | tactic_x_technique = q.q_insert_tactic_x_technique.insert_tactic_x_technique(tactic_id, technique_id) 222 | logging.info('Created tactic relationship') 223 | except KeyError: 224 | logging.info('Raised KeyError exception with tactic in %s: ' % technique_id) 225 | 226 | 227 | # Insert relation insert_advxtool 228 | def insert_advxtool(adversary_id, related_tools): 229 | 230 | for tool in related_tools: 231 | tool_attack_id = tool['external_references'][0]['external_id'] 232 | tool_id = q.q_get_element_id.get_element_id('tools', 'tool_id', tool_attack_id) 233 | 234 | result = q.q_insert_adversary_x_tool.insert_adversary_x_tool(adversary_id, tool_id) 235 | logging.info('Created adversary per tool relationship') 236 | return result 237 | 238 | 239 | # Insert Subtechniques x Techniques 240 | def insert_tecxsubtec(related_technique, subattack_id): 241 | 242 | insert_into_table = q.q_insert_relation_into_tables.insert_relation_into_tables('techniques_x_subtechniques', 'technique_id', 'subtechnique_id', related_technique, subattack_id) 243 | logging.info('Created technique per subtechnique relationship') 244 | return insert_into_table 245 | -------------------------------------------------------------------------------- /tmc/schema.sql: -------------------------------------------------------------------------------- 1 | -- Initialize the database. 2 | -- Drop any existing data and create empty tables. 3 | 4 | DROP TABLE IF EXISTS adversaries; 5 | DROP TABLE IF EXISTS tactics; 6 | DROP TABLE IF EXISTS techniques; 7 | DROP TABLE IF EXISTS subtechniques; 8 | DROP TABLE IF EXISTS events; 9 | DROP TABLE IF EXISTS tools; 10 | DROP TABLE IF EXISTS industries; 11 | DROP TABLE IF EXISTS events_x_industries; 12 | DROP TABLE IF EXISTS adversaries_x_events; 13 | DROP TABLE IF EXISTS events_x_industry; 14 | DROP TABLE IF EXISTS adversaries_x_tools; 15 | DROP TABLE IF EXISTS tools_x_techniques; 16 | DROP TABLE IF EXISTS tools_x_subtechniques; 17 | DROP TABLE IF EXISTS tactics_x_techniques; 18 | DROP TABLE IF EXISTS techniques_x_subtechniques; 19 | DROP TABLE IF EXISTS countries; 20 | DROP TABLE IF EXISTS users; 21 | 22 | 23 | CREATE TABLE users ( 24 | id INTEGER PRIMARY KEY AUTOINCREMENT, 25 | username TEXT UNIQUE NOT NULL, 26 | password TEXT NOT NULL 27 | ); 28 | 29 | CREATE TABLE adversaries ( 30 | id INTEGER PRIMARY KEY AUTOINCREMENT, 31 | author_id INTEGER NOT NULL, 32 | created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 33 | adversary_id TEXT NOT NULL, 34 | adversary_name TEXT NOT NULL, 35 | adversary_description TEXT NOT NULL, 36 | adversary_identifiers TEXT, 37 | adversary_sorigin TEXT, 38 | updated_date DATETIME, 39 | updated_by TEXT, 40 | FOREIGN KEY (author_id) REFERENCES users (id) 41 | ); 42 | 43 | CREATE TABLE tactics ( 44 | id INTEGER PRIMARY KEY AUTOINCREMENT, 45 | author_id INTEGER NOT NULL, 46 | created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 47 | tactic_id TEXT NOT NULL, 48 | tactic_name TEXT NOT NULL, 49 | tactic_description TEXT NOT NULL, 50 | updated_date DATETIME, 51 | updated_by TEXT, 52 | FOREIGN KEY (author_id) REFERENCES users (id) 53 | ); 54 | 55 | CREATE TABLE techniques ( 56 | id INTEGER PRIMARY KEY AUTOINCREMENT, 57 | author_id INTEGER NOT NULL, 58 | created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 59 | technique_id TEXT NOT NULL, 60 | technique_name TEXT NOT NULL, 61 | technique_description TEXT NOT NULL, 62 | updated_date DATETIME, 63 | updated_by TEXT, 64 | FOREIGN KEY (author_id) REFERENCES users (id) 65 | ); 66 | 67 | CREATE TABLE subtechniques ( 68 | id INTEGER PRIMARY KEY AUTOINCREMENT, 69 | author_id INTEGER NOT NULL, 70 | created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 71 | subtechnique_id TEXT NOT NULL, 72 | subtechnique_name TEXT NOT NULL, 73 | subtechnique_description TEXT NOT NULL, 74 | updated_date DATETIME, 75 | updated_by TEXT, 76 | FOREIGN KEY (author_id) REFERENCES users (id) 77 | ); 78 | 79 | CREATE TABLE tools ( 80 | id INTEGER PRIMARY KEY AUTOINCREMENT, 81 | author_id INTEGER NOT NULL, 82 | created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 83 | tool_id TEXT NOT NULL, 84 | tool_name TEXT NOT NULL, 85 | tool_description TEXT NOT NULL, 86 | tool_identifiers TEXT, 87 | updated_date DATETIME, 88 | updated_by TEXT, 89 | FOREIGN KEY (author_id) REFERENCES users (id) 90 | ); 91 | 92 | CREATE TABLE events ( 93 | id INTEGER PRIMARY KEY AUTOINCREMENT, 94 | author_id INTEGER NOT NULL, 95 | created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 96 | event_name TEXT NOT NULL, 97 | event_description TEXT NOT NULL, 98 | event_url TEXT, 99 | event_date DATETIME, 100 | FOREIGN KEY (author_id) REFERENCES users (id) 101 | ); 102 | 103 | CREATE TABLE adversaries_x_tools ( 104 | id INTEGER PRIMARY KEY AUTOINCREMENT, 105 | author_id INTEGER NOT NULL, 106 | created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 107 | adversary_id TEXT NOT NULL, 108 | tool_id TEXT NOT NULL, 109 | FOREIGN KEY (author_id) REFERENCES users (id), 110 | FOREIGN KEY (adversary_id) REFERENCES adversary (id), 111 | FOREIGN KEY (tool_id) REFERENCES tool (id) 112 | ); 113 | 114 | CREATE TABLE adversaries_x_events ( 115 | id INTEGER PRIMARY KEY AUTOINCREMENT, 116 | author_id INTEGER NOT NULL, 117 | created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 118 | adversary_id TEXT NOT NULL, 119 | event_id TEXT NOT NULL, 120 | FOREIGN KEY (author_id) REFERENCES users (id), 121 | FOREIGN KEY (adversary_id) REFERENCES adversary (id), 122 | FOREIGN KEY (event_id) REFERENCES event (id) 123 | ); 124 | 125 | CREATE TABLE tools_x_techniques ( 126 | id INTEGER PRIMARY KEY AUTOINCREMENT, 127 | author_id INTEGER NOT NULL, 128 | created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 129 | tool_id TEXT NOT NULL, 130 | technique_id TEXT NOT NULL, 131 | FOREIGN KEY (author_id) REFERENCES users (id), 132 | FOREIGN KEY (tool_id) REFERENCES tool (id), 133 | FOREIGN KEY (technique_id) REFERENCES technique (id) 134 | ); 135 | 136 | CREATE TABLE tools_x_subtechniques ( 137 | id INTEGER PRIMARY KEY AUTOINCREMENT, 138 | author_id INTEGER NOT NULL, 139 | created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 140 | tool_id TEXT NOT NULL, 141 | subtechnique_id TEXT NOT NULL, 142 | FOREIGN KEY (author_id) REFERENCES users (id), 143 | FOREIGN KEY (tool_id) REFERENCES tool (id), 144 | FOREIGN KEY (subtechnique_id) REFERENCES subtechnique (id) 145 | ); 146 | 147 | CREATE TABLE tactics_x_techniques ( 148 | id INTEGER PRIMARY KEY AUTOINCREMENT, 149 | author_id INTEGER NOT NULL, 150 | created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 151 | tactic_id TEXT NOT NULL, 152 | technique_id TEXT NOT NULL, 153 | FOREIGN KEY (author_id) REFERENCES users (id), 154 | FOREIGN KEY (tactic_id) REFERENCES tactic (id), 155 | FOREIGN KEY (technique_id) REFERENCES technique (id) 156 | ); 157 | 158 | CREATE TABLE techniques_x_subtechniques ( 159 | id INTEGER PRIMARY KEY AUTOINCREMENT, 160 | author_id INTEGER NOT NULL, 161 | created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 162 | technique_id TEXT NOT NULL, 163 | subtechnique_id TEXT NOT NULL, 164 | FOREIGN KEY (author_id) REFERENCES users (id), 165 | FOREIGN KEY (technique_id) REFERENCES technique (id), 166 | FOREIGN KEY (subtechnique_id) REFERENCES subtechnique (id) 167 | ); 168 | 169 | 170 | -- Industries taken from STIX 171 | -- https://docs.oasis-open.org/cti/stix/v2.1/cs01/stix-v2.1-cs01.html#_oogrswk3onck 172 | 173 | CREATE TABLE industries ( 174 | id INTEGER PRIMARY KEY AUTOINCREMENT, 175 | author_id INTEGER NOT NULL, 176 | created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 177 | industry_name TEXT NOT NULL, 178 | industry_description TEXT, 179 | FOREIGN KEY (author_id) REFERENCES users (id) 180 | ); 181 | 182 | CREATE TABLE countries ( 183 | id INTEGER PRIMARY KEY AUTOINCREMENT, 184 | author_id INTEGER NOT NULL, 185 | created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 186 | code TEXT NOT NULL, 187 | ctld TEXT NOT NULL, 188 | country TEXT NOT NULL, 189 | FOREIGN KEY (author_id) REFERENCES users (id) 190 | ); 191 | 192 | CREATE TABLE events_x_industries ( 193 | id INTEGER PRIMARY KEY AUTOINCREMENT, 194 | author_id INTEGER NOT NULL, 195 | created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 196 | event_id TEXT NOT NULL, 197 | industry_id TEXT NOT NULL, 198 | FOREIGN KEY (author_id) REFERENCES users (id), 199 | FOREIGN KEY (event_id) REFERENCES event (id), 200 | FOREIGN KEY (industry_id) REFERENCES industry (id) 201 | ); 202 | 203 | INSERT INTO adversaries (author_id, adversary_id, adversary_name, adversary_description) 204 | VALUES (1, 1, 'Unknown', 'Unknown'); 205 | 206 | INSERT INTO tools (author_id, tool_id, tool_name, tool_description) 207 | VALUES (1, 1, 'Unknown', 'Unknown'); 208 | 209 | INSERT INTO industries (author_id, industry_name) 210 | VALUES (1, 'Agriculture'), 211 | (1, 'Aerospace'), 212 | (1, 'Automotive'), 213 | (1, 'Chemical'), 214 | (1, 'Commercial'), 215 | (1, 'Communications'), 216 | (1, 'Construction'), 217 | (1, 'Defense'), 218 | (1, 'Education'), 219 | (1, 'Energy'), 220 | (1, 'Entertainment'), 221 | (1, 'Financial Services'), 222 | (1, 'Government'), 223 | (1, 'Healthcare'), 224 | (1, 'Hospitality & Leisure'), 225 | (1, 'Infrastructure'), 226 | (1, 'Insurance'), 227 | (1, 'Manufacturing'), 228 | (1, 'Mining'), 229 | (1, 'Non-profit'), 230 | (1, 'Pharmaceuticals'), 231 | (1, 'Retail'), 232 | (1, 'Technology'), 233 | (1, 'Telecommunications'), 234 | (1, 'Transportation'), 235 | (1, 'Utilities'), 236 | (1, 'Unspecified'), 237 | (1, 'Unknown'); 238 | 239 | INSERT INTO countries (author_id, code, ctld, country) 240 | VALUES (1, 'AFG', 'AF', 'Afghanistan'), 241 | (1, 'ALA', 'AX', 'Aland Islands'), 242 | (1, 'ALB', 'AL', 'Albania'), 243 | (1, 'DZA', 'DZ', 'Algeria'), 244 | (1, 'ASM', 'AS', 'American Samoa'), 245 | (1, 'AGO', 'AO', 'Angola'), 246 | (1, 'AIA', 'AI', 'Anguilla'), 247 | (1, 'ATA', 'AQ', 'Antarctica'), 248 | (1, 'ATG', 'AG', 'Antigua and Barbuda'), 249 | (1, 'ARG', 'AR', 'Argentina'), 250 | (1, 'ARM', 'AM', 'Armenia'), 251 | (1, 'ABW', 'AW', 'Aruba'), 252 | (1, 'AUS', 'AU', 'Australia'), 253 | (1, 'AUT', 'AT', 'Austria'), 254 | (1, 'AZE', 'AZ', 'Azerbaijan'), 255 | (1, 'BHS', 'BS', 'Bahamas'), 256 | (1, 'BHR', 'BH', 'Bahrain'), 257 | (1, 'BGD', 'BD', 'Bangladesh'), 258 | (1, 'BRB', 'BB', 'Barbados'), 259 | (1, 'BLR', 'BY', 'Belarus'), 260 | (1, 'BEL', 'BE', 'Belgium'), 261 | (1, 'BLZ', 'BZ', 'Belize'), 262 | (1, 'BEN', 'BJ', 'Benin'), 263 | (1, 'BMU', 'BM', 'Bermuda'), 264 | (1, 'BTN', 'BT', 'Bhutan'), 265 | (1, 'BOL', 'BO', 'Bolivia'), 266 | (1, 'BIH', 'BA', 'Bosnia and Herzegovina'), 267 | (1, 'BWA', 'BW', 'Botswana'), 268 | (1, 'BVT', 'BV', 'Bouvet Island'), 269 | (1, 'BRA', 'BR', 'Brazil'), 270 | (1, 'VGB', 'VG', 'British Virgin Islands'), 271 | (1, 'IOT', 'IO', 'British Indian Ocean Territory'), 272 | (1, 'BRN', 'BN', 'Brunei'), 273 | (1, 'BGR', 'BG', 'Bulgaria'), 274 | (1, 'BFA', 'BF', 'Burkina Faso'), 275 | (1, 'BDI', 'BI', 'Burundi'), 276 | (1, 'KHM', 'KH', 'Cambodia'), 277 | (1, 'CMR', 'CM', 'Cameroon'), 278 | (1, 'CAN', 'CA', 'Canada'), 279 | (1, 'CPV', 'CV', 'Cape Verde'), 280 | (1, 'CYM', 'KY', 'Cayman Islands'), 281 | (1, 'CAF', 'CF', 'Central African Republic'), 282 | (1, 'TCD', 'TD', 'Chad'), 283 | (1, 'CHL', 'CL', 'Chile'), 284 | (1, 'CHN', 'CN', 'China'), 285 | (1, 'HKG', 'HK', 'Hong Kong'), 286 | (1, 'MAC', 'MO', 'Macau'), 287 | (1, 'CXR', 'CX', 'Christmas Island'), 288 | (1, 'CCK', 'CC', 'Cocos Islands'), 289 | (1, 'COL', 'CO', 'Colombia'), 290 | (1, 'COM', 'KM', 'Comoros'), 291 | (1, 'COG', 'CG', 'Republic of the Congo'), 292 | (1, 'COD', 'CD', 'Democratic Republic of the Congo'), 293 | (1, 'COK', 'CK', 'Cook Islands'), 294 | (1, 'CRI', 'CR', 'Costa Rica'), 295 | (1, 'CIV', 'CI', 'Côte d''Ivoire'), 296 | (1, 'HRV', 'HR', 'Croatia'), 297 | (1, 'CUB', 'CU', 'Cuba'), 298 | (1, 'CYP', 'CY', 'Cyprus'), 299 | (1, 'CZE', 'CZ', 'Czech Republic'), 300 | (1, 'DNK', 'DK', 'Denmark'), 301 | (1, 'DJI', 'DJ', 'Djibouti'), 302 | (1, 'DMA', 'DM', 'Dominica'), 303 | (1, 'DOM', 'DO', 'Dominican Republic'), 304 | (1, 'ECU', 'EC', 'Ecuador'), 305 | (1, 'EGY', 'EG', 'Egypt'), 306 | (1, 'SLV', 'SV', 'El Salvador'), 307 | (1, 'GNQ', 'GQ', 'Equatorial Guinea'), 308 | (1, 'ERI', 'ER', 'Eritrea'), 309 | (1, 'EST', 'EE', 'Estonia'), 310 | (1, 'ETH', 'ET', 'Ethiopia'), 311 | (1, 'FLK', 'FK', 'Falkland Islands'), 312 | (1, 'FRO', 'FO', 'Faroe Islands'), 313 | (1, 'FJI', 'FJ', 'Fiji'), 314 | (1, 'FIN', 'FI', 'Finland'), 315 | (1, 'FRA', 'FR', 'France'), 316 | (1, 'GUF', 'GF', 'French Guiana'), 317 | (1, 'PYF', 'PF', 'French Polynesia'), 318 | (1, 'ATF', 'TF', 'French Southern Territories'), 319 | (1, 'GAB', 'GA', 'Gabon'), 320 | (1, 'GMB', 'GM', 'Gambia'), 321 | (1, 'GEO', 'GE', 'Georgia'), 322 | (1, 'DEU', 'DE', 'Germany'), 323 | (1, 'GHA', 'GH', 'Ghana'), 324 | (1, 'GIB', 'GI', 'Gibraltar'), 325 | (1, 'GRC', 'GR', 'Greece'), 326 | (1, 'GRL', 'GL', 'Greenland'), 327 | (1, 'GRD', 'GD', 'Grenada'), 328 | (1, 'GLP', 'GP', 'Guadeloupe'), 329 | (1, 'GUM', 'gu', 'Guam'), 330 | (1, 'GTM', 'GT', 'Guatemala'), 331 | (1, 'GGY', 'GG', 'Guernsey'), 332 | (1, 'GIN', 'GN', 'Guinea'), 333 | (1, 'GNB', 'GW', 'Guinea-Bissau'), 334 | (1, 'GUY', 'GY', 'Guyana'), 335 | (1, 'HTI', 'HT', 'Haiti'), 336 | (1, 'HMD', 'HM', 'Heard and Mcdonald Islands'), 337 | (1, 'VAT', 'VA', 'Vatican'), 338 | (1, 'HND', 'HN', 'Honduras'), 339 | (1, 'HUN', 'HU', 'Hungary'), 340 | (1, 'ISL', 'IS', 'Iceland'), 341 | (1, 'IND', 'IN', 'India'), 342 | (1, 'IDN', 'ID', 'Indonesia'), 343 | (1, 'IRN', 'IR', 'Iran'), 344 | (1, 'IRQ', 'IQ', 'Iraq'), 345 | (1, 'IRL', 'IE', 'Ireland'), 346 | (1, 'IMN', 'IM', 'Isle of Man'), 347 | (1, 'ISR', 'IL', 'Israel'), 348 | (1, 'ITA', 'IT', 'Italy'), 349 | (1, 'JAM', 'JM', 'Jamaica'), 350 | (1, 'JPN', 'JP', 'Japan'), 351 | (1, 'JEY', 'JE', 'Jersey'), 352 | (1, 'JOR', 'JO', 'Jordan'), 353 | (1, 'KAZ', 'KZ', 'Kazakhstan'), 354 | (1, 'KEN', 'KE', 'Kenya'), 355 | (1, 'KIR', 'KI', 'Kiribati'), 356 | (1, 'KOR', 'KR', 'South Korea'), 357 | (1, 'PRK', 'KP', 'North Korea'), 358 | (1, 'KWT', 'KW', 'Kuwait'), 359 | (1, 'KGZ', 'KG', 'Kyrgyzstan'), 360 | (1, 'LAO', 'LA', 'Laos'), 361 | (1, 'LVA', 'LV', 'Latvia'), 362 | (1, 'LBN', 'LB', 'Lebanon'), 363 | (1, 'LSO', 'LS', 'Lesotho'), 364 | (1, 'LBR', 'LR', 'Liberia'), 365 | (1, 'LBY', 'LY', 'Libya'), 366 | (1, 'LIE', 'LI', 'Liechtenstein'), 367 | (1, 'LTU', 'LT', 'Lithuania'), 368 | (1, 'LUX', 'LU', 'Luxembourg'), 369 | (1, 'MKD', 'MK', 'Macedonia'), 370 | (1, 'MDG', 'MG', 'Madagascar'), 371 | (1, 'MWI', 'MW', 'Malawi'), 372 | (1, 'MYS', 'MY', 'Malaysia'), 373 | (1, 'MDV', 'MV', 'Maldives'), 374 | (1, 'MLI', 'ML', 'Mali'), 375 | (1, 'MLT', 'MT', 'Malta'), 376 | (1, 'MHL', 'MH', 'Marshall Islands'), 377 | (1, 'MTQ', 'MQ', 'Martinique'), 378 | (1, 'MRT', 'MR', 'Mauritania'), 379 | (1, 'MUS', 'MU', 'Mauritius'), 380 | (1, 'MYT', 'YT', 'Mayotte'), 381 | (1, 'MEX', 'MX', 'Mexico'), 382 | (1, 'FSM', 'FM', 'Micronesia'), 383 | (1, 'MDA', 'MD', 'Moldova'), 384 | (1, 'MCO', 'MC', 'Monaco'), 385 | (1, 'MNG', 'MN', 'Mongolia'), 386 | (1, 'MNE', 'ME', 'Montenegro'), 387 | (1, 'MSR', 'MS', 'Montserrat'), 388 | (1, 'MAR', 'MA', 'Morocco'), 389 | (1, 'MOZ', 'MZ', 'Mozambique'), 390 | (1, 'MMR', 'MM', 'Myanmar'), 391 | (1, 'NAM', 'NA', 'Namibia'), 392 | (1, 'NRU', 'NR', 'Nauru'), 393 | (1, 'NPL', 'NP', 'Nepal'), 394 | (1, 'NLD', 'NL', 'Netherlands'), 395 | (1, 'ANT', 'AN', 'Netherlands Antilles'), 396 | (1, 'NCL', 'NC', 'New Caledonia'), 397 | (1, 'NZL', 'NZ', 'New Zealand'), 398 | (1, 'NIC', 'NI', 'Nicaragua'), 399 | (1, 'NER', 'NE', 'Niger'), 400 | (1, 'NGA', 'NG', 'Nigeria'), 401 | (1, 'NIU', 'NU', 'Niue'), 402 | (1, 'NFK', 'NF', 'Norfolk Island'), 403 | (1, 'MNP', 'MP', 'Northern Mariana Islands'), 404 | (1, 'NOR', 'NO', 'Norway'), 405 | (1, 'OMN', 'OM', 'Oman'), 406 | (1, 'PAK', 'PK', 'Pakistan'), 407 | (1, 'PLW', 'PW', 'Palau'), 408 | (1, 'PSE', 'PS', 'Palestine'), 409 | (1, 'PAN', 'PA', 'Panama'), 410 | (1, 'PNG', 'PG', 'Papua New Guinea'), 411 | (1, 'PRY', 'PY', 'Paraguay'), 412 | (1, 'PER', 'PE', 'Peru'), 413 | (1, 'PHL', 'PH', 'Philippines'), 414 | (1, 'PCN', 'PN', 'Pitcairn'), 415 | (1, 'POL', 'PL', 'Poland'), 416 | (1, 'PRT', 'PT', 'Portugal'), 417 | (1, 'PRI', 'PR', 'Puerto Rico'), 418 | (1, 'QAT', 'QA', 'Qatar'), 419 | (1, 'REU', 'RE', 'Réunion'), 420 | (1, 'ROU', 'RO', 'Romania'), 421 | (1, 'RUS', 'RU', 'Russia'), 422 | (1, 'RWA', 'RW', 'Rwanda'), 423 | (1, 'BES', 'BQ', 'Saba'), 424 | (1, 'BLM', 'BL', 'Saint-Barthélemy'), 425 | (1, 'SHN', 'SH', 'Saint Helena'), 426 | (1, 'KNA', 'KN', 'Saint Kitts and Nevis'), 427 | (1, 'LCA', 'LC', 'Saint Lucia'), 428 | (1, 'MAF', 'MF', 'Saint-Martin'), 429 | (1, 'SPM', 'PM', 'Saint Pierre and Miquelon'), 430 | (1, 'VCT', 'VC', 'Saint Vincent and Grenadines'), 431 | (1, 'WSM', 'WS', 'Samoa'), 432 | (1, 'SMR', 'SM', 'San Marino'), 433 | (1, 'STP', 'ST', 'Sao Tome and Principe'), 434 | (1, 'SAU', 'SA', 'Saudi Arabia'), 435 | (1, 'SEN', 'SN', 'Senegal'), 436 | (1, 'SRB', 'RS', 'Serbia'), 437 | (1, 'SYC', 'SC', 'Seychelles'), 438 | (1, 'SLE', 'SL', 'Sierra Leone'), 439 | (1, 'SGP', 'SG', 'Singapore'), 440 | (1, 'SVK', 'SK', 'Slovakia'), 441 | (1, 'SVN', 'SI', 'Slovenia'), 442 | (1, 'SLB', 'SB', 'Solomon Islands'), 443 | (1, 'SOM', 'SO', 'Somalia'), 444 | (1, 'ZAF', 'ZA', 'South Africa'), 445 | (1, 'SGS', 'GS', 'South Georgia and the South Sandwich Islands'), 446 | (1, 'SSD', 'SS', 'South Sudan'), 447 | (1, 'ESP', 'ES', 'Spain'), 448 | (1, 'LKA', 'LK', 'Sri Lanka'), 449 | (1, 'SDN', 'SD', 'Sudan'), 450 | (1, 'SUR', 'SR', 'Suriname'), 451 | (1, 'SJM', 'SJ', 'Svalbard and Jan Mayen Islands'), 452 | (1, 'SWZ', 'SZ', 'Swaziland'), 453 | (1, 'SWE', 'SE', 'Sweden'), 454 | (1, 'CHE', 'CH', 'Switzerland'), 455 | (1, 'SYR', 'SY', 'Syria'), 456 | (1, 'TWN', 'TW', 'Taiwan'), 457 | (1, 'TJK', 'TJ', 'Tajikistan'), 458 | (1, 'TZA', 'TZ', 'Tanzania'), 459 | (1, 'THA', 'TH', 'Thailand'), 460 | (1, 'TLS', 'TL', 'Timor-Leste'), 461 | (1, 'TGO', 'TG', 'Togo'), 462 | (1, 'TKL', 'TK', 'Tokelau'), 463 | (1, 'TON', 'TO', 'Tonga'), 464 | (1, 'TTO', 'TT', 'Trinidad and Tobago'), 465 | (1, 'TUN', 'TN', 'Tunisia'), 466 | (1, 'TUR', 'TR', 'Turkey'), 467 | (1, 'TKM', 'TM', 'Turkmenistan'), 468 | (1, 'TCA', 'TC', 'Turks and Caicos Islands'), 469 | (1, 'TUV', 'TV', 'Tuvalu'), 470 | (1, 'UGA', 'UG', 'Uganda'), 471 | (1, 'UKR', 'UA', 'Ukraine'), 472 | (1, 'ARE', 'AE', 'United Arab Emirates'), 473 | (1, 'GBR', 'UK', 'United Kingdom'), 474 | (1, 'USA', 'US', 'United States of America'), 475 | (1, 'UMI', 'UM', 'US Minor Outlying Islands'), 476 | (1, 'URY', 'UY', 'Uruguay'), 477 | (1, 'UZB', 'UZ', 'Uzbekistan'), 478 | (1, 'VUT', 'VU', 'Vanuatu'), 479 | (1, 'VEN', 'VE', 'Venezuela'), 480 | (1, 'VNM', 'VN', 'Vietnam'), 481 | (1, 'VIR', 'VI', 'Virgin Islands'), 482 | (1, 'WLF', 'WF', 'Wallis and Futuna Islands'), 483 | (1, 'ESH', 'EH', 'Western Sahara'), 484 | (1, 'YEM', 'YE', 'Yemen'), 485 | (1, 'ZMB', 'ZM', 'Zambia'), 486 | (1, 'ZWE', 'ZW', 'Zimbabwe'), 487 | (1, 'MMR', 'MM', 'Myanmar'), 488 | (1, 'CUW', 'CW', 'Curaçao'), 489 | (1, 'SXM', 'SX', 'Sint Maarten'); -------------------------------------------------------------------------------- /tmc/maps.py: -------------------------------------------------------------------------------- 1 | from flask import ( 2 | Blueprint, flash, g, redirect, render_template, request, url_for 3 | ) 4 | from werkzeug.exceptions import abort 5 | from tmc.auth import login_required 6 | from tmc.db import get_db 7 | from IPython import embed 8 | import tmc.queries as q 9 | from tmc.queries import * 10 | from random import random 11 | import tmc.processor as processor 12 | import tmc.config as config 13 | import time 14 | import json 15 | import ast 16 | import requests 17 | import csv 18 | import os 19 | import uuid 20 | import urllib 21 | 22 | 23 | bp = Blueprint('maps', __name__, template_folder='templates') 24 | 25 | 26 | # Loading ATT&CK to DB for the first time 27 | @bp.route('/first-time') 28 | @login_required 29 | def first_time(): 30 | 31 | print('Interacting with ATTACKCTI...') 32 | processor.get_elements() 33 | message="You can now start exploring the database." 34 | 35 | return render_template('maps/completed.html', message=message) 36 | 37 | 38 | # Homepage 39 | @bp.route('/', methods=["GET", "POST"]) 40 | def index(): 41 | 42 | adversaries_list = q.q_get_adversaries.get_adversaries() 43 | tools_list = q.q_get_tools.get_tools() 44 | industries_list = q.q_get_industries.get_industries() 45 | 46 | return render_template('maps/welcome.html', adversaries_list=adversaries_list, tools_list=tools_list, industries_list=industries_list) 47 | 48 | 49 | @bp.route('/tram-interaction', methods=["GET", "POST"]) 50 | def tram_mapping(): 51 | 52 | if request.method == "POST": 53 | 54 | req = request.form 55 | adversary_dict = ast.literal_eval(req['adversary']) # convert string dict into real dict 56 | adversary = adversary_dict['db_id'] 57 | tool_dict = ast.literal_eval(req['tool']) # convert string dict into real dict 58 | tool = tool_dict['db_id'] 59 | industry_dict = ast.literal_eval(req['industry']) # convert string dict into real dict 60 | industry = industry_dict['db_id'] 61 | event_name = req['event_name'] 62 | event_description = req['event_description'] 63 | event_url = req['url'] 64 | 65 | # Creates the security event and all its relationships 66 | event = q.q_insert_into_events.insert_into_events(event_name, event_description, event_url) 67 | adv_x_event = q.q_insert_adversary_x_event.insert_adversary_x_event(adversary, event) 68 | event_x_ind = q.q_insert_event_x_industry.insert_event_x_industry(event, industry) 69 | # Sends the URL provided to TRAM for mapping 70 | tram_id=str(tool)+'_'+event_name 71 | print(tram_id) 72 | send_url_to_tram(tram_id, event_url) 73 | message = 'Please complete TRAM workflow before continuing. TMC will automatically process TRAM mapping when done. Please wait before continuing..' 74 | 75 | return render_template('maps/completed.html', message=message) 76 | 77 | 78 | # Issues POST request to TRAM with the report URL you want to process 79 | def send_url_to_tram(tram_id, event_url): 80 | 81 | tram_insert = { 82 | "index":"insert_report", 83 | "url": [event_url], 84 | "title": [tram_id], 85 | "request": True 86 | } 87 | url = config.config_dict['tram'] + "/rest" 88 | requests.post(url=url, json=tram_insert, headers={"content-type":"application/json"}) 89 | 90 | 91 | # Process the techniques sent by TRAM issued POST request after analysis completion 92 | @bp.route('/tram-response', methods=['GET', 'POST']) 93 | def wait_tram_response(): 94 | 95 | if request.method == 'POST': 96 | tram_response = json.loads(request.data) 97 | print(tram_response) 98 | 99 | try: 100 | 101 | tool_id = tram_response[0]['title'].split('_')[0] 102 | 103 | for element in tram_response: 104 | if '.' in element['attack_tid']: 105 | subtechnique_attack_id = element['attack_tid'] 106 | db_id = q.q_get_element_id.get_element_id('subtechniques', 'subtechnique_id', subtechnique_attack_id) 107 | tool_x_techniques = q_insert_tool_x_subtechn.insert_tool_x_subtechn('tools_x_subtechniques', tool_id, db_id) 108 | else: 109 | technique_attack_id = element['attack_tid'] 110 | db_id = q.q_get_element_id.get_element_id('techniques', 'technique_id', technique_attack_id) 111 | tools_x_subtechniques = q.q_insert_tool_x_techn.insert_tool_x_techn('tools_x_techniques', tool_id, db_id) 112 | 113 | message="Tram interaction has been completed. You can now explore your mapping in the TMC." 114 | 115 | except IndexError: 116 | message="TMC did not expcet a TRAM request for this report." 117 | 118 | return render_template('maps/completed.html', message=message) 119 | 120 | # Navigator Functions 121 | 122 | 123 | # Open Navigator 124 | @bp.route('/nav//') 125 | def open_in_nav(element, element_id): 126 | nav_dict = { 127 | "name":"Threat Mapping Catalogue", 128 | "versions": { 129 | "attack": "8", 130 | "navigator": "4.0", 131 | "layer": "4.0" 132 | }, 133 | "description": element + " related techniques", 134 | "domain":"mitre-enterprise", 135 | "gradient":{ 136 | "colors":[ 137 | "#778ca3", 138 | "#778ca3" 139 | ], 140 | "minValue":0, 141 | "maxValue":100 142 | }, 143 | "legendItems":[ 144 | { 145 | "label":"Has at least one test", 146 | "color":"#ce232e" 147 | } 148 | ], 149 | "techniques":[] 150 | } 151 | 152 | element_techniques = '' 153 | 154 | # Both queries bring techniques & subtechniques 155 | if element == 'adversary': 156 | element_techniques = q_get_adversaries_techniques.get_adversaries_techniques(element_id) 157 | elif element == 'tool': 158 | element_techniques = q_get_tools_techniques.get_tools_techniques(element_id) 159 | 160 | for technique in element_techniques: 161 | if technique['SubtechniqueID']: 162 | new_technique = { 163 | "techniqueID": technique['SubtechniqueID'], 164 | "score":100, 165 | "enabled":True 166 | } 167 | else: 168 | new_technique = { 169 | "techniqueID": technique['TechniqueID'], 170 | "score":100, 171 | "enabled":True 172 | } 173 | 174 | nav_dict['techniques'].append(new_technique) 175 | 176 | # Creates a json file with the retrieved information from the database 177 | filename = create_nav_file(nav_dict, element, element_id) 178 | # Creates the url with the path to the created file to send it to the navigator 179 | encoded_path = urllib.parse.quote_plus(str(config.config_dict['tmc'])+'/static/export/'+str(filename)) 180 | encoded_url = str(config.config_dict['navigator']+'/fetch/'+encoded_path) 181 | 182 | return redirect(encoded_url) 183 | 184 | 185 | def create_nav_file(nav_dict, element, element_id): 186 | filename = element + '_' + str(element_id) + '_' + str(uuid.uuid1()) + '.json' 187 | directory = os.path.dirname(os.path.realpath(__file__)) + '/static/export/' 188 | filepath = directory + filename 189 | 190 | with open(filepath, 'w+') as fh: 191 | json.dump(nav_dict, fh) 192 | fh.close() 193 | 194 | return filename 195 | 196 | # Export Results 197 | @bp.route('/export//') 198 | def export_file(element, element_id): 199 | 200 | # Both queries bring techniques & subtechniques 201 | if element == 'adversary': 202 | element_techniques = q_get_adversaries_techniques.get_adversaries_techniques(element_id) 203 | create_csv_file(element_techniques, element, element_id) 204 | elif element == 'tool': 205 | element_techniques = q_get_tools_techniques.get_tools_techniques(element_id) 206 | create_csv_file(element_techniques, element, element_id) 207 | 208 | return render_template('maps/explore/main.html') 209 | 210 | def create_csv_file(element_techniques, element, element_id): 211 | 212 | headers = 'Tool \t Technique ID \t Technique \t Subtechnique ID \t Subtechnique' 213 | filename = element + '_' + str(element_id) + '_techniques_mapped_' + str(uuid.uuid1()) +'.csv' 214 | parent = os.path.abspath(os.path.join(os.path.dirname( __file__ ), '..')) 215 | filepath = parent + '/export/' + filename 216 | 217 | 218 | with open(filepath, 'w+', newline='') as new_file: 219 | new_file.write(headers +'\n') 220 | for line in element_techniques: 221 | for key, value in line.items(): 222 | new_file.write(str(value) + '\t') 223 | new_file.write('\n') 224 | new_file.close() 225 | 226 | ''' 227 | # Download SVG --------------- 228 | @bp.route('/svg//') 229 | def download_svg(element, element_id): 230 | 231 | # Both queries bring techniques & subtechniques 232 | if element == 'adversary': 233 | element_techniques = q_get_adversaries_techniques.get_adversaries_techniques(element_id) 234 | elif element == 'tool': 235 | element_techniques = q_get_tools_techniques.get_tools_techniques(element_id) 236 | 237 | 238 | return render_template('maps/explore/main.html') 239 | ''' 240 | 241 | # List all posible actions to display data 242 | @bp.route('/explore') 243 | def explore(): 244 | 245 | return render_template('maps/explore/main.html') 246 | 247 | 248 | # Displays all adversaries in the DB 249 | @bp.route('/explore-adversaries') 250 | def explore_adversaries(): 251 | 252 | title='Adversaries' 253 | adversaries = q.q_get_adversaries.get_adversaries() 254 | adversaries_th = adversaries[0].keys() 255 | 256 | return render_template('maps/explore/explore-element.html', title=title, element_th=adversaries_th, paginated_element=adversaries) 257 | 258 | 259 | # Displays all events in the DB 260 | @bp.route('/explore-events') 261 | def explore_events(): 262 | 263 | title='Events' 264 | events = q.q_get_events.get_events() 265 | try: 266 | events_th = events[0].keys() #add test for no elements 267 | except IndexError: 268 | return render_template('maps/no-data.html') 269 | else: 270 | return render_template('maps/explore/explore-element.html', title=title, events=events, element_th=events_th, paginated_element=events) 271 | 272 | 273 | # Displays all tools in the DB 274 | @bp.route('/explore-tools') 275 | def explore_tools(): 276 | 277 | title='Tools' 278 | tools = q.q_get_tools.get_tools() 279 | tools_th = tools[0].keys() 280 | 281 | return render_template('maps/explore/explore-element.html', title=title, tools=tools, element_th=tools_th, paginated_element=tools) 282 | 283 | 284 | # Displays all tactics in the DB 285 | @bp.route('/explore-tactics') 286 | def explore_tactics(): 287 | 288 | title='Tactics' 289 | tactics = q.q_get_tactics.get_tactics() 290 | tactics_th = tactics[0].keys() 291 | 292 | return render_template('maps/explore/explore-element.html', title=title, tactics=tactics, element_th=tactics_th, paginated_element=tactics) 293 | 294 | 295 | # Displays all technique in the DB 296 | @bp.route('/explore-techniques') 297 | def explore_techniques(): 298 | 299 | title='Techniques' 300 | techniques = q.q_get_techniques.get_techniques() 301 | techniques_th = techniques[0].keys() 302 | 303 | return render_template('maps/explore/explore-element.html', title=title, techniques=techniques, element_th=techniques_th, paginated_element=techniques) 304 | 305 | 306 | # Displays all subtechniques in the DB 307 | @bp.route('/explore-subtechniques') 308 | def explore_subtechniques(): 309 | 310 | title='Subtechniques' 311 | subtechniques = q.q_get_subtechniques.get_subtechniques() 312 | subtechniques_th = subtechniques[0].keys() 313 | 314 | return render_template('maps/explore/explore-element.html', title=title, techniques=subtechniques, element_th=subtechniques_th, paginated_element=subtechniques) 315 | 316 | 317 | # DB Analysis Screens 318 | 319 | # Display Adversaries per event 320 | @bp.route('/adversaries-x-event') 321 | def get_adversaries_x_event(): 322 | 323 | title='Adversaries per event' 324 | 325 | adversaries_x_event = q.q_get_adversaries_x_event.get_adversaries_x_event() 326 | adversaries_x_event_th = adversaries_x_event[0].keys() 327 | 328 | if not adversaries_x_event: 329 | return render_template('maps/no-data.html') 330 | 331 | return render_template('maps/explore/explore-element.html', title=title, element_th=adversaries_x_event_th, paginated_element=adversaries_x_event) 332 | 333 | 334 | # Display Adversaries per suspected origin 335 | @bp.route('/adversaries-x-sorigin') 336 | def get_adversaries_x_sorigin(): 337 | 338 | title='Adversaries Suspected Origin' 339 | adversaries_x_sorigin = q.q_get_adversaries_sorigin.get_adversaries_sorigin() 340 | 341 | try: 342 | adversaries_x_sorigin_th = adversaries_x_sorigin[0].keys() 343 | 344 | except IndexError: 345 | return render_template('maps/no-data.html') 346 | 347 | return render_template('maps/explore/explore-element.html', title=title, element_th=adversaries_x_sorigin_th, paginated_element=adversaries_x_sorigin) 348 | 349 | 350 | # Display Adversaries per industry 351 | @bp.route('/adversaries-x-industry') 352 | def get_adversaries_x_industry(): 353 | 354 | adversaries_x_industry = '' 355 | 356 | if not adversaries_x_industry: 357 | return render_template('maps/no-data.html') 358 | 359 | else: 360 | return render_template('maps/explore/explore-element.html', title=title, element_th=adversaries_x_sorigin_th, paginated_element=adversaries_x_sorigin) 361 | 362 | 363 | # Display events by industry 364 | @bp.route('/events-x-industry') 365 | def get_events_x_industry(): 366 | 367 | title = 'Events per industry' 368 | events_x_industry = q.q_get_events_x_industry.get_events_x_industry() 369 | events_x_industry_th = events_x_industry[0].keys() 370 | 371 | if not events_x_industry: 372 | return render_template('maps/no-data.html') 373 | 374 | else: 375 | return render_template('maps/explore/explore-element.html', title=title, element_th=events_x_industry_th, paginated_element=events_x_industry) 376 | 377 | 378 | # Display Techniques per industry 379 | @bp.route('/techniques-per-industry') 380 | def get_techniques_per_industry(): 381 | 382 | techniques_per_industry = '' 383 | 384 | if not techniques_per_industry: 385 | return render_template('maps/no-data.html') 386 | 387 | else: 388 | 389 | return render_template('maps/explore/explore-element.html', title=title, element_th=adversaries_x_sorigin_th, paginated_element=adversaries_x_sorigin) 390 | 391 | 392 | # Display Adversaries per tool 393 | @bp.route('/adversaries-x-tool') 394 | def get_adversaries_x_tool(): 395 | 396 | title='Adversaries per tool' 397 | 398 | adversaries_x_tool = q.q_get_adversaries_x_tool.get_adversaries_x_tool() 399 | adversaries_x_tool_th = adversaries_x_tool[0].keys() 400 | 401 | return render_template('maps/explore/explore-element.html', title=title, element_th=adversaries_x_tool_th, paginated_element=adversaries_x_tool) 402 | 403 | 404 | # Display Adversaries per technique 405 | @bp.route('/adversaries-x-technique') 406 | def get_adversaries_x_technique(): 407 | 408 | title = 'Adversaries per technique' 409 | 410 | adversaries_x_technique = q.q_get_adversaries_x_technique.get_adversaries_x_technique() 411 | adversaries_x_technique_th = adversaries_x_technique[0].keys() 412 | 413 | return render_template('maps/explore/explore-element.html', title=title, element_th=adversaries_x_technique_th, paginated_element=adversaries_x_technique) 414 | 415 | 416 | # Display Tools per techniques 417 | @bp.route('/tools-x-techniques') 418 | def get_tools_x_techniques(): 419 | 420 | title = 'Tools per technique' 421 | 422 | tools_x_techniques = q.q_get_tools_x_techniques.get_tools_x_techniques() 423 | tools_x_techniques_th = tools_x_techniques[0].keys() 424 | 425 | return render_template('maps/explore/explore-element.html', title=title, element_th=tools_x_techniques_th, paginated_element=tools_x_techniques) 426 | 427 | 428 | # Display Most used techniques 429 | @bp.route('/most-used-technique') 430 | def get_most_used_techniques(): 431 | 432 | title = 'Most used techniques' 433 | 434 | most_used_techniques = q.q_get_most_used_techniques.get_most_used_techniques() 435 | most_used_techniques_th = most_used_techniques[0].keys() 436 | 437 | return render_template('maps/explore/explore-element.html', title=title, element_th=most_used_techniques_th, paginated_element=most_used_techniques) 438 | 439 | 440 | # Creates the new event in the database 441 | @bp.route('/create-events', methods=('GET', 'POST')) 442 | @login_required 443 | def create_event(): 444 | 445 | return render_template('maps/creation/create-adversary.html') 446 | 447 | 448 | # DB INTERACTION FROM FRONT-END 449 | 450 | @bp.route('/create-adversary', methods=('GET', 'POST')) 451 | @login_required 452 | def create_adversary(): 453 | 454 | countries_list = q.q_get_countries.get_countries() 455 | 456 | if request.method == "POST": 457 | adversary_id = request.form['adversary_id'] 458 | adversary_name = request.form['adversary_name'] 459 | adversary_description = request.form['description'] 460 | adversary_identifiers = request.form['adversary_identifiers'] 461 | adversary_origin = request.form['sorigin'] 462 | error = None 463 | 464 | if not adversary_name: 465 | error = 'Adversary name is required.' 466 | 467 | if error is not None: 468 | flash(error) 469 | else: 470 | db = get_db() 471 | db.execute( 472 | 'INSERT INTO adversaries (adversary_id, adversary_name, adversary_description, adversary_identifiers, adversary_sorigin, author_id)' 473 | ' VALUES (?, ?, ?, ?, ?, ?)', 474 | (adversary_id, adversary_name, adversary_description, adversary_identifiers, adversary_origin, g.user['id']) 475 | ) 476 | db.commit() 477 | message = 'Successfully created Adversary' 478 | return render_template('maps/completed.html', message=message) 479 | 480 | return render_template('maps/creation/create-adversary.html', countries_list=countries_list, request_adversary = '') 481 | 482 | 483 | @bp.route('/edit/adversary/', methods=('GET', 'POST')) 484 | def render_edit_adversary(element): 485 | 486 | countries_list = q.q_get_countries.get_countries() 487 | request_adversary=q.q_get_adversaries.get_adversaries(element) 488 | request_adversary_techniques=q.q_get_adversaries_techniques.get_adversaries_techniques(element) 489 | 490 | if request_adversary_techniques: 491 | adversary_techniques_th = request_adversary_techniques[0].keys() 492 | else: 493 | adversary_techniques_th = '' 494 | 495 | if not request_adversary: 496 | return render_template('maps/404.html') 497 | else: 498 | return render_template('maps/creation/create-adversary.html', request_adversary_techniques=request_adversary_techniques, element_th=adversary_techniques_th, request_adversary=request_adversary, countries_list=countries_list) 499 | 500 | 501 | @bp.route('/edit-adversary', methods=('GET', 'POST')) 502 | @login_required 503 | def edit_adversary(): 504 | try: 505 | if request.method == 'POST': 506 | edited = request.form 507 | db_id = edited['db_id'] 508 | adversary_id = edited['adversary_id'] 509 | adversary_name = edited['adversary_name'] 510 | adversary_identifiers = edited['adversary_identifiers'] 511 | adversary_sorigin = edited['sorigin'] 512 | adversary_description = edited['description'] 513 | updated_date = time.strftime('%Y-%m-%d %H:%M:%S') 514 | 515 | db = get_db() 516 | db.execute( 517 | 'UPDATE adversaries SET adversary_id=?, adversary_name=?, adversary_description=?, adversary_identifiers=?, adversary_sorigin=?, updated_date=?, updated_by=? WHERE id=?', 518 | (adversary_id, adversary_name, adversary_description, adversary_identifiers, adversary_sorigin, updated_date, g.user['id'], db_id,) 519 | ) 520 | db.commit() 521 | 522 | message = 'Successfully created adversaries' 523 | 524 | return render_template('maps/completed.html', message=message) 525 | except: 526 | return render_template('maps/404.html') 527 | 528 | 529 | # Creates the new tool in the database 530 | @bp.route('/create-tool', methods=('GET', 'POST')) 531 | @login_required 532 | def create_tool(): 533 | adversary_list = q.q_get_adversaries.get_adversaries() 534 | 535 | if request.method == "POST": 536 | 537 | tool_id = request.form['tool_id'] 538 | tool_name = request.form['tool_name'] 539 | tool_identifiers = request.form['tool_identifiers'] 540 | adversary_id = rast.literal_eval(request.form['related_adversary'])['db_id'] 541 | tool_description = request.form['description'] 542 | updated_date = time.strftime('%Y-%m-%d %H:%M:%S') 543 | 544 | db = get_db() 545 | db.execute( 546 | 'INSERT INTO tools (tool_id, tool_name, tool_description, tool_description, author_id)' 547 | ' VALUES (?, ?, ?, ?, ?)', 548 | (tool_id, tool_name, tool_description, tool_description, g.user['id']) 549 | ) 550 | db.commit() 551 | q_insert_adversary_x_tool.insert_adversary_x_tool(adversary_id, tool_id) 552 | message = 'Successfully created tool' 553 | 554 | return render_template('maps/completed.html', message=message) 555 | 556 | return render_template('maps/creation/create-tool.html', adversary_list=adversary_list) 557 | 558 | 559 | @bp.route('/edit/tool/', methods=('GET', 'POST')) 560 | def render_edit_tool(element): 561 | request_tool = q.q_get_tools.get_tools(element) 562 | adversary_list = q.q_get_adversaries.get_adversaries() 563 | request_tools_techniques = q.q_get_tools_techniques.get_tools_techniques(element) 564 | 565 | if request_tools_techniques: 566 | tool_techniques_th = request_tools_techniques[0].keys() 567 | else: 568 | tool_techniques_th = '' 569 | 570 | if not request_tool: 571 | return render_template('maps/404.html') 572 | else: 573 | return render_template('maps/creation/create-tool.html', request_tools_techniques=request_tools_techniques, element_th=tool_techniques_th, request_tool=request_tool, adversary_list=adversary_list) 574 | 575 | 576 | @bp.route('/edit-tool', methods=('GET', 'POST')) 577 | @login_required 578 | def edit_tool(): 579 | 580 | try: 581 | if request.method == 'POST': 582 | edited = request.form 583 | db_id = edited['db_id'] 584 | tool_id = edited['tool_id'] 585 | tool_name = edited['tool_name'] 586 | tool_identifiers = edited['tool_identifiers'] 587 | adversary_id = ast.literal_eval(request.form['related_adversary'])['db_id'] 588 | tool_description = edited['tool_description'] 589 | updated_date = time.strftime('%Y-%m-%d %H:%M:%S') 590 | 591 | db = get_db() 592 | db.execute( 593 | 'UPDATE tools SET tool_id=?, tool_name=?, tool_description=?, tool_identifiers=?, updated_date=?, updated_by=? WHERE id=?', 594 | (tool_id, tool_name, tool_description, tool_identifiers, updated_date, g.user['id'], db_id,) 595 | ) 596 | db.commit() 597 | q_insert_adversary_x_tool.insert_adversary_x_tool(adversary_id, tool_id) 598 | 599 | return redirect('/explore-tools') 600 | except: 601 | return render_template('maps/404.html') 602 | 603 | 604 | # Creates the new technique in the database 605 | @bp.route('/create-technique', methods=('GET', 'POST')) 606 | @login_required 607 | def create_technique(): 608 | 609 | tactics_list = q.q_get_tactics.get_tactics() 610 | 611 | if request.method == "POST": 612 | 613 | technique_id = request.form['id'] 614 | technique_name = request.form['name'] 615 | technique_description = request.form['description'] 616 | unprocessed_tactic = request.form['tactic'] 617 | processed_tactic_str =request.form['tactic'].replace('\'','\"') 618 | technique_tactic = json.loads(processed_tactic_str) 619 | tactic = technique_tactic['Name'] 620 | 621 | db = get_db() 622 | result = db.execute( 623 | 'INSERT INTO techniques (technique_id, technique_name, technique_description, author_id)' 624 | ' VALUES (?, ?, ?, ?)', 625 | (technique_id, technique_name, technique_description, g.user['id']) 626 | ) 627 | db.commit() 628 | technique_db_id = result.lastrowid 629 | tactic_id = q.q_get_element_id.get_element_id('tactics', 'tactic_name', tactic) 630 | q_insert_tactic_x_technique.insert_tactic_x_technique(tactic_id, technique_db_id) 631 | 632 | message = 'Successfully created technique' 633 | 634 | return render_template('maps/completed.html', message=message) 635 | 636 | return render_template('maps/creation/create-technique.html', tactics_list=tactics_list) 637 | 638 | 639 | @bp.route('/edit/technique/', methods=('GET', 'POST')) 640 | def render_edit_technique(element): 641 | 642 | tactics_list = q.q_get_tactics.get_tactics() 643 | request_technique=q.q_get_techniques.get_techniques(element) 644 | request_related_tools=q.q_get_tools_x_techniques.get_tools_x_techniques(element) 645 | 646 | if request_related_tools: 647 | request_related_tools_th = request_related_tools[0].keys() 648 | else: 649 | request_related_tools_th = '' 650 | 651 | if not request_technique: 652 | return render_template('maps/404.html') 653 | else: 654 | return render_template('maps/creation/create-technique.html', request_related_tools=request_related_tools, element_th=request_related_tools_th, request_technique=request_technique, tactics_list=tactics_list) 655 | 656 | 657 | @bp.route('/edit-technique', methods=('GET', 'POST')) 658 | @login_required 659 | def edit_technique(): 660 | try: 661 | if request.method == 'POST': 662 | edited = request.form 663 | 664 | db_id = edited['db_id'] 665 | technique_id = edited['technique_id'] 666 | technique_name = edited['technique_name'] 667 | tactic_id = ast.literal_eval(request.form['related_tactic'])['db_id'] 668 | technique_description = edited['description'] 669 | updated_date = time.strftime('%Y-%m-%d %H:%M:%S') 670 | 671 | db = get_db() 672 | db.execute( 673 | 'UPDATE tools SET technique_id=?, technique_name=?, technique_description=?, updated_date=?, updated_by=? WHERE id=?', 674 | (technique_id, technique_name, technique_description, updated_date, g.user['id'], db_id,) 675 | ) 676 | db.commit() 677 | 678 | q_insert_tactic_x_technique.insert_tactic_x_technique(tactic_id, technique_db_id) 679 | 680 | return redirect('/explore-techniques') 681 | except: 682 | return render_template('maps/404.html') 683 | 684 | 685 | # Creates the new subtechnique in the database 686 | @bp.route('/create-subtechnique', methods=('GET', 'POST')) 687 | def create_subtechnique(): 688 | 689 | techniques_list = q.q_get_techniques.get_techniques() 690 | 691 | if request.method == "POST": 692 | subtechnique_id = request.form['db_id'] 693 | subtechnique_name = request.form['subtechnique_name'] 694 | technique_id = ast.literal_eval(request.form['related_technique'])['db_id'] 695 | subtechnique_description = request.form['description'] 696 | 697 | db = get_db() 698 | result = db.execute( 699 | 'INSERT INTO subtechniques (subtechnique_id, subtechnique_name, subtechnique_description, author_id)' 700 | ' VALUES (?, ?, ?, ?)', 701 | (subtechnique_id, subtechnique_name, subtechnique_description, g.user['id']) 702 | ) 703 | db.commit() 704 | subtechnique_db_id = result.lastrowid 705 | 706 | insert_into_table = q.q_insert_relation_into_tables.insert_relation_into_tables('techniques_x_subtechniques', 'technique_id', 'subtechnique_id', technique_id, subtechnique_db_id) 707 | 708 | message = 'Successfully created subtechnique' 709 | 710 | return render_template('maps/completed.html', message=message) 711 | 712 | return render_template('maps/creation/create-subtechnique.html', techniques_list=techniques_list) 713 | 714 | 715 | @bp.route('/edit/subtechnique/', methods=('GET', 'POST')) 716 | def render_edit_subtechnique(element): 717 | 718 | techniques_list = q.q_get_techniques.get_techniques() 719 | request_subtechnique=q.q_get_subtechniques.get_subtechniques(element) 720 | request_related_tools=q.q_get_tools_x_subtechniques.get_tools_x_subtechniques(element) 721 | 722 | if request_related_tools: 723 | request_related_tools_th = request_related_tools[0].keys() 724 | else: 725 | request_related_tools_th = '' 726 | 727 | if not request_subtechnique: 728 | return render_template('maps/404.html') 729 | else: 730 | return render_template('maps/creation/create-subtechnique.html', request_related_tools=request_related_tools, element_th=request_related_tools_th, request_subtechnique=request_subtechnique, techniques_list=techniques_list) 731 | 732 | 733 | @bp.route('/edit-subtechnique', methods=('GET', 'POST')) 734 | @login_required 735 | def edit_subtechnique(): 736 | try: 737 | if request.method == 'POST': 738 | edited = request.form 739 | 740 | db_id = edited['db_id'] 741 | subtechnique_id = edited['subtechnique_id'] 742 | subtechnique_name = edited['subtechnique_name'] 743 | subtechnique_tactic = edited['subtechnique_tactic'] 744 | subtechnique_description = edited['description'] 745 | updated_date = time.strftime('%Y-%m-%d %H:%M:%S') 746 | 747 | db = get_db() 748 | db.execute( 749 | 'UPDATE techniques SET subtechnique_id=?, subtechnique_name=?, subtechnique_description=?, updated_date=?, updated_by=? WHERE id=?', 750 | (subtechnique_id, subtechnique_name, subtechnique_description, updated_date, g.user['id'], db_id,) 751 | ) 752 | db.commit() 753 | 754 | return redirect('/explore-subtechniques') 755 | except: 756 | return render_template('maps/404.html') 757 | 758 | 759 | 760 | @bp.route('/edit/tactic/', methods=('GET', 'POST')) 761 | def render_edit_tactic(element): 762 | 763 | request_tactic=q.q_get_tactics.get_tactics(element) 764 | request_related_tactics=q.q_get_related_tactics.get_related_tactics(element) 765 | 766 | if request_related_tactics: 767 | request_tactics_th = request_related_tactics[0].keys() 768 | else: 769 | request_related_tactics_th = '' 770 | 771 | if request_tactic is False: 772 | return render_template('maps/404.html') 773 | else: 774 | return render_template('maps/creation/create-adversary.html', request_related_tactics=request_related_tactics, element_th=request_tactics_th, request_tactic=request_tactic) 775 | 776 | 777 | @bp.route('/edit-tactic', methods=('GET', 'POST')) 778 | @login_required 779 | def edit_tactic(): 780 | try: 781 | if request.method == 'POST': 782 | edited = request.form 783 | 784 | db_id = edited['db_id'] 785 | tactic_id = edited['adversary_id'] 786 | tactic_name = edited['adversary_name'] 787 | tactic_description = edited['description'] 788 | updated_date = time.strftime('%Y-%m-%d %H:%M:%S') 789 | 790 | db = get_db() 791 | db.execute( 792 | 'UPDATE tactics SET tactic_id=?, tactic_name=?, tactic_description=?, updated_date=?, updated_by=? WHERE id=?', 793 | (tactic_id, tactic_name, tactic_description, updated_date, g.user['id'], db_id,) 794 | ) 795 | db.commit() 796 | 797 | return redirect('/explore-tactics') 798 | except: 799 | return render_template('maps/404.html') 800 | --------------------------------------------------------------------------------