Merge pull request #74 from KasperskyLab/dev
PR of the version v0.7-test
This commit is contained in:
commit
7c1b0f7ced
@ -25,9 +25,6 @@
|
|||||||
<li class="menu-item">
|
<li class="menu-item">
|
||||||
<span @click="$router.push('/device/db')">Manage database</span>
|
<span @click="$router.push('/device/db')">Manage database</span>
|
||||||
</li>
|
</li>
|
||||||
<!-- <li class="menu-item">
|
|
||||||
<span @click="$router.push('/device/user')">User configuration</a>
|
|
||||||
</li> -->
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -42,6 +39,9 @@
|
|||||||
<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>
|
||||||
|
<li class="menu-item">
|
||||||
|
<span @click="$router.push('/iocs/misp')">MISP Instances</span>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -651,3 +651,127 @@ 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.4em;
|
||||||
|
overflow:auto;
|
||||||
|
grid-template-columns: 10em 0.5em 1fr;
|
||||||
|
width: 100%;
|
||||||
|
border-radius:.1rem;
|
||||||
|
margin-bottom: .8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.misp-label {
|
||||||
|
/* placing all <label> elements in the grid column 1 (the first): */
|
||||||
|
grid-column: 1;
|
||||||
|
text-align: left;
|
||||||
|
padding-top: .3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.misp-offline {
|
||||||
|
background-color: #e85600;
|
||||||
|
color: #FFF;
|
||||||
|
font-size: 11px;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding:3px 6px 3px 6px;
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
.misp-online {
|
||||||
|
background-color: #64c800;
|
||||||
|
color: #FFF;
|
||||||
|
font-size: 11px;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding:3px 6px 3px 6px;
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading::after {
|
||||||
|
animation: loading .5s infinite linear;
|
||||||
|
background: 0 0;
|
||||||
|
border: .1rem solid #66758c;
|
||||||
|
border-radius: 50%;
|
||||||
|
border-right-color: transparent;
|
||||||
|
border-top-color: transparent;
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
height: .8rem;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -.4rem;
|
||||||
|
margin-top: -.4rem;
|
||||||
|
opacity: 1;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
width: .8rem;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading.loading-lg::after {
|
||||||
|
height: 1.6rem;
|
||||||
|
margin-left: -.8rem;
|
||||||
|
margin-top: -.8rem;
|
||||||
|
width: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip::after{
|
||||||
|
background:rgba(48,55,66,.95);
|
||||||
|
border-radius:3px;
|
||||||
|
bottom:100%;
|
||||||
|
color:#fff;
|
||||||
|
content:attr(data-tooltip);
|
||||||
|
display:block;
|
||||||
|
font-size: 11px;
|
||||||
|
left:50%;
|
||||||
|
max-width:320px;
|
||||||
|
opacity:0;
|
||||||
|
overflow:hidden;
|
||||||
|
padding:3px 6px 3px 6px;
|
||||||
|
pointer-events:none;
|
||||||
|
position:absolute;
|
||||||
|
text-overflow:ellipsis;
|
||||||
|
transform:translate(-50%,.4rem);
|
||||||
|
transition:opacity .2s,transform .2s;
|
||||||
|
white-space:pre;
|
||||||
|
z-index:300
|
||||||
|
}
|
@ -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',
|
||||||
|
@ -35,6 +35,10 @@
|
|||||||
<input type="checkbox" @change="switch_config('frontend', 'virtual_keyboard')" v-model="config.frontend.virtual_keyboard">
|
<input type="checkbox" @change="switch_config('frontend', 'virtual_keyboard')" v-model="config.frontend.virtual_keyboard">
|
||||||
<i class="form-icon"></i> Use virtual keyboard (for touch screen)
|
<i class="form-icon"></i> Use virtual keyboard (for touch screen)
|
||||||
</label>
|
</label>
|
||||||
|
<label class="form-switch">
|
||||||
|
<input type="checkbox" @change="switch_config('frontend', 'choose_net')" v-model="config.frontend.choose_net">
|
||||||
|
<i class="form-icon"></i> Allow the end-user to choose the network even if connected.
|
||||||
|
</label>
|
||||||
<label class="form-switch">
|
<label class="form-switch">
|
||||||
<input type="checkbox" @change="switch_config('frontend', 'reboot_option')" v-model="config.frontend.reboot_option">
|
<input type="checkbox" @change="switch_config('frontend', 'reboot_option')" v-model="config.frontend.reboot_option">
|
||||||
<i class="form-icon"></i> Allow the end-user to reboot the device from the interface.
|
<i class="form-icon"></i> Allow the end-user to reboot the device from the interface.
|
||||||
|
175
app/backend/src/views/iocs-misp.vue
Normal file
175
app/backend/src/views/iocs-misp.vue
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
<template>
|
||||||
|
<div class="backend-content" id="content">
|
||||||
|
<div class="column col-8 col-xs-12">
|
||||||
|
<h3 class="s-title">Manage MISP instances</h3>
|
||||||
|
<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">
|
||||||
|
<div class="misp-form">
|
||||||
|
<label class="misp-label">Instance name</label><span></span>
|
||||||
|
<input class="form-input" type="text" ref="misp_name" placeholder="CYBERACME MISP" v-model="mispinst.name" required>
|
||||||
|
<label class="misp-label">Instance URL</label><span></span>
|
||||||
|
<input class="form-input" type="text" ref="misp_url" placeholder="https://misp.cyberacme.com" v-model="mispinst.url" required>
|
||||||
|
<label class="misp-label">Authentication key</label><span></span>
|
||||||
|
<input class="form-input" type="text" ref="misp_key" placeholder="OqHSMyAuth3ntic4t10nK3y0MyAuth3ntic4t10nK3y3iiH" v-model="mispinst.key" required>
|
||||||
|
<label class="misp-label" v-if="mispinst.url.startsWith('https://')">Verify certificate? </label><span v-if="mispinst.url.startsWith('https://')"></span>
|
||||||
|
<div style="flex:50%" v-if="mispinst.url.startsWith('https://')"><label class="form-switch">
|
||||||
|
<input type="checkbox" v-model="mispinst.ssl">
|
||||||
|
<i class="form-icon"></i>
|
||||||
|
</label></div>
|
||||||
|
</div>
|
||||||
|
<button class="btn-primary btn col-12" v-on:click="add_instance()">Add MISP instance</button>
|
||||||
|
<div class="form-group" v-if="added">
|
||||||
|
<div class="toast toast-success">
|
||||||
|
✓ MISP instance added successfully. Redirecting to instances in 2 seconds.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" v-if="error">
|
||||||
|
<div class="toast toast-error">
|
||||||
|
✗ MISP instance not added. {{error}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" v-if="tabs.instances">
|
||||||
|
<div v-if="instances.length">
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Server</th>
|
||||||
|
<th>Authkey</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="i in instances" v-bind:key="i.id">
|
||||||
|
<td>{{ i.name }}</td>
|
||||||
|
<td>{{ i.url.replace('https://', '') .replace('http://', '') }}</td>
|
||||||
|
<td>{{ i.apikey.slice(0,5) }} [...] {{ i.apikey.slice(35,40) }}</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="i.connected" class="misp-online tooltip" :data-tooltip="i.lastsync">✓ ONLINE</span>
|
||||||
|
<span v-else class="misp-offline tooltip" :data-tooltip="i.lastsync">⚠ OFFLINE</span>
|
||||||
|
</td>
|
||||||
|
<td><button class="btn btn-sm" v-on:click="delete_instance(i)">Delete</button></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<div class="empty">
|
||||||
|
<div v-if="loading">
|
||||||
|
<p class="empty-title h5">
|
||||||
|
<span class="loading loading-lg"></span>
|
||||||
|
</p>
|
||||||
|
<p class="empty-subtitle">Testing and loading your MISP instances.</p>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<p class="empty-title h5">No MISP instance found.</p>
|
||||||
|
<p class="empty-subtitle">Do not hesitate to add a MISP instance.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'managemisp',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
error:false,
|
||||||
|
loading:false,
|
||||||
|
added:false,
|
||||||
|
mispinst:{ name:'', url:'',key:'', ssl:false },
|
||||||
|
instances:[],
|
||||||
|
tabs: { "addmisp" : true, "instances" : false },
|
||||||
|
jwt:""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: { },
|
||||||
|
methods: {
|
||||||
|
add_instance: function()
|
||||||
|
{
|
||||||
|
this.added = false;
|
||||||
|
this.error = false;
|
||||||
|
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.added = true;
|
||||||
|
setTimeout(function (){
|
||||||
|
this.switch_tab('instances')
|
||||||
|
this.mispinst = { name:'', url:'',key:'', ssl:false }
|
||||||
|
this.added = false
|
||||||
|
}.bind(this), 2000);
|
||||||
|
} else {
|
||||||
|
this.error = response.data.message;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => (console.log(err)))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
delete_instance(elem)
|
||||||
|
{
|
||||||
|
axios.get(`/api/misp/delete/${elem.id}`, { timeout: 10000, headers: {'X-Token': this.jwt} })
|
||||||
|
.then(response => {
|
||||||
|
if(response.data.status){
|
||||||
|
this.instances = this.instances.filter(function(el) { return el != elem; });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => (console.log(err)))
|
||||||
|
},
|
||||||
|
get_misp_instances()
|
||||||
|
{
|
||||||
|
this.loading = true;
|
||||||
|
this.instances = []
|
||||||
|
axios.get(`/api/misp/get_all`, { timeout: 10000, headers: {'X-Token': this.jwt} })
|
||||||
|
.then(response => {
|
||||||
|
if(response.data.results){
|
||||||
|
this.instances = response.data.results;
|
||||||
|
this.instances.forEach(e => {
|
||||||
|
var lastsync = parseInt((Date.now()/1000 - e.lastsync) / 86400)
|
||||||
|
e.lastsync = (!lastsync)? "Synchronized today" : `Synchronized ${lastsync} day(s) ago`
|
||||||
|
} )
|
||||||
|
}
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
.catch(err => (console.log(err)))
|
||||||
|
},
|
||||||
|
switch_tab: function(tab) {
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
@ -13,27 +13,37 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Indicator</th>
|
<th>Indicator</th>
|
||||||
<th>Type</th>
|
|
||||||
<th>Tag</th>
|
<th>Tag</th>
|
||||||
<th>TLP</th>
|
<th>TLP</th>
|
||||||
<th> </th>
|
<th>Source</th>
|
||||||
|
<th>Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="r in results" :key="r.tlp">
|
<tr v-for="r in results" :key="r.tlp">
|
||||||
<td>{{ r.value }}</td>
|
<td>{{ r.value }}</td>
|
||||||
<td class="capi">{{ r.type }}</td>
|
|
||||||
<td class="upper">{{ r.tag }}</td>
|
<td class="upper">{{ r.tag }}</td>
|
||||||
<td><label :class="['tlp-' + r.tlp]">{{ r.tlp }}</label></td>
|
<td><label :class="['tlp-' + r.tlp]">{{ r.tlp }}</label></td>
|
||||||
|
<td class="capi">{{ r.source }}</td>
|
||||||
<td><button class="btn btn-sm" v-on:click="remove(r)">Delete</button></td>
|
<td><button class="btn btn-sm" v-on:click="remove(r)">Delete</button></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="first_search==false">
|
<div v-else-if="first_search==false">
|
||||||
<div class="empty">
|
<div v-if="loading">
|
||||||
<p class="empty-title h5">IOC<span v-if="this.iocs.match(/[^\r\n]+/g).length>1">s</span> not found.</p>
|
<div class="empty">
|
||||||
<p class="empty-subtitle">Try wildcard search to expend your search.</p>
|
<p class="empty-title h5">
|
||||||
|
<span class="loading loading-lg"></span>
|
||||||
|
</p>
|
||||||
|
<p class="empty-subtitle">Finding your IOC(s)...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<div class="empty">
|
||||||
|
<p class="empty-title h5">IOC<span v-if="this.iocs.match(/[^\r\n]+/g).length>1">s</span> not found.</p>
|
||||||
|
<p class="empty-subtitle">Try wildcard search to expend your search.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -49,7 +59,8 @@ export default {
|
|||||||
return {
|
return {
|
||||||
results: [],
|
results: [],
|
||||||
first_search: true,
|
first_search: true,
|
||||||
jwt:""
|
jwt:"",
|
||||||
|
loading:false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: { },
|
props: { },
|
||||||
@ -57,6 +68,7 @@ export default {
|
|||||||
search_iocs: function() {
|
search_iocs: function() {
|
||||||
this.results = []
|
this.results = []
|
||||||
this.first_search = false
|
this.first_search = false
|
||||||
|
this.loading = true;
|
||||||
this.iocs.match(/[^\r\n]+/g).forEach(ioc => {
|
this.iocs.match(/[^\r\n]+/g).forEach(ioc => {
|
||||||
ioc = ioc.trim()
|
ioc = ioc.trim()
|
||||||
if("alert " != ioc.slice(0,6)) {
|
if("alert " != ioc.slice(0,6)) {
|
||||||
@ -72,6 +84,7 @@ export default {
|
|||||||
if(response.data.results.length>0){
|
if(response.data.results.length>0){
|
||||||
this.results = [].concat(this.results, response.data.results);
|
this.results = [].concat(this.results, response.data.results);
|
||||||
}
|
}
|
||||||
|
this.loading = false;
|
||||||
})
|
})
|
||||||
.catch(err => (console.log(err)))
|
.catch(err => (console.log(err)))
|
||||||
});
|
});
|
||||||
|
@ -128,8 +128,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.report-wrapper {
|
.report-wrapper {
|
||||||
width:90%;
|
|
||||||
margin:auto;
|
margin:auto;
|
||||||
|
padding-bottom:1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-ctx {
|
.device-ctx {
|
||||||
@ -212,10 +212,11 @@
|
|||||||
.report-wrapper {
|
.report-wrapper {
|
||||||
width:60%;
|
width:60%;
|
||||||
margin:auto;
|
margin:auto;
|
||||||
|
padding-bottom: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-ctx {
|
.device-ctx {
|
||||||
padding:15px;
|
padding:15px 0px 15px 0px;
|
||||||
margin:auto;
|
margin:auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,6 +304,17 @@ footer {
|
|||||||
padding-left:40px;
|
padding-left:40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.device-ctx-legend {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #a6a6a6;
|
||||||
|
margin-top: 10px;
|
||||||
|
padding-top: 10px;
|
||||||
|
background-color: #fbfbfb;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.group-title {
|
.group-title {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
color : #999;
|
color : #999;
|
||||||
|
@ -50,8 +50,11 @@
|
|||||||
"low_msg": "Només teniu {nb} alertes baixes, <br /> Si us plau comproveu-les.",
|
"low_msg": "Només teniu {nb} alertes baixes, <br /> Si us plau comproveu-les.",
|
||||||
"save_report": "Desa l'informe",
|
"save_report": "Desa l'informe",
|
||||||
"report_of": "Informe de",
|
"report_of": "Informe de",
|
||||||
"ip_address": "Adreça IP",
|
"ip_address": "Adreça IP:",
|
||||||
"mac_address": "Adreça MAC",
|
"mac_address": "Adreça MAC:",
|
||||||
|
"pcap_sha1": "SHA1:",
|
||||||
|
"capture_started": "La captura va començar a:",
|
||||||
|
"capture_ended": "La captura va acabar el:",
|
||||||
"high": "alt",
|
"high": "alt",
|
||||||
"moderat": "moderat",
|
"moderat": "moderat",
|
||||||
"low": "baix"
|
"low": "baix"
|
||||||
|
@ -50,8 +50,11 @@
|
|||||||
"low_msg": "Sie haben nur {nb} Warnungen der Stufe \"Niedrig\":<br> Überprüfen Sie sie gerne.",
|
"low_msg": "Sie haben nur {nb} Warnungen der Stufe \"Niedrig\":<br> Überprüfen Sie sie gerne.",
|
||||||
"save_report": "Bericht speichern",
|
"save_report": "Bericht speichern",
|
||||||
"report_of": "Bericht zu",
|
"report_of": "Bericht zu",
|
||||||
"ip_address": "IP-Adresse",
|
"ip_address": "IP-Adresse:",
|
||||||
"mac_address": "MAC-Adresse",
|
"mac_address": "MAC-Adresse:",
|
||||||
|
"pcap_sha1": "SHA1:",
|
||||||
|
"capture_started": "Capture begann mit:",
|
||||||
|
"capture_ended": "Capture endete an:",
|
||||||
"high": "Hoch",
|
"high": "Hoch",
|
||||||
"moderate": "Mittel",
|
"moderate": "Mittel",
|
||||||
"low": "Niedrig"
|
"low": "Niedrig"
|
||||||
|
@ -50,8 +50,11 @@
|
|||||||
"low_msg": "You have only {nb} low alerts,<br /> don't hesitate to check them.",
|
"low_msg": "You have only {nb} low alerts,<br /> don't hesitate to check them.",
|
||||||
"save_report": "Save the report",
|
"save_report": "Save the report",
|
||||||
"report_of": "Report of",
|
"report_of": "Report of",
|
||||||
"ip_address": "IP address",
|
"ip_address": "IP address:",
|
||||||
"mac_address": "MAC address",
|
"mac_address": "MAC address:",
|
||||||
|
"pcap_sha1": "SHA1:",
|
||||||
|
"capture_started": "Capture started on:",
|
||||||
|
"capture_ended": "Capture ended on:",
|
||||||
"high": "high",
|
"high": "high",
|
||||||
"moderate": "moderate",
|
"moderate": "moderate",
|
||||||
"low": "low"
|
"low": "low"
|
||||||
|
@ -50,8 +50,11 @@
|
|||||||
"low_msg": "Solo tiene {nb} alertas bajas, <br /> por favor revíselas",
|
"low_msg": "Solo tiene {nb} alertas bajas, <br /> por favor revíselas",
|
||||||
"save_report": "Guardar el informe",
|
"save_report": "Guardar el informe",
|
||||||
"report_of": "Informe de",
|
"report_of": "Informe de",
|
||||||
"ip_address": "dirección IP",
|
"ip_address": "Dirección IP:",
|
||||||
"mac_address": "dirección MAC",
|
"mac_address": "Dirección MAC:",
|
||||||
|
"pcap_sha1": "SHA1:",
|
||||||
|
"capture_started": "Captura comenzó a:",
|
||||||
|
"capture_ended": "Captura terminó a:",
|
||||||
"high": "alta",
|
"high": "alta",
|
||||||
"moderate": "moderada",
|
"moderate": "moderada",
|
||||||
"low": "bajo"
|
"low": "bajo"
|
||||||
|
@ -52,6 +52,9 @@
|
|||||||
"report_of": "Rapport de",
|
"report_of": "Rapport de",
|
||||||
"ip_address": "Adresse IP :",
|
"ip_address": "Adresse IP :",
|
||||||
"mac_address": "Adresse MAC :",
|
"mac_address": "Adresse MAC :",
|
||||||
|
"pcap_sha1": "SHA1 :",
|
||||||
|
"capture_started": "Capture débutée le :",
|
||||||
|
"capture_ended": "Capture finie le :",
|
||||||
"high": "elevee",
|
"high": "elevee",
|
||||||
"moderate": "moyenne",
|
"moderate": "moyenne",
|
||||||
"low": "basse"
|
"low": "basse"
|
||||||
|
@ -50,8 +50,11 @@
|
|||||||
"low_msg": "Sono presenti solo {nb} avvisi con priorità bassa<br /> da controllare.",
|
"low_msg": "Sono presenti solo {nb} avvisi con priorità bassa<br /> da controllare.",
|
||||||
"save_report": "Salva il rapporto",
|
"save_report": "Salva il rapporto",
|
||||||
"report_of": "Rapporto di",
|
"report_of": "Rapporto di",
|
||||||
"ip_address": "Indirizzo IP",
|
"ip_address": "Indirizzo IP:",
|
||||||
"mac_address": "Indirizzo MAC",
|
"mac_address": "Indirizzo MAC:",
|
||||||
|
"pcap_sha1": "SHA1:",
|
||||||
|
"capture_started": "Cattura è iniziata su:",
|
||||||
|
"capture_ended": "Cattura terminata su:",
|
||||||
"high": "elevata",
|
"high": "elevata",
|
||||||
"moderate": "moderata",
|
"moderate": "moderata",
|
||||||
"low": "bassa"
|
"low": "bassa"
|
||||||
|
@ -50,8 +50,11 @@
|
|||||||
"low_msg": "Você tem apenas {nb} alertas leves,<br /> não deixe de verificá-los.",
|
"low_msg": "Você tem apenas {nb} alertas leves,<br /> não deixe de verificá-los.",
|
||||||
"save_report": "Salvar o relatório",
|
"save_report": "Salvar o relatório",
|
||||||
"report_of": "Relatório de",
|
"report_of": "Relatório de",
|
||||||
"ip_address": "Endereço IP",
|
"ip_address": "Endereço IP:",
|
||||||
"mac_address": "Endereço MAC",
|
"mac_address": "Endereço MAC:",
|
||||||
|
"pcap_sha1": "SHA1:",
|
||||||
|
"capture_started": "Captura iniciada em:",
|
||||||
|
"capture_ended": "Captura terminou em:",
|
||||||
"high": "crítico",
|
"high": "crítico",
|
||||||
"moderate": "moderado",
|
"moderate": "moderado",
|
||||||
"low": "leve"
|
"low": "leve"
|
||||||
|
@ -50,8 +50,11 @@
|
|||||||
"low_msg": "У вас {nb} предупреждение низкого уровня<br />, проверьте их.",
|
"low_msg": "У вас {nb} предупреждение низкого уровня<br />, проверьте их.",
|
||||||
"save_report": "Сохранить отчет",
|
"save_report": "Сохранить отчет",
|
||||||
"report_of": "Отчет",
|
"report_of": "Отчет",
|
||||||
"ip_address": "IP-адрес",
|
"ip_address": "IP-адрес:",
|
||||||
"mac_address": "MAC-адрес",
|
"mac_address": "MAC-адрес:",
|
||||||
|
"pcap_sha1": " SHA1:",
|
||||||
|
"capture_started": "Захват начался:",
|
||||||
|
"capture_ended": "захват закончился:",
|
||||||
"high": "высокий",
|
"high": "высокий",
|
||||||
"moderate": "средний",
|
"moderate": "средний",
|
||||||
"low": "низкий"
|
"low": "низкий"
|
||||||
|
@ -55,6 +55,7 @@ export default {
|
|||||||
router.replace({ name: 'report',
|
router.replace({ name: 'report',
|
||||||
params: { alerts : response.data.alerts,
|
params: { alerts : response.data.alerts,
|
||||||
device : response.data.device,
|
device : response.data.device,
|
||||||
|
pcap : response.data.pcap,
|
||||||
capture_token : this.capture_token } });
|
capture_token : this.capture_token } });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -24,6 +24,8 @@ export default {
|
|||||||
var internet = this.internet
|
var internet = this.internet
|
||||||
if (window.config.iface_out.charAt(0) == 'e'){
|
if (window.config.iface_out.charAt(0) == 'e'){
|
||||||
router.push({ name: 'generate-ap' });
|
router.push({ name: 'generate-ap' });
|
||||||
|
} else if (!window.config.choose_net && this.internet){
|
||||||
|
router.push({ name: 'generate-ap' });
|
||||||
} else {
|
} else {
|
||||||
router.push({ name: 'wifi-select',
|
router.push({ name: 'wifi-select',
|
||||||
params: { saved_ssid: saved_ssid,
|
params: { saved_ssid: saved_ssid,
|
||||||
|
@ -46,8 +46,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-else-if="show_report" class="report-wrapper">
|
<div v-else-if="show_report" class="report-wrapper">
|
||||||
<div class="device-ctx">
|
<div class="device-ctx">
|
||||||
<h3 style="margin: 0;">{{ $t("report.report_of") }} {{device.name}}</h3>
|
<h3 style="margin: 0; padding-left:10px;">{{ $t("report.report_of") }} {{device.name}}</h3>
|
||||||
{{ $t("report.ip_address") }} {{device.ip_address}}<br />{{ $t("report.mac_address") }} {{device.mac_address}}
|
<div class="device-ctx-legend">
|
||||||
|
{{ $t("report.pcap_sha1") }} {{ pcap.SHA1 }}<br />
|
||||||
|
{{ $t("report.capture_started") }} {{ pcap["First packet time"].split(",")[0] }}<br />
|
||||||
|
{{ $t("report.capture_ended") }} {{ pcap["Last packet time"].split(",")[0] }}<br />
|
||||||
|
<!-- {{ $t("report.ip_address") }} {{device.ip_address}}<br /> -->
|
||||||
|
{{ $t("report.mac_address") }} {{device.mac_address}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul class="alerts">
|
<ul class="alerts">
|
||||||
<li class="alert" v-for="alert in alerts.high" :key="alert.message">
|
<li class="alert" v-for="alert in alerts.high" :key="alert.message">
|
||||||
@ -105,6 +111,7 @@ export default {
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
device: Object,
|
device: Object,
|
||||||
|
pcap: Object,
|
||||||
alerts: Array,
|
alerts: Array,
|
||||||
capture_token: String
|
capture_token: String
|
||||||
},
|
},
|
||||||
|
@ -2,6 +2,7 @@ ipwhois
|
|||||||
M2Crypto
|
M2Crypto
|
||||||
pyOpenSSL
|
pyOpenSSL
|
||||||
pydig
|
pydig
|
||||||
|
pymisp
|
||||||
netaddr
|
netaddr
|
||||||
pyyaml
|
pyyaml
|
||||||
flask
|
flask
|
||||||
|
@ -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 "misp" (
|
||||||
|
"id" INTEGER UNIQUE,
|
||||||
|
"name" TEXT,
|
||||||
|
"url" TEXT NOT NULL,
|
||||||
|
"apikey" TEXT NOT NULL,
|
||||||
|
"verifycert" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"added_on" NUMERIC NOT NULL,
|
||||||
|
"last_sync" NUMERIC NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY("id" AUTOINCREMENT)
|
||||||
|
);
|
||||||
|
@ -51,6 +51,7 @@ frontend:
|
|||||||
virtual_keyboard: true
|
virtual_keyboard: true
|
||||||
user_lang: userlang
|
user_lang: userlang
|
||||||
update: updateoption
|
update: updateoption
|
||||||
|
choose_net: false
|
||||||
|
|
||||||
# NETWORK -
|
# NETWORK -
|
||||||
# Some elements related to the network configuration, such as
|
# Some elements related to the network configuration, such as
|
||||||
|
@ -441,7 +441,7 @@ change_configs() {
|
|||||||
|
|
||||||
feeding_iocs() {
|
feeding_iocs() {
|
||||||
echo -e "\e[39m[+] Feeding your TinyCheck instance with fresh IOCs and whitelist, please wait."
|
echo -e "\e[39m[+] Feeding your TinyCheck instance with fresh IOCs and whitelist, please wait."
|
||||||
python3 /usr/share/tinycheck/server/backend/watchers.py
|
python3 /usr/share/tinycheck/server/backend/watchers.py 2>/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
reboot_box() {
|
reboot_box() {
|
||||||
|
@ -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):
|
||||||
|
44
server/backend/app/blueprints/misp.py
Normal file
44
server/backend/app/blueprints/misp.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#!/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.misp import MISP
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
misp_bp = Blueprint("misp", __name__)
|
||||||
|
misp = MISP()
|
||||||
|
|
||||||
|
|
||||||
|
@misp_bp.route('/add', methods=['POST'])
|
||||||
|
@require_header_token
|
||||||
|
def add_instance():
|
||||||
|
"""
|
||||||
|
Parse and add a MISP instance to the database.
|
||||||
|
:return: status of the operation in JSON
|
||||||
|
"""
|
||||||
|
data = json.loads(request.data)
|
||||||
|
res = misp.add_instance(data["data"]["instance"])
|
||||||
|
return jsonify(res)
|
||||||
|
|
||||||
|
@misp_bp.route('/delete/<misp_id>', methods=['GET'])
|
||||||
|
@require_header_token
|
||||||
|
def delete_instance(misp_id):
|
||||||
|
"""
|
||||||
|
Delete a MISP instance by its id to the database.
|
||||||
|
:return: status of the operation in JSON
|
||||||
|
"""
|
||||||
|
res = misp.delete_instance(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 = misp.get_instances()
|
||||||
|
return jsonify({"results": [i for i in res]})
|
@ -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",
|
||||||
@ -111,7 +116,8 @@ class IOCs(object):
|
|||||||
"type": ioc["type"],
|
"type": ioc["type"],
|
||||||
"tag": ioc["tag"],
|
"tag": ioc["tag"],
|
||||||
"tlp": ioc["tlp"],
|
"tlp": ioc["tlp"],
|
||||||
"value": ioc["value"]}
|
"value": ioc["value"],
|
||||||
|
"source": ioc["source"]}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_types():
|
def get_types():
|
||||||
|
159
server/backend/app/classes/misp.py
Normal file
159
server/backend/app/classes/misp.py
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from app import db
|
||||||
|
from app.db.models import MISPInst
|
||||||
|
from app.definitions import definitions as defs
|
||||||
|
|
||||||
|
from sqlalchemy.sql import exists
|
||||||
|
from urllib.parse import unquote
|
||||||
|
from flask import escape
|
||||||
|
from pymisp import PyMISP
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
class MISP(object):
|
||||||
|
def __init__(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def add_instance(self, instance):
|
||||||
|
"""
|
||||||
|
Parse and add a MISP instance to the database.
|
||||||
|
:return: status of the operation in JSON
|
||||||
|
"""
|
||||||
|
|
||||||
|
url = instance["url"]
|
||||||
|
name = instance["name"]
|
||||||
|
apikey = instance["key"]
|
||||||
|
verify = instance["ssl"]
|
||||||
|
last_sync = int(time.time()-31536000) # One year
|
||||||
|
|
||||||
|
sameinstances = db.session.query(MISPInst).filter(
|
||||||
|
MISPInst.url == url, MISPInst.apikey == apikey)
|
||||||
|
if sameinstances.count():
|
||||||
|
return {"status": False,
|
||||||
|
"message": "This MISP instance already exists"}
|
||||||
|
if name:
|
||||||
|
if self.test_instance(url, apikey, verify):
|
||||||
|
added_on = int(time.time())
|
||||||
|
db.session.add(MISPInst(name, escape(
|
||||||
|
url), apikey, verify, added_on, last_sync))
|
||||||
|
db.session.commit()
|
||||||
|
return {"status": True,
|
||||||
|
"message": "MISP instance added"}
|
||||||
|
else:
|
||||||
|
return {"status": False,
|
||||||
|
"message": "Please verify the connection to the MISP instance"}
|
||||||
|
else:
|
||||||
|
return {"status": False,
|
||||||
|
"message": "Please provide a name for your instance"}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def delete_instance(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"}
|
||||||
|
|
||||||
|
def get_instances(self):
|
||||||
|
"""
|
||||||
|
Get MISP instances from the database
|
||||||
|
:return: generator of the records.
|
||||||
|
"""
|
||||||
|
for misp in db.session.query(MISPInst).all():
|
||||||
|
misp = misp.__dict__
|
||||||
|
yield {"id": misp["id"],
|
||||||
|
"name": misp["name"],
|
||||||
|
"url": misp["url"],
|
||||||
|
"apikey": misp["apikey"],
|
||||||
|
"verifycert": True if misp["verifycert"] else False,
|
||||||
|
"connected": self.test_instance(misp["url"], misp["apikey"], misp["verifycert"]),
|
||||||
|
"lastsync": misp["last_sync"]}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def test_instance(url, apikey, verify):
|
||||||
|
"""
|
||||||
|
Test the connection of the MISP instance.
|
||||||
|
:return: generator of the records.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
PyMISP(url, apikey, verify)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def update_sync(misp_id):
|
||||||
|
"""
|
||||||
|
Update the last synchronization date by the actual date.
|
||||||
|
:return: bool, True if updated.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
misp = MISPInst.query.get(int(misp_id))
|
||||||
|
misp.last_sync = int(time.time())
|
||||||
|
db.session.commit()
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_iocs(misp_id):
|
||||||
|
"""
|
||||||
|
Get all IOCs from specific MISP instance
|
||||||
|
:return: generator containing the IOCs.
|
||||||
|
"""
|
||||||
|
misp = MISPInst.query.get(int(misp_id))
|
||||||
|
if misp is not None:
|
||||||
|
if misp.url and misp.apikey:
|
||||||
|
try:
|
||||||
|
# Connect to MISP instance and get network activity attributes.
|
||||||
|
m = PyMISP(misp.url, misp.apikey, misp.verifycert)
|
||||||
|
r = m.search("attributes", category="Network activity", date_from=int(misp.last_sync))
|
||||||
|
except:
|
||||||
|
print("Unable to connect to the MISP instance ({}/{}).".format(misp.url, misp.apikey))
|
||||||
|
return []
|
||||||
|
|
||||||
|
for attr in r["Attribute"]:
|
||||||
|
if attr["type"] in ["ip-dst", "domain", "snort", "x509-fingerprint-sha1"]:
|
||||||
|
|
||||||
|
ioc = {"value": attr["value"],
|
||||||
|
"type": None,
|
||||||
|
"tag": "suspect",
|
||||||
|
"tlp": "white"}
|
||||||
|
|
||||||
|
# Deduce the IOC type.
|
||||||
|
if re.match(defs["iocs_types"][0]["regex"], attr["value"]):
|
||||||
|
ioc["type"] = "ip4addr"
|
||||||
|
elif re.match(defs["iocs_types"][1]["regex"], attr["value"]):
|
||||||
|
ioc["type"] = "ip6addr"
|
||||||
|
elif re.match(defs["iocs_types"][2]["regex"], attr["value"]):
|
||||||
|
ioc["type"] = "cidr"
|
||||||
|
elif re.match(defs["iocs_types"][3]["regex"], attr["value"]):
|
||||||
|
ioc["type"] = "domain"
|
||||||
|
elif re.match(defs["iocs_types"][4]["regex"], attr["value"]):
|
||||||
|
ioc["type"] = "sha1cert"
|
||||||
|
elif "alert " in attr["value"][0:6]:
|
||||||
|
ioc["type"] = "snort"
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if "Tag" in attr:
|
||||||
|
for tag in attr["Tag"]:
|
||||||
|
# Add a TLP to the IOC if defined in tags.
|
||||||
|
tlp = re.search(r"^(?:tlp:)(red|green|amber|white)", tag['name'].lower())
|
||||||
|
if tlp: ioc["tlp"] = tlp.group(1)
|
||||||
|
|
||||||
|
# Add possible tag (need to match TinyCheck tags)
|
||||||
|
if tag["name"].lower() in [t["tag"] for t in defs["iocs_tags"]]:
|
||||||
|
ioc["tag"] = tag["name"].lower()
|
||||||
|
yield ioc
|
@ -1,5 +1,6 @@
|
|||||||
from app import db
|
from app import db
|
||||||
|
|
||||||
|
|
||||||
class Ioc(db.Model):
|
class Ioc(db.Model):
|
||||||
def __init__(self, value, type, tlp, tag, source, added_on):
|
def __init__(self, value, type, tlp, tag, source, added_on):
|
||||||
self.value = value
|
self.value = value
|
||||||
@ -9,6 +10,7 @@ class Ioc(db.Model):
|
|||||||
self.source = source
|
self.source = source
|
||||||
self.added_on = added_on
|
self.added_on = added_on
|
||||||
|
|
||||||
|
|
||||||
class Whitelist(db.Model):
|
class Whitelist(db.Model):
|
||||||
def __init__(self, element, type, source, added_on):
|
def __init__(self, element, type, source, added_on):
|
||||||
self.element = element
|
self.element = element
|
||||||
@ -16,5 +18,17 @@ 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, added_on, last_sync):
|
||||||
|
self.name = name
|
||||||
|
self.url = url
|
||||||
|
self.apikey = key
|
||||||
|
self.verifycert = ssl
|
||||||
|
self.added_on = added_on
|
||||||
|
self.last_sync = last_sync
|
||||||
|
|
||||||
|
|
||||||
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('misp', db.metadata, autoload=True))
|
||||||
|
@ -6,6 +6,7 @@ from app.decorators import auth
|
|||||||
from app.blueprints.ioc import ioc_bp
|
from app.blueprints.ioc import ioc_bp
|
||||||
from app.blueprints.whitelist import whitelist_bp
|
from app.blueprints.whitelist import whitelist_bp
|
||||||
from app.blueprints.config import config_bp
|
from app.blueprints.config import config_bp
|
||||||
|
from app.blueprints.misp import misp_bp
|
||||||
import datetime
|
import datetime
|
||||||
import secrets
|
import secrets
|
||||||
import jwt
|
import jwt
|
||||||
@ -56,6 +57,7 @@ def page_not_found(e):
|
|||||||
app.register_blueprint(ioc_bp, url_prefix='/api/ioc')
|
app.register_blueprint(ioc_bp, url_prefix='/api/ioc')
|
||||||
app.register_blueprint(whitelist_bp, url_prefix='/api/whitelist')
|
app.register_blueprint(whitelist_bp, url_prefix='/api/whitelist')
|
||||||
app.register_blueprint(config_bp, url_prefix='/api/config')
|
app.register_blueprint(config_bp, url_prefix='/api/config')
|
||||||
|
app.register_blueprint(misp_bp, url_prefix='/api/misp')
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
ssl_cert = "{}/{}".format(path[0], 'cert.pem')
|
ssl_cert = "{}/{}".format(path[0], 'cert.pem')
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
from app.utils import read_config
|
from app.utils import read_config
|
||||||
from app.classes.iocs import IOCs
|
from app.classes.iocs import IOCs
|
||||||
from app.classes.whitelist import WhiteList
|
from app.classes.whitelist import WhiteList
|
||||||
|
from app.classes.misp import MISP
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
@ -16,11 +17,6 @@ from multiprocessing import Process
|
|||||||
in the configuration file. This in order to get
|
in the configuration file. This in order to get
|
||||||
automatically new iocs / elements from remote
|
automatically new iocs / elements from remote
|
||||||
sources without user interaction.
|
sources without user interaction.
|
||||||
|
|
||||||
As of today the default export JSON format from
|
|
||||||
the backend and unauthenticated HTTP requests
|
|
||||||
are accepted. The code is little awkward, it'll
|
|
||||||
be better in a next version ;)
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||||
@ -29,7 +25,7 @@ urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|||||||
def watch_iocs():
|
def watch_iocs():
|
||||||
"""
|
"""
|
||||||
Retrieve IOCs from the remote URLs defined in config/watchers.
|
Retrieve IOCs from the remote URLs defined in config/watchers.
|
||||||
For each (new ?) IOC, add it to the DB.
|
For each IOC, add it to the DB.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Retrieve the URLs from the configuration
|
# Retrieve the URLs from the configuration
|
||||||
@ -120,8 +116,32 @@ def watch_whitelists():
|
|||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def watch_misp():
|
||||||
|
"""
|
||||||
|
Retrieve IOCs from misp instances. Each new element is
|
||||||
|
tested and then added to the database.
|
||||||
|
"""
|
||||||
|
iocs, misp = IOCs(), MISP()
|
||||||
|
instances = [i for i in misp.get_instances()]
|
||||||
|
|
||||||
|
while instances:
|
||||||
|
for i, ist in enumerate(instances):
|
||||||
|
status = misp.test_instance(ist["url"],
|
||||||
|
ist["apikey"],
|
||||||
|
ist["verifycert"])
|
||||||
|
if status:
|
||||||
|
for ioc in misp.get_iocs(ist["id"]):
|
||||||
|
iocs.add(ioc["type"], ioc["tag"], ioc["tlp"],
|
||||||
|
ioc["value"], "misp-{}".format(ist["id"]))
|
||||||
|
misp.update_sync(ist["id"])
|
||||||
|
instances.pop(i)
|
||||||
|
if instances: time.sleep(60)
|
||||||
|
|
||||||
|
|
||||||
p1 = Process(target=watch_iocs)
|
p1 = Process(target=watch_iocs)
|
||||||
p2 = Process(target=watch_whitelists)
|
p2 = Process(target=watch_whitelists)
|
||||||
|
p3 = Process(target=watch_misp)
|
||||||
|
|
||||||
p1.start()
|
p1.start()
|
||||||
p2.start()
|
p2.start()
|
||||||
|
p3.start()
|
||||||
|
@ -72,5 +72,6 @@ def get_config():
|
|||||||
"shutdown_option": read_config(("frontend", "shutdown_option")),
|
"shutdown_option": read_config(("frontend", "shutdown_option")),
|
||||||
"reboot_option": read_config(("frontend", "reboot_option")),
|
"reboot_option": read_config(("frontend", "reboot_option")),
|
||||||
"iface_out": read_config(("network", "out")),
|
"iface_out": read_config(("network", "out")),
|
||||||
"user_lang": read_config(("frontend", "user_lang"))
|
"user_lang": read_config(("frontend", "user_lang")),
|
||||||
|
"choose_net": read_config(("frontend", "choose_net"))
|
||||||
})
|
})
|
||||||
|
@ -41,13 +41,18 @@ class Analysis(object):
|
|||||||
:return: dict containing the report or error message.
|
:return: dict containing the report or error message.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
device, alerts = {}, {}
|
device, alerts, pcap = {}, {}, {}
|
||||||
|
|
||||||
# Getting device configuration.
|
# Getting device configuration.
|
||||||
if os.path.isfile("/tmp/{}/assets/device.json".format(self.token)):
|
if os.path.isfile("/tmp/{}/assets/device.json".format(self.token)):
|
||||||
with open("/tmp/{}/assets/device.json".format(self.token), "r") as f:
|
with open("/tmp/{}/assets/device.json".format(self.token), "r") as f:
|
||||||
device = json.load(f)
|
device = json.load(f)
|
||||||
|
|
||||||
|
# Getting pcap infos.
|
||||||
|
if os.path.isfile("/tmp/{}/assets/capinfos.json".format(self.token)):
|
||||||
|
with open("/tmp/{}/assets/capinfos.json".format(self.token), "r") as f:
|
||||||
|
pcap = json.load(f)
|
||||||
|
|
||||||
# Getting alerts configuration.
|
# Getting alerts configuration.
|
||||||
if os.path.isfile("/tmp/{}/assets/alerts.json".format(self.token)):
|
if os.path.isfile("/tmp/{}/assets/alerts.json".format(self.token)):
|
||||||
with open("/tmp/{}/assets/alerts.json".format(self.token), "r") as f:
|
with open("/tmp/{}/assets/alerts.json".format(self.token), "r") as f:
|
||||||
@ -55,6 +60,7 @@ class Analysis(object):
|
|||||||
|
|
||||||
if device != {} and alerts != {}:
|
if device != {} and alerts != {}:
|
||||||
return {"alerts": alerts,
|
return {"alerts": alerts,
|
||||||
"device": device}
|
"device": device,
|
||||||
|
"pcap": pcap}
|
||||||
else:
|
else:
|
||||||
return {"message": "No report yet"}
|
return {"message": "No report yet"}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
import subprocess as sp
|
import subprocess as sp
|
||||||
import netifaces as ni
|
import netifaces as ni
|
||||||
import requests as rq
|
import requests as rq
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import qrcode
|
import qrcode
|
||||||
@ -214,11 +215,13 @@ class Network(object):
|
|||||||
:return: bool - if hostapd configuration file created
|
:return: bool - if hostapd configuration file created
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
chan = self.set_ap_channel()
|
||||||
with open("{}/app/assets/hostapd.conf".format(sys.path[0]), "r") as f:
|
with open("{}/app/assets/hostapd.conf".format(sys.path[0]), "r") as f:
|
||||||
conf = f.read()
|
conf = f.read()
|
||||||
conf = conf.replace("{IFACE}", self.iface_in)
|
conf = conf.replace("{IFACE}", self.iface_in)
|
||||||
conf = conf.replace("{SSID}", self.AP_SSID)
|
conf = conf.replace("{SSID}", self.AP_SSID)
|
||||||
conf = conf.replace("{PASS}", self.AP_PASS)
|
conf = conf.replace("{PASS}", self.AP_PASS)
|
||||||
|
conf = conf.replace("{CHAN}", chan)
|
||||||
with open("/tmp/hostapd.conf", "w") as c:
|
with open("/tmp/hostapd.conf", "w") as c:
|
||||||
c.write(conf)
|
c.write(conf)
|
||||||
return True
|
return True
|
||||||
@ -335,3 +338,18 @@ class Network(object):
|
|||||||
return True
|
return True
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def set_ap_channel(self):
|
||||||
|
"""
|
||||||
|
Deduce the channel to have for the AP in order to prevent
|
||||||
|
kind of jamming between the two wifi interfaces.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Get the channel of the connected interface
|
||||||
|
sh = sp.Popen(["iw", self.iface_out, "info"],
|
||||||
|
stdout=sp.PIPE, stderr=sp.PIPE).communicate()
|
||||||
|
res = re.search("channel ([0-9]{1,2})", sh[0].decode('utf8'))
|
||||||
|
chn = res.group(1)
|
||||||
|
|
||||||
|
# Return a good candidate.
|
||||||
|
return "11" if int(chn) < 7 else "1"
|
||||||
|
12
update.sh
12
update.sh
@ -40,13 +40,17 @@ elif [ $PWD = "/tmp/tinycheck" ]; then
|
|||||||
cd /usr/share/tinycheck/app/frontend/ && npm install && npm audit fix && npm run build
|
cd /usr/share/tinycheck/app/frontend/ && npm install && npm audit fix && npm run build
|
||||||
cd /usr/share/tinycheck/app/backend/ && npm install && npm audit fix && npm run build
|
cd /usr/share/tinycheck/app/backend/ && npm install && npm audit fix && npm run build
|
||||||
|
|
||||||
|
echo "[+] Updating the database scheme..."
|
||||||
|
cd /usr/share/tinycheck/
|
||||||
|
sqlite3 tinycheck.sqlite3 < /tmp/tinycheck/assets/scheme.sql 2>/dev/null
|
||||||
|
|
||||||
echo "[+] Updating current configuration with new values."
|
echo "[+] Updating current configuration with new values."
|
||||||
if ! grep -q reboot_option /usr/share/tinycheck/config.yaml; then
|
if ! grep -q reboot_option /usr/share/tinycheck/config.yaml; then
|
||||||
sed -i 's/frontend:/frontend:\n reboot_option: true/g' /usr/share/tinycheck/config.yaml
|
sed -i 's/frontend:/frontend:\n reboot_option: true/g' /usr/share/tinycheck/config.yaml
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! grep -q user_lang /usr/share/tinycheck/config.yaml; then
|
if ! grep -q choose_net /usr/share/tinycheck/config.yaml; then
|
||||||
sed -i 's/frontend:/frontend:\n user_lang: en/g' /usr/share/tinycheck/config.yaml
|
sed -i 's/frontend:/frontend:\n choose_net: false/g' /usr/share/tinycheck/config.yaml
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! grep -q shutdown_option /usr/share/tinycheck/config.yaml; then
|
if ! grep -q shutdown_option /usr/share/tinycheck/config.yaml; then
|
||||||
@ -65,6 +69,10 @@ elif [ $PWD = "/tmp/tinycheck" ]; then
|
|||||||
sed -i 's/frontend:/frontend:\n update: true/g' /usr/share/tinycheck/config.yaml
|
sed -i 's/frontend:/frontend:\n update: true/g' /usr/share/tinycheck/config.yaml
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if ! grep -q user_lang /usr/share/tinycheck/config.yaml; then
|
||||||
|
sed -i 's/frontend:/frontend:\n user_lang: en/g' /usr/share/tinycheck/config.yaml
|
||||||
|
fi
|
||||||
|
|
||||||
if ! grep -q "CN=R3,O=Let's Encrypt,C=US" /usr/share/tinycheck/config.yaml; then
|
if ! grep -q "CN=R3,O=Let's Encrypt,C=US" /usr/share/tinycheck/config.yaml; then
|
||||||
sed -i "s/free_issuers:/free_issuers:\n - CN=R3,O=Let's Encrypt,C=US/g" /usr/share/tinycheck/config.yaml
|
sed -i "s/free_issuers:/free_issuers:\n - CN=R3,O=Let's Encrypt,C=US/g" /usr/share/tinycheck/config.yaml
|
||||||
fi
|
fi
|
||||||
|
Loading…
x
Reference in New Issue
Block a user