Construye tu propio mapa para ataques en tiempo real

Los que os consideréis un poco mas "viejos" os acordaréis de aquella época en la que conectábamos un Windows XP sin parchear a la red y este era infectado al cabo de 5-10 minutos.

noticia_1

 

Todos sabemos que hoy existen botnets o simplemente máquinas por Internet que están todo el día escaneando en busca de servicios vulnerables o con passwords por defecto para propagar malware.

Una de las prácticas que se llevan a  cabo para saber como funciona la mente de un atacante es publicar servicios aparentemente vulnerables para que sean atacados y nosotros registraremos las acciones que se lleven a a cabo.

Normalmente esto se conoce como un HoneyPot o Tarro de Miel, que se puede volver mas chulo si no solo registrar esos ataques sino que los montamos en un mapa y ver como ocurren en tiempo real al rededor del globo.

La idea de este poste es montar nuestro propio servicio de mapeo de ataques en tiempo real utilizando el proyecto Modern HoneyPot

https://github.com/threatstream/mhn

Para instalarlo es recomendable usar una distribución de Ubuntu LTS. En mi caso Ubuntu 12.04 LTS, tambien necesitaremos GIT, python 2.7 y conexión a Internet. Es recomendable tener instalado GCC, automake etc..

La suerte, es que el deploy es automático y eso gustará a mas de uno/a!

Seguiremos las siguientes instrucciones:

$ cd /opt/
$ git clone https://github.com/threatstream/mhn.git
$ cd mhn/scripts/
$ sudo ./install_hpfeeds.sh
$ sudo ./install_mnemosyne.sh
$ sudo ./install_honeymap.sh

Cuando ya hemos acabado todo, nos pedirá de configurar el servidor:

===========================================================
MHN Configuration
===========================================================
Do you wish to run in Debug mode?: y/n n
Superuser email: YOUR_EMAIL @ YOURSITE.COM
Superuser password: 
Server base url ["http://1.2.3.4"]: 
Honeymap url ["http://1.2.3.4:3000"]:
Mail server address ["localhost"]: 
Mail server port [25]: 
Use TLS for email?: y/n n
Use SSL for email?: y/n n
Mail server username [""]: 
Mail server password [""]: 
Mail default sender [""]: 
Path for log file ["mhn.log"]: 

Una vez que lo tenemos instalado, lo primero que tendremos que hacer es desplegar un sensor.

deploy

Lo que haremos simplemente haciendo un wget es hacer construir un sensor que volcará los logs en nuestro panel de control central

El script que hay por debajo es el siguiente:


#!/bin/bash

set -e
set -x

