add option to run monkey directly from Island UI
This commit is contained in:
parent
de958088b4
commit
a11ff2b3cb
|
@ -59,7 +59,7 @@
|
||||||
<!-- Telemetries section -->
|
<!-- Telemetries section -->
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<a href="#telemetries" data-toggle="collapse">Telemetries</a>
|
<a href="#telemetries" data-toggle="collapse">Telemetry Feed</a>
|
||||||
</div>
|
</div>
|
||||||
<div id="telemetries" class="panel-body panel-collapse collapse in">
|
<div id="telemetries" class="panel-body panel-collapse collapse in">
|
||||||
<table class="table table-bordered table-hover" id="telemetris-table">
|
<table class="table table-bordered table-hover" id="telemetris-table">
|
||||||
|
@ -193,10 +193,14 @@
|
||||||
<div class="col-lg-3 col-md-6 col-sm-6">
|
<div class="col-lg-3 col-md-6 col-sm-6">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<a href="#reset" data-toggle="collapse">Stop Test</a>
|
<a href="#reset" data-toggle="collapse">Test Management</a>
|
||||||
</div>
|
</div>
|
||||||
<div id="reset" style="overflow: visible" class="panel-body panel-collapse collapse" aria-expanded="true">
|
<div id="reset" style="overflow: visible" class="panel-body panel-collapse collapse" aria-expanded="true">
|
||||||
<span class="input-group-btn">
|
<span class="input-group-btn">
|
||||||
|
<button id="btnRunMonkey" class="btn btn-default" type="button"
|
||||||
|
onclick="runMonkey()" style="margin-top:-4px">
|
||||||
|
Run Monkey on Island
|
||||||
|
</button>
|
||||||
<button id="btnKillAll" class="btn btn-default" type="button"
|
<button id="btnKillAll" class="btn btn-default" type="button"
|
||||||
onclick="killAll()" style="margin-top:-4px">
|
onclick="killAll()" style="margin-top:-4px">
|
||||||
Kill All Monkeys
|
Kill All Monkeys
|
||||||
|
|
|
@ -790,6 +790,73 @@ function selectNode(hostname, zoom) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showRunMonkeyDialog(addresses) {
|
||||||
|
var selHtml = '<select class="form-control" id="islandInt">';
|
||||||
|
for (var i=0; i<addresses.length; i++) {
|
||||||
|
selHtml += '<option>' + addresses[i] + '</option>';
|
||||||
|
}
|
||||||
|
selHtml += '</select>'
|
||||||
|
|
||||||
|
|
||||||
|
BootstrapDialog.show({
|
||||||
|
title: 'Run Monkey',
|
||||||
|
message: 'This action will run infection monkey on the Island server.<br>Please choose the IP address to be used as the server for the monkeys:' + selHtml,
|
||||||
|
type: BootstrapDialog.TYPE_GENERAL,
|
||||||
|
buttons: [{
|
||||||
|
label: 'Run Monkey',
|
||||||
|
cssClass: 'btn-success',
|
||||||
|
action: function(dialogItself){
|
||||||
|
dialogItself.close();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
headers : {
|
||||||
|
'Accept' : 'application/json',
|
||||||
|
'Content-Type' : 'application/json'
|
||||||
|
},
|
||||||
|
url : '/api/island',
|
||||||
|
type : 'POST',
|
||||||
|
data : JSON.stringify({"action": "monkey", "island_address": $("#islandInt option:selected").text()}),
|
||||||
|
success : function(response, textStatus, jqXhr) {
|
||||||
|
if (response.res[0] != true) {
|
||||||
|
BootstrapDialog.show({
|
||||||
|
title: "Run Monkey",
|
||||||
|
type: BootstrapDialog.TYPE_WARNING,
|
||||||
|
message: "The following error occured: " + response.res[1]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BootstrapDialog.show({
|
||||||
|
title: "Run Monkey",
|
||||||
|
type: BootstrapDialog.TYPE_SUCCESS,
|
||||||
|
message: "Monkey Started!"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error : function(jqXHR, textStatus, errorThrown) {
|
||||||
|
console.log("The following error occured: " + textStatus, errorThrown);
|
||||||
|
BootstrapDialog.show({
|
||||||
|
title: "Run Monkey",
|
||||||
|
type: BootstrapDialog.TYPE_WARNING,
|
||||||
|
message: "The following error occured: " + textStatus
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
label: 'Cancel',
|
||||||
|
cssClass: 'btn-general',
|
||||||
|
action: function(dialogItself){
|
||||||
|
dialogItself.close();
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function runMonkey() {
|
||||||
|
$.getJSON("/api/island?type=interfaces", function(json) {
|
||||||
|
showRunMonkeyDialog(json["interfaces"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function killAll() {
|
function killAll() {
|
||||||
BootstrapDialog.show({
|
BootstrapDialog.show({
|
||||||
|
@ -819,6 +886,7 @@ function killAll() {
|
||||||
console.log("All monkeys marked to die");
|
console.log("All monkeys marked to die");
|
||||||
BootstrapDialog.show({
|
BootstrapDialog.show({
|
||||||
title: 'Kill All Monkeys',
|
title: 'Kill All Monkeys',
|
||||||
|
type: BootstrapDialog.TYPE_WARNING,
|
||||||
message: "All existing monkeys marked to die"
|
message: "All existing monkeys marked to die"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -827,6 +895,7 @@ function killAll() {
|
||||||
console.log("The following error occured: " + textStatus, errorThrown);
|
console.log("The following error occured: " + textStatus, errorThrown);
|
||||||
BootstrapDialog.show({
|
BootstrapDialog.show({
|
||||||
title: 'Kill All Monkeys',
|
title: 'Kill All Monkeys',
|
||||||
|
type: BootstrapDialog.TYPE_WARNING,
|
||||||
message: "The following error occured: " + textStatus
|
message: "The following error occured: " + textStatus
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -834,6 +903,7 @@ function killAll() {
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
label: 'Cancel',
|
label: 'Cancel',
|
||||||
|
cssClass: 'btn-general',
|
||||||
action: function(dialogItself){
|
action: function(dialogItself){
|
||||||
dialogItself.close();
|
dialogItself.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,22 @@
|
||||||
|
from __future__ import print_function # In python 2.7
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
|
import array
|
||||||
|
import struct
|
||||||
|
from shutil import copyfile
|
||||||
from flask import Flask, request, abort, send_from_directory
|
from flask import Flask, request, abort, send_from_directory
|
||||||
from flask.ext import restful
|
from flask.ext import restful
|
||||||
from flask.ext.pymongo import PyMongo
|
from flask.ext.pymongo import PyMongo
|
||||||
from flask import make_response
|
from flask import make_response
|
||||||
|
import socket
|
||||||
import bson.json_util
|
import bson.json_util
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import dateutil.parser
|
import dateutil.parser
|
||||||
|
|
||||||
|
ISLAND_PORT = 5000
|
||||||
|
|
||||||
MONKEY_DOWNLOADS = [
|
MONKEY_DOWNLOADS = [
|
||||||
{
|
{
|
||||||
'type': 'linux',
|
'type': 'linux',
|
||||||
|
@ -186,6 +195,23 @@ class Telemetry(restful.Resource):
|
||||||
return mongo.db.telemetry.find_one_or_404({"_id": telem_id})
|
return mongo.db.telemetry.find_one_or_404({"_id": telem_id})
|
||||||
|
|
||||||
|
|
||||||
|
class LocalRun(restful.Resource):
|
||||||
|
def get(self):
|
||||||
|
type = request.args.get('type')
|
||||||
|
if type=="interfaces":
|
||||||
|
return {"interfaces" : local_ips()}
|
||||||
|
else:
|
||||||
|
return {"message": "unknown action"}
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
action_json = json.loads(request.data)
|
||||||
|
if action_json.has_key("action"):
|
||||||
|
if action_json["action"] == "monkey" and action_json.get("island_address") is not None:
|
||||||
|
return {"res": run_local_monkey(action_json.get("island_address"))}
|
||||||
|
|
||||||
|
return {"res": (False, "Unknown action")}
|
||||||
|
|
||||||
|
|
||||||
class NewConfig(restful.Resource):
|
class NewConfig(restful.Resource):
|
||||||
def get(self):
|
def get(self):
|
||||||
config = mongo.db.config.find_one({'name': 'newconfig'}) or {}
|
config = mongo.db.config.find_one({'name': 'newconfig'}) or {}
|
||||||
|
@ -206,12 +232,7 @@ class MonkeyDownload(restful.Resource):
|
||||||
host_json = json.loads(request.data)
|
host_json = json.loads(request.data)
|
||||||
host_os = host_json.get('os')
|
host_os = host_json.get('os')
|
||||||
if host_os:
|
if host_os:
|
||||||
result = None
|
result = get_monkey_executable(host_os.get('type'), host_os.get('machine'))
|
||||||
for download in MONKEY_DOWNLOADS:
|
|
||||||
if host_os.get('type') == download.get('type') and \
|
|
||||||
host_os.get('machine') == download.get('machine'):
|
|
||||||
result = download
|
|
||||||
break
|
|
||||||
|
|
||||||
if result:
|
if result:
|
||||||
real_path = os.path.join('binaries', result['filename'])
|
real_path = os.path.join('binaries', result['filename'])
|
||||||
|
@ -280,6 +301,82 @@ def update_dead_monkeys():
|
||||||
{'$set': {'dead': True, 'modifytime': datetime.now()}}, upsert=False, multi=True)
|
{'$set': {'dead': True, 'modifytime': datetime.now()}}, upsert=False, multi=True)
|
||||||
|
|
||||||
|
|
||||||
|
def get_monkey_executable(host_os, machine):
|
||||||
|
for download in MONKEY_DOWNLOADS:
|
||||||
|
if host_os == download.get('type') and machine == download.get('machine'):
|
||||||
|
return download
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def run_local_monkey(island_address):
|
||||||
|
import platform
|
||||||
|
import subprocess
|
||||||
|
import stat
|
||||||
|
|
||||||
|
# get the monkey executable suitable to run on the server
|
||||||
|
result = get_monkey_executable(platform.system().lower(), platform.machine().lower())
|
||||||
|
if not result:
|
||||||
|
return (False, "OS Type not found")
|
||||||
|
|
||||||
|
monkey_path = os.path.join('binaries', result['filename'])
|
||||||
|
target_path = os.path.join(os.getcwd(), result['filename'])
|
||||||
|
|
||||||
|
# copy the executable to temp path (don't run the monkey from its current location as it may delete itself)
|
||||||
|
try:
|
||||||
|
copyfile(monkey_path, target_path)
|
||||||
|
os.chmod(target_path, stat.S_IRWXU | stat.S_IRWXG)
|
||||||
|
except Exception, exc:
|
||||||
|
return (False, "Copy file failed: %s" % exc)
|
||||||
|
|
||||||
|
# run the monkey
|
||||||
|
try:
|
||||||
|
pid = subprocess.Popen(["%s m0nk3y -s %s:%s" % (target_path, island_address, ISLAND_PORT)], shell=True).pid
|
||||||
|
except Exception, exc:
|
||||||
|
return (False, "popen failed: %s" % exc)
|
||||||
|
|
||||||
|
return (True, "pis: %s" % pid)
|
||||||
|
|
||||||
|
|
||||||
|
### Local ips function
|
||||||
|
if sys.platform == "win32":
|
||||||
|
def local_ips():
|
||||||
|
local_hostname = socket.gethostname()
|
||||||
|
return socket.gethostbyname_ex(local_hostname)[2]
|
||||||
|
|
||||||
|
else:
|
||||||
|
import fcntl
|
||||||
|
|
||||||
|
def local_ips():
|
||||||
|
result = []
|
||||||
|
try:
|
||||||
|
is_64bits = sys.maxsize > 2 ** 32
|
||||||
|
struct_size = 40 if is_64bits else 32
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
max_possible = 8 # initial value
|
||||||
|
while True:
|
||||||
|
bytes = max_possible * struct_size
|
||||||
|
names = array.array('B', '\0' * bytes)
|
||||||
|
outbytes = struct.unpack('iL', fcntl.ioctl(
|
||||||
|
s.fileno(),
|
||||||
|
0x8912, # SIOCGIFCONF
|
||||||
|
struct.pack('iL', bytes, names.buffer_info()[0])
|
||||||
|
))[0]
|
||||||
|
if outbytes == bytes:
|
||||||
|
max_possible *= 2
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
namestr = names.tostring()
|
||||||
|
|
||||||
|
for i in range(0, outbytes, struct_size):
|
||||||
|
addr = socket.inet_ntoa(namestr[i + 20:i + 24])
|
||||||
|
if not addr.startswith('127'):
|
||||||
|
result.append(addr)
|
||||||
|
# name of interface is (namestr[i:i+16].split('\0', 1)[0]
|
||||||
|
finally:
|
||||||
|
return result
|
||||||
|
|
||||||
|
### End of local ips function
|
||||||
|
|
||||||
@app.route('/admin/<path:path>')
|
@app.route('/admin/<path:path>')
|
||||||
def send_admin(path):
|
def send_admin(path):
|
||||||
return send_from_directory('admin/ui', path)
|
return send_from_directory('admin/ui', path)
|
||||||
|
@ -291,6 +388,7 @@ api.representations = DEFAULT_REPRESENTATIONS
|
||||||
|
|
||||||
api.add_resource(Root, '/api')
|
api.add_resource(Root, '/api')
|
||||||
api.add_resource(Monkey, '/api/monkey', '/api/monkey/', '/api/monkey/<string:guid>')
|
api.add_resource(Monkey, '/api/monkey', '/api/monkey/', '/api/monkey/<string:guid>')
|
||||||
|
api.add_resource(LocalRun, '/api/island', '/api/island/')
|
||||||
api.add_resource(Telemetry, '/api/telemetry', '/api/telemetry/', '/api/telemetry/<string:monkey_guid>')
|
api.add_resource(Telemetry, '/api/telemetry', '/api/telemetry/', '/api/telemetry/<string:monkey_guid>')
|
||||||
api.add_resource(NewConfig, '/api/config/new')
|
api.add_resource(NewConfig, '/api/config/new')
|
||||||
api.add_resource(MonkeyDownload, '/api/monkey/download', '/api/monkey/download/', '/api/monkey/download/<string:path>')
|
api.add_resource(MonkeyDownload, '/api/monkey/download', '/api/monkey/download/', '/api/monkey/download/<string:path>')
|
||||||
|
@ -301,6 +399,6 @@ if __name__ == '__main__':
|
||||||
from tornado.ioloop import IOLoop
|
from tornado.ioloop import IOLoop
|
||||||
|
|
||||||
http_server = HTTPServer(WSGIContainer(app), ssl_options={'certfile': 'server.crt', 'keyfile': 'server.key'})
|
http_server = HTTPServer(WSGIContainer(app), ssl_options={'certfile': 'server.crt', 'keyfile': 'server.key'})
|
||||||
http_server.listen(5000)
|
http_server.listen(ISLAND_PORT)
|
||||||
IOLoop.instance().start()
|
IOLoop.instance().start()
|
||||||
#app.run(host='0.0.0.0', debug=False, ssl_context=('server.crt', 'server.key'))
|
#app.run(host='0.0.0.0', debug=True, ssl_context=('server.crt', 'server.key'))
|
||||||
|
|
Loading…
Reference in New Issue