main.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. #!/usr/bin/python3
  2. # -*- coding: iso-8859-15 -*-
  3. """
  4. This file is part of the cintruder project, https://cintruder.03c8.net
  5. Copyright (c) 2012/2020 psy <epsylon@riseup.net>
  6. cintruder is free software; you can redistribute it and/or modify it under
  7. the terms of the GNU General Public License as published by the Free
  8. Software Foundation version 3 of the License.
  9. cintruder is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  11. FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  12. details.
  13. You should have received a copy of the GNU General Public License along
  14. with cintruder; if not, write to the Free Software Foundation, Inc., 51
  15. Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. """
  17. import os, traceback, hashlib, sys, time, socket, datetime
  18. import platform, subprocess, re, webbrowser, shutil
  19. from core.options import CIntruderOptions
  20. from core.crack import CIntruderCrack
  21. from core.ocr import CIntruderOCR
  22. from core.curl import CIntruderCurl
  23. from core.xml_export import CIntruderXML
  24. from core.update import Updater
  25. try:
  26. from urlparse import urlparse
  27. except:
  28. import urllib.request, urllib.parse, urllib.error
  29. from urllib.parse import urlparse
  30. # set to emit debug messages about errors (0 = off).
  31. DEBUG = 0
  32. class cintruder():
  33. """
  34. CIntruder application class
  35. """
  36. def __init__(self):
  37. self.captcha = ""
  38. self.optionOCR = []
  39. self.optionCrack = []
  40. self.optionParser = None
  41. self.optionCurl = None
  42. self.options = None
  43. self.word_sug = None
  44. self.train == 0
  45. self.crack == 0
  46. self.ignoreproxy = 1
  47. self.isurl = 0
  48. self.os_sys = platform.system()
  49. self._webbrowser = webbrowser
  50. self.GIT_REPOSITORY = 'https://code.03c8.net/epsylon/cintruder' # oficial code source [OK! 26/01/2019]
  51. self.GIT_REPOSITORY2 = 'https://github.com/epsylon/cintruder' # mirror source [since: 04/06/2018]
  52. def set_options(self, options):
  53. """
  54. Set cintruder options
  55. """
  56. self.options = options
  57. def create_options(self, args=None):
  58. """
  59. Create the program options for OptionParser.
  60. """
  61. self.optionParser = CIntruderOptions()
  62. self.options = self.optionParser.get_options(args)
  63. if not self.options:
  64. return False
  65. return self.options
  66. def set_webbrowser(self, browser):
  67. self._webbrowser = browser
  68. def banner(self):
  69. print('='*75)
  70. print("")
  71. print(" o8%8888, ")
  72. print(" o88%8888888. ")
  73. print(" 8'- -:8888b ")
  74. print(" 8' 8888 ")
  75. print(" d8.-=. ,==-.:888b ")
  76. print(" >8 `~` :`~' d8888 ")
  77. print(" 88 ,88888 ")
  78. print(" 88b. `-~ ':88888 ")
  79. print(" 888b \033[1;31m~==~\033[1;m .:88888 ")
  80. print(" 88888o--:':::8888 ")
  81. print(" `88888| :::' 8888b ")
  82. print(" 8888^^' 8888b ")
  83. print(" d888 ,%888b. ")
  84. print(" d88% %%%8--'-. ")
  85. print(" /88:.__ , _%-' --- - ")
  86. print(" '''::===..-' = --. `\n")
  87. print(self.optionParser.description, "\n")
  88. print('='*75)
  89. def get_attack_captchas(self):
  90. """
  91. Get captchas to brute force
  92. """
  93. captchas = []
  94. options = self.options
  95. p = self.optionParser
  96. if options.train:
  97. print(('='*75))
  98. print((str(p.version)))
  99. print(('='*75))
  100. print("Starting to 'train'...")
  101. print(('='*75))
  102. captchas = [options.train]
  103. if options.crack:
  104. print(('='*75))
  105. print((str(p.version)))
  106. print(('='*75))
  107. print("Starting to 'crack'")
  108. print(('='*75))
  109. captchas = [options.crack]
  110. if options.track:
  111. print(('='*75))
  112. print((str(p.version)))
  113. print(('='*75))
  114. print("Tracking captchas from url...")
  115. print(('='*75))
  116. captchas = [options.track]
  117. return captchas
  118. def train(self, captcha):
  119. """
  120. Learn mode:
  121. + Add words to the brute forcing dictionary
  122. """
  123. self.train_captcha(captcha)
  124. def train_captcha(self, captcha):
  125. """
  126. Learn mode:
  127. 1- Apply OCR to captcha/image and split into unities
  128. 2- Human-Recognize that unities like alphanumeric words (gui supported)
  129. 3- Move that words into dictionary (gui supported)
  130. """
  131. options = self.options
  132. # step 1: applying OCR techniques
  133. if options.name: # with a specific OCR module
  134. print("[Info] Loading module: [ "+ options.name + " ]\n")
  135. try:
  136. sys.path.append('mods/%s/'%(options.name))
  137. exec("from " + options.name + "_ocr" + " import CIntruderOCR") # import module
  138. except Exception:
  139. print("\n[Error] '"+ options.name+ "' module not found!\n")
  140. return #sys.exit(2)
  141. if options.setids: # with a specific colour ID
  142. setids = int(options.setids)
  143. if setids >= 0 and setids <= 255:
  144. self.optionOCR = CIntruderOCR(captcha, options)
  145. else:
  146. print("\n[Error] You must enter a valid RGB colour ID number (between 0 and 255)\n")
  147. return #sys.exit(2)
  148. else:
  149. self.optionOCR = CIntruderOCR(captcha, options)
  150. else: # using general OCR algorithm
  151. if options.setids: # with a specific colour ID
  152. setids = int(options.setids)
  153. if setids >= 0 and setids <= 255:
  154. self.optionOCR = CIntruderOCR(captcha, options)
  155. else:
  156. print("\n[Error] You must enter a valid RGB colour ID number (between 0 and 255)\n")
  157. return #sys.exit(2)
  158. else:
  159. self.optionOCR = CIntruderOCR(captcha, options)
  160. def crack(self, captcha):
  161. """
  162. Crack mode:
  163. + Brute force target's captcha against a dictionary
  164. """
  165. self.crack_captcha(captcha)
  166. def crack_captcha(self, captcha):
  167. """
  168. Crack mode: bruteforcing...
  169. """
  170. options = self.options
  171. if options.name:
  172. print("[Info] Loading module: ["+ str(options.name) + "] \n")
  173. try:
  174. sys.path.append('mods/%s/'%(options.name))
  175. exec("from " + options.name + "_crack" + " import CIntruderCrack")
  176. except Exception:
  177. print("\n[Error] '"+ options.name+ "' module not found!\n")
  178. return #sys.exit(2)
  179. self.optionCrack = CIntruderCrack(captcha)
  180. w = self.optionCrack.crack(options)
  181. self.word_sug = w
  182. else:
  183. self.optionCrack = CIntruderCrack(captcha)
  184. w = self.optionCrack.crack(options)
  185. self.word_sug = w
  186. def remote(self, captchas, proxy):
  187. """
  188. Get remote captchas
  189. """
  190. l = []
  191. if not os.path.exists("inputs/"):
  192. os.mkdir("inputs/")
  193. for captcha in captchas:
  194. c = self.remote_captcha(captcha, proxy)
  195. if c:
  196. l.append(c)
  197. return l
  198. else:
  199. return
  200. def remote_captcha(self, captcha, proxy):
  201. """
  202. Get remote captcha
  203. """
  204. if proxy:
  205. self.ignoreproxy=0
  206. self.optionCurl = CIntruderCurl(captcha, self.ignoreproxy, proxy)
  207. buf = self.optionCurl.request()
  208. if buf != "exit":
  209. m = hashlib.md5()
  210. try:
  211. m.update(captcha.encode('utf-8')+str(datetime.datetime.now()))
  212. except:
  213. m.update(str(datetime.datetime.now()).encode('utf-8'))
  214. c = "%s.gif"%(m.hexdigest())
  215. h = "inputs/" + str(c)
  216. f = open(h, 'wb')
  217. f.write(buf.getvalue())
  218. f.close
  219. buf.close
  220. return h
  221. else:
  222. return #sys.exit(2)
  223. def export(self, captchas):
  224. """
  225. Export results
  226. """
  227. if self.options.xml and not (self.options.train):
  228. self.optionXML = CIntruderXML(captchas)
  229. if self.word_sug == None:
  230. print("[Info] XML NOT created!. There are not words to suggest...")
  231. else:
  232. self.optionXML.print_xml_results(captchas, self.options.xml, self.word_sug)
  233. print("[Info] XML created: "+str(self.options.xml)+ "\n")
  234. def track(self, captchas, proxy, num_tracks):
  235. """
  236. Download captchas from url
  237. """
  238. for captcha in captchas:
  239. self.track_captcha(captcha, proxy, num_tracks)
  240. def track_captcha(self, captcha, proxy, num_tracks):
  241. """
  242. This technique is useful to create a dictionary of 'session based' captchas
  243. """
  244. options = self.options
  245. urlp = urlparse(captcha)
  246. self.domain = urlp.hostname
  247. if not os.path.exists("inputs/%s"%(self.domain)):
  248. os.mkdir("inputs/%s"%(self.domain))
  249. if proxy:
  250. self.ignoreproxy = 0
  251. buf = ""
  252. i=0
  253. while i < int(num_tracks) and buf != "exit":
  254. self.optionCurl = CIntruderCurl(captcha, self.ignoreproxy, proxy)
  255. buf = self.optionCurl.request()
  256. if options.verbose:
  257. print("\n[-]Connection data:")
  258. out = self.optionCurl.print_options()
  259. print('-'*45)
  260. if buf != "exit":
  261. m = hashlib.md5()
  262. try:
  263. m.update(captcha.encode('utf-8')+str(datetime.datetime.now()))
  264. except:
  265. m.update(str(datetime.datetime.now()).encode('utf-8'))
  266. h = "inputs/%s/%s.gif"%(self.domain, m.hexdigest())
  267. f = open(h, 'wb')
  268. f.write(buf.getvalue())
  269. f.close
  270. buf.close
  271. print("[Info] Saved: "+ str(h))
  272. print("------------")
  273. i=i+1
  274. if buf != "exit":
  275. print("\n=================")
  276. print("Tracking Results:")
  277. print("=================")
  278. print("\nNumber of tracked captchas: [ "+ str(num_tracks)+" ] \n")
  279. def run(self, opts=None):
  280. """
  281. Run cintruder
  282. """
  283. if opts:
  284. options = self.create_options(opts)
  285. self.set_options(options)
  286. options = self.options
  287. #step -1: run GUI/Web interface
  288. if options.web:
  289. self.create_web_interface()
  290. return
  291. #step -1: check/update for latest stable version
  292. if options.update:
  293. self.banner()
  294. try:
  295. print("\nTrying to update automatically to the latest stable version\n")
  296. Updater()
  297. except:
  298. print("Not any .git repository found!\n")
  299. print("="*30)
  300. print("\nTo have working this feature, you should clone CIntruder with:\n")
  301. print("$ git clone %s" % self.GIT_REPOSITORY)
  302. print("\nAlso you can try this other mirror:\n")
  303. print("$ git clone %s" % self.GIT_REPOSITORY2 + "\n")
  304. #step 0: list output results and get captcha targets
  305. if options.listmods:
  306. print("=====================================")
  307. print("Listing specific OCR exploit modules:")
  308. print("=====================================\n")
  309. top = 'mods/'
  310. for root, dirs, files in os.walk(top, topdown=False):
  311. for name in files:
  312. if name == 'DESCRIPTION':
  313. if self.os_sys == "Windows": #check for win32 sys
  314. subprocess.call("type %s/%s"%(root, name), shell=True)
  315. else:
  316. subprocess.call("cat %s/%s"%(root, name), shell=True)
  317. print("\n[Info] List end...\n")
  318. return
  319. if options.track_list:
  320. print("==============================")
  321. print("Listing last tracked captchas:")
  322. print("==============================\n")
  323. top = 'inputs/'
  324. tracked_captchas = []
  325. for root, dirs, files in os.walk(top, topdown=False):
  326. for name in files:
  327. path = os.path.relpath(os.path.join(root, name))
  328. if self.os_sys == "Windows": #check for win32 sys
  329. atime = os.path.getatime(path)
  330. else:
  331. date = os.stat(path)
  332. atime = time.ctime(date.st_atime)
  333. tracked_captchas.append([atime,str(" + "+name),str(" |-> "+path)])
  334. tracked_captchas=sorted(tracked_captchas,key=lambda x:x[0]) # sorted by accessed time
  335. ca = 0 # to control max number of results
  336. for t in tracked_captchas:
  337. ca = ca + 1
  338. print("-------------------------")
  339. for c in t:
  340. if ca < 26:
  341. print(c)
  342. else:
  343. break
  344. print("-------------------------")
  345. print("\n[Info] List end...\n")
  346. return
  347. captchas = self.get_attack_captchas()
  348. captchas = self.sanitize_captchas(captchas)
  349. captchas2track = captchas
  350. if self.isurl == 1 and (options.train or options.crack):
  351. if options.proxy:
  352. captchas = self.remote(captchas, options.proxy)
  353. else:
  354. captchas = self.remote(captchas, "")
  355. if options.verbose:
  356. print("\n[-] Connection data:")
  357. out = self.optionCurl.print_options()
  358. print('-'*45)
  359. #step 0: track
  360. if options.track:
  361. if options.s_num:
  362. num_tracks = int(options.s_num) # tracking number defined by user
  363. else:
  364. num_tracks = int(5) # default track connections
  365. if options.proxy:
  366. self.track(captchas2track, options.proxy, num_tracks)
  367. else:
  368. self.track(captchas2track, "", num_tracks)
  369. #step 1: train
  370. if options.train:
  371. try:
  372. if len(captchas) == 1:
  373. for captcha in captchas:
  374. if captcha is None:
  375. print("\n[Error] Applying OCR algorithm... Is that captcha supported?\n")
  376. if os.path.exists('core/images/previews'):
  377. shutil.rmtree('core/images/previews') # remove last OCR
  378. else:
  379. print("\n[Info] Target: "+ options.train+"\n")
  380. self.train(captcha)
  381. else:
  382. for captcha in captchas:
  383. if len(captchas) > 1 and captcha is None:
  384. pass
  385. else:
  386. print("\n[Info] Target: "+ options.train+"\n")
  387. self.train(captcha)
  388. except:
  389. print("[Error] Something wrong getting captcha. Aborting...\n")
  390. if options.xml:
  391. print("[Info] You don't need export to XML on this mode... File not generated!\n")
  392. #step 2: crack
  393. if options.crack:
  394. if len(captchas) == 1:
  395. for captcha in captchas:
  396. if captcha is None:
  397. print("\n[Error] Trying to bruteforce... Is that captcha supported?\n")
  398. if os.path.exists('core/images/previews'):
  399. shutil.rmtree('core/images/previews') # remove last OCR
  400. else:
  401. print("\n[Info] Target: "+ options.crack+"\n")
  402. self.crack(captcha)
  403. else:
  404. for captcha in captchas:
  405. if len(captchas) > 1 and captcha is None:
  406. pass
  407. else:
  408. print("\n[Info] Target: "+ options.crack+"\n")
  409. self.crack(captcha)
  410. if options.command:
  411. print("[Info] Executing tool connector... \n")
  412. if self.word_sug is not None:
  413. print("[Info] This is the word suggested by CIntruder: [ "+ str(self.word_sug)+ " ] \n")
  414. else:
  415. print("[Error] CIntruder hasn't any word to suggest... Handlering tool process aborted! ;(\n")
  416. sys.exit(2)
  417. if "CINT" in options.command: # check parameter CINT on command (*)
  418. # change cintruder suggested word for the users captchas input form parameter
  419. # and execute handlered tool with it.
  420. if self.word_sug is not None:
  421. cmd = options.command.replace("CINT", self.word_sug)
  422. subprocess.call(cmd, shell=True)
  423. else:
  424. cmd = options.command
  425. subprocess.call(cmd, shell=True)
  426. else:
  427. print("[Error] Captcha's parameter flag: 'CINT' is not present on: "+ str(options.command)+ "\n")
  428. #step 3: export
  429. if options.xml:
  430. self.export(captchas)
  431. def sanitize_captchas(self, captchas):
  432. """
  433. Sanitize correct input of source target(s)
  434. """
  435. options = self.options
  436. all_captchas = set()
  437. for captcha in captchas:
  438. # captcha from url
  439. if "http://" in captcha or "https://" in captcha:
  440. all_captchas.add(captcha)
  441. self.isurl = 1
  442. elif self.isurl == 0: # captcha from file
  443. (root, ext) = os.path.splitext(captcha)
  444. if ext != '.gif' and ext != '.jpg' and ext != '.jpeg' and ext != '.png': # by the moment
  445. captcha = None
  446. all_captchas.add(captcha)
  447. else:
  448. all_captchas.add(captcha)
  449. self.isurl = 0
  450. return all_captchas
  451. def create_web_interface(self):
  452. # launch webserver+gui
  453. from .webgui import ClientThread
  454. import webbrowser
  455. host = '0.0.0.0'
  456. port = 9999
  457. try:
  458. webbrowser.open('http://127.0.0.1:9999', new=1)
  459. tcpsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  460. tcpsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  461. tcpsock.bind((host,port))
  462. while True:
  463. tcpsock.listen(4)
  464. (clientsock, (ip, port)) = tcpsock.accept()
  465. newthread = ClientThread(ip, port, clientsock)
  466. newthread.start()
  467. except (KeyboardInterrupt, SystemExit):
  468. sys.exit()
  469. if __name__ == "__main__":
  470. app = cintruder()
  471. options = app.create_options()
  472. if options:
  473. app.set_options(options)
  474. app.run()