killerbee_msfrelay.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. #!/usr/bin/env python2
  2. # Note: This module explicitly requires Python2 as KillerBee does not yet support Python3:
  3. # https://github.com/riverloopsec/killerbee/tree/cb04e169a635ad4bc0772b631e1b332320f7a8da#killerbee
  4. # KillerBee Metasploit relay server
  5. import re
  6. import os
  7. import sys
  8. import cmd
  9. import time
  10. import json
  11. import base64
  12. import socket
  13. import threading
  14. import pkg_resources # Used to get killerbee version
  15. from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
  16. from urlparse import parse_qs, urlparse
  17. from killerbee import *
  18. last_errors = 0
  19. starttime = 0
  20. packets_sent = 0
  21. last_sent = 0
  22. username = None
  23. password = None
  24. kb = None
  25. class MSFHandler(BaseHTTPRequestHandler):
  26. def status(self):
  27. status = {}
  28. hw_versions = []
  29. fw_version = pkg_resources.get_distribution("killerbee").version
  30. device_names = []
  31. for dev in kbutils.devlist():
  32. hw_versions.append(dev[2])
  33. device_names.append(dev[1])
  34. if len(hw_versions) > 0:
  35. status["operational"] = 1
  36. else:
  37. status["operational"] = 0
  38. status["hw_specialty"] = { "zigbee": True }
  39. # TODO: We should check firmware before reporting transmit capabilities
  40. status["hw_capabilities"] = { "transmit": True}
  41. status["last_10_errors"] = last_errors
  42. status["api_version"] = "0.0.3"
  43. status["fw_version"] = fw_version
  44. if len(hw_versions) == 1:
  45. status["hw_version"] = hw_versions[0]
  46. status["device_name"] = device_names[0]
  47. elif len(hw_versions) > 1:
  48. status["hw_version"] = ', '.join(hw_versions)
  49. status["device_name"] = ', '.join(device_names)
  50. else:
  51. status["hw_version"] = "Not Supported"
  52. return status
  53. def statistics(self):
  54. global packets_sent
  55. stats = {}
  56. stats["uptime"] = int(time.time()) - starttime
  57. stats["packet_stats"] = packets_sent
  58. stats["last_request"] = last_sent
  59. stats["voltage"] = "0.0v"
  60. return stats
  61. def datetime(self):
  62. return { "sytem_datetime": int(time.time()) }
  63. def timezone(self):
  64. return { "system_timezone": time.strftime("%Z") }
  65. def set_channel(self, args):
  66. if not "chan" in args:
  67. return self.not_supported()
  68. chan = int(args["chan"][0])
  69. kb.set_channel(chan)
  70. return { "success": True }
  71. def inject(self, args):
  72. global packets_sent
  73. if not "data" in args:
  74. return self.not_supported()
  75. try:
  76. kb.inject(base64.urlsafe_b64decode(args["data"][0]))
  77. packets_sent+=1
  78. except Exception as e:
  79. print("ERROR: Unable to inject packet: {0}".format(e))
  80. return { "success": False }
  81. return { "success": True }
  82. def recv(self):
  83. pkt = kb.pnext()
  84. if pkt != None and pkt[1]:
  85. return {"data": base64.urlsafe_b64encode(pkt[0]), "valid_crc": pkt[1], "rssi": pkt[2] }
  86. return {}
  87. def sniffer_off(self):
  88. kb.sniffer_off()
  89. return {"success": True }
  90. def sniffer_on(self):
  91. kb.sniffer_on()
  92. return {"success": True }
  93. def supported_devices(self):
  94. devices = []
  95. for dev in kbutils.devlist():
  96. devices.append(dev[0])
  97. return { "devices": devices }
  98. def not_supported(self):
  99. return { "status": "not supported" }
  100. def send(self, data, resp=200):
  101. self.send_response(resp)
  102. self.send_header('Content-type', 'application/json')
  103. self.end_headers()
  104. self.wfile.write(json.dumps(data))
  105. return
  106. def do_AUTHHEAD(self):
  107. self.send_response(401)
  108. self.send_header('WWW-Authenticate', 'Basic realm=\"Killerbee MSF Relay\"')
  109. self.send_header('Content-type', 'text/html')
  110. self.end_headers()
  111. self.wfile.write("Please Authenticate")
  112. def do_GET(self):
  113. if not password == None:
  114. if self.headers.getheader('Authorization') == None:
  115. print("Did not authenticate")
  116. self.do_AUTHHEAD()
  117. return
  118. if not self.headers.getheader('Authorization') == 'Basic '+base64.b64encode(username + ":" + password):
  119. print("Bad Authentication")
  120. self.do_AUTHHEAD()
  121. return
  122. url = urlparse(self.path)
  123. args = parse_qs(url.query)
  124. if self.path=="/status":
  125. self.send(self.status())
  126. elif self.path=="/statistics":
  127. self.send(self.statistics())
  128. elif self.path=="/settings/datetime":
  129. self.send(self.datetime())
  130. elif self.path=="/settings/timezone":
  131. self.send(self.timezone())
  132. elif self.path=="/zigbee/supported_devices":
  133. self.send(self.supported_devices())
  134. elif self.path.startswith("/zigbee/"):
  135. re_dev = re.compile("/zigbee/([\d\w:]+)/")
  136. m = re_dev.match(self.path)
  137. if m:
  138. dev = m.group(1)
  139. if self.path.find("/set_channel?") > -1:
  140. self.send(self.set_channel(args))
  141. elif self.path.find("/inject?") > -1:
  142. self.send(self.inject(args))
  143. elif self.path.find("/recv") > -1:
  144. self.send(self.recv())
  145. elif self.path.find("/sniffer_off") > -1:
  146. self.send(self.sniffer_off())
  147. elif self.path.find("/sniffer_on") > -1:
  148. self.send(self.sniffer_on())
  149. else:
  150. self.send(self.not_supported(), 404)
  151. else:
  152. self.send(self.not_supported(), 404)
  153. else:
  154. self.send(self.not_supported(), 404)
  155. return
  156. class Killerbee_MSFRelay(cmd.Cmd):
  157. intro = """
  158. KillerBee Metasploit Relay
  159. """
  160. def __init__(self, ip='0.0.0.0', port=8080):
  161. cmd.Cmd.__init__(self)
  162. self._ip = ip
  163. self._port = port
  164. self._sock = None
  165. self._pause = False
  166. self.start()
  167. def start(self):
  168. self._go = True
  169. while self._go:
  170. # serve the NIC port
  171. try:
  172. self._sock = HTTPServer((self._ip, self._port), MSFHandler)
  173. starttime = int(time.time())
  174. print("KillerBee MSFRelay running.")
  175. self._sock.serve_forever()
  176. except KeyboardInterrupt:
  177. self._sock.socket.close()
  178. self._go = False
  179. except:
  180. sys.excepthook(*sys.exc_info())
  181. if __name__ == "__main__":
  182. import argparse
  183. parser = argparse.ArgumentParser()
  184. parser.add_argument('-i', '--iface', '--dev', action='store', dest='devstring')
  185. parser.add_argument('-u', '--user', default="msf_relay", help='HTTP Username', type=str)
  186. parser.add_argument('-p', '--password', default="rfcat_relaypass", help='HTTP Password', type=str)
  187. parser.add_argument('-P', '--Port', default=8080, type=int)
  188. parser.add_argument('--noauth', default=False, action="store_true", help='Do not require authentication')
  189. parser.add_argument('--localonly', default=False, action="store_true", help='Listen on localhost only')
  190. ifo = parser.parse_args()
  191. try:
  192. kb = KillerBee(device=ifo.devstring)
  193. except KBInterfaceError as e:
  194. print("Interface Error: {0}".format(e))
  195. sys.exit(-1)
  196. username = ifo.user
  197. password = ifo.password
  198. ip = "0.0.0.0"
  199. port = ifo.Port
  200. if ifo.noauth:
  201. username = None
  202. password = None
  203. if ifo.localonly:
  204. host = "127.0.0.1"
  205. wait_msg = False
  206. dev_found = False
  207. while not dev_found:
  208. try:
  209. devs = kbutils.devlist()
  210. if len(devs) > 0:
  211. dev_found = True
  212. elif not wait_msg:
  213. print("Insert KillerBee compatible ZigBee device. (You may need to add permissions)")
  214. wait_msg = True
  215. except KeyboardInterrupt:
  216. sys.exit()
  217. except:
  218. if not wait_msg:
  219. print("Insert KillerBee compatible ZigBee device. (You may need to add permissions)")
  220. wait_msg = True
  221. beerelay = Killerbee_MSFRelay(ip, port)
  222. import atexit
  223. atexit.register(cleanupInteractiveAtExit)