Browse Source

moved from https://github.com/epsylon/cintruder

psy 1 year ago
parent
commit
131e33eca3
40 changed files with 2790 additions and 2 deletions
  1. 45 2
      README.md
  2. 34 0
      cintruder
  3. 20 0
      core/__init__.py
  4. 158 0
      core/crack.py
  5. 145 0
      core/curl.py
  6. BIN
      core/images/cintruder.png
  7. 294 0
      core/js/web.js
  8. 465 0
      core/main.py
  9. 152 0
      core/ocr.py
  10. 86 0
      core/options.py
  11. 43 0
      core/update.py
  12. 577 0
      core/webgui.py
  13. 46 0
      core/xml_export.py
  14. BIN
      dictionary/3/249fcc8dbc1fe5777bfd18ae7a385021.gif
  15. BIN
      dictionary/6/4745f51c32b7cafeca28493e332f0088.gif
  16. BIN
      dictionary/p/98e592e3f7d4ba4f3455b933336f3c29.gif
  17. BIN
      dictionary/w/e14593d0867fb3dc291bd7b42ed062ff.gif
  18. 47 0
      docs/CHANGELOG
  19. 38 0
      docs/INSTALL
  20. 209 0
      docs/LICENSE
  21. 97 0
      docs/README
  22. BIN
      inputs/test1.gif
  23. BIN
      inputs/test2.png
  24. BIN
      inputs/test3.gif
  25. BIN
      inputs/test4.jpeg
  26. BIN
      inputs/test5.jpeg
  27. 7 0
      mods/easy/DESCRIPTION
  28. 20 0
      mods/easy/__init__.py
  29. BIN
      mods/easy/dictionary/3/249fcc8dbc1fe5777bfd18ae7a385021.gif
  30. BIN
      mods/easy/dictionary/6/4745f51c32b7cafeca28493e332f0088.gif
  31. BIN
      mods/easy/dictionary/p/98e592e3f7d4ba4f3455b933336f3c29.gif
  32. BIN
      mods/easy/dictionary/w/e14593d0867fb3dc291bd7b42ed062ff.gif
  33. 159 0
      mods/easy/easy_crack.py
  34. 148 0
      mods/easy/easy_ocr.py
  35. BIN
      mods/easy/easycaptcha.png
  36. BIN
      outputs/last-ocr_image-processed.gif
  37. BIN
      outputs/words/1e6293a28aa77269664af15fdad1a296.gif
  38. BIN
      outputs/words/2a47fdf6a6bc96c8e5074aa588c85166.gif
  39. BIN
      outputs/words/78727c0a7eae7c418bebd221ae2218a0.gif
  40. BIN
      outputs/words/ed34117c939ae18f1bdc497c4f8af4be.gif

+ 45 - 2
README.md

