Merge pull request #70 from JulAkx/misp

New Feature : Import IoCs from an added MISP instance.
This commit is contained in:
Félix Aimé 2021-06-06 11:08:35 +02:00 committed by GitHub
commit c1b8f4a447
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 825 additions and 4 deletions

View File

@ -39,6 +39,9 @@
<li class="menu-item"> <li class="menu-item">
<span @click="$router.push('/iocs/manage')">Manage IOCs</span> <span @click="$router.push('/iocs/manage')">Manage IOCs</span>
</li> </li>
<li class="menu-item">
<span @click="$router.push('/iocs/misp')">MISP IOCs</span>
</li>
<li class="menu-item"> <li class="menu-item">
<span @click="$router.push('/iocs/search')">Search IOCs</span> <span @click="$router.push('/iocs/search')">Search IOCs</span>
</li> </li>
@ -95,4 +98,4 @@
.fade-leave-active { .fade-leave-active {
opacity: 0 opacity: 0
} }
</style> </style>

View File

@ -650,4 +650,57 @@ h4, h5 {
.upper { .upper {
text-transform: uppercase; text-transform: uppercase;
} }
/*** MISP CSS ***/
.misp-form {
/* Using CSS Grid to lay out the elements in two-dimensions: */
display: grid;
/* specifying a 0.2em gutter/gap between adjacent elements: */
gap: 0.2em;
overflow:auto;
grid-template-columns: 10em 0.5em 1fr;
width: 100%;
border:0.05rem solid #cecece;
border-radius:.1rem;
margin-bottom:.4rem;
padding:.4rem;
}
.misp-label {
/* placing all <label> elements in the grid column 1 (the first): */
grid-column: 1;
text-align: left;
}
.misp-name {
font-size: 1rem;
font-family: "Roboto-Bold";
color: #484848;
}
.misp-name:disabled {
border-style: none;
color:inherit;
background-color: inherit;
padding: unset;
}
.misp-input {
grid-column: 3;
align-self: center;
}
.misp-input:disabled {
border-style: none;
color:inherit;
background-color: inherit;
padding: unset;
}
.misp-button {
/* positioning the <button> element in the grid-area identified
by the name of 'submit': */
grid-area: submit;
}

View File

@ -34,6 +34,12 @@ const routes = [
component: () => import('../views/iocs-manage.vue'), component: () => import('../views/iocs-manage.vue'),
props: true props: true
}, },
{
path: '/iocs/misp',
name: 'iocs-manage',
component: () => import('../views/iocs-misp.vue'),
props: true
},
{ {
path: '/iocs/search', path: '/iocs/search',
name: 'iocs-search', name: 'iocs-search',

View File

@ -0,0 +1,402 @@
<template>
<div class="backend-content" id="content">
<div class="column col-6 col-xs-12">
<h3 class="s-title">Manage MISP IOCs</h3>
<div>
Here you can add IOCs from your MISP instances. To do so, you first need to fullfil the "Add a new MISP instance" form. Then go to the "Existing instances" tab and scroll to the desired instance.
Finally, just fill the parameters as you wish and click on the "Import IOCs" button. All the IOCs that are not already in the database will be added.
Note that only IOCs (attributes) that belongs to the "Network activity" category will be inserted.
</div>
<ul class="tab tab-block">
<li class="tab-item">
<a href="#" v-on:click="switch_tab('addmisp')" v-bind:class="{ active: tabs.addmisp }">Add instance</a>
</li>
<li class="tab-item">
<a href="#" v-on:click="switch_tab('instances')" v-bind:class="{ active: tabs.instances }">Existing instances</a>
</li>
</ul>
<div v-if="tabs.addmisp">
<h5>Add a new MISP instance</h5>
<div class="misp-form">
<label class="misp-label">Name</label><span>:</span>
<input class="misp-input" type="text" ref="misp_name" placeholder="Enter the name to give to your MISP instance" v-model="mispinst.name" required>
<label class="misp-label">URL</label><span>:</span>
<input class="misp-input" type="text" ref="misp_url" placeholder="Enter your MISP instance URL" v-model="mispinst.url" required>
<label class="misp-label">API key</label><span>:</span>
<input class="misp-input" type="text" ref="misp_key" placeholder="Enter the API key to use" v-model="mispinst.key" required>
<label class="misp-label">Verify certificate</label><span>:</span>
<div style="flex:50%"><input class="misp-input" style="margin-right: 5px;" type="checkbox" id="checkbox" v-model="mispinst.ssl"><label for="checkbox">{{ mispinst.ssl }}</label></div>
</div>
<button class="btn-primary btn col-12" v-on:click="add_misp_instance()">Add MISP instance</button>
<div class="form-group" v-if="addedInstance.length>0">
<div class="toast toast-success">
MISP instance added successfully.
</div>
</div>
<div v-if="errorsInstance.length>0">
<div class="form-group">
<div class="toast toast-error">
MISP instance not added, see details below.
</div>
</div>
<div class="form-group">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Name</th>
<th>URL</th>
<th>API key</th>
<th>Reason</th>
</tr>
</thead>
<tbody>
<tr v-for="e in errorsInstance" v-bind:key="e.name">
<td>{{ e.name }}</td>
<td>{{ e.url }}</td>
<td>{{ e.apikey }}</td>
<td>{{ e.message }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="form-group" v-if="tabs.instances">
<div v-if="mispInstances.length>0">
<div v-for="r in mispInstances" v-bind:key="r.id">
<div style="position: relative">
<input class="misp-name" :id="r.id + 'name'" v-bind:value="r.name" v-model="r.name" disabled="disabled" required>
<button class="btn btn-sm" :id="r.id + 'edit'" :ref="r.id + 'edit'" v-on:click="edit_misp_instance(r)" style="position: absolute; right:120px;">Edit instance</button>
<button class="btn btn-sm" :id="r.id + 'delete'" :ref="r.id + 'delete'" v-on:click="remove_or_cancel_edit_misp_instance(r)" style="position: absolute; right:0;">Delete instance</button>
</div>
<div class="misp-form">
<label class="misp-label">URL</label><span>:</span>
<input class="misp-input" :id="r.id + 'insturl'" v-bind:value="r.url" v-model="r.url" disabled="disabled" required>
<label class="misp-label">API Key</label><span>:</span>
<input class="misp-input" :id="r.id + 'instkey'" v-bind:value="r.apikey" v-model="r.apikey" disabled="disabled" required>
<label class="misp-label">Verify certificate</label><span>:</span>
<div style="flex:50%;"><input class="misp-input" :id="r.id + 'check'" type="checkbox" v-bind:value="r.verifycert" v-model="r.verifycert" style="visibility: hidden; width: 0;"><label v-bind:value="r.verifycert">{{ r.verifycert == 0 ? 'false' : 'true'}}</label></div>
<label class="misp-label">Limit</label><span>:</span>
<input class="misp-input" type="number" step="1" min="0" :id="r.id + 'limit'" placeholder="Enter the maximum number of IOCs to retrieve">
<label class="misp-label">Page index</label><span>:</span>
<input class="misp-input" type="number" step="1" min="0" :id="r.id + 'page'" placeholder="Enter the page index where to start retrieving IOCs">
<button class="btn btn-sm" :id="r.id + 'import'" v-on:click="import_misp_iocs(r)">Import IOCs</button>
</div>
</div>
<div class="form-group" v-if="addedInstance.length>0">
<div class="toast toast-success">
MISP instance edited successfully.
</div>
</div>
<div v-if="errorsInstance.length>0">
<div class="form-group">
<div class="toast toast-error">
MISP instance count not be edited, see details below.
</div>
</div>
<div class="form-group">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Name</th>
<th>URL</th>
<th>API key</th>
<th>Reason</th>
</tr>
</thead>
<tbody>
<tr v-for="e in errorsInstance" v-bind:key="e.name">
<td>{{ e.name }}</td>
<td>{{ e.url }}</td>
<td>{{ e.apikey }}</td>
<td>{{ e.message }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div v-else>
<p>No MISP instance found. Click the "Add" button to add new MISP instance.</p>
<button class="btn btn-sm" v-on:click="switch_tab('addmisp')" v-bind:class="{ active: tabs.addmisp }">Add a new instance</button>
</div>
</div>
<div class="form-group" v-if="imported.length>0">
<div class="toast toast-success">
{{imported.length}} IOC<span v-if="imported.length>1">s</span> imported successfully.
</div>
<div class="form-group">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Indicator</th>
<th>Type</th>
<th>Tag</th>
<th>TLP</th>
</tr>
</thead>
<tbody>
<tr v-for="i in imported" v-bind:key="i.ioc">
<td>{{ i.ioc }}</td>
<td>{{ i.type }}</td>
<td>{{ i.tag }}</td>
<td>{{ i.tlp }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div v-if="errors.length>0">
<div class="form-group">
<div class="toast toast-error">
{{errors.length}} IOC<span v-if="errors.length>1">s</span> not imported, see details below.
</div>
</div>
<div class="form-group">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Indicator</th>
<th>Importation error</th>
</tr>
</thead>
<tbody>
<tr v-for="e in errors" v-bind:key="e.ioc">
<td>{{ e.ioc }}</td>
<td>{{ e.message }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div v-else-if="type_tag_error==true">
<div class="form-group">
<div class="toast toast-error">
IOC(s) not imported, see details below.
</div>
</div>
<div class="form-group">
<div class="empty">
<p class="empty-title h5">Please select a tag and a type.</p>
<p class="empty-subtitle">If different IOCs types, select "Unknown (regex parsing)".</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'managemisp',
data() {
return {
errors:[],
imported:[],
errorsInstance:[],
addedInstance:[],
mispinst:{name:'', url:'',key:'', ssl:false},
mispInstances:[],
tabs: { "addmisp" : true, "instances" : false },
jwt:"",
type_tag_error: false
}
},
props: { },
methods: {
add_misp_instance: function()
{
this.errors = [];
this.imported = [];
this.errorsInstance = []
this.addedInstance = []
if (this.mispinst["name"] != "" && this.mispinst["url"] != "" && this.mispinst["key"] != "")
{
axios.post(`/api/misp/add`, { data: { instance: this.mispinst } }, { headers: {'X-Token': this.jwt} }).then(response => {
if(response.data.status){
this.addedInstance.push(response.data);
} else if (response.data.message){
this.errorsInstance.push(response.data);
}
})
.catch(err => (console.log(err)))
}
else
{
console.log(this.mispinst["name"]);
console.log(this.mispinst["url"]);
console.log(this.mispinst["key"]);
}
},
edit_misp_instance (elem)
{
if (document.getElementById(elem.id+'insturl').disabled == false)
{ // The misp instance was in edit mode
this.errors = [];
this.imported = [];
this.errorsInstance = []
this.addedInstance = []
if (elem["name"] != "" && elem["url"] != "" && elem["key"] != "")
{
axios.post(`/api/misp/edit`, { data: { instance: elem } }, { headers: {'X-Token': this.jwt} }).then(response => {
if(response.data.status){
this.addedInstance.push(response.data);
} else if (response.data.message){
this.errorsInstance.push(response.data);
}
})
.catch(err => (console.log(err)))
}
this.cancel_edit_misp(elem);
}
else
{ // the misp instance should enter in edit mode
document.getElementById(elem.id+'edit').innerText = 'Validate edit';
document.getElementById(elem.id+'delete').innerText = 'Cancel edit';
document.getElementById(elem.id+'name').disabled = false;
document.getElementById(elem.id+'insturl').disabled = false;
document.getElementById(elem.id+'instkey').disabled = false;
document.getElementById(elem.id+'limit').disabled = true;
document.getElementById(elem.id+'page').disabled = true;
document.getElementById(elem.id+'import').disabled = true;
document.getElementById(elem.id+'check').style = "margin-right: 5px;";
}
},
remove_or_cancel_edit_misp_instance(elem)
{
if (document.getElementById(elem.id+'insturl').disabled == false)
{ // The misp instance was in edit mode
this.cancel_edit_misp(elem)
}
else
{ // The misp instance should be delete
axios.get(`/api/misp/delete/${elem.id}`, { timeout: 10000, headers: {'X-Token': this.jwt} })
.then(response => {
if(response.data.status){
this.mispInstances = this.mispInstances.filter(function(el) { return el != elem; });
}
})
.catch(err => (console.log(err)))
}
},
cancel_edit_misp(elem)
{
document.getElementById(elem.id+'edit').innerText = 'Edit instance';
document.getElementById(elem.id+'delete').innerText = 'Delete instance';
document.getElementById(elem.id+'name').disabled = true;
document.getElementById(elem.id+'insturl').disabled = true;
document.getElementById(elem.id+'instkey').disabled = true;
document.getElementById(elem.id+'limit').disabled = false;
document.getElementById(elem.id+'page').disabled = false;
document.getElementById(elem.id+'import').disabled = false;
document.getElementById(elem.id+'check').style = "visibility: hidden; width: 0;";
},
import_misp_iocs(elem)
{
this.errors = [];
this.imported = [];
this.errorsInstance = []
this.addedInstance = []
axios.post(`/api/misp/get_iocs`, { data: { misp_id: elem.id, page: document.getElementById(elem.id+'page').value, limit: document.getElementById(elem.id+'limit').value } }, { headers: {'X-Token': this.jwt} })
.then(response => {
if(response.data.results.length>0){
console.log(response.data.results);
response.data.results.forEach(ioc => {
this.import_ioc(ioc["tag"], ioc["type"], ioc["tlp"], ioc["value"], elem.name + "_" + elem.id);
});
}
else
{
console.log(response);
}
})
.catch(err => (console.log(err)))
},
import_ioc: function(tag, type, tlp, ioc, source) {
if (ioc != "" && ioc.slice(0,1) != "#"){
if("alert " != ioc.slice(0,6)) {
ioc = ioc.trim()
ioc = ioc.replace(" ", "")
ioc = ioc.replace("[", "")
ioc = ioc.replace("]", "")
ioc = ioc.replace("\\", "")
ioc = ioc.replace("(", "")
ioc = ioc.replace(")", "")
}
let finalioc = {ioc_tag: tag, ioc_type: type, ioc_tlp: tlp, ioc_value: ioc, ioc_source: "misp_" + source}
axios.post(`/api/ioc/add_post`, { data: { ioc: finalioc } }, { headers: {'X-Token': this.jwt} })
.then(response => {
if(response.data.status){
this.imported.push(response.data);
} else if (response.data.message){
this.errors.push(response.data);
}
})
.catch(err => (console.log(err)))
}
},
get_misp_instances()
{
this.errorsInstance = []
this.addedInstance = []
this.mispInstances = []
axios.get(`/api/misp/get_all`, { timeout: 10000, headers: {'X-Token': this.jwt} })
.then(response => {
console.log(response.data);
if(response.data.results.length>0){
this.mispInstances = [].concat(this.mispInstances, response.data.results);
}
})
.catch(err => (console.log(err)))
},
switch_tab: function(tab) {
this.errors = []
this.errorsInstance = []
this.addedInstance = []
Object.keys(this.tabs).forEach(key => {
if( key == tab ){
this.tabs[key] = true
if (key == "instances")
{
this.get_misp_instances();
}
} else {
this.tabs[key] = false
}
});
},
get_jwt(){
axios.get(`/api/get-token`, { timeout: 10000 })
.then(response => {
if(response.data.token){
this.jwt = response.data.token
}
})
.catch(err => (console.log(err)))
}
},
created: function() {
this.get_jwt();
this.get_misp_instances();
if (this.mispInstances.length>0)
{
this.tabs["addmisp"] = false;
this.tabs["instances"] = true;
}
}
}
</script>

View File

@ -2,6 +2,7 @@ ipwhois
M2Crypto M2Crypto
pyOpenSSL pyOpenSSL
pydig pydig
pymisp
netaddr netaddr
pyyaml pyyaml
flask flask
@ -14,4 +15,4 @@ wifi
qrcode qrcode
netifaces netifaces
weasyprint weasyprint
python-whois python-whois

View File

@ -17,3 +17,14 @@ CREATE TABLE "whitelist" (
"added_on" INTEGER NOT NULL, "added_on" INTEGER NOT NULL,
PRIMARY KEY("id" AUTOINCREMENT) PRIMARY KEY("id" AUTOINCREMENT)
); );
CREATE TABLE "mispinstance" (
"id" INTEGER UNIQUE,
"name" TEXT,
"url" TEXT NOT NULL,
"apikey" TEXT NOT NULL,
"verifycert" INTEGER NOT NULL DEFAULT 0,
"source" TEXT NOT NULL,
"added_on" NUMERIC NOT NULL,
PRIMARY KEY("id" AUTOINCREMENT)
);

View File

@ -26,6 +26,20 @@ def add(ioc_type, ioc_tag, ioc_tlp, ioc_value):
return jsonify(res) return jsonify(res)
@ioc_bp.route('/add_post', methods=['POST'])
@require_header_token
def add_post():
"""
Parse and add an IOC to the database using the post method.
:return: status of the operation in JSON
"""
data = json.loads(request.data)
ioc = data["data"]["ioc"]
res = IOCs.add(ioc["ioc_type"], ioc["ioc_tag"], ioc["ioc_tlp"], ioc["ioc_value"], ioc["ioc_source"])
return jsonify(res)
@ioc_bp.route('/delete/<ioc_id>', methods=['GET']) @ioc_bp.route('/delete/<ioc_id>', methods=['GET'])
@require_header_token @require_header_token
def delete(ioc_id): def delete(ioc_id):

View File

@ -0,0 +1,76 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from flask import Blueprint, jsonify, Response, request
from app.decorators import require_header_token, require_get_token
from app.classes.mispobj import MISPObj
import json
misp_bp = Blueprint("misp", __name__)
misp = MISPObj()
@misp_bp.route('/add', methods=['POST'])
@require_header_token
def add ():
"""
Parse and add a MISP instance to the database.
:return: status of the operation in JSON
"""
data = json.loads(request.data)
instance = data["data"]["instance"]
source = "backend"
res = MISPObj.add(instance["name"], instance["url"], instance["key"], instance["ssl"], source)
return jsonify(res)
@misp_bp.route('/delete/<misp_id>', methods=['GET'])
@require_header_token
def delete(misp_id):
"""
Delete a MISP instance by its id to the database.
:return: status of the operation in JSON
"""
res = MISPObj.delete(misp_id)
return jsonify(res)
@misp_bp.route('/get_all', methods=['GET'])
@require_header_token
def get_all():
"""
Retreive a list of all MISP instances.
:return: list of MISP instances in JSON.
"""
res = MISPObj.get_all()
return jsonify({"results": [i for i in res]})
@misp_bp.route('/get_iocs', methods=['POST'])
#@require_header_token
def get_iocs():
"""
Retreive a list of all MISP instances.
:return: list of MISP instances in JSON.
"""
data = json.loads(request.data)
data = data["data"]
res = MISPObj.get_iocs(data["misp_id"], data["limit"], data["page"])
print(res)
return jsonify(res)
@misp_bp.route('/edit', methods=['POST'])
@require_header_token
def edit ():
"""
Parse and edit the desired MISP instance.
:return: status of the operation in JSON
"""
data = json.loads(request.data)
instance = data["data"]["instance"]
print(instance)
res = MISPObj.edit(instance["id"], instance["name"], instance["url"], instance["apikey"], instance["verifycert"])
return jsonify(res)

View File

@ -56,7 +56,12 @@ class IOCs(object):
db.session.commit() db.session.commit()
return {"status": True, return {"status": True,
"message": "IOC added", "message": "IOC added",
"ioc": escape(ioc_value)} "ioc": escape(ioc_value),
"type": escape(ioc_type),
"tlp": escape(ioc_tlp),
"tag": escape(ioc_tag),
"source": escape(source),
"added_on": escape(added_on)}
else: else:
return {"status": False, return {"status": False,
"message": "Wrong IOC format", "message": "Wrong IOC format",

View File

@ -0,0 +1,240 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from app import db
from app.db.models import MISPInst
from sqlalchemy.sql import exists
from app.definitions import definitions
from urllib.parse import unquote
from flask import escape
from pymisp import PyMISP
import re
import time
import sys
class MISPObj(object):
def __init__(self):
return None
@staticmethod
def add(misp_name, misp_url, misp_key, misp_verifycert, source):
"""
Parse and add a MISP"instance to the database.
:return: status of the operation in JSON
"""
sameinstances = db.session.query(MISPInst).filter(MISPInst.url == misp_url, MISPInst.apikey == misp_key)
if sameinstances.count() > 0:
return {"status": False,
"message": "This MISP instance already exists",
"name": escape(misp_name),
"url": escape(misp_url),
"apikey": escape(misp_key),
"verifycert": escape(misp_verifycert)}
elif misp_name != "":
if misp_url != "":
if re.match(r"^(?:(?:http|https)://)", misp_url):
if misp_key != "":
added_on = int(time.time())
db.session.add(MISPInst(misp_name, escape(misp_url), misp_key, misp_verifycert, source, added_on))
db.session.commit()
return {"status": True,
"message": "MISP instance added",
"name": escape(misp_name),
"url": escape(misp_url),
"apikey": escape(misp_key),
"verifycert": escape(misp_verifycert)}
else:
return {"status": False,
"message": "The key can't be empty",
"name": escape(misp_name),
"url": escape(misp_url),
"apikey": "",
"verifycert": escape(misp_verifycert)}
else:
return {"status": False,
"message": "The url must begin with http:// or https://",
"name": escape(misp_name),
"url": escape(misp_url),
"apikey": escape(misp_key),
"verifycert": escape(misp_verifycert)}
else:
return {"status": False,
"message": "The url can't be empty",
"name": escape(misp_name),
"url": "",
"apikey": escape(misp_key),
"verifycert": escape(misp_verifycert)}
else:
return {"status": False,
"message": "The MISP instance name can't be empty",
"name":"",
"url": escape(misp_url),
"apikey": escape(misp_key),
"verifycert": escape(misp_verifycert)}
@staticmethod
def edit(misp_id, misp_name, misp_url, misp_key, misp_verifycert):
"""
Parse and edit the desired MISP instance.
:return: status of the operation in JSON
"""
mispinstance = MISPInst.query.get(int(misp_id))
otherinstances = db.session.query(MISPInst).filter(MISPInst.id != int(misp_id), MISPInst.url == misp_url, MISPInst.apikey == misp_key)
if mispinstance is None:
return {"status": False,
"message": "Can't find the MISP instance"}
if otherinstances.count() > 0:
return {"status": False,
"message": "This MISP instance already exists",
"name": escape(misp_name),
"url": escape(misp_url),
"apikey": escape(misp_key),
"verifycert": escape(misp_verifycert)}
elif misp_name != "":
if misp_url != "":
if re.match(r"^(?:(?:http|https)://)", misp_url):
if misp_key != "":
mispinstance.name = misp_name
mispinstance.url = misp_url
mispinstance.apikey = misp_key
mispinstance.verifycert = misp_verifycert
db.session.commit()
return {"status": True,
"message": "MISP instance edited",
"name": escape(misp_name),
"url": escape(misp_url),
"apikey": escape(misp_key),
"verifycert": escape(misp_verifycert)}
else:
return {"status": False,
"message": "The key can't be empty",
"name": escape(misp_name),
"url": escape(misp_url),
"apikey": "",
"verifycert": escape(misp_verifycert)}
else:
return {"status": False,
"message": "The url must begin with http:// or https://",
"name": escape(misp_name),
"url": escape(misp_url),
"apikey": escape(misp_key),
"verifycert": escape(misp_verifycert)}
else:
return {"status": False,
"message": "The url can't be empty",
"name": escape(misp_name),
"url": "",
"apikey": escape(misp_key),
"verifycert": escape(misp_verifycert)}
else:
return {"status": False,
"message": "The MISP instance name can't be empty",
"name":"",
"url": escape(misp_url),
"apikey": escape(misp_key),
"verifycert": escape(misp_verifycert)}
@staticmethod
def delete(misp_id):
"""
Delete a MISP instance by its id in the database.
:return: status of the operation in JSON
"""
if db.session.query(exists().where(MISPInst.id == misp_id)).scalar():
db.session.query(MISPInst).filter_by(id=misp_id).delete()
db.session.commit()
return {"status": True,
"message": "MISP instance deleted"}
else:
return {"status": False,
"message": "MISP instance not found"}
@staticmethod
def get_all():
"""
Get all MISP instances from the database
:return: generator of the records.
"""
for mispinstance in db.session.query(MISPInst).all():
mispinstance = mispinstance.__dict__
yield {"id": mispinstance["id"],
"name": mispinstance["name"],
"url": mispinstance["url"],
"apikey": mispinstance["apikey"],
"verifycert": mispinstance["verifycert"]}
@staticmethod
def get_iocs(misp_id, limit, page):
"""
Get all IOCs from the desired MISP instance
:return: generator of the records.
"""
mispinstance = MISPInst.query.get(int(misp_id))
if mispinstance is not None:
if mispinstance.url != "":
if mispinstance.apikey != "":
try:
# Connects to the desired MISP instance
mispinstance = PyMISP(mispinstance.url, mispinstance.apikey, mispinstance.verifycert)
# Retreives the attributes (or IOCs) that are supported by Tinycheck
attributes = mispinstance.search('attributes', category='Network activity', limit=limit, page=page, metadata=True)
if 'Attribute' in attributes:
iocs = []
for attribute in attributes['Attribute']:
#print(attribute)
if 'value' in attribute and attribute['value'] != '':
# We have a valid value
ioc_value = attribute['value']
ioc_type = "unknown"
ioc_tag = "No tag"
ioc_tlp = "white"
isFirstTag = True
if 'Tag' in attribute:
# We have some tags
#print (attribute['Tag'])
for tag in attribute['Tag']:
tlp = re.search(r"^(?:tlp:)(red|green|amber|white)", tag['name'])
if tlp:
# The current tag is the tlp level
ioc_tlp = tlp.group(1)
#print(ioc_tlp)
elif isFirstTag:
# It is the first retreived tag that is not a tlp
isFirstTag = False
ioc_tag = tag['name']
else:
# It is another tag and not the first one
ioc_tag += ", " + tag['name']
ioc = { "value": ioc_value,
"type": ioc_type,
"tag": ioc_tag,
"tlp": ioc_tlp }
iocs.append(ioc)
return { "status":True,
"results": iocs}
else:
return { "status":False,
"message":"No valid IOCs found."}
except TypeError as error:
print (error)
pass
except:
print("An exception has been raised: ", sys.exc_info()[0])
pass
else:
{"status": False,
"message": "The api key can't be empty"}
else:
return {"status": False,
"message": "The url can't be empty"}
else:
return {"status": False,
"message": "Unknown MISP instance."}

View File

@ -16,5 +16,15 @@ class Whitelist(db.Model):
self.source = source self.source = source
self.added_on = added_on self.added_on = added_on
class MISPInst(db.Model):
def __init__(self, name, url, key, ssl, source, added_on):
self.name = name
self.url = url
self.apikey = key
self.verifycert = ssl
self.source = source
self.added_on = added_on
db.mapper(Whitelist, db.Table('whitelist', db.metadata, autoload=True)) db.mapper(Whitelist, db.Table('whitelist', db.metadata, autoload=True))
db.mapper(Ioc, db.Table('iocs', db.metadata, autoload=True)) db.mapper(Ioc, db.Table('iocs', db.metadata, autoload=True))
db.mapper(MISPInst, db.Table('mispinstance', db.metadata, autoload=True))