summaryrefslogtreecommitdiffstats
path: root/Reaktor
diff options
context:
space:
mode:
Diffstat (limited to 'Reaktor')
-rw-r--r--Reaktor/IRC/README.md49
-rwxr-xr-xReaktor/IRC/asybot.py209
-rwxr-xr-xReaktor/IRC/index6
-rw-r--r--Reaktor/IRC/translate_colors.py31
-rw-r--r--Reaktor/Makefile23
-rw-r--r--Reaktor/README.md26
-rw-r--r--Reaktor/UDP/README52
-rw-r--r--Reaktor/UDP/config15
-rwxr-xr-xReaktor/UDP/index94
-rwxr-xr-xReaktor/commands/caps4
-rwxr-xr-xReaktor/commands/hello2
-rwxr-xr-xReaktor/commands/licht_resolver37
-rwxr-xr-xReaktor/commands/reload2
-rwxr-xr-xReaktor/commands/retard1
-rwxr-xr-xReaktor/commands/rev2
l---------Reaktor/commands/revip1
l---------Reaktor/commands/subdomains1
-rwxr-xr-xReaktor/commands/uptime8
-rwxr-xr-xReaktor/commands/whatweb7
-rwxr-xr-xReaktor/index7
l---------Reaktor/public_commands/caps1
l---------Reaktor/public_commands/hello1
l---------Reaktor/public_commands/reload1
l---------Reaktor/public_commands/retard1
l---------Reaktor/public_commands/rev1
l---------Reaktor/public_commands/uptime1
-rw-r--r--Reaktor/repos/bxfr/bxfr.py238
-rw-r--r--Reaktor/repos/bxfr/bxfr_api.py238
-rwxr-xr-xReaktor/repos/consolidate_dns/index86
m---------Reaktor/repos/dnsrecon0
m---------Reaktor/repos/gxfr0
-rwxr-xr-xReaktor/repos/revip/revip48
m---------Reaktor/repos/whatweb0
-rw-r--r--Reaktor/startup/conf.d/reaktor2
-rwxr-xr-xReaktor/startup/init.d/reaktor-debian102
-rw-r--r--Reaktor/startup/supervisor/Reaktor.conf6
l---------Reaktor/udp_commands/licht_resolver1
37 files changed, 1304 insertions, 0 deletions
diff --git a/Reaktor/IRC/README.md b/Reaktor/IRC/README.md
new file mode 100644
index 00000000..63a0ebd2
--- /dev/null
+++ b/Reaktor/IRC/README.md
@@ -0,0 +1,49 @@
+# //Reaktor/IRC
+
+This component implements a remote shell daemon that exposes the
+executable files (which may be symlinks) below
+`//Reaktor/public_commands/` through IRC.
+
+## Security
+
+Access to the IRC server implies full access to all the exposed executable
+files. The daemon is executing the commands without dropping privileges.
+
+## Quickstart
+
+ #? /bin/sh
+ set -euf
+
+ export nick="$LOGNAME|$HOSTNAME"
+ export host=irc.freenode.org
+ export target='#tincspasm'
+
+ exec Reaktor/IRC/index
+
+## Environment variables
+
+The following environment variables are processed by `//Reaktor/IRC`:
+
+### nick
+
+Use a specific nickname.
+
+Optional if the node running `//Reaktor/IRC` is part of Retiolum, in
+which case it defaults to `Name` in `/etc/tinc/retiolum/tinc.conf`.
+
+### host and port
+
+Connect to a specific IRC server.
+
+Optional if the node running `//Reaktor/IRC` is part of Retiolum, in
+which case it defaults to `supernode` and `6667` (well, it always
+defaults to these two, but they only make science in Retiolum^_^).
+
+### target
+
+Join a specific channel.
+
+As always, this does the right thing for properly configured hosts: it
+uses the default `#retiolum`, which is the only really relevant
+channel.^_^
+
diff --git a/Reaktor/IRC/asybot.py b/Reaktor/IRC/asybot.py
new file mode 100755
index 00000000..2cb533ea
--- /dev/null
+++ b/Reaktor/IRC/asybot.py
@@ -0,0 +1,209 @@
+#! /usr/bin/env python
+#
+# //Reaktor/IRC/asybot.py
+#
+from translate_colors import translate_colors
+def is_executable(x):
+ import os
+ return os.path.exists(x) and os.access(x, os.X_OK)
+
+from asynchat import async_chat as asychat
+from asyncore import loop
+from socket import AF_INET, SOCK_STREAM,gethostname
+from signal import SIGALRM, signal, alarm
+from datetime import datetime as date, timedelta
+import shlex
+from time import sleep
+from sys import exit
+from re import split, search
+from textwrap import TextWrapper
+import logging,logging.handlers
+log = logging.getLogger('asybot')
+hdlr = logging.handlers.SysLogHandler(facility=logging.handlers.SysLogHandler.LOG_DAEMON)
+formatter = logging.Formatter( '%(filename)s: %(levelname)s: %(message)s')
+hdlr.setFormatter(formatter)
+log.addHandler(hdlr)
+
+# s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g -- removes color codes
+
+
+class asybot(asychat):
+ def __init__(self, server, port, nickname, targets, **kwargs):
+ asychat.__init__(self)
+ self.server = server
+ self.port = port
+ self.nickname = nickname
+ self.targets = targets
+ self.username = kwargs['username'] if 'username' in kwargs else nickname
+ self.hostname = kwargs['hostname'] if 'hostname' in kwargs else nickname
+ self.ircname = kwargs['ircname'] if 'ircname' in kwargs else nickname
+ self.realname = kwargs['realname'] if 'realname' in kwargs else nickname
+ self.data = ''
+ self.set_terminator('\r\n')
+ self.create_socket(AF_INET, SOCK_STREAM)
+ self.connect((self.server, self.port))
+ self.wrapper = TextWrapper(subsequent_indent=" ",width=400)
+
+ # When we don't receive data for alarm_timeout seconds then issue a
+ # PING every hammer_interval seconds until kill_timeout seconds have
+ # passed without a message. Any incoming message will reset alarm.
+ self.alarm_timeout = 300
+ self.hammer_interval = 10
+ self.kill_timeout = 360
+ signal(SIGALRM, lambda signum, frame: self.alarm_handler())
+ self.reset_alarm()
+
+
+ def reset_alarm(self):
+ self.last_activity = date.now()
+ alarm(self.alarm_timeout)
+
+ def alarm_handler(self):
+ delta = date.now() - self.last_activity
+ if delta > timedelta(seconds=self.kill_timeout):
+ log.error('No data for %s. Giving up...' % delta)
+ exit(2)
+ else:
+ log.error('No data for %s. PINGing server...' % delta)
+ self.push('PING :%s' % self.nickname)
+ alarm(self.hammer_interval)
+
+ def collect_incoming_data(self, data):
+ self.data += data
+
+ def found_terminator(self):
+ log.debug('<< %s' % self.data)
+
+ message = self.data
+ self.data = ''
+
+ _, prefix, command, params, rest, _ = \
+ split('^(?::(\S+)\s)?(\S+)((?:\s[^:]\S*)*)(?:\s:(.*))?$', message)
+ params = params.split(' ')[1:]
+ #print([prefix, command, params, rest])
+
+ if command == 'PING':
+ self.push('PONG :%s' % rest)
+ log.debug("Replying to servers PING with PONG :%s" %rest)
+
+ elif command == 'PRIVMSG':
+ self.on_privmsg(prefix, command, params, rest)
+
+ elif command == '433':
+ # ERR_NICKNAMEINUSE, retry with another name
+ _, nickname, int, _ = split('^.*[^0-9]([0-9]+)$', self.nickname) \
+ if search('[0-9]$', self.nickname) \
+ else ['', self.nickname, 0, '']
+ self.nickname = nickname + str(int + 1)
+ self.handle_connect()
+
+ self.reset_alarm()
+
+ def push(self, message):
+ log.debug('>> %s' % message)
+ asychat.push(self, message + self.get_terminator())
+
+ def handle_connect(self):
+ self.push('NICK %s' % self.nickname)
+ self.push('USER %s %s %s :%s' %
+ (self.username, self.hostname, self.server, self.realname))
+ self.push('JOIN %s' % ','.join(self.targets))
+
+ def on_privmsg(self, prefix, command, params, rest):
+ def PRIVMSG(text):
+ for line in self.wrapper.wrap(text):
+ msg = 'PRIVMSG %s :%s' % (','.join(params), line)
+ log.info(msg)
+ self.push(msg)
+ sleep(1)
+
+ def ME(text):
+ PRIVMSG('ACTION ' + text + '')
+
+ _from = prefix.split('!', 1)[0]
+
+ try:
+ _, _handle, _command, _argument, _ = split(
+ '^(\w+|\*):\s*(\w+)(?:\s+(.*))?$', rest)
+ except ValueError, error:
+ if search(self.nickname, rest):
+ PRIVMSG('I\'m so famous')
+ return # ignore
+
+ if _handle == self.nickname or _handle == '*':
+
+ from os.path import realpath, dirname, join
+ from subprocess import Popen as popen, PIPE
+ from time import time
+ Reaktor_dir = dirname(realpath(dirname(__file__)))
+ public_commands = join(Reaktor_dir, 'public_commands')
+ command = join(public_commands, _command)
+
+ if is_executable(command):
+
+ env = {}
+ args = []
+ start = time()
+ if _argument != None:
+ env['argument'] = _argument
+ args = shlex.split(_argument)
+ try:
+ p = popen([command] + args,bufsize=1, stdout=PIPE, stderr=PIPE, env=env)
+ except OSError, error:
+ ME('brain damaged')
+ log.error('OSError@%s: %s' % (command, error))
+ return
+ pid = p.pid
+ for line in iter(p.stdout.readline,""):
+ PRIVMSG(translate_colors(line))
+ log.debug('%s stdout: %s' % (pid, line))
+ p.wait()
+ elapsed = time() - start
+ code = p.returncode
+ log.info('command: %s -> %s in %d seconds' % (command, code,elapsed))
+ [log.debug('%s stderr: %s' % (pid, x)) for x in p.stderr.readlines()]
+
+ if code != 0:
+ ME('mimimi')
+
+ else:
+ if _handle != '*':
+ PRIVMSG(_from + ': you are made of stupid')
+
+
+# retrieve the value of a [singleton] variable from a tinc.conf(5)-like file
+def getconf1(x, path):
+ from re import findall
+ pattern = '(?:^|\n)\s*' + x + '\s*=\s*(.*\w)\s*(?:\n|$)'
+ y = findall(pattern, open(path, 'r').read())
+ if len(y) < 1:
+ raise AttributeError("len(getconf1('%s', '%s') < 1)" % (x, path))
+ if len(y) > 1:
+ y = ' '.join(y)
+ raise AttributeError("len(getconf1('%s', '%s') > 1)\n ====> %s"
+ % (x, path, y))
+ return y[0]
+
+if __name__ == "__main__":
+ from os import environ as env
+
+ lol = logging.DEBUG if env.get('debug',False) else logging.INFO
+ logging.basicConfig(level=lol)
+ try:
+ name = getconf1('Name', '/etc/tinc/retiolum/tinc.conf')
+ hostname = '%s.retiolum' % name
+ except:
+ name = gethostname()
+ hostname = name
+ nick = str(env.get('nick', name))
+ host = str(env.get('host', 'supernode'))
+ port = int(env.get('port', 6667))
+ target = str(env.get('target', '#retiolum'))
+ log.info('=> irc://%s@%s:%s/%s' % (nick, host, port, target))
+
+ from getpass import getuser
+ asybot(host, port, nick, [target], username=getuser(),
+ ircname='//Reaktor running at %s' % hostname,
+ hostname=hostname)
+
+ loop()
diff --git a/Reaktor/IRC/index b/Reaktor/IRC/index
new file mode 100755
index 00000000..cc2652fe
--- /dev/null
+++ b/Reaktor/IRC/index
@@ -0,0 +1,6 @@
+#! /bin/sh
+set -xeuf
+
+# cd //Reaktor
+cd $(dirname $(readlink -f $0))/..
+host=irc.freenode.net target='#krebs' python IRC/asybot.py "$@"
diff --git a/Reaktor/IRC/translate_colors.py b/Reaktor/IRC/translate_colors.py
new file mode 100644
index 00000000..bd716618
--- /dev/null
+++ b/Reaktor/IRC/translate_colors.py
@@ -0,0 +1,31 @@
+
+
+COLOR_MAP = {
+ "\x1b[0m" : "\x0F", # reset
+ "\x1b[37m" : "\x0300",
+ "\x1b[30m" : "\x0301",
+ "\x1b[34m" : "\x0302",
+ "\x1b[32m" : "\x0303",
+ "\x1b[31m" : "\x0304",
+ "\x1b[33m" : "\x0305",
+ "\x1b[35m" : "\x0306",
+ "\x1b[33m" : "\x0307",
+ "\x1b[33m" : "\x0308",
+ "\x1b[32m" : "\x0309",
+ "\x1b[36m" : "\x0310",
+ "\x1b[36m" : "\x0311",
+ "\x1b[34m" : "\x0312",
+ "\x1b[31m" : "\x0313",
+ "\x1b[30m" : "\x0314",
+ "\x1b[37m" : "\x0315",
+ "\x1b[1m" : "\x02", # bold on
+ "\x1b[22m" : "\x02" # bold off
+ }
+def translate_colors (line):
+ for color,replace in COLOR_MAP.items():
+ line = line.replace(color,replace)
+ return line
+
+if __name__ == "__main__":
+ import sys
+ print (translate_colors(sys.stdin.read()))
diff --git a/Reaktor/Makefile b/Reaktor/Makefile
new file mode 100644
index 00000000..2241dba6
--- /dev/null
+++ b/Reaktor/Makefile
@@ -0,0 +1,23 @@
+submodules = gxfr dnsrecon bxfr whatweb
+security_modules = subdomains revip whatweb
+
+all: init all-mods
+
+init: init-submodules $(submodules)
+init-submodules:
+ cd ..;git submodule init; git submodule update
+$(submodules):
+ cd repos/$@ ; git checkout master;git pull
+
+all-mods: $(addprefix public_commands/,$(security_modules))
+public_commands/%:commands/%
+ ln -s ../$< $@
+
+debian-autostart:
+ useradd reaktor ||:
+ cp startup/init.d/reaktor-debian /etc/init.d/reaktor
+ cp startup/conf.d/reaktor /etc/default/
+ update-rc.d reaktor defaults
+supervisor-autostart:
+ useradd reaktor ||:
+ cp startup/supervisor/Reaktor.conf /etc/supervisor/conf.d/
diff --git a/Reaktor/README.md b/Reaktor/README.md
new file mode 100644
index 00000000..a581c6bd
--- /dev/null
+++ b/Reaktor/README.md
@@ -0,0 +1,26 @@
+# //Reaktor
+
+## Quickstart
+
+ ## 1. create a dedicated user
+ useradd reaktor
+
+ ## 2. marry Reaktor with /sbin/init
+
+ ## 2a. /etc/inittab-like foo
+ echo 10:2345:respawn:/bin/su reaktor -c /krebs/Reaktor/index >>/etc/inittab
+ # or 10:2345:respawn:/usr/bin/sudo -u reaktor /krebs/Reaktor/index
+ # if reaktor's shell is /bin/false or similar
+ # [check with e.g getent passwd reaktor]
+ telinit q
+
+ ## 2b. upstart-like foo
+
+ cat > /etc/init/Reaktor.conf <<EOF
+ description "Krebs Reaktor"
+ author "The Ministerium"
+ stop on runlevel [016]
+ respawn
+ exec /usr/bin/sudo -u reaktor /krebs/Reaktor/index
+ EOF
+ start Reaktor
diff --git a/Reaktor/UDP/README b/Reaktor/UDP/README
new file mode 100644
index 00000000..587c8046
--- /dev/null
+++ b/Reaktor/UDP/README
@@ -0,0 +1,52 @@
+# Specifications
+From 2011-09-16:
+## Use Case
+ [
+ {
+ "port": 1337,
+ "pattern": "XXYY",
+ "comment" : "does the right thing",
+ "action" : {
+ "POST": {
+ "url" : "xyz",
+ "data" : "abc"
+ }
+ }
+ }
+ ]
+## Head definition
+ {
+ "bind_addr" : "127.0.0.1",
+ "port" : 1234,
+ "pattern" : "XXZZ",
+ "action" : { },
+ }
+bind is an optional entry which lets the user define a bind address for the server.
+terminator is optional which lets the user define the EOM terminator.
+
+## Actions
+### POST
+"POST" : {
+ "url" : "http://euer.krebsco.de/",
+ "data" : "{ \"something\" : \"else\" }"
+ }
+
+### PROCESS
+Process is taken from //hyper/process/test/bc.json - rev:a7fd3f
+
+stdout/stderr are optional and if left away data will be written to real stdout/stderr
+in the process in envp => "payload" will be allocated for the given payload from the network
+
+ "PROCESS" : {
+ {
+ "path": "/usr/bin/bc",
+ "argv": [
+ "bc"
+ ],
+ "envp": {
+ "was": "geht",
+ },
+ "stdout": "uri:///path/to/somewhere",
+ "stderr": "uri:///path/to/somewhere"
+ }
+ }
diff --git a/Reaktor/UDP/config b/Reaktor/UDP/config
new file mode 100644
index 00000000..623c721c
--- /dev/null
+++ b/Reaktor/UDP/config
@@ -0,0 +1,15 @@
+[
+ {
+ "comment" : "listener for licht.shack",
+ "bind_addr" : "0.0.0.0",
+ "port" : 2342,
+ "pattern" : "",
+ "action" : {
+ "PROCESS" : {
+ "path" : "/krebs/Reaktor/udp_commands/licht_resolver",
+ "argv" : [ ],
+ "envp" : { }
+ }
+ }
+ }
+]
diff --git a/Reaktor/UDP/index b/Reaktor/UDP/index
new file mode 100755
index 00000000..ffe3d6b1
--- /dev/null
+++ b/Reaktor/UDP/index
@@ -0,0 +1,94 @@
+#!/usr/bin/python
+
+import asyncore, socket
+import logging,subprocess
+import re
+import json
+log = logging.getLogger('ubot')
+
+def enable_syslog(logger):
+ import logging.handlers as handlers
+ from logging import Formatter
+ hdlr = handlers.SysLogHandler(
+ facility=handlers.SysLogHandler.LOG_DAEMON)
+ formatter = Formatter(
+ '%(filename)s: %(levelname)s: %(message)s')
+ hdlr.setFormatter(formatter)
+ logger.addHandler(hdlr)
+
+from twisted.internet.protocol import DatagramProtocol
+from twisted.internet import reactor
+from twisted.application.internet import MulticastServer
+from socket import SOL_SOCKET,SO_BROADCAST
+class ubot(DatagramProtocol):
+# def startProtocol(self):
+# log.info("Starting Listener for Multicast")
+# self.transport.joinGroup("255.255.255.255")
+
+ """ UDP Bot """
+ def startProtocol(self):
+ log.info("starting Protocol at host (%s)" % self.bind_addr)
+ #self.transport.
+ if self.bind_addr != "255.255.255.255":
+ self.transport.joinGroup(self.bind_addr)
+ else:
+ self.transport.socket.setsockopt(SOL_SOCKET,SO_BROADCAST,True)
+ def __init__(self, pattern,action,bind_addr,**kwargs):
+ #DatagramProtocol.__init__(self)
+ self.data = ''
+ self.pattern = pattern
+ self.action = action
+ self.bind_addr = bind_addr
+
+ def datagramReceived(self,datagram,addr):
+ self.data = datagram
+ log.debug('<< %s' % self.data)
+ if self.find_pattern():
+ self.start_action()
+
+ def find_pattern(self):
+ """ returns true if own pattern is found"""
+ log.debug("Pattern is %s" %self.pattern)
+ ret = re.search(self.pattern,self.data)
+ if ret:
+ log.info("Match \"%s\" with pattern \"%s\"" % ((ret.string.strip()),self.pattern))
+ else:
+ log.info("No Match")
+ return ret
+
+
+ def start_action(self):
+ """ runs all the defined actions"""
+ log.debug("Actions: %s" % str(self.action))
+ self.start_process()
+ self.start_post()
+
+ def start_process(self):
+ try:
+ act = self.action["PROCESS"]
+ proc = []
+ proc.append(act["path"])
+ proc.extend(act["argv"])
+
+ env = act["envp"]
+ env["payload"] = json.dumps(self.data)
+ log.info("Starting Process: %s (env: %s)" % (proc,env))
+ subprocess.Popen(proc,env=env)
+ except Exception as e:
+ log.error(e)
+ def start_post(self):
+ pass
+
+def load_conf(conf_file):
+ return json.load(open(conf_file))
+
+
+if __name__ == "__main__":
+ import os
+ #enable_syslog(log)
+ HERE = os.path.dirname(os.path.realpath(__file__))
+ lol = logging.DEBUG if os.environ.get('debug',False) else logging.INFO
+ logging.basicConfig(level=lol)
+ for i in load_conf("%s/config" %HERE):
+ reactor.listenMulticast(i["port"], ubot(**i))
+ reactor.run()
diff --git a/Reaktor/commands/caps b/Reaktor/commands/caps
new file mode 100755
index 00000000..bc3d7ba2
--- /dev/null
+++ b/Reaktor/commands/caps
@@ -0,0 +1,4 @@
+#! /bin/sh
+set -euf
+cd public_commands
+echo `ls`
diff --git a/Reaktor/commands/hello b/Reaktor/commands/hello
new file mode 100755
index 00000000..df3b6bb9
--- /dev/null
+++ b/Reaktor/commands/hello
@@ -0,0 +1,2 @@
+#! /bin/sh
+echo "Hello${argument+, $argument}!"
diff --git a/Reaktor/commands/licht_resolver b/Reaktor/commands/licht_resolver
new file mode 100755
index 00000000..5bdb6510
--- /dev/null
+++ b/Reaktor/commands/licht_resolver
@@ -0,0 +1,37 @@
+#! /usr/bin/python
+# -*- coding: utf-8 -*-
+
+map = {
+ 'shackspace': {
+ 'device': {
+ 0: 'Licht0, Zickenzone; Fenster',
+ 1: 'Licht1, Sofaecke; Fenster',
+ 2: 'Licht2, Zickenzone; Ghetto',
+ 3: 'Licht3, Sofaecke; Ghetto',
+ 4: 'Licht4, Richtung Getränkelager',
+ 5: 'Licht5, Porschekonsole',
+ 6: 'Licht6, Tomatenecke',
+ 7: 'Licht7, Ghetto',
+ 10: 'Hauptschalter'
+ },
+ 'state': {
+ 0: 'aus',
+ 1: 'an',
+ 2: 'aus in T-10s'
+ },
+ '->': 'ist'
+ }
+}
+
+from struct import unpack
+import json
+from os import environ as env
+
+location = "shackspace"
+
+map = map[location]
+print (env["payload"]+"")
+did, sid= unpack('BB', json.loads(env['payload']))
+device,state = map['device'][did], map['state'][sid]
+arrow = map['->']
+print ' '.join([device, arrow, state])
diff --git a/Reaktor/commands/reload b/Reaktor/commands/reload
new file mode 100755
index 00000000..bfa1f041
--- /dev/null
+++ b/Reaktor/commands/reload
@@ -0,0 +1,2 @@
+#! /bin/sh
+exec pkill -U reaktor
diff --git a/Reaktor/commands/retard b/Reaktor/commands/retard
new file mode 100755
index 00000000..c59b4d1c
--- /dev/null
+++ b/Reaktor/commands/retard
@@ -0,0 +1 @@
+#? //retard
diff --git a/Reaktor/commands/rev b/Reaktor/commands/rev
new file mode 100755
index 00000000..a8681ab9
--- /dev/null
+++ b/Reaktor/commands/rev
@@ -0,0 +1,2 @@
+#! /bin/sh
+git log -1 --format="%h by %an, %ar"
diff --git a/Reaktor/commands/revip b/Reaktor/commands/revip
new file mode 120000
index 00000000..e2c3b7ab
--- /dev/null
+++ b/Reaktor/commands/revip
@@ -0,0 +1 @@
+../repos/revip/revip \ No newline at end of file
diff --git a/Reaktor/commands/subdomains b/Reaktor/commands/subdomains
new file mode 120000
index 00000000..0489555f
--- /dev/null
+++ b/Reaktor/commands/subdomains
@@ -0,0 +1 @@
+../repos/consolidate_dns/index \ No newline at end of file
diff --git a/Reaktor/commands/uptime b/Reaktor/commands/uptime
new file mode 100755
index 00000000..7ff64168
--- /dev/null
+++ b/Reaktor/commands/uptime
@@ -0,0 +1,8 @@
+#! /bin/sh
+#
+# //Reactor/commands/uptime - IRC-optimized uptime output
+#
+uptime | sed '
+ s/^\(.*\) up \(.*\) days\?, *\(.*\), *\(.*\) users\?, *load average: \(.*\)$/\1; \2d \3; \4u; \5/;t
+ s/^\(.*\) up *\(.*\), *\(.*\) users\?, *load average: \(.*\)$/\1; 0d \2; \3u; \4/;t
+'
diff --git a/Reaktor/commands/whatweb b/Reaktor/commands/whatweb
new file mode 100755
index 00000000..afe20360
--- /dev/null
+++ b/Reaktor/commands/whatweb
@@ -0,0 +1,7 @@
+#!/bin/sh
+#wrapper for WhatWeb
+here=$(dirname `readlink -f $0`)
+whatweb_bin="$here/../repos/whatweb/whatweb"
+[ ! -e "$whatweb_bin" ] && echo "!! Whatweb app does not exist" && exit 1
+[ -z "${1:-}" ] && echo "!! no host given" && exit 1
+exec $whatweb_bin -a 3 "$1" 2>&1
diff --git a/Reaktor/index b/Reaktor/index
new file mode 100755
index 00000000..ac647ca3
--- /dev/null
+++ b/Reaktor/index
@@ -0,0 +1,7 @@
+#! /bin/sh
+set -euf
+
+# cd //Reaktor
+cd $(dirname $(readlink -f $0))
+
+exec IRC/index
diff --git a/Reaktor/public_commands/caps b/Reaktor/public_commands/caps
new file mode 120000
index 00000000..43c0a342
--- /dev/null
+++ b/Reaktor/public_commands/caps
@@ -0,0 +1 @@
+../commands/caps \ No newline at end of file
diff --git a/Reaktor/public_commands/hello b/Reaktor/public_commands/hello
new file mode 120000
index 00000000..4509249b
--- /dev/null
+++ b/Reaktor/public_commands/hello
@@ -0,0 +1 @@
+../commands/hello \ No newline at end of file
diff --git a/Reaktor/public_commands/reload b/Reaktor/public_commands/reload
new file mode 120000
index 00000000..9a337850
--- /dev/null
+++ b/Reaktor/public_commands/reload
@@ -0,0 +1 @@
+../commands/reload \ No newline at end of file
diff --git a/Reaktor/public_commands/retard b/Reaktor/public_commands/retard
new file mode 120000
index 00000000..29ae4b01
--- /dev/null
+++ b/Reaktor/public_commands/retard
@@ -0,0 +1 @@
+../commands/retard \ No newline at end of file
diff --git a/Reaktor/public_commands/rev b/Reaktor/public_commands/rev
new file mode 120000
index 00000000..f5433412
--- /dev/null
+++ b/Reaktor/public_commands/rev
@@ -0,0 +1 @@
+../commands/rev \ No newline at end of file
diff --git a/Reaktor/public_commands/uptime b/Reaktor/public_commands/uptime
new file mode 120000
index 00000000..ffe5fbd7
--- /dev/null
+++ b/Reaktor/public_commands/uptime
@@ -0,0 +1 @@
+../commands/uptime \ No newline at end of file
diff --git a/Reaktor/repos/bxfr/bxfr.py b/Reaktor/repos/bxfr/bxfr.py
new file mode 100644
index 00000000..8e6bd101
--- /dev/null
+++ b/Reaktor/repos/bxfr/bxfr.py
@@ -0,0 +1,238 @@
+#!/usr/bin/python -tt
+
+# gxfr replicates dns zone transfers by enumerating subdomains using advanced search engine queries and conducting dns lookups.
+# By Tim Tomes (LaNMaSteR53)
+# Available for download at http://LaNMaSteR53.com or http://code.google.com/p/gxfr/
+
+import sys, os.path, urllib, urllib2, re, time, socket, random, socket
+
+
+def help():
+ print """ Syntax: ./gxfr.py domain [options]
+
+ -h, --help this screen
+ -v enable verbose mode
+ -t [num of seconds] set number of seconds to wait between queries (default=15)
+ -q [max num of queries] restrict to maximum number of queries (default=0, indefinite)
+ --dns-lookup enable dns lookups of all subdomains
+ --proxy [file|ip:port|-] use a proxy or list of open proxies to send queries (@random w/list)
+ - [file] must consist of 1 or more ip:port pairs
+ - replace filename with '-' (dash) to accept stdin
+ --user-agent ['string'] set custom user-agent string
+ --timeout [seconds] set socket timeout (default=system default)
+ --csv [file]
+
+ Examples:
+ $ ./gxfr.py foxnews.com --dns-lookup -v
+ $ ./gxfr.py foxnews.com --dns-lookup --proxy open_proxies.txt --timeout 10
+ $ ./gxfr.py foxnews.com --dns-lookup -t 5 -q 5 -v --proxy 127.0.0.1:8080
+ $ curl http://rmccurdy.com/scripts/proxy/good.txt | ./gxfr.py website.com -v -t 3 --proxy -
+ """
+ sys.exit(2)
+
+if len(sys.argv) < 2:
+ help()
+
+if '-h' in sys.argv or '--help' in sys.argv:
+ help()
+
+# declare vars and process arguments
+#http://www.bing.com/search?q=site%3agoogle.de&qs=n&filt=all&pq=site%3agoogle.d&sc=8-5&sp=-1&sk=&first=1&FORM=PORE
+query_cnt = 0
+csvname = False
+domain = sys.argv[1]
+sys.argv = sys.argv[2:]
+lookup = False
+encrypt = True
+base_url = 'http://www.bing.com'
+base_uri = '/search?qs=n&form=QBRE&sc=0-0&sp=-1&sk='
+base_query = 'site:' + domain
+pattern = '//([\.\w-]*)\.%s.+?' % (domain)
+proxy = False
+user_agent = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; FDM; .NET CLR 2.0.50