@@ -1,3 +1,46 @@
-# cintruder
+  ![CIntruder](https://cintruder.03c8.net/cintruder/cintruder-gui1.png "CIntruder GUI")
+
+=================================================================== 
+
+  Captcha Intruder is an automatic pentesting tool to bypass captchas.
+
+----------
+
+ CIntruder is released under the GPLv3. You can find the full license text
+in the [LICENSE](./docs/LICENSE) file.
+
+----------
+
+ + Web:  http://cintruder.03c8.net
+
+----------
+
+  ![CIntruder](https://cintruder.03c8.net/cintruder/cintruder-banner.png "CIntruder Banner")
+
+#### Installing:
+
+  CIntruder runs on many platforms.  It requires Python (2.x.y) and the following libraries:
+
+       python-pycurl - Python bindings to libcurl
+       python-libxml2 - Python bindings for the GNOME XML library
+       python-imaging - Python Imaging Library
+
+  On Debian-based systems (ex: Ubuntu), run: 
+
+       sudo apt-get install python-pycurl python-libxml2 python-imaging
+
+####  Source libs:
+
+       * Python: https://www.python.org/downloads/
+       * PyCurl: http://pycurl.sourceforge.net/
+       * PyLibxml2: https://pypi.python.org/pypi/libxml2-python/
+       * Python Imaging Library (PIL): http://pythonware.com/products/pil/
+
+----------
+
+####  Screenshots:
+
+  ![CIntruder](https://cintruder.03c8.net/cintruder/cintruder-gui2.png "CIntruder GUI")
+
+  ![CIntruder](https://cintruder.03c8.net/cintruder/cintruder-gui3.png "CIntruder GUI")
 
-Captcha Intruder (CIntruder) is an automatic pentesting tool to bypass captchas.

+ 34 - 0
cintruder

@@ -0,0 +1,34 @@
+#!/usr/bin/python
+# -*- coding: iso-8859-15 -*-
+"""
+This file is part of the cintruder project, http://cintruder.03c8.net
+
+Copyright (c) 2012/2016 psy <epsylon@riseup.net>
+
+cintruder is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation version 3 of the License.
+
+cintruder is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along
+with cintruder; if not, write to the Free Software Foundation, Inc., 51
+Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+"""
+from core.main import cintruder
+
+class NullOutput(object):
+    def write(self, text):
+        pass
+    def flush(self):
+        pass
+
+if __name__ == "__main__":
+    app = cintruder()
+    options = app.create_options()
+    if options:
+        app.set_options(options)
+        app.run()

+ 20 - 0
core/__init__.py

@@ -0,0 +1,20 @@
+#!/usr/bin/python
+# -*- coding: iso-8859-15 -*-
+"""
+This file is part of the cintruder project, http://cintruder.03c8.net
+
+Copyright (c) 2012/2016 psy <epsylon@riseup.net>
+
+cintruder is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation version 3 of the License.
+
+cintruder is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along
+with cintruder; if not, write to the Free Software Foundation, Inc., 51
+Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+"""

+ 158 - 0
core/crack.py

@@ -0,0 +1,158 @@
+#!/usr/bin/python
+# -*- coding: iso-8859-15 -*-
+"""
+This file is part of the cintruder project, http://cintruder.03c8.net
+
+Copyright (c) 2012/2016 psy <epsylon@riseup.net>
+
+cintruder is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation version 3 of the License.
+
+cintruder is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along
+with cintruder; if not, write to the Free Software Foundation, Inc., 51
+Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+"""
+from PIL import Image
+import hashlib, os, math, time
+import shutil
+
+class VectorCompare:
+    def magnitude(self, concordance):
+        total = 0
+        for word, count in concordance.iteritems():
+            # print concordance 
+            total += count ** 2
+        return math.sqrt(total)
+
+    def relation(self, concordance1, concordance2):
+        topvalue = 0
+        for word, count in concordance1.iteritems():
+            if concordance2.has_key(word):
+                topvalue += count * concordance2[word]
+        return topvalue / (self.magnitude(concordance1) * self.magnitude(concordance2))
+
+class CIntruderCrack(object):
+    """
+    Class to bruteforce captchas
+    """
+    def __init__(self, captcha=""):
+        """
+        Initialize main CIntruder
+        """
+        self.captcha = self.set_captcha(captcha)
+        start = time.time()
+        if not os.path.exists("core/images/previews/"):
+            os.mkdir("core/images/previews/")
+        else:
+            shutil.rmtree("core/images/previews/")
+            os.mkdir("core/images/previews/")
+
+    def buildvector(self, im):
+        d1 = {}
+        count = 0
+        for i in im.getdata():
+            d1[count] = i
+            count += 1
+        return d1
+
+    def set_captcha(self, captcha):
+        """
+        Set the captcha.
+        """
+        self.captcha = captcha
+        return captcha
+ 
+    def crack(self, options):
+        v = VectorCompare()
+        path, dirs, files = os.walk("dictionary/").next()
+        dictionary = dirs
+        imageset = []
+        last_letter = None
+        print "\n[Info] Loading dictionary...\n"
+        for letter in dictionary:
+            for img in os.listdir('dictionary/'+letter):
+                temp = []
+                temp.append(self.buildvector(Image.open("dictionary/%s/%s"%(letter, img))))
+                imageset.append({letter:temp})
+        try:
+            im = Image.open(self.captcha)
+            im.save("core/images/previews/last-preview.gif")
+            im2 = Image.new("P", im.size, 255)
+            im = im.convert("P")
+        except:
+            print "\nError during cracking!. Is that captcha supported?\n"
+            return
+        temp = {}
+        for x in range(im.size[1]):
+            for y in range(im.size[0]):
+                pix = im.getpixel((y, x))
+                temp[pix] = pix
+                if pix == 3: # pixel colour id 
+                    im2.putpixel((y, x), 0)
+        inletter = False
+        foundletter = False
+        start = 0
+        end = 0
+        letters = []
+        for y in range(im2.size[0]): # slice across
+            for x in range(im2.size[1]): # slice down
+                pix = im2.getpixel((y, x))
+                if pix != 255:
+                    inletter = True
+            if foundletter == False and inletter == True:
+                foundletter = True
+                start = y
+            if foundletter == True and inletter == False:
+                foundletter = False
+                end = y
+                letters.append((start, end))
+            inletter = False
+        count = 0
+        countid = 1
+        word_sug = None
+        end = time.time()
+        elapsed = end - start
+        words = {}
+        for letter in letters:
+            m = hashlib.md5()
+            im3 = im2.crop((letter[0], 0, letter[1], im2.size[1]))
+            guess = []
+            for image in imageset:
+                for x, y in image.iteritems():
+                    if len(y) != 0:
+                        guess.append(( v.relation(y[0], self.buildvector(im3)), x))
+            guess.sort(reverse=True)
+            word_per = guess[0][0] * 100
+            if str(word_per) == "100.0":
+                print "Image position   :", countid
+                print "Broken Percent   :", int(round(float(word_per))), "%", "[+CRACKED!]"
+                words[countid] = guess[0][1]
+            else:
+                print "Image position   :", countid
+                print "Broken Percent   :", "%.4f" % word_per, "%"
+                words[countid] = "_"
+            print "Word suggested   :", guess[0][1]
+            print "-------------------"
+            if word_sug == None:
+                word_sug = str(guess[0][1])
+            else:
+                word_sug = word_sug + str(guess[0][1])
+            count += 1
+            countid = countid + 1
+        print "\n========================================"
+        if options.verbose:
+            print "[Info] Elapsed OCR time :", elapsed
+            print "========================================"
+        if word_sug is None:
+            print "Suggested Solution: ", "[ No idea!. Try to add more images to your dictionary/]"
+        else:
+            print "Cracked Words: ", words.values()
+            print "Suggested Solution: ", "[", word_sug, "]"
+        print "========================================\n"
+        return word_sug

+ 145 - 0
core/curl.py

@@ -0,0 +1,145 @@
+#!/usr/bin/python
+# -*- coding: iso-8859-15 -*-
+"""
+This file is part of the cintruder project, http://cintruder.03c8.net
+
+Copyright (c) 2012/2016 psy <epsylon@riseup.net>
+
+cintruder is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation version 3 of the License.
+
+cintruder is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along
+with cintruder; if not, write to the Free Software Foundation, Inc., 51
+Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+"""
+import pycurl
+
+try:
+    from cStringIO import StringIO
+except ImportError:
+    from StringIO import StringIO
+
+class CIntruderCurl(object):
+    """
+    Class to control curl on behalf of the application.
+    """
+    agent = 'Googlebot/2.1 (+http://www.google.com/bot.html)'
+    referer = '127.0.0.1'
+    proxy = None
+    ignoreproxy = None
+
+    def __init__(self, captcha="", ignoreproxy="", proxy=""):
+        """
+        Class init
+        """
+        self.handle = pycurl.Curl()
+        self.verbosity = 0
+        self.url = self.set_url(captcha)
+        self.captcha = StringIO()
+        self.proxy = self.set_proxy(ignoreproxy, proxy)
+        self.set_option(pycurl.SSL_VERIFYHOST, 0)
+        self.set_option(pycurl.SSL_VERIFYPEER, 0)
+        #self.set_option(pycurl.SSLVERSION, pycurl.SSLVERSION_SSLv3)
+        self.set_option(pycurl.COOKIEFILE, '/dev/null')
+        self.set_option(pycurl.COOKIEJAR, '/dev/null')
+        self.set_option(pycurl.NETRC, 1)
+
+    def set_url(self, url):
+        """
+        Set the url.
+        """
+        self.url = url
+        self.set_option(pycurl.URL, self.url)
+        return url
+
+    def set_agent(self, agent):
+        """
+        Set the user agent.
+        """
+        self.agent = agent
+        self.set_option(pycurl.USERAGENT, self.agent)
+        return agent
+
+    def set_referer(self, referer):
+        """
+        Set the referer.
+        """
+        self.referer = referer
+        self.set_option(pycurl.REFERER, self.referer)
+        return referer
+
+    def set_proxy(self, ignoreproxy, proxy):
+        """
+        Set the proxy to use.
+        """
+        self.proxy = proxy
+        self.ignoreproxy = ignoreproxy
+        if self.ignoreproxy == 1:
+            self.set_option(pycurl.PROXY, "")
+        else:
+            self.set_option(pycurl.PROXY, self.proxy)
+        return proxy
+
+    def set_verbosity(self, level):
+        """
+        Set the verbosity level.
+        """
+        self.set_option(pycurl.VERBOSE, level)
+
+    def set_option(self, *args):
+        """
+        Set the given option.
+        """
+        apply(self.handle.setopt, args)
+
+    def request(self):
+        """
+        Perform a request and returns the payload.
+        """
+        if self.agent:
+            self.set_option(pycurl.USERAGENT, self.agent)
+        if self.referer:
+            self.set_option(pycurl.REFERER, self.referer) 
+        if self.proxy:
+            self.set_option(pycurl.PROXY, self.proxy)
+        if self.ignoreproxy:
+            self.set_option(pycurl.PROXY, "")
+        if self.url:
+            self.set_option(pycurl.URL, self.url)
+        self.set_option(pycurl.SSL_VERIFYHOST, 0)
+        self.set_option(pycurl.SSL_VERIFYPEER, 0)
+        self.handle.setopt(self.handle.WRITEFUNCTION, self.captcha.write)
+        try:
+            self.handle.perform()
+            print "[Info] Getting captcha...\n"                
+            return self.captcha
+        except pycurl.error, error:
+            errno, errstr = error
+            print '\n[Error] Connection error!:', errstr, "\n"
+            return "exit"
+ 
+    def close(self):
+        """
+        Close the curl handle.
+        """
+        self.handle.close()
+        self.captcha.close()
+
+    def print_options(self):
+        """
+        Print selected options.
+        """
+        print "\n[-]Verbose: active"
+        print "[-]HTTP User Agent:", self.agent
+        print "[-]HTTP Referer:", self.referer
+        if self.ignoreproxy:
+            print "[-]Proxy:", "No proxy!"
+        else:
+            print "[-]Proxy:", self.proxy
+        print "[-]URL:", self.url, "\n"

BIN
core/images/cintruder.png


+ 294 - 0
core/js/web.js

@@ -0,0 +1,294 @@
+/*
+This file is part of the cintruder project, http://cintruder.03c8.net
+
+Copyright (c) 2012/2016 psy <epsylon@riseup.net>
+
+cintruder is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation version 3 of the License.
+
+cintruder is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along
+with cintruder; if not, write to the Free Software Foundation, Inc., 51
+Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+window.onload = function() {
+    document.getElementById('ifTrack').style.display = 'block';
+    document.getElementById('ifTrain').style.display = 'none';
+    document.getElementById('ifCrack').style.display = 'none';
+    document.getElementById('ifLocal').style.display = 'block';
+    document.getElementById('ifUrl').style.display = 'none';
+    document.getElementById('ifCrackLocal').style.display = 'block';
+    document.getElementById('ifCrackUrl').style.display = 'none';
+    document.getElementById('ifMod_set').style.display = 'none';
+    document.getElementById('ifMod_set_crack').style.display = 'none';
+    document.getElementById('ifMod_colour').style.display = 'none';
+    document.getElementById('ifMod_xml').style.display = 'none';
+    document.getElementById('Results').style.display = 'none';
+    document.getElementById('Captcha-IN').style.display = 'none';
+    document.getElementById('OCR-out').style.display = 'none';
+}
+
+function SetDefault(){
+        document.getElementById('track_url').value = '';
+        document.getElementById('track_num').value = '5';
+        document.getElementById('tor').checked = false;
+        document.getElementById('verbose').checked = false;
+        document.getElementById('SourceFile').value = '';
+        document.getElementById('train_url').value = '';
+        document.getElementById('tor2').checked = false;
+        document.getElementById('verbose2').checked = false;
+        document.getElementById('set_module').checked = false;
+        document.getElementById('use_mod').value = '';
+        document.getElementById('set_colour_id').checked = false;
+        document.getElementById('set_id').value = '';
+        document.getElementById('SourceFile2').value = '';
+        document.getElementById('crack_url').value = '';
+        document.getElementById('tor3').checked = false;
+        document.getElementById('set_module_crack').checked = false;
+        document.getElementById('use_mod_crack').value = '';
+        document.getElementById('set_xml').checked = false;
+        document.getElementById('set_xml_file').value = '';
+        document.getElementById('verbose3').checked = false;
+        document.getElementById('Results').style.display = 'none';
+        document.getElementById('Captcha-IN').style.display = 'none';
+        document.getElementById('OCR-out').style.display = 'none';
+        document.getElementById('ifMod_set').style.display = 'none';
+        document.getElementById('ifMod_set_crack').style.display = 'none';
+        document.getElementById('ifMod_colour').style.display = 'none';
+        document.getElementById('ifMod_xml').style.display = 'none';
+}
+function OptionsCheck() {
+    if (document.getElementById('track').checked) {
+        document.getElementById('ifTrack').style.display = 'block';
+        document.getElementById('ifTrain').style.display = 'none';
+        document.getElementById('ifCrack').style.display = 'none';
+        SetDefault()
+    } 
+    else if(document.getElementById('train').checked) {
+        document.getElementById('ifTrain').style.display = 'block';
+        document.getElementById('ifTrack').style.display = 'none';
+        document.getElementById('ifCrack').style.display = 'none';
+        SetDefault()
+        TrainSourcesCheck()
+   }
+    else if(document.getElementById('crack').checked) {
+        document.getElementById('ifCrack').style.display = 'block';
+        document.getElementById('ifTrack').style.display = 'none';
+        document.getElementById('ifTrain').style.display = 'none';
+        SetDefault()
+        CrackingCheck()
+   }
+}
+function TrainSourcesCheck() {
+   if(document.getElementById('training_local').checked) {
+        document.getElementById('ifLocal').style.display = 'block';
+        document.getElementById('ifUrl').style.display = 'none';
+        SetDefault()
+        SetTrainModule()
+   }
+   else if(document.getElementById('training_url').checked) {
+        document.getElementById('ifUrl').style.display = 'block';
+        document.getElementById('ifLocal').style.display = 'none';
+        SetDefault()
+        SetTrainModule()
+   }
+}
+function CrackingCheck() {
+   if(document.getElementById('cracking_local').checked) {
+        document.getElementById('ifCrackLocal').style.display = 'block';
+        document.getElementById('ifCrackUrl').style.display = 'none';
+        SetDefault()
+        SetCrackModule()
+   }
+   else if(document.getElementById('cracking_url').checked) {
+        document.getElementById('ifCrackUrl').style.display = 'block';
+        document.getElementById('ifCrackLocal').style.display = 'none';
+        SetDefault()
+        SetCrackModule()
+   }
+}
+function SetTrainModule() {
+   if((document.getElementById('set_module').checked == true)) {
+        document.getElementById('ifMod_set').style.display = 'block';
+        document.getElementsByName('train_url')[0].placeholder='Train using a specific OCR exploiting module';
+   }
+   else{
+        document.getElementById("use_mod").value ='';
+        document.getElementById('ifMod_set').style.display = 'none';
+        document.getElementsByName('train_url')[0].placeholder='Apply common OCR techniques to a remote captcha';
+   }
+}
+function SetColourID() {
+   if((document.getElementById('set_colour_id').checked == true)) {
+        document.getElementById('ifMod_colour').style.display = 'block';
+   }
+   else{
+        document.getElementById("set_id").value ='';
+        document.getElementById('ifMod_colour').style.display = 'none';
+   }
+}
+function SetCrackModule() {
+   if((document.getElementById('set_module_crack').checked == true)) {
+        document.getElementById('ifMod_set_crack').style.display = 'block';
+        document.getElementsByName('crack_url')[0].placeholder='Brute force using a specific OCR exploiting module';
+   }
+   else if((document.getElementById('set_module_crack').checked == false)) {
+        document.getElementById('ifMod_set_crack').style.display = 'none';
+        document.getElementsByName('crack_url')[0].placeholder="Brute force using local dictionary (from: 'dictionary/')";
+   }
+}
+function SetXML() {
+   if((document.getElementById('set_xml').checked == true)) {
+        document.getElementById('ifMod_xml').style.display = 'block';
+   }
+   else{
+        document.getElementById("set_xml_file").value ='';
+        document.getElementById('ifMod_xml').style.display = 'none';
+   }
+}
+function loadRemoteOCR(train_url){
+       document.getElementById("target_captcha_img_path").src="images/previews/last-preview.gif#"+ new Date().getTime();
+       document.getElementById('Captcha-IN').style.display = 'block';
+       document.getElementById("directory-words").src = "directory-words";
+       document.getElementById("OCR-out").style.display = "block";
+}
+function loadRemoteOCRCrack(crack_url){
+       document.getElementById("target_captcha_img_path").src="images/previews/last-preview.gif#"+ new Date().getTime();
+       document.getElementById('Captcha-IN').style.display = 'block';
+}
+function loadOCRCrack(){
+       document.getElementById("target_captcha_img_path").src="images/previews/last-preview.gif#"+ new Date().getTime();
+       document.getElementById('Captcha-IN').style.display = 'block';
+}
+function loadOCR(){
+       document.getElementById("target_captcha_img_path").src="images/previews/last-preview.gif#"+ new Date().getTime();
+       document.getElementById('Captcha-IN').style.display = 'block';
+       document.getElementById("directory-words").src = "directory-words";
+       document.getElementById("OCR-out").style.display = "block";
+}
+function TrackCaptchas(){
+        if(document.getElementById("tor").checked) {
+        tor="on";
+        }else{
+        tor="off";
+        }
+        if(document.getElementById("verbose").checked){
+         verbose="on";
+        }else{
+         verbose="off";
+        }
+        tracking_source=document.getElementById("track_url").value
+        tracking_num=document.getElementById("track_num").value
+        if(tracking_source == "") {
+          window.alert("You need to enter a valid URL to be tracked!");
+          return
+         }else{
+          params="tracking_source="+escape(tracking_source)+"&tracking_num="+escape(tracking_num)+"&tor="+escape(tor)+"&verbose="+escape(verbose)
+         runCommandX("cmd_track",params)
+         document.getElementById("Results").style.display = "block";
+         }
+       }
+function TrainCaptchas(){
+        document.getElementById('Captcha-IN').style.display = 'none';
+        document.getElementById("OCR-out").style.display = "none";
+        if(document.getElementById("set_colour_id").checked) 
+        {
+         colourID=document.getElementById("set_id").value;
+        }else {
+         colourID="off";
+        }
+        if(document.getElementById("set_module").checked) 
+        {
+         module=document.getElementById("use_mod").value;
+        }else {
+         module="off";
+        }
+        if(document.getElementById("tor2").checked) 
+        {
+        tor="on";
+        }else {
+         tor="off";
+        }
+        if(document.getElementById("verbose2").checked) 
+        {
+         verbose="on";
+        }else {
+         verbose="off";
+        }
+        source_file=document.getElementById("SourceFile").value;
+        train_url=document.getElementById("train_url").value;
+        if((source_file == "") && (train_url == "")){
+          window.alert("You need to enter any input!");
+          return;
+         }else{
+        if(source_file==""){
+        source_file="off"
+        }
+        params="train_url="+escape(train_url)+"&source_file="+escape(source_file)+"&colourID="+escape(colourID)+"&module="+escape(module)+"&tor="+escape(tor)+"&verbose="+escape(verbose);
+         }
+         runCommandX("cmd_train",params);
+         if(source_file=="off"){
+         document.getElementById("Results").style.display = "block";
+         setTimeout(function() { loadRemoteOCR(train_url) }, 10000);
+         }else{
+         document.getElementById("Results").style.display = "block";
+         setTimeout("loadOCR()", 6000); // delay 6 on local
+         }
+}
+function CrackCaptchas(){
+        document.getElementById('Captcha-IN').style.display = 'none';
+        document.getElementById("OCR-out").style.display = "none";
+        if(document.getElementById("set_module_crack").checked) 
+        {
+         module=document.getElementById("use_mod_crack").value;
+        }else {
+         module="off";
+        }
+        if(document.getElementById("set_xml").checked)
+        {
+        xml=document.getElementById("set_xml_file").value;
+        }else {
+        xml="off";
+        }
+        if(document.getElementById("tor3").checked) 
+        {
+        tor="on";
+        }else {
+         tor="off";
+        }
+        if(document.getElementById("verbose3").checked) 
+        {
+         verbose="on";
+        }else {
+         verbose="off";
+        }
+        source_file=document.getElementById("SourceFile2").value;
+        crack_url=document.getElementById("crack_url").value;
+        if((source_file == "") && (crack_url == "")){
+          window.alert("You need to enter any input!");
+          return;
+         }else{
+        if(source_file==""){
+        source_file="off"
+        }
+        params="crack_url="+escape(crack_url)+"&source_file="+escape(source_file)+"&module="+escape(module)+"&tor="+escape(tor)+"&verbose="+escape(verbose)+"&xml="+escape(xml);
+         }
+         runCommandX("cmd_crack",params);
+         if(source_file=="off"){
+         document.getElementById("Results").style.display = "block";
+         setTimeout(function() { loadRemoteOCRCrack(crack_url) }, 10000);
+         }else{
+         document.getElementById("Results").style.display = "block";
+         setTimeout("loadOCRCrack()", 6000); // delay 6 on local
+         }
+}
+function showResults() {
+         document.getElementById("Results").style.display = "block";
+         document.getElementById('Captcha-IN').style.display = 'none';
+         document.getElementById('OCR-out').style.display = 'none';
+}

+ 465 - 0
core/main.py

@@ -0,0 +1,465 @@
+#!/usr/bin/python
+# -*- coding: iso-8859-15 -*-
+"""
+This file is part of the cintruder project, http://cintruder.03c8.net
+
+Copyright (c) 2012/2016 psy <epsylon@riseup.net>
+
+cintruder is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation version 3 of the License.
+
+cintruder is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along
+with cintruder; if not, write to the Free Software Foundation, Inc., 51
+Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+"""
+import os, traceback, hashlib, sys, time, socket, urlparse
+import platform, subprocess, re, webbrowser
+from core.options import CIntruderOptions
+from core.crack import CIntruderCrack
+from core.ocr import CIntruderOCR
+from core.curl import CIntruderCurl
+from core.xml_export import CIntruderXML
+from core.update import Updater
+from urlparse import urlparse
+
+# set to emit debug messages about errors (0 = off).
+DEBUG = 0
+
+class cintruder():
+    """
+    CIntruder application class
+    """
+    def __init__(self):
+        self.captcha = ""
+        self.optionOCR = []
+        self.optionCrack = []
+        self.optionParser = None
+        self.optionCurl = None
+        self.options = None
+        self.word_sug = None
+        self.train == 0
+        self.crack == 0
+        self.ignoreproxy = 1  
+        self.isurl = 0
+        self.os_sys = platform.system()
+        self._webbrowser = webbrowser
+
+    def set_options(self, options):
+        """
+        Set cintruder options
+        """
+        self.options = options
+
+    def create_options(self, args=None):
+        """
+        Create the program options for OptionParser.
+        """
+        self.optionParser = CIntruderOptions()
+        self.options = self.optionParser.get_options(args)
+        if not self.options:
+            return False
+        return self.options
+
+    def set_webbrowser(self, browser):
+        self._webbrowser = browser
+
+    def banner(self):
+        print '='*75
+        print ""
+        print "        o8%8888,    "
+        print "       o88%8888888. "
+        print "      8'-    -:8888b   "
+        print "     8'         8888  "
+        print "    d8.-=. ,==-.:888b  "
+        print "    >8 `~` :`~' d8888   "
+        print "    88         ,88888   "
+        print "    88b. `-~  ':88888  "
+        print "    888b \033[1;31m~==~\033[1;m .:88888 "
+        print "    88888o--:':::8888      "
+        print "    `88888| :::' 8888b  "
+        print "    8888^^'       8888b  "
+        print "   d888           ,%888b.   "
+        print "  d88%            %%%8--'-.  "
+        print " /88:.__ ,       _%-' ---  -  "
+        print "     '''::===..-'   =  --.  `\n"
+        print self.optionParser.description, "\n"
+        print '='*75
+
+    @classmethod
+    def try_running(cls, func, error, args=None):
+        """
+        Try running a function and print some error if it fails and exists with
+        a fatal error.
+        """
+        args = args or []
+        try:
+            return func(*args)
+        except Exception:
+            print(error, "error")
+            if DEBUG:
+                traceback.print_exc()
+
+    def get_attack_captchas(self):
+        """
+        Get captchas to brute force
+        """
+        captchas = []
+        options = self.options
+        p = self.optionParser
+
+        if options.train:
+            print('='*75)
+            print(str(p.version))
+            print('='*75)
+            print("Starting to 'train'...")
+            print('='*75)
+            captchas = [options.train]
+        if options.crack:
+            print('='*75)
+            print(str(p.version))
+            print('='*75)
+            print("Starting to 'crack'")
+            print('='*75)
+            captchas = [options.crack]
+        if options.track:
+            print('='*75)
+            print(str(p.version))
+            print('='*75)
+            print("Tracking captchas from url...")
+            print('='*75+"\n")
+            captchas = [options.track]       
+        return captchas
+
+    def train(self, captchas):
+        """
+        Learn mode:
+            + Add words to the brute forcing dictionary
+        """
+        self.train_captcha(captchas)
+
+    def train_captcha(self, captcha):
+        """
+        Learn mode:
+            1- Apply OCR to captcha/image and split into unities
+            2- Human-Recognize that unities like alphanumeric words (gui supported)
+            3- Move that words into dictionary (gui supported)
+        """
+        options = self.options
+        # step 1: applying OCR techniques
+        if options.name: # with a specific OCR module
+            print "[Info] Using module: [", options.name, "]"
+            try:
+                sys.path.append('mods/%s/'%(options.name))
+                exec("from " + options.name + "_ocr" + " import CIntruderOCR") # import module
+            except Exception:
+                print "\n[Error] '"+ options.name+ "' module not found!\n"
+                return #sys.exit(2)
+            if options.setids: # with a specific colour ID
+                setids = int(options.setids)
+                if setids >= 0 and setids <= 255:
+                    self.optionOCR = CIntruderOCR(captcha, options)
+                else:
+                    print "\n[Error] You must enter a valid RGB colour ID number (between 0 and 255)\n"
+                    return #sys.exit(2)
+            else:
+                self.optionOCR = CIntruderOCR(captcha, options)
+        else: # using general OCR algorithm
+            if options.setids: # with a specific colour ID
+                setids = int(options.setids)
+                if setids >= 0 and setids <= 255:
+                    self.optionOCR = CIntruderOCR(captcha, options)
+                else:
+                    print "\n[Error] You must enter a valid RGB colour ID number (between 0 and 255)\n"
+                    return #sys.exit(2)
+            else:
+                self.optionOCR = CIntruderOCR(captcha, options)
+
+    def crack(self, captchas):
+        """
+        Crack mode:
+            + Brute force target's captcha against a dictionary
+        """
+        self.crack_captcha(captchas)
+
+    def crack_captcha(self, captcha):
+        """
+        Crack mode: bruteforcing...
+        """
+        options = self.options
+        if options.name:
+            print "Loading module:", options.name
+            print "==============="
+            try:
+                sys.path.append('mods/%s/'%(options.name))
+                exec("from " + options.name + "_crack" + " import CIntruderCrack")
+            except Exception:
+                print "\n[Error] '"+ options.name+ "' module not found!\n"
+                return #sys.exit(2)
+            self.optionCrack = CIntruderCrack(captcha)
+            w = self.optionCrack.crack(options)
+            self.word_sug = w
+        else:
+            self.optionCrack = CIntruderCrack(captcha)
+            w = self.optionCrack.crack(options)
+            self.word_sug = w
+
+    def remote(self, captchas, proxy):
+        """
+        Get remote captchas 
+        """
+        l = []
+        if not os.path.exists("inputs/"):
+            os.mkdir("inputs/")
+        for captcha in captchas:
+            c = self.remote_captcha(captcha, proxy)
+            l.append(c)
+        return l
+
+    def remote_captcha(self, captcha, proxy):
+        """
+        Get remote captcha
+        """
+        if proxy:
+            self.ignoreproxy=0
+        self.optionCurl = CIntruderCurl(captcha, self.ignoreproxy, proxy)
+        buf = self.optionCurl.request()
+        if buf != "exit":
+            m = hashlib.md5()
+            m.update(captcha)
+            c = "%s.gif"%(m.hexdigest())
+            h = "inputs/" + c
+            f = open(h, 'wb')
+            f.write(buf.getvalue())
+            f.close
+            buf.close
+            return h
+        else:
+            return #sys.exit(2)
+
+    def export(self, captchas):
+        """
+        Export results
+        """
+        if self.options.xml and not (self.options.train):
+            self.optionXML = CIntruderXML(captchas)
+            if self.word_sug == None:
+                print "[Info] XML NOT created!. There are not words to suggest..."
+            else:
+                self.optionXML.print_xml_results(captchas, self.options.xml, self.word_sug)
+                print "[Info] XML created:", self.options.xml, "\n"
+
+    def track(self, captchas, proxy, num_tracks):
+        """
+        Download captchas from url
+        """
+        for captcha in captchas:
+            self.track_captcha(captcha, proxy, num_tracks)
+
+    def track_captcha(self, captcha, proxy, num_tracks):
+        """
+        This technique is useful to create a dictionary of 'session based' captchas
+        """
+        options = self.options
+        urlp = urlparse(captcha)
+        self.domain = urlp.hostname
+        if not os.path.exists("inputs/%s"%(self.domain)):
+            os.mkdir("inputs/%s"%(self.domain))
+        if proxy:
+            self.ignoreproxy = 0
+        buf = ""
+        i=0
+        while i < int(num_tracks) and buf != "exit":
+            self.optionCurl = CIntruderCurl(captcha, self.ignoreproxy, proxy)
+            buf = self.optionCurl.request()
+            if options.verbose:
+                print "\n[-]Connection data:"
+                out = self.optionCurl.print_options()
+                print '-'*45
+            if buf != "exit":
+                m = hashlib.md5()
+                m.update("%s%s"%(time.time(), captcha))
+                h = "inputs/%s/%s.gif"%(self.domain, m.hexdigest())
+                f = open(h, 'wb')
+                f.write(buf.getvalue())
+                f.close
+                buf.close
+                print "[Info] Saved:", h
+                print "------------"
+            i=i+1
+        if buf != "exit":
+            print "\n================="
+            print "Tracking Results:"
+            print "================="
+            print "\nNumber of tracked captchas: [", num_tracks, "] \n"
+
+    def run(self, opts=None):
+        """ 
+        Run cintruder
+        """ 
+        if opts:
+            options = self.create_options(opts)
+            self.set_options(options)
+        options = self.options
+        #step -1: run GUI/Web interface
+        if options.web:
+            self.create_web_interface()
+            return
+        #step -1: check/update for latest stable version
+        if options.update:
+            self.banner()
+            try:
+                print("\nTrying to update automatically to the latest stable version\n")
+                Updater()
+            except:
+                print("\nSomething was wrong!. To have working this feature, you should clone CIntruder with:\n")
+                print("$ git clone https://github.com/epsylon/cintruder\n")
+        #step 0: list output results and get captcha targets
+        if options.listmods:
+            print "====================================="
+            print "Listing specific OCR exploit modules:"
+            print "=====================================\n"
+            top = 'mods/'
+            for root, dirs, files in os.walk(top, topdown=False):
+                for name in files:
+                    if name == 'DESCRIPTION':
+                        if self.os_sys == "Windows": #check for win32 sys
+                            subprocess.call("type %s/%s"%(root, name), shell=True)
+                        else:
+                            subprocess.call("cat %s/%s"%(root, name), shell=True)
+
+            print "\n[Info] List end...\n"
+            return #sys.exit(2)    
+        captchas = self.try_running(self.get_attack_captchas, "\nInternal error getting -captchas-. look at the end of this Traceback.")
+        captchas = self.sanitize_captchas(captchas)
+        captchas2track = captchas
+        if self.isurl == 1 and (options.train or options.crack):
+            if options.proxy:
+                captchas = self.remote(captchas, options.proxy)
+            else:
+                captchas = self.remote(captchas, "")
+            if options.verbose:
+                print "[-] Connection data:"
+                out = self.optionCurl.print_options()
+                print '-'*45
+        #step 0: track
+        if options.track:
+            if options.s_num:
+                num_tracks = int(options.s_num) # tracking number defined by user
+            else:
+                num_tracks = int(5) # default track connections
+            if options.proxy:
+                self.try_running(self.track, "\nInternal problems tracking: ", (captchas2track, options.proxy, num_tracks))
+            else:
+                self.try_running(self.track, "\nInternal problems tracking: ", (captchas2track, "", num_tracks))
+        #step 1: train
+        if options.train:
+            try:
+                if len(captchas) == 1:
+                    for captcha in captchas:
+                        if captcha is None:
+                            print "\n[Error] Applying OCR algorithm... Is that captcha supported?\n"
+                        else:
+                            print "Target:", options.train
+                            print "=======\n"
+                            self.try_running(self.train, "\nInternal problems training: ", (captchas))   
+                else:
+                    for captcha in captchas:
+                        if len(captchas) > 1 and captcha is None:
+                            pass
+                        else:
+                            print "Target: ", options.train
+                            self.try_running(self.train, "\nInternal problems training: ", (captchas))
+            except:
+                print "\n[Error] Something wrong getting captcha. Aborting...\n"
+            if options.xml:
+                print "[Info] You don't need export to XML on this mode... File not generated!\n"
+        #step 2: crack
+        if options.crack:
+            if len(captchas) == 1:
+                for captcha in captchas:
+                    if captcha is None:
+                        print "\n[Error] Trying to bruteforce... Is that captcha supported?\n"
+                    else:
+                        print "Target: ", options.crack
+                        print "======="
+                        self.try_running(self.crack, "\nInternal problems cracking: ", (captchas))
+            else:
+                for captcha in captchas:
+                    if len(captchas) > 1 and captcha is None:
+                        pass
+                    else:
+                        print "Target: ", options.crack
+                        print "======="
+                        self.try_running(self.crack, "\nInternal problems cracking: ", (captchas))
+            if options.command:
+                print "[Info] Executing tool connector... \n"
+                if self.word_sug is not None:
+                    print "[Info] This is the word suggested by CIntruder: [", self.word_sug, "] \n"
+                else:
+                    print "[Error] CIntruder hasn't any word to suggest... Handlering tool process aborted! ;(\n"
+                    sys.exit(2)
+                if "CINT" in options.command: # check parameter CINT on command (*)
+                    # change cintruder suggested word for the users captchas input form parameter
+                    # and execute handlered tool with it.
+                    if self.word_sug is not None:      
+                        cmd = options.command.replace("CINT", self.word_sug)
+                        subprocess.call(cmd, shell=True)
+                    else:
+                        cmd = options.command
+                        subprocess.call(cmd, shell=True)
+                else:
+                    print "[Error] Captcha's parameter flag: 'CINT' is not present on:", options.command, "\n"
+        #step 3: export
+        if options.xml:
+            self.try_running(self.export, "\nInternal problems exporting: ", (captchas))
+
+    def sanitize_captchas(self, captchas):
+        """
+        Sanitize correct input of source target(s)
+        """
+        options = self.options
+        all_captchas = set()
+        for captcha in captchas:
+            # captcha from url
+            if "http://" in captcha or "https://" in captcha:
+                all_captchas.add(captcha)
+                self.isurl = 1
+            elif self.isurl == 0: # captcha from file
+                (root, ext) = os.path.splitext(captcha)       
+                if ext != '.gif' and ext != '.jpg' and ext != '.jpeg' and ext != '.png': #by the moment                    
+                    captcha = None
+                    all_captchas.add(captcha)
+                else:
+                    all_captchas.add(captcha)
+                self.isurl = 0
+        return all_captchas
+
+    def create_web_interface(self):
+        from webgui import ClientThread
+        host = '0.0.0.0'
+        port = 9999
+        try: 
+            webbrowser.open('http://127.0.0.1:9999', new=1)
+            tcpsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+	    tcpsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+	    tcpsock.bind((host,port))
+	    while True:
+	        tcpsock.listen(4)
+	        (clientsock, (ip, port)) = tcpsock.accept()
+	        newthread = ClientThread(ip, port, clientsock)
+                newthread.start()
+        except (KeyboardInterrupt, SystemExit):
+            sys.exit()
+
+if __name__ == "__main__":
+    app = cintruder()
+    options = app.create_options()
+    if options:
+        app.set_options(options)
+        app.run()

+ 152 - 0
core/ocr.py

@@ -0,0 +1,152 @@
+#!/usr/bin/python
+# -*- coding: iso-8859-15 -*-
+"""
+This file is part of the cintruder project, http://cintruder.03c8.net
+
+Copyright (c) 2012/2016 psy <epsylon@riseup.net>
+
+cintruder is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation version 3 of the License.
+
+cintruder is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along
+with cintruder; if not, write to the Free Software Foundation, Inc., 51
+Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+"""
+from PIL import Image
+from operator import itemgetter
+import os, hashlib, time, sys, subprocess, platform
+import shutil
+
+class CIntruderOCR(object):
+    """
+    Class to apply OCR techniques into captchas (general algorithm)
+    """
+    def __init__(self, captcha, options):
+        # generate words structure (+ previews for gui)
+        if not os.path.exists("outputs/words/"):
+            os.mkdir("outputs/words/")
+        else:
+            shutil.rmtree("outputs/words/") 
+            os.mkdir("outputs/words/")
+        if not os.path.exists("core/images/previews/"):
+            os.mkdir("core/images/previews/")
+        else:
+            shutil.rmtree("core/images/previews/")
+            os.mkdir("core/images/previews/")
+        if not os.path.exists("core/images/previews/ocr/"):
+            os.mkdir("core/images/previews/ocr/")
+        else:
+            shutil.rmtree("core/images/previews/ocr/")
+            os.mkdir("core/images/previews/ocr/")
+        # initialize main CIntruder
+        try:
+            im = Image.open(captcha)
+            im.save("core/images/previews/last-preview.gif")
+            im2 = Image.new("P", im.size, 255)
+            im = im.convert("P")
+        except:
+            print "Error during OCR process... Is that captcha supported?\n"
+            return
+        colourid = []
+        try: # extract colour histogram
+            hist = im.histogram()
+        except:
+            print "\n[Error] Something wrong extracting histogram. Aborting...\n"
+            return
+        values = {}
+        for i in range(256):
+            values[i] = hist[i]
+        if options.verbose:
+            print "\n[Info] Extracting advanced OCR info..."
+            print "\n=============================" 
+            print "Image Histogram (order by >):"
+            print "============================="
+        count = 0
+        for j, k in sorted(values.items(), key=itemgetter(1), reverse=True)[:10]:
+            colourid.append(j)  
+            if options.verbose:
+                count = count + 1
+                if count == 1: # first is background
+                    print "Colour ID: [", j, "] -> Total pixels:", k, "[Background]"
+                else:
+                    print "Colour ID: [", j, "] -> Total pixels:", k
+        if options.verbose:
+            print ""
+        temp = {}
+        for x in range(im.size[1]):
+            for y in range(im.size[0]):
+                pix = im.getpixel((y, x))
+                temp[pix] = pix
+                if options.setids:
+                    colourid = int(options.setids)
+                    if pix == colourid:
+                        im2.putpixel((y, x), 0)
+                else:
+                    if pix == colourid[1]: #id numbers of colours to get (*)
+                        im2.putpixel((y, x), 0)
+        im2.save("outputs/last-ocr_image-processed.gif")
+        inletter = False
+        foundletter = False
+        start = 0
+        end = 0
+        letters = []
+        for y in range(im2.size[0]): 
+            for x in range(im2.size[1]): 
+                pix = im2.getpixel((y, x))
+                if pix != 255:
+                    inletter = True
+            if foundletter == False and inletter == True:
+                foundletter = True
+                start = y
+            if foundletter == True and inletter == False:
+                foundletter = False
+                end = y
+                letters.append((start, end))
+            inletter = False
+        count = 0
+        for letter in letters:
+            m = hashlib.md5()
+            im3 = im2.crop(( letter[0], 0, letter[1], im2.size[1] ))
+            m.update("%s%s"%(time.time(), count))
+            im3.save("outputs/words/%s.gif"%(m.hexdigest()))
+            im3.save("core/images/previews/ocr/%s.gif"%(m.hexdigest()))
+            count += 1
+        print "[Info] Processing captcha/image with OCR algorithms. Please wait...\n"
+        print "================="
+        print "Training Results:"
+        print "================="
+        print "[Info] Number of 'symbols' found: [", count, "]"
+        if count == 0:
+            print "\nOuch!. Looks like this captcha is resisting to our OCR methods... by the moment ;-)\n"
+            print "Try this...\n" 
+            print "    1) Check colour's ID values and quantity of pixels of each by using verbose" 
+            print "    2) Set different ID values to your OCR configuration and try it again"
+            print "    3) Try to apply some image filters (ex: B/W) manually with an editor (ex: GIMP) to your target"
+            print "    4) Maybe there is a module that works correctly for this captcha...\n"
+            print "------------\n"
+        else:
+            path, dirs, files = os.walk("outputs/words/").next()
+            file_count = str(len(files))
+            print "[Info] Generated [ "+ file_count+ " ] OCR images here:", "outputs/words/\n"
+            if options.verbose:
+                # checking for platform to list new words added to dictionary
+                os_sys = platform.system()
+                if os_sys == "Windows":
+                    subprocess.call("dir outputs/words/", shell=True)
+                else:
+                    subprocess.call("ls outputs/words/", shell=True)
+                print ""
+            print "Now move each (human-recognized) OCR image to the correct folder on: dictionary/\n"
+
+if __name__ == "__main__":
+    if sys.argv[1:]:
+        ocr = CIntruderOCR(sys.argv[1:])
+        print ("Data correctly extracted!")
+    else:
+        print ("You must set a captcha for learn. Ex: inputs/test1.gif")

+ 86 - 0
core/options.py

@@ -0,0 +1,86 @@
+#!/usr/bin/python
+# -*- coding: iso-8859-15 -*-
+"""
+This file is part of the cintruder project, http://cintruder.03c8.net
+
+Copyright (c) 2012/2016 psy <epsylon@riseup.net>
+
+cintruder is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation version 3 of the License.
+
+cintruder is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along
+with cintruder; if not, write to the Free Software Foundation, Inc., 51
+Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+"""
+import optparse
+
+class CIntruderOptions(optparse.OptionParser):
+    def __init__(self, *args):
+        optparse.OptionParser.__init__(self, 
+                           description='Captcha Intruder - OCR Bruteforcing Toolkit - by psy',
+                           prog='cintruder.py',
+			   version='\nCIntruder v0.3 - 2016 - (GPLv3.0) -> by psy\n',
+                           usage= '\n\ncintruder [OPTIONS]')
+        self.add_option("-v", "--verbose", action="store_true", dest="verbose", help="active verbose mode output results")
+        self.add_option("--proxy", action="store", dest="proxy", help="use proxy server (tor: http://localhost:8118)")
+        self.add_option("--gui", action="store_true", dest="web", help="run GUI (CIntruder Web Interface)")
+        self.add_option("--update", action="store_true", dest="update", help="check for latest stable version")
+        group1 = optparse.OptionGroup(self, "->Tracking")
+        group1.add_option("--track", action="store", dest="track", help="download captchas from url (to: 'inputs/')")
+        group1.add_option("--track-num", action="store", dest="s_num", help="set number of captchas to download (default: 5)")
+        self.add_option_group(group1)
+        group2 = optparse.OptionGroup(self, "->Training")
+        group2.add_option("--train", action="store", dest="train", help="train using common OCR techniques")
+        group2.add_option("--set-id", action="store", dest="setids", help="set colour's ID manually (use -v for details)")
+        self.add_option_group(group2)
+        group3 = optparse.OptionGroup(self, "->Cracking")
+        group3.add_option("--crack", action="store", dest="crack", help="brute force using local dictionary")
+        self.add_option_group(group3)
+        group4 = optparse.OptionGroup(self, "->Modules (training/cracking)")
+        group4.add_option("--list", action="store_true", dest="listmods", help="list available modules (from: 'mods/')")
+        group4.add_option("--mod", action="store", dest="name", help="set a specific OCR exploiting module")
+        self.add_option_group(group4)
+        group5 = optparse.OptionGroup(self, "->Post-Exploitation (cracking)")
+        group5.add_option("--xml", action="store", dest="xml", help="export result to xml format")
+        group5.add_option("--tool", action="store", dest="command", help="replace suggested word on commands of another tool. use 'CINT' marker like flag (ex: 'txtCaptcha=CINT')")
+        self.add_option_group(group5)
+
+    def get_options(self, user_args=None):
+        (options, args) = self.parse_args(user_args)
+        options.args = args
+        if (not options.train and not options.crack and not options.track and not options.listmods and not options.web and not options.update):
+            print '='*75
+            print ""
+            print "        o8%8888,    "
+            print "       o88%8888888. " 
+            print "      8'-    -:8888b   "
+            print "     8'         8888  "
+            print "    d8.-=. ,==-.:888b  "
+            print "    >8 `~` :`~' d8888   "
+            print "    88         ,88888   "
+            print "    88b. `-~  ':88888  "
+            print "    888b \033[1;31m~==~\033[1;m .:88888 "
+            print "    88888o--:':::8888      "
+            print "    `88888| :::' 8888b  "
+            print "    8888^^'       8888b  "
+            print "   d888           ,%888b.   "
+            print "  d88%            %%%8--'-.  "
+            print " /88:.__ ,       _%-' ---  -  "
+            print "     '''::===..-'   =  --.  `\n"
+            print  self.description, "\n"
+            print '='*75, "\n"
+            print " * Project site: http://cintruder.03c8.net", "\n"
+            print " * IRC: irc.freenode.net -> #cintruder", "\n"
+            print " * Mailing list: cintruder-users@lists.sf.net", "\n"
+            print '='*75
+            print "\n -> For HELP use: -h or --help"
+            print "\n -> For WEB interface use: --gui\n"
+            print '='*55, "\n"
+            return False
+        return options

+ 43 - 0
core/update.py

@@ -0,0 +1,43 @@
+#!/usr/bin/python
+# -*- coding: iso-8859-15 -*-
+"""
+This file is part of the cintruder project, http://cintruder.03c8.net
+
+Copyright (c) 2012/2016/2018 psy <epsylon@riseup.net>
+
+cintruder is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation version 3 of the License.
+
+cintruder is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along
+with cintruder; if not, write to the Free Software Foundation, Inc., 51
+Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+"""
+import os
+from subprocess import PIPE
+from subprocess import Popen as execute
+        
+class Updater(object):
+    """     
+    Update CIntruder automatically from a .git repository
+    """     
+    def __init__(self):
+        GIT_REPOSITORY = "https://github.com/epsylon/cintruder"
+        rootDir = os.path.abspath(os.path.join(os.path.dirname( __file__ ), '..', ''))
+        if not os.path.exists(os.path.join(rootDir, ".git")):
+            print "Not any .git repository found!\n"
+            print "="*30
+            print "\nTo have working this feature, you should clone CIntruder with:\n"
+            print "$ git clone %s" % GIT_REPOSITORY
+        else:
+            checkout = execute("git checkout . && git pull", shell=True, stdout=PIPE, stderr=PIPE).communicate()[0]
+            print checkout
+            if not "Already up-to-date" in checkout:
+                print "Congratulations!! CIntruder has been updated... ;-)\n"
+            else:
+                print "Your CIntruder doesn't need to be updated... ;-)\n"

File diff suppressed because it is too large
+ 577 - 0
core/webgui.py


+ 46 - 0
core/xml_export.py

@@ -0,0 +1,46 @@
+#!/usr/bin/python
+# -*- coding: iso-8859-15 -*-
+"""
+This file is part of the cintruder project, http://cintruder.03c8.net
+
+Copyright (c) 2012/2016 psy <epsylon@riseup.net>
+
+cintruder is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation version 3 of the License.
+
+cintruder is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along
+with cintruder; if not, write to the Free Software Foundation, Inc., 51
+Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+"""
+import xml.etree.ElementTree as xml
+import datetime, os
+
+class CIntruderXML(object):
+    """
+    Print results from an attack in an XML fashion
+    """
+    def __init__(self, cintruder):
+        # initialize main CIntruder
+        self.instance = cintruder
+
+    def print_xml_results(self, captchas, filename, word_sug):
+        dirname = os.path.dirname(filename)
+        if dirname and not os.path.exists(dirname):
+            os.mkdir(dirname)
+        root = xml.Element("report")
+        hdr = xml.SubElement(root, "header")
+        title = xml.SubElement(hdr, "title")
+        title.text = "Captcha Intruder [http://cintruder.03c8.net] Report: " + str(datetime.datetime.now())
+        target = xml.SubElement(root, "target")
+        captcha = xml.SubElement(target, "captcha")
+        words = xml.SubElement(captcha, "word")
+        captcha.text = str(captchas)
+        words.text = str(word_sug)
+        tree = xml.ElementTree(root)
+        tree.write(filename)

BIN
dictionary/3/249fcc8dbc1fe5777bfd18ae7a385021.gif


BIN
dictionary/6/4745f51c32b7cafeca28493e332f0088.gif


BIN
dictionary/p/98e592e3f7d4ba4f3455b933336f3c29.gif


BIN
dictionary/w/e14593d0867fb3dc291bd7b42ed062ff.gif


+ 47 - 0
docs/CHANGELOG

@@ -0,0 +1,47 @@
+================================================================
+Changelog: CIntruder v0.3 (cintruder.03c8.net)
+==============================
+
+===================
+December 23, 2016:
+===================
+
+- Code cleaning
+- Removed deprecated features
+- Added a Web User Interface (GUI)
+- Added --update option
+- Updated docs
+
+=================
+April 27, 2012:
+=================
+
+- Code cleaning
+- Rebuiled input parameter options
+- Created OCR modularity (core/mods)
+- Added --list modules option
+- Added --mod selector option
+- Builded module example (easy captcha)
+- Added handlering with other tools
+- Added connectivity with Ostatus
+- Created distributed online dictionary
+- Added launcher to view CIntruderNet
+- Updated docs
+
+=================
+April 1, 2012:
+=================
+
+- Created OCR techniques
+- Created Brute forcing techniques
+- Builded main core 
+- Builded options parser
+- Created Curl handlers
+- Created 'track' option
+- Created 'train' option
+- Created 'crack' option
+- Added proxy option
+- Added XML exporting option
+- Added verbose outputs results
+- Added colour's id calibration
+- Added noise advanced option

+ 38 - 0
docs/INSTALL

@@ -0,0 +1,38 @@
+============================================
+CIntruder - [cintruder.03c8.net] - 2012/2016
+============================================
+
+Captcha Intruder is an automatic pentesting tool to bypass captchas.
+
+===================
+How-to INSTALL:
+===================
+
+CIntruder runs on many platforms.  It requires Python and the following libraries:
+
+    - python-pycurl  - Python bindings to libcurl
+    - python-libxml2 - Python bindings for the GNOME XML library
+    - python-imaging - Python Imaging Library
+
+=========
+
+On Debian-based systems (ex: Ubuntu), run: 
+
+    sudo apt-get install python-pycurl python-libxml2 python-imaging
+
+=========
+
+Please report any problems you encounter using/installing CIntruder to the cintruder-users mailing-list:
+
+    - mailto:cintruder-users@lists.sourceforge.net
+
+Or write directly to:
+
+    - epsylon@riseup.net [GPG ID: 0xB8AC3776 -> https://03c8.net/gpg.php]
+
+Website: 
+
+    - http://cintruder.03c8.net/
+
+=========
+

File diff suppressed because it is too large
+ 209 - 0
docs/LICENSE


+ 97 - 0
docs/README

@@ -0,0 +1,97 @@
+================================================================
+Introduction:
+==============================
+
+Captcha Intruder is an automatic pentesting tool to bypass captchas.
+
+================================================================
+Options and features:
+==============================
+ 
+cintruder [OPTIONS]
+
+Options:
+  --version            show program's version number and exit
+  -h, --help           show this help message and exit
+  -v, --verbose        active verbose mode output results
+  --proxy=PROXY        use proxy server (tor: http://localhost:8118)
+  --gui                run GUI (CIntruder Web Interface)
+  --update             check for latest stable version
+
+  ->Tracking:
+    --track=TRACK      download captchas from url (to: 'inputs/')
+    --track-num=S_NUM  set number of captchas to download (default: 5)
+
+  ->Training:
+    --train=TRAIN      train using common OCR techniques
+    --set-id=SETIDS    set colour's ID manually (use -v for details)
+
+  ->Cracking:
+    --crack=CRACK      brute force using local dictionary
+
+  ->Modules (training/cracking):
+    --list             list available modules (from: 'mods/')
+    --mod=NAME         set a specific OCR exploiting module
+
+  ->Post-Exploitation (cracking):
+    --xml=XML          export result to xml format
+    --tool=COMMAND     replace suggested word on commands of another tool. use
+                       'CINT' marker like flag (ex: 'txtCaptcha=CINT')
+
+================================================================
+Examples of usage:
+==============================
+
+-------------------
+* View help:
+
+$ ./cintruder --help
+-------------------
+* Update to latest version:
+
+$ ./cintruder --update
+-------------------
+* Launch web interface (GUI):
+
+$ ./cintruder --gui
+-------------------
+* Simple crack from file:
+
+$ ./cintruder --crack "inputs/captcha.gif"
+-------------------
+* Simple crack from URL:
+
+$ ./cintruder --crack "http://host.com/path/captcha_url"
+-------------------
+* Simple crack from local, exporting results to a xml file:
+
+$ ./cintruder --crack "inputs/captcha.gif" --xml "test.xml"
+-------------------
+* Simple crack from url, with proxy TOR and verbose output:
+
+$ ./cintruder --crack "http://host.com/path/captcha_url" --proxy="http://127.0.0.1:8118" -v
+-------------------
+* Train captcha(s) from url, with proxy TOR and verbose output:
+
+$ ./cintruder --train "http://host.com/path/captcha_url" --proxy "http://127.0.0.1:8118" -v
+-------------------
+* Track 50 captcha(s) from url, with proxy TOR:
+
+$ ./cintruder --track "http://host.com/path/captcha.gif" --track-num "50" --proxy "http://127.0.0.1:8118"
+-------------------
+* List available modules (from "mods/"):
+
+$ ./cintruder --list
+-------------------
+* Launch an OCR module to train a specific local captcha:
+
+$ ./cintruder --train "inputs/easycaptcha.gif" --mod "module_invocation_name"
+-------------------
+* Launch an OCR module to crack a specific online captcha, with verbose output:
+
+$ ./cintruder --crack "http://host.com/path/captcha_url" --mod "module_invocation_name" -v
+-------------------
+* Replace suggested word by CIntruder after cracking a remote url on commands of another tool (ex: "XSSer"):
+
+$ ./cintruder --crack "http://host.com/path/captcha_url" --tool "xsser -u http://host.com/path/param1=foo&param2=bar&txtCaptcha=CINT"
+-------------------

BIN
inputs/test1.gif


BIN
inputs/test2.png


BIN
inputs/test3.gif


BIN
inputs/test4.jpeg


BIN
inputs/test5.jpeg


+ 7 - 0
mods/easy/DESCRIPTION

@@ -0,0 +1,7 @@
+Module: 'EasyCaptcha' [mods/easy/]
+Date: 17/04/2012
+URL: http://kestas.kuliukas.com/EasyCaptcha/
+Author: psy (epsylon@riseup.net)
+License: GPLv3
+Invocation: '--mod easy'
+-------------

+ 20 - 0
mods/easy/__init__.py

@@ -0,0 +1,20 @@
+#!/usr/bin/python
+# -*- coding: iso-8859-15 -*-
+"""
+This file is part of the cintruder project, http://cintruder.03c8.net
+
+Copyright (c) 2012/2016 psy <epsylon@riseup.net>
+
+cintruder is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation version 3 of the License.
+
+cintruder is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along
+with cintruder; if not, write to the Free Software Foundation, Inc., 51
+Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+"""

BIN
mods/easy/dictionary/3/249fcc8dbc1fe5777bfd18ae7a385021.gif


BIN
mods/easy/dictionary/6/4745f51c32b7cafeca28493e332f0088.gif


BIN
mods/easy/dictionary/p/98e592e3f7d4ba4f3455b933336f3c29.gif


BIN
mods/easy/dictionary/w/e14593d0867fb3dc291bd7b42ed062ff.gif


+ 159 - 0
mods/easy/easy_crack.py

@@ -0,0 +1,159 @@
+#!/usr/bin/python
+# -*- coding: iso-8859-15 -*-
+"""
+This file is part of the cintruder project, http://cintruder.03c8.net
+
+Copyright (c) 2012/2016 psy <epsylon@riseup.net>
+
+cintruder is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation version 3 of the License.
+
+cintruder is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along
+with cintruder; if not, write to the Free Software Foundation, Inc., 51
+Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+"""
+from PIL import Image
+import hashlib, os, math, time
+import shutil
+
+class VectorCompare:
+    def magnitude(self, concordance):
+        total = 0
+        for word, count in concordance.iteritems():
+            # print concordance 
+            total += count ** 2
+        return math.sqrt(total)
+
+    def relation(self, concordance1, concordance2):
+        topvalue = 0
+        for word, count in concordance1.iteritems():
+            if concordance2.has_key(word):
+                topvalue += count * concordance2[word]
+        return topvalue / (self.magnitude(concordance1) * self.magnitude(concordance2))
+
+class CIntruderCrack(object):
+    """
+    Class to bruteforce captchas
+    """
+    def __init__(self, captcha=""):
+        """
+        Initialize main CIntruder
+        """
+        self.captcha = self.set_captcha(captcha)
+        start = time.time()
+        self.dictionary_path = 'mods/easy/dictionary/'
+        if not os.path.exists("core/images/previews/"):
+            os.mkdir("core/images/previews/")
+        else:
+            shutil.rmtree("core/images/previews/")
+            os.mkdir("core/images/previews/")
+
+    def buildvector(self, im):
+        d1 = {}
+        count = 0
+        for i in im.getdata():
+            d1[count] = i
+            count += 1
+        return d1
+
+    def set_captcha(self, captcha):
+        """
+        Set the captcha.
+        """
+        self.captcha = captcha
+        return captcha
+ 
+    def crack(self, options):
+        v = VectorCompare()
+        path, dirs, files = os.walk(self.dictionary_path).next()
+        dictionary = dirs
+        imageset = []
+        last_letter = None
+        print "\n[Info] Loading dictionary...\n"
+        for letter in dictionary:
+            for img in os.listdir(self.dictionary_path+letter):
+                temp = []
+                temp.append(self.buildvector(Image.open(self.dictionary_path+"%s/%s"%(letter, img))))
+                imageset.append({letter:temp})
+        try:
+            im = Image.open(self.captcha)
+            im.save("core/images/previews/last-preview.gif")
+            im2 = Image.new("P", im.size, 255)
+            im = im.convert("P")
+        except:
+            print "\nError during cracking!. Is that captcha supported?\n"
+            return
+        temp = {}
+        for x in range(im.size[1]):
+            for y in range(im.size[0]):
+                pix = im.getpixel((y, x))
+                temp[pix] = pix
+                if pix == 3: 
+                    im2.putpixel((y, x), 0)
+        inletter = False
+        foundletter = False
+        start = 0
+        end = 0
+        letters = []
+        for y in range(im2.size[0]): # slice across
+            for x in range(im2.size[1]): # slice down
+                pix = im2.getpixel((y, x))
+                if pix != 255:
+                    inletter = True
+            if foundletter == False and inletter == True:
+                foundletter = True
+                start = y
+            if foundletter == True and inletter == False:
+                foundletter = False
+                end = y
+                letters.append((start, end))
+            inletter = False
+        count = 0
+        countid = 1
+        word_sug = None
+        end = time.time()
+        elapsed = end - start
+        words = {}
+        for letter in letters:
+            m = hashlib.md5()
+            im3 = im2.crop((letter[0], 0, letter[1], im2.size[1]))
+            guess = []
+            for image in imageset:
+                for x, y in image.iteritems():
+                    if len(y) != 0:
+                        guess.append(( v.relation(y[0], self.buildvector(im3)), x))
+            guess.sort(reverse=True)
+            word_per = guess[0][0] * 100
+            if str(word_per) == "100.0":
+                print "Image position   :", countid
+                print "Broken Percent   :", int(round(float(word_per))), "%", "[+CRACKED!]"
+                words[countid] = guess[0][1]
+            else:
+                print "Image position   :", countid
+                print "Broken Percent   :", "%.4f" % word_per, "%"
+                words[countid] = "_"
+            print "Word suggested   :", guess[0][1]
+            print "-------------------"
+            if word_sug == None:
+                word_sug = str(guess[0][1])
+            else:
+                word_sug = word_sug + str(guess[0][1])
+            count += 1
+            countid = countid + 1
+        print "\n========================================"
+        if options.verbose:
+            print "[Info] Elapsed OCR time :", elapsed
+            print "========================================"
+        if word_sug is None:
+            print "Suggested Solution: ", "[ No idea!. Try to add more images to your dictionary/]"
+        else:
+            print "Cracked Words: ", words.values()
+            print "Suggested Solution: ", "[", word_sug, "]"
+        print "========================================\n"
+        return word_sug

+ 148 - 0
mods/easy/easy_ocr.py

@@ -0,0 +1,148 @@
+#!/usr/bin/python
+# -*- coding: iso-8859-15 -*-
+"""
+This file is part of the cintruder project, http://cintruder.03c8.net
+
+Copyright (c) 2012/2016 psy <epsylon@riseup.net>
+
+cintruder is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation version 3 of the License.
+
+cintruder is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+details.
+
+You should have received a copy of the GNU General Public License along
+with cintruder; if not, write to the Free Software Foundation, Inc., 51
+Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+"""
+from PIL import Image
+from operator import itemgetter
+import os, hashlib, time, sys, subprocess, platform
+import shutil
+
+class CIntruderOCR(object):
+    """
+    Class to apply OCR techniques to EasyCaptcha (http://kestas.kuliukas.com/EasyCaptcha/EasyCaptcha/easycaptcha.php)
+    """
+    def __init__(self, captcha, options):
+        # generate words structure (+ previews for gui)
+        if not os.path.exists("outputs/words/"):
+            os.mkdir("outputs/words/")
+        else:
+            shutil.rmtree("outputs/words/")
+            os.mkdir("outputs/words/")
+        if not os.path.exists("core/images/previews/"):
+            os.mkdir("core/images/previews/")
+        else:
+            shutil.rmtree("core/images/previews/")
+            os.mkdir("core/images/previews/")
+        if not os.path.exists("core/images/previews/ocr/"):
+            os.mkdir("core/images/previews/ocr/")
+        else:
+            shutil.rmtree("core/images/previews/ocr/")
+            os.mkdir("core/images/previews/ocr/")
+
+        # initialize main CIntruder
+        try:
+            im = Image.open(captcha)
+            im.save("core/images/previews/last-preview.gif")
+            im2 = Image.new("P", im.size, 255)
+            im = im.convert("P")
+        except:
+            print "Error during OCR process!. Is that captcha supported?\n"
+            return
+        colourid = []
+        try: # extract colour histogram
+            hist = im.histogram()
+        except:
+            print "\n[Error] Something wrong extracting histogram. Aborting...\n"
+            return
+        values = {}
+        for i in range(256):
+            values[i] = hist[i]
+        if options.verbose:
+            print "[Info] Extracting advanced OCR info..."
+            print "\n============================="
+            print "Image Histogram (order by >):"
+            print "============================="
+        for j, k in sorted(values.items(), key=itemgetter(1), reverse=True)[:10]:
+            colourid.append(j)  
+            if options.verbose:
+                print "Colour ID: [", j, "] -> Total pixels:", k
+        if options.verbose:
+            print ""
+        temp = {}
+        for x in range(im.size[1]):
+            for y in range(im.size[0]):
+                pix = im.getpixel((y, x))
+                temp[pix] = pix
+                if options.setids:
+                    colourid = int(options.setids)
+                    if pix == colourid:
+                        im2.putpixel((y, x), 0)
+                else:
+                    if pix == colourid[1]: #id numbers of colours to get (*)
+                        im2.putpixel((y, x), 0)
+        im2.save("outputs/last-ocr_image-processed.gif")
+        inletter = False
+        foundletter = False
+        start = 0
+        end = 0
+        letters = []
+        for y in range(im2.size[0]): 
+            for x in range(im2.size[1]): 
+                pix = im2.getpixel((y, x))
+                if pix != 255:
+                    inletter = True
+            if foundletter == False and inletter == True:
+                foundletter = True
+                start = y
+            if foundletter == True and inletter == False:
+                foundletter = False
+                end = y
+                letters.append((start, end))
+            inletter = False
+        count = 0
+        for letter in letters:
+            m = hashlib.md5()
+            im3 = im2.crop(( letter[0], 0, letter[1], im2.size[1] ))
+            m.update("%s%s"%(time.time(), count))
+            im3.save("outputs/words/%s.gif"%(m.hexdigest()))
+            im3.save("core/images/previews/ocr/%s.gif"%(m.hexdigest()))
+            count += 1
+        print "[Info] Processing captcha/image with OCR algorithms. Please wait...\n"
+        print "================="
+        print "Training Results:"
+        print "================="
+        print "\nNumber of 'symbols' found: [", count, "]"
+        if count == 0:
+            print "\nOuch!. Looks like this captcha is resisting to our OCR methods... by the moment ;-)\n"
+            print "Try this...\n" 
+            print "    1) Check colour's ID values and quantity of pixels of each by using verbose" 
+            print "    2) Set different ID values to your OCR configration and try it again"
+            print "    3) Try to apply some image filters (ex: B/W) manually with an editor (ex: GIMP) to your target"
+            print "    4) Maybe this module that you are using is not working for this captcha...\n"
+            print "------------\n"
+        else:
+            path, dirs, files = os.walk("outputs/words/").next()
+            file_count = str(len(files))
+            print "\n[Info] Generated [ "+ file_count+ " ] OCR images here:", "outputs/words/\n"
+            if options.verbose:
+                # checking for platform to list new words added to dictionary
+                os_sys = platform.system()
+                if os_sys == "Windows":
+                    subprocess.call("dir outputs/words/", shell=True)
+                else:
+                    subprocess.call("ls outputs/words/", shell=True)
+                print ""
+            print "Now move each (human-recognized) OCR image to the correct folder on: dictionary/\n"
+
+if __name__ == "__main__":
+    if sys.argv[1:]:
+        ocr = CIntruderOCR(sys.argv[1:])
+        print ("Data correctly extracted!")
+    else:
+        print ("You must set a captcha for learn. Ex: inputs/test1.gif")

BIN
mods/easy/easycaptcha.png


BIN
outputs/last-ocr_image-processed.gif


BIN
outputs/words/1e6293a28aa77269664af15fdad1a296.gif


BIN
outputs/words/2a47fdf6a6bc96c8e5074aa588c85166.gif


BIN
outputs/words/78727c0a7eae7c418bebd221ae2218a0.gif


BIN
outputs/words/ed34117c939ae18f1bdc497c4f8af4be.gif