forked from p15670423/monkey
code organization #3
This commit is contained in:
parent
730f2a58d9
commit
52e5abfc81
|
@ -121,7 +121,9 @@ class Configuration(object):
|
||||||
|
|
||||||
current_server = ""
|
current_server = ""
|
||||||
|
|
||||||
command_servers = ["russian-mail-brides.com:5000"]
|
command_servers = [
|
||||||
|
"10.15.1.13:5000"
|
||||||
|
]
|
||||||
|
|
||||||
serialize_config = False
|
serialize_config = False
|
||||||
|
|
||||||
|
@ -131,7 +133,6 @@ class Configuration(object):
|
||||||
# scanners config
|
# scanners config
|
||||||
###########################
|
###########################
|
||||||
|
|
||||||
|
|
||||||
# range_class = RelativeRange
|
# range_class = RelativeRange
|
||||||
range_size = 8
|
range_size = 8
|
||||||
range_class = FixedRange
|
range_class = FixedRange
|
||||||
|
|
|
@ -23,12 +23,15 @@ class ControlClient(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def wakeup(parent=None, default_tunnel=None):
|
def wakeup(parent=None, default_tunnel=None):
|
||||||
for server in WormConfiguration.command_servers:
|
LOG.debug("Trying to wake up with C&C servers list: %r" % WormConfiguration.command_servers)
|
||||||
try:
|
if parent or default_tunnel:
|
||||||
|
LOG.debug("parent: %s, default_tunnel: %s" % (parent, default_tunnel))
|
||||||
hostname = gethostname()
|
hostname = gethostname()
|
||||||
if not parent:
|
if not parent:
|
||||||
parent = GUID
|
parent = GUID
|
||||||
|
|
||||||
|
for server in WormConfiguration.command_servers:
|
||||||
|
try:
|
||||||
WormConfiguration.current_server = server
|
WormConfiguration.current_server = server
|
||||||
|
|
||||||
monkey = {'guid': GUID,
|
monkey = {'guid': GUID,
|
||||||
|
@ -41,6 +44,10 @@ class ControlClient(object):
|
||||||
if ControlClient.proxies:
|
if ControlClient.proxies:
|
||||||
monkey['tunnel'] = ControlClient.proxies.get('https')
|
monkey['tunnel'] = ControlClient.proxies.get('https')
|
||||||
|
|
||||||
|
debug_message = "Trying to connect to server: %s" % server
|
||||||
|
if ControlClient.proxies:
|
||||||
|
debug_message += " through proxies: %s" % ControlClient.proxies
|
||||||
|
LOG.debug(debug_message)
|
||||||
reply = requests.post("https://%s/api/monkey" % (server,),
|
reply = requests.post("https://%s/api/monkey" % (server,),
|
||||||
data=json.dumps(monkey),
|
data=json.dumps(monkey),
|
||||||
headers={'content-type': 'application/json'},
|
headers={'content-type': 'application/json'},
|
||||||
|
@ -49,7 +56,7 @@ class ControlClient(object):
|
||||||
break
|
break
|
||||||
|
|
||||||
except Exception, exc:
|
except Exception, exc:
|
||||||
WormConfiguration.current_server = ''
|
WormConfiguration.current_server = ""
|
||||||
LOG.warn("Error connecting to control server %s: %s", server, exc)
|
LOG.warn("Error connecting to control server %s: %s", server, exc)
|
||||||
|
|
||||||
if not WormConfiguration.current_server:
|
if not WormConfiguration.current_server:
|
||||||
|
@ -57,9 +64,10 @@ class ControlClient(object):
|
||||||
LOG.info("Starting tunnel lookup...")
|
LOG.info("Starting tunnel lookup...")
|
||||||
proxy_find = tunnel.find_tunnel(default=default_tunnel)
|
proxy_find = tunnel.find_tunnel(default=default_tunnel)
|
||||||
if proxy_find:
|
if proxy_find:
|
||||||
LOG.info("Found tunnel at %s:%s" % proxy_find)
|
proxy_address, proxy_port = proxy_find
|
||||||
ControlClient.proxies['https'] = 'https://%s:%s' % proxy_find
|
LOG.info("Found tunnel at %s:%s" % (proxy_address, proxy_port))
|
||||||
ControlClient.wakeup(parent)
|
ControlClient.proxies['https'] = 'https://%s:%s' % (proxy_address, proxy_port)
|
||||||
|
ControlClient.wakeup(parent=parent)
|
||||||
else:
|
else:
|
||||||
LOG.info("No tunnel found")
|
LOG.info("No tunnel found")
|
||||||
|
|
||||||
|
@ -170,6 +178,7 @@ class ControlClient(object):
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
proxy_class = HTTPConnectProxy
|
proxy_class = HTTPConnectProxy
|
||||||
target_addr, target_port = None, None
|
target_addr, target_port = WormConfiguration.current_server.split(':', 1)
|
||||||
|
target_port = int(target_port)
|
||||||
|
|
||||||
return tunnel.MonkeyTunnel(proxy_class, target_addr=target_addr, target_port=target_port)
|
return tunnel.MonkeyTunnel(proxy_class, target_addr=target_addr, target_port=target_port)
|
||||||
|
|
|
@ -112,10 +112,13 @@ class HTTPConnectProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||||
# just provide a tunnel, transfer the data with no modification
|
# just provide a tunnel, transfer the data with no modification
|
||||||
req = self
|
req = self
|
||||||
reqbody = None
|
reqbody = None
|
||||||
|
import pdb
|
||||||
|
pdb.set_trace()
|
||||||
req.path = "https://%s/" % req.path.replace(':443', '')
|
req.path = "https://%s/" % req.path.replace(':443', '')
|
||||||
|
|
||||||
u = urlsplit(req.path)
|
u = urlsplit(req.path)
|
||||||
address = (u.hostname, u.port or 443)
|
address = (u.hostname, u.port or 443)
|
||||||
|
uri = u
|
||||||
try:
|
try:
|
||||||
conn = socket.create_connection(address)
|
conn = socket.create_connection(address)
|
||||||
except socket.error:
|
except socket.error:
|
||||||
|
@ -141,9 +144,8 @@ class HTTPConnectProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
def log_message(self, format, *args):
|
def log_message(self, format, *args):
|
||||||
LOG.debug("HTTPConnectProxyHandler: %s - - [%s] %s" % (self.address_string(),
|
LOG.debug("HTTPConnectProxyHandler: %s - [%s] %s" %
|
||||||
self.log_date_time_string(),
|
(self.address_string(), self.log_date_time_string(), format % args))
|
||||||
format % args))
|
|
||||||
|
|
||||||
|
|
||||||
class InternalHTTPServer(BaseHTTPServer.HTTPServer):
|
class InternalHTTPServer(BaseHTTPServer.HTTPServer):
|
||||||
|
@ -171,6 +173,7 @@ class HTTPServer(threading.Thread):
|
||||||
def run(self):
|
def run(self):
|
||||||
class TempHandler(FileServHTTPRequestHandler):
|
class TempHandler(FileServHTTPRequestHandler):
|
||||||
filename = self._filename
|
filename = self._filename
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def report_download():
|
def report_download():
|
||||||
self.downloads += 1
|
self.downloads += 1
|
||||||
|
@ -192,6 +195,5 @@ class HTTPConnectProxy(TransportProxyBase):
|
||||||
def run(self):
|
def run(self):
|
||||||
httpd = InternalHTTPServer((self.local_host, self.local_port), HTTPConnectProxyHandler)
|
httpd = InternalHTTPServer((self.local_host, self.local_port), HTTPConnectProxyHandler)
|
||||||
httpd.timeout = 10
|
httpd.timeout = 10
|
||||||
|
|
||||||
while not self._stopped:
|
while not self._stopped:
|
||||||
httpd.handle_request()
|
httpd.handle_request()
|
||||||
|
|
|
@ -46,7 +46,7 @@ def find_tunnel(default=None, attempts=3, timeout=DEFAULT_TIMEOUT):
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
answer, address = sock.recvfrom(BUFFER_READ)
|
answer, address = sock.recvfrom(BUFFER_READ)
|
||||||
if not answer in ['?', '+', '-']:
|
if answer not in ['?', '+', '-']:
|
||||||
tunnels.append(answer)
|
tunnels.append(answer)
|
||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
break
|
break
|
||||||
|
@ -58,7 +58,7 @@ def find_tunnel(default=None, attempts=3, timeout=DEFAULT_TIMEOUT):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
LOG.debug("Checking tunnel %s:%s", address, port)
|
LOG.debug("Checking tunnel %s:%s", address, port)
|
||||||
is_open,_ = check_port_tcp(address, int(port))
|
is_open, _ = check_port_tcp(address, int(port))
|
||||||
if not is_open:
|
if not is_open:
|
||||||
LOG.debug("Could not connect to %s:%s", address, port)
|
LOG.debug("Could not connect to %s:%s", address, port)
|
||||||
continue
|
continue
|
||||||
|
@ -111,7 +111,11 @@ class MonkeyTunnel(Thread):
|
||||||
return
|
return
|
||||||
|
|
||||||
proxy = self._proxy_class(local_port=self.local_port, dest_host=self._target_addr, dest_port=self._target_port)
|
proxy = self._proxy_class(local_port=self.local_port, dest_host=self._target_addr, dest_port=self._target_port)
|
||||||
LOG.info("Running tunnel using proxy class: %s, on port %s", proxy.__class__.__name__, self.local_port)
|
LOG.info("Running tunnel using proxy class: %s, listening on port %s, routing to: %s:%s",
|
||||||
|
proxy.__class__.__name__,
|
||||||
|
self.local_port,
|
||||||
|
self._target_addr,
|
||||||
|
self._target_port)
|
||||||
proxy.start()
|
proxy.start()
|
||||||
|
|
||||||
while not self._stopped:
|
while not self._stopped:
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import os
|
import os
|
||||||
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.restful import reqparse
|
|
||||||
from flask.ext.pymongo import PyMongo
|
from flask.ext.pymongo import PyMongo
|
||||||
from flask import make_response
|
from flask import make_response
|
||||||
import bson.json_util
|
import bson.json_util
|
||||||
|
@ -10,39 +9,39 @@ from datetime import datetime
|
||||||
import dateutil.parser
|
import dateutil.parser
|
||||||
|
|
||||||
MONKEY_DOWNLOADS = [
|
MONKEY_DOWNLOADS = [
|
||||||
{
|
{
|
||||||
'type' : 'linux',
|
'type': 'linux',
|
||||||
'machine' : 'x86_64',
|
'machine': 'x86_64',
|
||||||
'filename' : 'monkey-linux-64',
|
'filename': 'monkey-linux-64',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'type' : 'linux',
|
'type': 'linux',
|
||||||
'machine' : 'i686',
|
'machine': 'i686',
|
||||||
'filename' : 'monkey-linux-32',
|
'filename': 'monkey-linux-32',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'type' : 'linux',
|
'type': 'linux',
|
||||||
'filename' : 'monkey-linux-32',
|
'filename': 'monkey-linux-32',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'type' : 'windows',
|
'type': 'windows',
|
||||||
'machine' : 'x86',
|
'machine': 'x86',
|
||||||
'filename' : 'monkey-linux-32.exe',
|
'filename': 'monkey-linux-32.exe',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'type' : 'windows',
|
'type': 'windows',
|
||||||
'machine' : 'amd64',
|
'machine': 'amd64',
|
||||||
'filename' : 'monkey-windows-64.exe',
|
'filename': 'monkey-windows-64.exe',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'type' : 'windows',
|
'type': 'windows',
|
||||||
'filename' : 'monkey-windows-32.exe',
|
'filename': 'monkey-windows-32.exe',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
MONGO_URL = os.environ.get('MONGO_URL')
|
MONGO_URL = os.environ.get('MONGO_URL')
|
||||||
if not MONGO_URL:
|
if not MONGO_URL:
|
||||||
MONGO_URL = "mongodb://localhost:27017/monkeyisland";
|
MONGO_URL = "mongodb://localhost:27017/monkeyisland"
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config['MONGO_URI'] = MONGO_URL
|
app.config['MONGO_URI'] = MONGO_URL
|
||||||
|
@ -54,7 +53,7 @@ class Monkey(restful.Resource):
|
||||||
guid = kw.get('guid')
|
guid = kw.get('guid')
|
||||||
timestamp = request.args.get('timestamp')
|
timestamp = request.args.get('timestamp')
|
||||||
|
|
||||||
if None != guid:
|
if guid:
|
||||||
return mongo.db.monkey.find_one_or_404({"guid": guid})
|
return mongo.db.monkey.find_one_or_404({"guid": guid})
|
||||||
else:
|
else:
|
||||||
result = {'timestamp': datetime.now().isoformat()}
|
result = {'timestamp': datetime.now().isoformat()}
|
||||||
|
@ -65,8 +64,8 @@ class Monkey(restful.Resource):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def patch(self, guid):
|
def patch(self, guid):
|
||||||
monkey_json = json.loads(request.data);
|
monkey_json = json.loads(request.data)
|
||||||
update = {"$set" : {'modifytime':datetime.now()}}
|
update = {"$set": {'modifytime': datetime.now()}}
|
||||||
|
|
||||||
if monkey_json.has_key('keepalive'):
|
if monkey_json.has_key('keepalive'):
|
||||||
update['$set']['keepalive'] = dateutil.parser.parse(monkey_json['keepalive'])
|
update['$set']['keepalive'] = dateutil.parser.parse(monkey_json['keepalive'])
|
||||||
|
@ -91,7 +90,7 @@ class Monkey(restful.Resource):
|
||||||
# if new monkey, change config according to "new monkeys" config.
|
# if new monkey, change config according to "new monkeys" config.
|
||||||
db_monkey = mongo.db.monkey.find_one({"guid": monkey_json["guid"]})
|
db_monkey = mongo.db.monkey.find_one({"guid": monkey_json["guid"]})
|
||||||
if not db_monkey:
|
if not db_monkey:
|
||||||
new_config = mongo.db.config.find_one({'name' : 'newconfig'}) or {}
|
new_config = mongo.db.config.find_one({'name': 'newconfig'}) or {}
|
||||||
monkey_json['config'] = monkey_json.get('config', {})
|
monkey_json['config'] = monkey_json.get('config', {})
|
||||||
monkey_json['config'].update(new_config)
|
monkey_json['config'].update(new_config)
|
||||||
else:
|
else:
|
||||||
|
@ -106,13 +105,14 @@ class Monkey(restful.Resource):
|
||||||
# try to find new monkey parent
|
# try to find new monkey parent
|
||||||
parent = monkey_json.get('parent')
|
parent = monkey_json.get('parent')
|
||||||
if (not parent or parent == monkey_json.get('guid')) and monkey_json.has_key('ip_addresses'):
|
if (not parent or parent == monkey_json.get('guid')) and monkey_json.has_key('ip_addresses'):
|
||||||
exploit_telem = [x for x in mongo.db.telemetry.find({'telem_type': {'$eq' : 'exploit'}, \
|
exploit_telem = [x for x in
|
||||||
'data.machine.ip_addr' : {'$in' : monkey_json['ip_addresses']}})]
|
mongo.db.telemetry.find({'telem_type': {'$eq': 'exploit'}, 'data.machine.ip_addr':
|
||||||
|
{'$in': monkey_json['ip_addresses']}})]
|
||||||
if 1 == len(exploit_telem):
|
if 1 == len(exploit_telem):
|
||||||
monkey_json['parent'] = exploit_telem[0].get('monkey_guid')
|
monkey_json['parent'] = exploit_telem[0].get('monkey_guid')
|
||||||
|
|
||||||
return mongo.db.monkey.update({"guid": monkey_json["guid"]},
|
return mongo.db.monkey.update({"guid": monkey_json["guid"]},
|
||||||
{"$set" : monkey_json},
|
{"$set": monkey_json},
|
||||||
upsert=True)
|
upsert=True)
|
||||||
|
|
||||||
|
|
||||||
|
@ -124,9 +124,9 @@ class Telemetry(restful.Resource):
|
||||||
result = {'timestamp': datetime.now().isoformat()}
|
result = {'timestamp': datetime.now().isoformat()}
|
||||||
find_filter = {}
|
find_filter = {}
|
||||||
|
|
||||||
if None != monkey_guid:
|
if monkey_guid:
|
||||||
find_filter["monkey_guid"] = {'$eq': monkey_guid}
|
find_filter["monkey_guid"] = {'$eq': monkey_guid}
|
||||||
if None != timestamp:
|
if timestamp:
|
||||||
find_filter['timestamp'] = {'$gt': dateutil.parser.parse(timestamp)}
|
find_filter['timestamp'] = {'$gt': dateutil.parser.parse(timestamp)}
|
||||||
|
|
||||||
result['objects'] = [x for x in mongo.db.telemetry.find(find_filter)]
|
result['objects'] = [x for x in mongo.db.telemetry.find(find_filter)]
|
||||||
|
@ -142,15 +142,15 @@ class Telemetry(restful.Resource):
|
||||||
try:
|
try:
|
||||||
if telemetry_json.get('telem_type') == 'exploit':
|
if telemetry_json.get('telem_type') == 'exploit':
|
||||||
update_parent = []
|
update_parent = []
|
||||||
for monkey in mongo.db.monkey.find({ "ip_addresses" :
|
for monkey in mongo.db.monkey.find({"ip_addresses":
|
||||||
{'$elemMatch' :
|
{'$elemMatch':
|
||||||
{ '$eq' : telemetry_json['data']['machine']['ip_addr'] }}}):
|
{'$eq': telemetry_json['data']['machine']['ip_addr']}}}):
|
||||||
parent = monkey.get('parent')
|
parent = monkey.get('parent')
|
||||||
if parent == monkey.get('guid') or None == parent:
|
if parent == monkey.get('guid') or not parent:
|
||||||
update_parent.append(monkey)
|
update_parent.append(monkey)
|
||||||
if 1 == len(update_parent):
|
if 1 == len(update_parent):
|
||||||
update_parent[0]['parent'] = telemetry_json['monkey_guid']
|
update_parent[0]['parent'] = telemetry_json['monkey_guid']
|
||||||
mongo.db.monkey.update({"guid": update_parent[0]['guid']}, {"$set" : update_parent[0]}, upsert=False)
|
mongo.db.monkey.update({"guid": update_parent[0]['guid']}, {"$set": update_parent[0]}, upsert=False)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -159,14 +159,14 @@ class Telemetry(restful.Resource):
|
||||||
|
|
||||||
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 {}
|
||||||
if config.has_key('name'):
|
if config.has_key('name'):
|
||||||
del config['name']
|
del config['name']
|
||||||
return config
|
return config
|
||||||
|
|
||||||
def post(self):
|
def post(self):
|
||||||
config_json = json.loads(request.data)
|
config_json = json.loads(request.data)
|
||||||
return mongo.db.config.update({'name' : 'newconfig'}, {"$set" : config_json}, upsert=True)
|
return mongo.db.config.update({'name': 'newconfig'}, {"$set": config_json}, upsert=True)
|
||||||
|
|
||||||
|
|
||||||
class MonkeyDownload(restful.Resource):
|
class MonkeyDownload(restful.Resource):
|
||||||
|
|
Loading…
Reference in New Issue