if [ $# -ne 2 ]
then
echo "Wrong number of arguments supplied."
echo "Usage: $0 <server_url> <deploy_key>."
exit 1
fi

server_url=$1
deploy_key=$2

wget $server_url/static/registration.txt -O registration.sh
chmod 755 registration.sh
# Note: this will export the HPF_* variables
. ./registration.sh $server_url $deploy_key "dionaea"

# Add ppa to apt sources (Needed for Dionaea).
apt-get update
apt-get install -y python-software-properties
add-apt-repository -y ppa:honeynet/nightly
apt-get update

# Installing Dionaea.
apt-get install -y dionaea supervisor patch

cp /etc/dionaea/dionaea.conf.dist /etc/dionaea/dionaea.conf
cat > /tmp/dionaea.hpfeeds.patch <<EOF
index 43e17b4..7b16529 100644
--- /etc/dionaea/dionaea.conf
+++ /etc/dionaea/dionaea.conf.new
@@ -344,6 +344,16 @@ modules = {
user = "" // username (optional)
pass = "" // password (optional)
}
+ hpfeeds = {
+ hp1 = {
+ server = "$HPF_HOST"
+ port = "$HPF_PORT"
+ ident = "$HPF_IDENT"
+ secret = "$HPF_SECRET"
+ // dynip_resolve: enable to lookup the sensor ip through a webservice
+ dynip_resolve = "http://queryip.net/ip/"
+ }
+ }
logsql = {
mode = "sqlite" // so far there is only sqlite
sqlite = {
@@ -457,6 +467,7 @@ modules = {
// "virustotal",
// "mwserv",
// "submit_http",
+ "hpfeeds",
// "logxmpp",
// "nfq",
// "p0f",
index 5b40dd7..8bddc63 100644
--- /usr/lib/dionaea/python/dionaea/ihandlers.py
+++ /usr/lib/dionaea/python/dionaea/ihandlers.py.new
@@ -129,6 +129,13 @@ def new():
import dionaea.submit_http
g_handlers.append(dionaea.submit_http.handler('*'))

+ if "hpfeeds" in g_dionaea.config()['modules']['python']['ihandlers']['handlers'] and 'hpfeeds' in g_dionaea.config()['modules']['python']:
+ import dionaea.hpfeeds
+ for client in g_dionaea.config()['modules']['python']['hpfeeds']:
+ conf = g_dionaea.config()['modules']['python']['hpfeeds'][client]
+ x = dionaea.hpfeeds.hpfeedihandler(conf)
+ g_handlers.append(x)
+
if "fail2ban" in g_dionaea.config()['modules']['python']['ihandlers']['handlers']:
import dionaea.fail2ban
g_handlers.append(dionaea.fail2ban.fail2banhandler())
EOF

cat > /usr/lib/dionaea/python/dionaea/hpfeeds.py <<EOF
#********************************************************************************
#* Dionaea
#* - catches bugs -
#*
#*
#*
#* Copyright (C) 2010 Mark Schloesser
#*
#* This program is free software; you can redistribute it and/or
#* modify it under the terms of the GNU General Public License
#* as published by the Free Software Foundation; either version 2
#* of the License, or (at your option) any later version.
#*
#* This program is distributed in the hope that it will be useful,
#* but WITHOUT ANY WARRANTY; without even the implied warranty of
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#* GNU General Public License for more details.
#*
#* You should have received a copy of the GNU General Public License
#* along with this program; if not, write to the Free Software
#* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#*
#*
#* contact nepenthesdev at gmail.com
#*
#*******************************************************************************/

from dionaea.core import ihandler, incident, g_dionaea, connection
from dionaea.util import sha512file

import os
import logging
import struct
import hashlib
import json
try: import pyev
except: pyev = None

logger = logging.getLogger('hpfeeds')
logger.setLevel(logging.DEBUG)

#def DEBUGPERF(msg):
# print(msg)
#logger.debug = DEBUGPERF
#logger.critical = DEBUGPERF

BUFSIZ = 16384

OP_ERROR = 0
OP_INFO = 1
OP_AUTH = 2
OP_PUBLISH = 3
OP_SUBSCRIBE = 4

MAXBUF = 1024**2
SIZES = {
OP_ERROR: 5+MAXBUF,
OP_INFO: 5+256+20,
OP_AUTH: 5+256+20,
OP_PUBLISH: 5+MAXBUF,
OP_SUBSCRIBE: 5+256*2,
}

CONNCHAN = 'dionaea.connections'
CAPTURECHAN = 'dionaea.capture'
DCECHAN = 'dionaea.dcerpcrequests'
SCPROFCHAN = 'dionaea.shellcodeprofiles'
UNIQUECHAN = 'mwbinary.dionaea.sensorunique'

class BadClient(Exception):
pass

# packs a string with 1 byte length field
def strpack8(x):
if isinstance(x, str): x = x.encode('latin1')
return struct.pack('!B', len(x)%0xff) + x

# unpacks a string with 1 byte length field
def strunpack8(x):
l = x[0]
return x[1:1+l], x[1+l:]

def msghdr(op, data):
return struct.pack('!iB', 5+len(data), op) + data
def msgpublish(ident, chan, data):
return msghdr(OP_PUBLISH, strpack8(ident) + strpack8(chan) + data)
def msgsubscribe(ident, chan):
if isinstance(chan, str): chan = chan.encode('latin1')
return msghdr(OP_SUBSCRIBE, strpack8(ident) + chan)
def msgauth(rand, ident, secret):
hash = hashlib.sha1(bytes(rand)+secret).digest()
return msghdr(OP_AUTH, strpack8(ident) + hash)

class FeedUnpack(object):
def __init__(self):
self.buf = bytearray()
def __iter__(self):
return self
def __next__(self):
return self.unpack()
def feed(self, data):
self.buf.extend(data)
def unpack(self):
if len(self.buf) < 5:
raise StopIteration('No message.')

ml, opcode = struct.unpack('!iB', self.buf[:5])
if ml > SIZES.get(opcode, MAXBUF):
raise BadClient('Not respecting MAXBUF.')

if len(self.buf) < ml:
raise StopIteration('No message.')

data = self.buf[5:ml]
del self.buf[:ml]
return opcode, data

class hpclient(connection):
def __init__(self, server, port, ident, secret):
logger.debug('hpclient init')
connection.__init__(self, 'tcp')
self.unpacker = FeedUnpack()
self.ident, self.secret = ident.encode('latin1'), secret.encode('latin1')

self.connect(server, port)
self.timeouts.reconnect = 10.0
self.sendfiles = []
self.msgqueue = []
self.filehandle = None
self.connected = False

def handle_established(self):
self.connected = True
logger.debug('hpclient established')

def handle_io_in(self, indata):
self.unpacker.feed(indata)

# if we are currently streaming a file, delay handling incoming messages
if self.filehandle:
return len(indata)

try:
for opcode, data in self.unpacker:
logger.debug('hpclient msg opcode {0} data {1}'.format(opcode, data))
if opcode == OP_INFO:
name, rand = strunpack8(data)
logger.debug('hpclient server name {0} rand {1}'.format(name, rand))
self.send(msgauth(rand, self.ident, self.secret))

elif opcode == OP_PUBLISH:
ident, data = strunpack8(data)
chan, data = strunpack8(data)
logger.debug('publish to {0} by {1}: {2}'.format(chan, ident, data))

elif opcode == OP_ERROR:
logger.debug('errormessage from server: {0}'.format(data))
else:
logger.debug('unknown opcode message: {0}'.format(opcode))
except BadClient:
logger.critical('unpacker error, disconnecting.')
self.close()

return len(indata)

def handle_io_out(self):
if self.filehandle: self.sendfiledata()
else:
if self.msgqueue:
m = self.msgqueue.pop(0)
self.send(m)

def publish(self, channel, **kwargs):
if self.filehandle: self.msgqueue.append(msgpublish(self.ident, channel, json.dumps(kwargs).encode('latin1')))
else: self.send(msgpublish(self.ident, channel, json.dumps(kwargs).encode('latin1')))

def sendfile(self, filepath):
# does not read complete binary into memory, read and send chunks
if not self.filehandle:
self.sendfileheader(filepath)
self.sendfiledata()
else: self.sendfiles.append(filepath)

def sendfileheader(self, filepath):
self.filehandle = open(filepath, 'rb')
fsize = os.stat(filepath).st_size
headc = strpack8(self.ident) + strpack8(UNIQUECHAN)
headh = struct.pack('!iB', 5+len(headc)+fsize, OP_PUBLISH)
self.send(headh + headc)

def sendfiledata(self):
tmp = self.filehandle.read(BUFSIZ)
if not tmp:
if self.sendfiles:
fp = self.sendfiles.pop(0)
self.sendfileheader(fp)
else:
self.filehandle = None
self.handle_io_in(b'')
else:
self.send(tmp)

def handle_timeout_idle(self):
pass

def handle_disconnect(self):
logger.info('hpclient disconnect')
self.connected = False
return 1

def handle_error(self, err):
logger.warn('hpclient error {0}'.format(err))
self.connected = False
return 1

class hpfeedihandler(ihandler):
def __init__(self, config):
logger.debug('hpfeedhandler init')
self.client = hpclient(config['server'], int(config['port']), config['ident'], config['secret'])
ihandler.__init__(self, '*')

self.dynip_resolve = config.get('dynip_resolve', '')
self.dynip_timer = None
self.ownip = None
if self.dynip_resolve and 'http' in self.dynip_resolve:
if pyev == None:
logger.debug('You are missing the python pyev binding in your dionaea installation.')
else:
logger.debug('hpfeedihandler will use dynamic IP resolving!')
self.loop = pyev.default_loop()
self.dynip_timer = pyev.Timer(2., 300, self.loop, self._dynip_resolve)
self.dynip_timer.start()

def stop(self):
if self.dynip_timer:
self.dynip_timer.stop()
self.dynip_timer = None
self.loop = None

def _ownip(self, icd):
if self.dynip_resolve and 'http' in self.dynip_resolve and pyev != None:
if self.ownip: return self.ownip
else: raise Exception('Own IP not yet resolved!')
return icd.con.local.host

def __del__(self):
#self.client.close()
pass

def connection_publish(self, icd, con_type):
try:
con=icd.con
self.client.publish(CONNCHAN, connection_type=con_type, connection_transport=con.transport, connection_protocol=con.protocol, remote_host=con.remote.host, remote_port=con.remote.port, remote_hostname=con.remote.hostname, local_host=self._ownip(icd), local_port=con.local.port)
except Exception as e:
logger.warn('exception when publishing: {0}'.format(e))

def handle_incident(self, i):
pass

def handle_incident_dionaea_connection_tcp_listen(self, icd):
self.connection_publish(icd, 'listen')
con=icd.con
logger.info("listen connection on %s:%i" %
(con.remote.host, con.remote.port))

def handle_incident_dionaea_connection_tls_listen(self, icd):
self.connection_publish(icd, 'listen')
con=icd.con
logger.info("listen connection on %s:%i" %
(con.remote.host, con.remote.port))

def handle_incident_dionaea_connection_tcp_connect(self, icd):
self.connection_publish(icd, 'connect')
con=icd.con
logger.info("connect connection to %s/%s:%i from %s:%i" %
(con.remote.host, con.remote.hostname, con.remote.port, self._ownip(icd), con.local.port))

def handle_incident_dionaea_connection_tls_connect(self, icd):
self.connection_publish(icd, 'connect')
con=icd.con
logger.info("connect connection to %s/%s:%i from %s:%i" %
(con.remote.host, con.remote.hostname, con.remote.port, self._ownip(icd), con.local.port))

def handle_incident_dionaea_connection_udp_connect(self, icd):
self.connection_publish(icd, 'connect')
con=icd.con
logger.info("connect connection to %s/%s:%i from %s:%i" %
(con.remote.host, con.remote.hostname, con.remote.port, self._ownip(icd), con.local.port))

def handle_incident_dionaea_connection_tcp_accept(self, icd):
self.connection_publish(icd, 'accept')
con=icd.con
logger.info("accepted connection from %s:%i to %s:%i" %
(con.remote.host, con.remote.port, self._ownip(icd), con.local.port))

def handle_incident_dionaea_connection_tls_accept(self, icd):
self.connection_publish(icd, 'accept')
con=icd.con
logger.info("accepted connection from %s:%i to %s:%i" %
(con.remote.host, con.remote.port, self._ownip(icd), con.local.port))
def handle_incident_dionaea_connection_tcp_reject(self, icd):
self.connection_publish(icd, 'reject')
con=icd.con
logger.info("reject connection from %s:%i to %s:%i" %
(con.remote.host, con.remote.port, self._ownip(icd), con.local.port))

def handle_incident_dionaea_connection_tcp_pending(self, icd):
self.connection_publish(icd, 'pending')
con=icd.con
logger.info("pending connection from %s:%i to %s:%i" %
(con.remote.host, con.remote.port, self._ownip(icd), con.local.port))

def handle_incident_dionaea_download_complete_unique(self, i):
self.handle_incident_dionaea_download_complete_again(i)
if not hasattr(i, 'con') or not self.client.connected: return
logger.debug('unique complete, publishing md5 {0}, path {1}'.format(i.md5hash, i.file))
try:
self.client.sendfile(i.file)
except Exception as e:
logger.warn('exception when publishing: {0}'.format(e))

def handle_incident_dionaea_download_complete_again(self, i):
if not hasattr(i, 'con') or not self.client.connected: return
logger.debug('hash complete, publishing md5 {0}, path {1}'.format(i.md5hash, i.file))
try:
sha512 = sha512file(i.file)
self.client.publish(CAPTURECHAN, saddr=i.con.remote.host,
sport=str(i.con.remote.port), daddr=self._ownip(i),
dport=str(i.con.local.port), md5=i.md5hash, sha512=sha512,
url=i.url
)
except Exception as e:
logger.warn('exception when publishing: {0}'.format(e))

def handle_incident_dionaea_modules_python_smb_dcerpc_request(self, i):
if not hasattr(i, 'con') or not self.client.connected: return
logger.debug('dcerpc request, publishing uuid {0}, opnum {1}'.format(i.uuid, i.opnum))
try:
self.client.publish(DCECHAN, uuid=i.uuid, opnum=i.opnum,
saddr=i.con.remote.host, sport=str(i.con.remote.port),
daddr=self._ownip(i), dport=str(i.con.local.port),
)
except Exception as e:
logger.warn('exception when publishing: {0}'.format(e))

def handle_incident_dionaea_module_emu_profile(self, icd):
if not hasattr(icd, 'con') or not self.client.connected: return
logger.debug('emu profile, publishing length {0}'.format(len(icd.profile)))
try:
self.client.publish(SCPROFCHAN, profile=icd.profile)
except Exception as e:
logger.warn('exception when publishing: {0}'.format(e))

def _dynip_resolve(self, events, data):
i = incident("dionaea.upload.request")
i._url = self.dynip_resolve
i._callback = "dionaea.modules.python.hpfeeds.dynipresult"
i.report()

def handle_incident_dionaea_modules_python_hpfeeds_dynipresult(self, icd):
fh = open(icd.path, mode="rb")
self.ownip = fh.read().strip().decode('latin1')
logger.debug('resolved own IP to: {0}'.format(self.ownip))
fh.close()
EOF

cd /
patch -p0 < /tmp/dionaea.hpfeeds.patch

# Editing configuration for Dionaea.
mkdir -p /var/dionaea/wwwroot /var/dionaea/binaries /var/dionaea/log
chown -R nobody:nogroup /var/dionaea
sed -i 's/var\/dionaea\///g' /etc/dionaea/dionaea.conf
sed -i 's/log\//\/var\/dionaea\/log\//g' /etc/dionaea/dionaea.conf
sed -i 's/levels = "all"/levels = "warning,error"/1' /etc/dionaea/dionaea.conf
sed -i 's/mode = "getifaddrs"/mode = "manual"/1' /etc/dionaea/dionaea.conf

mkdir -p /var/dionaea/bistreams
chown nobody:nogroup /var/dionaea/bistreams

# Config for supervisor.
cat > /etc/supervisor/conf.d/dionaea.conf <<EOF
[program:dionaea]
command=dionaea -c /etc/dionaea/dionaea.conf -w /var/dionaea -u nobody -g nogroup
directory=/var/dionaea
stdout_logfile=/var/log/dionaea.out
stderr_logfile=/var/log/dionaea.err
autostart=true
autorestart=true
redirect_stderr=true
stopsignal=QUIT
EOF

supervisorctl update

Cuando el sensor empiece a recibir ataques quedarán registrados de esta forma:

ataque

Los ataques aquí se verían en tiempo real, luego tenemos un apartado donde podremos ver el detalle de los ataques:

ataque2

Como véis, podemos ver el detalle del Honeypot que ha saltado, ya que con MHN, ya podremos instalar mas de un Honeypot

Hay empresas que implementan su propio producto y podreis ver cosas como estas:

mapa_ataques

A disfrutar registrando ataques, espero que disfrutaran esta nota.

Subir