Permalink
Browse files

Updated README and fixed minor tweaks

  • Loading branch information...
1 parent ff4d2e6 commit aade78c192b5fbfe5e3e3fe61f4037b89a9836b7 pjlantz committed Sep 25, 2010
Showing with 108 additions and 50 deletions.
  1. +24 −7 README.md
  2. +18 −10 src/conf/configHandler.py
  3. +3 −3 src/conf/hale.conf
  4. +2 −2 src/conf/modules.conf
  5. +14 −0 src/main.py
  6. +3 −4 src/modules/httpModule.py
  7. +19 −3 src/utils/logHandler.py
  8. +15 −14 src/utils/moduleCoordinator.py
  9. +10 −7 src/xmpp/producerBot.py
View
@@ -21,7 +21,7 @@ Hale has the following dependencies:
django-piston == 0.2.3rc1
pefile == 1.2.10-63
pyreadline == 1.6.1.dev-r0 (on Windows)
- sleekxmpp == 0.2.3.1
+ sleekxmpp == 0.9Rrc1
wsgiref == 0.1.2
zope.interface == 3.6.1
oauth2 == 1.2.0
@@ -130,13 +130,12 @@ Add decorator for the register function (in this case module_setup) which will b
Also follow the naming convention **`nameModule.py`** and **`@moduleManager.register("name")`** and import the **`moduleManager`**, if not the moduleManager will notify you about any errors.
The rest of the module code is omitted but should create a twisted factory object and start this with the reactor in the run method, see the existing modules for an example. For tutorials on programming with Twisted, please see [here](http://twistedmatrix.com/trac/wiki/Documentation). There are also some utils to make use of when developing modules, this is done as following:
-
+
+Socksify:
# import all utils
from utils import *
- Socksify:
-
# in the constructor create a new proxy object
self.prox = proxySelector.ProxySelector()
@@ -153,7 +152,25 @@ The rest of the module code is omitted but should create a twisted factory objec
if len(proxyUser) == 0:
self.connector = socksify.connectSocks5Proxy(host, port, proxyHost, proxyPort, "HALE")
else:
- self.connector = socksify.connectSocks5Proxy(host, port, proxyHost, proxyPort, "HALE", proxyUser, proxyPass)
+ self.connector = socksify.connectSocks5Proxy(host, port, proxyHost, proxyPort, "HALE", proxyUser, proxyPass)
+
+Connection errors handling in the factory object:
+
+ def clientConnectionFailed(self, connector, reason):
+ """
+ Called on failed connection to server
+ """
+
+ moduleCoordinator.ModuleCoordinator().putError("Error connecting to " + self.config['botnet'], self.module)
+
+ def clientConnectionLost(self, connector, reason):
+ """
+ Called on lost connection to server
+ """
+
+ moduleCoordinator.ModuleCoordinator().putError("Connection lost to " + self.config['botnet'], self.module)
+
+This will send the errors to the error bucket which is accessible by issuing a **`showlog`** in the CLI.
Logging:
@@ -245,7 +262,7 @@ where config is a string representation of the configuration, for example
module=irc botnet=irc.freenode.net etc..
The sensor then replies with with an acknowledgement together with the config hash
-which can be used to distringuish the botnet logs from the other logs in the share channel
+which can be used to distinguish the botnet logs from the other logs in the share channel.
Example of acknowledgment:
startTrackAck hash
@@ -254,7 +271,7 @@ if no one else is monitoring this botnet, otherwise a startTrackNack is received
botnet is already monitored or the sensor does not have the module installed for this botnet.
Malware share is done by sensors sending a message like:
- FileCaptured hash=353f6650... file content
+ fileCaptured hash=353f6650... file content
where the content is Base64 encoded and comes directly after the file hash value.
View
@@ -148,6 +148,8 @@ def getHashFromConfStr(self, confStr, toDB=False):
return '', moduleError
dictStr = self.getStrFromDict(dict, True)
md5 = hashlib.new('md5')
+ if not dictStr:
+ return "", True
md5.update(dictStr.strip())
return md5.hexdigest(), moduleError
@@ -157,11 +159,12 @@ def __checkModule(self, conf):
on external event
"""
- module = conf['module']
- from modules import moduleManager
- if module in moduleManager.get_modules():
- return False
- return True
+ if conf:
+ module = conf['module']
+ from modules import moduleManager
+ if module in moduleManager.get_modules():
+ return False
+ return True
def getStrFromDict(self, dict, external=False, toDB=False):
"""
@@ -176,6 +179,8 @@ def getStrFromDict(self, dict, external=False, toDB=False):
dictStr += key + '=' + dict[key] + ' '
return dictStr.strip()
items = None
+ if not dict:
+ return
if external:
items = dict.items()
unique = self.getUniqueKeys(dict['module'].strip(), items)
@@ -198,10 +203,13 @@ def getDictFromStr(self, string):
try:
value = config.split('=')[1]
except IndexError:
- newValue = dict[previousKey].strip()
- newValue = newValue + ' ' + key.strip()
- dict[previousKey] = newValue
- continue
+ try:
+ newValue = dict[previousKey].strip()
+ newValue = newValue + ' ' + key.strip()
+ dict[previousKey] = newValue
+ continue
+ except KeyError:
+ return
previousKey = key
dict[key] = value.strip()
return dict
@@ -266,4 +274,4 @@ def __striplist(self, l):
"""
return([x.strip() for x in l])
-
+
View
@@ -1,10 +1,10 @@
[xmpp]
use = False
-server = example.com
+server = server.com
port = 5222
-jid = id@example.com
+jid = user@server.com
password = pass
datashare_channel = share
-coordination_channel = coord
+coordination_channel = coord
View
@@ -4,13 +4,13 @@ http = botnet, method, *base64, *grammar, response_separator
[ircConf]
module = irc
-botnet = irc.host.net
+botnet = irc.server.net
port = 6667
password = None
nick = AgentNick
username = agent007
realname = Spying
-channel = #channelname
+channel = #channel
channel_pass = None
pass_grammar = PASS
nick_grammar = NICK
View
@@ -117,6 +117,20 @@ def do_lsexec(self, arg):
for ident in idlist:
listStr += ident + "\n"
print listStr
+
+ def do_execinfo(self, arg):
+ """
+ List info about executing module
+ Usage: execinfo id
+ """
+
+ info = moduleCoordinator.ModuleCoordinator().getInfo(arg)
+ if len(info) == 0:
+ print "No such id running"
+ return
+
+ print "\nModule ID\tHash\n=========\t=================\n"
+ print arg + "\t" + info + "\n"
def do_lsconf(self, arg):
"""
@@ -57,7 +57,7 @@ def run(self):
"""
self.prox = proxySelector.ProxySelector()
- self.factory = HTTPClientFactory(self, self.hash, self.config, self)
+ self.factory = HTTPClientFactory(self, self.hash, self.config)
self.host = self.config['botnet']
self.port = int(self.config['port'])
self.proxyInfo = self.prox.getRandomProxy()
@@ -127,12 +127,11 @@ class HTTPClientFactory(protocol.ClientFactory):
protocol = HTTPProtocol
- def __init__(self, module, hash, config, module):
+ def __init__(self, module, hash, config):
"""
Constructor
"""
-
- self.module = module
+
self.hash = hash
self.config = config
self.cookies = {}
View
@@ -18,11 +18,13 @@
#
################################################################################
+import GeoIP
import sys, pefile, base64, socks
import re, urllib2, hashlib, os, socket
from twisted.internet import reactor
import proxySelector
from xmpp import producerBot
+from conf import configHandler
from django.db import IntegrityError
from webdb.hale.models import Botnet, Log, File, RelatedIPs
@@ -37,7 +39,10 @@ def __init__(self):
Constructor
"""
- pass
+ geodata = os.getcwd() + "/utils/GeoIP.dat"
+ if os.name == "nt":
+ geodata = geodata.replace("/", "\\")
+ self.geo = GeoIP.open(geodata, GeoIP.GEOIP_STANDARD)
def handleLog(self, data, botnethash, config):
"""
@@ -46,13 +51,24 @@ def handleLog(self, data, botnethash, config):
XMPP bot
"""
- reactor.callInThread(self.putToDB, data, botnethash)
+ reactor.callInThread(self.putToDB, data, botnethash, config)
self.putToXMPP(data, config, botnethash)
- def putToDB(self, data, botnethash):
+ def putToDB(self, data, botnethash, conf):
"""
Creates new log entry in the database
"""
+
+ confStr = configHandler.ConfigHandler().getStrFromDict(conf, toDB=True)
+ coord = self.geo.record_by_name(conf['botnet'])
+ b = Botnet(botnethashvalue=botnethash, botnettype=conf['module'], host=conf['botnet'], config=confStr, longitude=coord['longitude'], latitude=coord['latitude'])
+ try:
+ b.save()
+ except IntegrityError:
+ b = Botnet.objects.get(botnethashvalue=botnethash)
+ b.longitude = coord['longitude']
+ b.latitude = coord['latitude']
+ b.save()
botnetobject = Botnet.objects.get(botnethashvalue=botnethash)
Log(botnet=botnetobject, logdata=data).save()
@@ -218,32 +218,21 @@ def add(self, moduleExe, moduleId, hash, external=False):
monitored = producerBot.ProducerBot().getMonitoredBotnets()
botnet = moduleExe.getConfig()['botnet']
if not external:
- if hash in monitored:
- print "You are already monitoring this!"
- return
- if producerBot.ProducerBot().sendTrackReq(hash):
- print "Botnet already monitored!"
+ if hash in monitored or producerBot.ProducerBot().sendTrackReq(hash):
+ self.putError("Botnet: " + hash + " already monitored")
return
self.modules[moduleId] = moduleExe
self.configHashes[moduleId] = hash
conf = self.modules[moduleId].getConfig()
- confStr = configHandler.ConfigHandler().getStrFromDict(conf, toDB=True)
coord = self.geo.record_by_name(conf['botnet'])
if coord == None:
self.putError("Unkown host: " + conf['botnet'])
self.modules.pop(moduleId)
self.configHashes.pop(moduleId)
return
- b = Botnet(botnethashvalue=hash, botnettype=conf['module'], host=conf['botnet'], config=confStr, longitude=coord['longitude'], latitude=coord['latitude'])
- try:
- b.save()
- except IntegrityError:
- b = Botnet.objects.get(botnethashvalue=hash)
- b.longitude = coord['longitude']
- b.latitude = coord['latitude']
- b.save()
+
moduleExe.run()
if self.dispatcherFirstStart:
Dispatcher().start()
@@ -300,6 +289,18 @@ def getAll(self):
"""
return self.modules.keys()
+
+ def getInfo(self, moduleId):
+ """
+ Return hash of botnet monitored by module identified by moduleId
+ """
+
+ try:
+ conf = self.modules[moduleId].getConfig()
+ except KeyError:
+ return ""
+ confStr = configHandler.ConfigHandler().getStrFromDict(conf, toDB=True)
+ return configHandler.ConfigHandler().getHashFromConfStr(confStr, toDB=False)[0]
def stopAll(self):
"""
View
@@ -135,8 +135,8 @@ def run(self):
if self.__useBot():
self.xmpp.connect((self.server, int(self.port)))
- self.xmpp.process()
-
+ self.xmpp.process(threaded=True)
+
def handleXMPPDisconnected(self, event):
"""
Handle disconnected state
@@ -199,7 +199,10 @@ def handleIncomingGroupChatMessage(self, message):
Handles groupchat messages from the coordination
channel
"""
-
+
+ if message['subject']:
+ return
+
channel = str(message['from'])
if channel.split('/')[1] != self.jid.split('@')[0]:
coordchan = self.coordchannel.split('@')[0]
@@ -218,13 +221,13 @@ def handleIncomingGroupChatMessage(self, message):
self.foundTrack = True
if body[0].strip() == 'sensorLoadReq':
msg = 'sensorLoadAck id=' + self.id + ' queue=' + str(len(self.monitoredBotnets))
- self.xmpp.sendMessage(self.coordchannel, msg, None, "groupchat")
+ self.xmpp.sendMessage(self.coordchannel, msg, None, "groupchat")
def handleIncomingMessage(self, msg):
"""
Takes care of incoming private chat messages
"""
-
+
if msg['type'] == 'chat':
body = msg['body'].split(' ')
toStr = msg['from']
@@ -243,15 +246,15 @@ def handleIncomingMessage(self, msg):
self.xmpp.sendMessage(toStr, 'startTrackAck ' + hash, None, "chat")
else:
self.xmpp.sendMessage(toStr, 'startTrackNack', None, "chat")
-
+
def sendFile(self, data, hash):
"""
Sends base64 encoded file and its hash value
to the share channel
"""
if self.__useBot():
- msg = "FileCaptured hash=" + hash + " " + data
+ msg = "fileCaptured hash=" + hash + " " + data
self.xmpp.sendMessage(self.sharechannel, msg, None, "groupchat")
def sendLog(self, msg):

0 comments on commit aade78c

Please sign in to comment.