├── src ├── __init__.py ├── messages.py ├── base92.py └── base_chain.py ├── examples ├── example-input.txt ├── ocr-example.jpg └── exif-example.jpg ├── assets ├── basecrack-logo.png ├── basecrack-tool.png ├── basecrack-magic-mode.png ├── basecrack-screenshot.png ├── basecrack-ocr-detection.png └── basecrack-exif-detection.png ├── requirements.txt ├── config.json ├── Dockerfile ├── api_example.py ├── setup.py ├── LICENSE ├── tests └── tests.py ├── .gitignore ├── README.md └── basecrack.py /src/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/example-input.txt: -------------------------------------------------------------------------------- 1 | YnJ1aA== 2 | bXkgbmFtZSBpcyBqZWZm 3 | `DVKR`dTi#1timlLer;I 4 | 7G2PV9mvwQBmQ6 -------------------------------------------------------------------------------- /examples/ocr-example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mufeedvh/basecrack/HEAD/examples/ocr-example.jpg -------------------------------------------------------------------------------- /assets/basecrack-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mufeedvh/basecrack/HEAD/assets/basecrack-logo.png -------------------------------------------------------------------------------- /assets/basecrack-tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mufeedvh/basecrack/HEAD/assets/basecrack-tool.png -------------------------------------------------------------------------------- /examples/exif-example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mufeedvh/basecrack/HEAD/examples/exif-example.jpg -------------------------------------------------------------------------------- /assets/basecrack-magic-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mufeedvh/basecrack/HEAD/assets/basecrack-magic-mode.png -------------------------------------------------------------------------------- /assets/basecrack-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mufeedvh/basecrack/HEAD/assets/basecrack-screenshot.png -------------------------------------------------------------------------------- /assets/basecrack-ocr-detection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mufeedvh/basecrack/HEAD/assets/basecrack-ocr-detection.png -------------------------------------------------------------------------------- /assets/basecrack-exif-detection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mufeedvh/basecrack/HEAD/assets/basecrack-exif-detection.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | argparse 2 | colorama 3 | termcolor 4 | pathlib 5 | anybase32 6 | base36 7 | base58 8 | pybase62 9 | base91 10 | pybase100 11 | exifread 12 | opencv-python 13 | pytesseract -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "tesseract_path": { 3 | "32bit": "C:\\Program Files\\Tesseract-OCR\\tesseract.exe", 4 | "64bit": "C:\\Program Files (x86)\\Tesseract-OCR\\tesseract.exe" 5 | } 6 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3 2 | 3 | WORKDIR /app 4 | 5 | COPY . /app 6 | 7 | RUN apt-get update &&\ 8 | apt-get -y install tesseract-ocr libtesseract-dev &&\ 9 | python -m pip install -r requirements.txt 10 | 11 | WORKDIR /data 12 | 13 | ENTRYPOINT [ "python", "/app/basecrack.py" ] -------------------------------------------------------------------------------- /src/messages.py: -------------------------------------------------------------------------------- 1 | from termcolor import colored 2 | 3 | def print_line_separator(): 4 | print( 5 | colored('\n{{<<', 'red') + 6 | colored('='*70, 'yellow') + 7 | colored('>>}}', 'red') 8 | ) 9 | 10 | def push_error(message): 11 | print(colored('\n[!] {}\n'.format(message), 'red')) -------------------------------------------------------------------------------- /api_example.py: -------------------------------------------------------------------------------- 1 | # import the BaseCrack class from basecrack.py 2 | from basecrack import BaseCrack 3 | 4 | # calling the api function decode() with the encoded base 5 | result = BaseCrack().decode('c3BhZ2hldHRp') 6 | 7 | # printing the output 8 | """ 9 | result is tuple where: 10 | result[0] = DECODED STRING 11 | result[1] = ENCODING SCHEME 12 | """ 13 | print('Decoded String: {}'.format(result[0])) 14 | print('Encoding Scheme: {}'.format(result[1])) 15 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name='basecrack', 5 | version=4.0 , 6 | description='Decode All Bases - Base Scheme Decoder', 7 | author='Mufeed VH', 8 | url='https://github.com/mufeedvh/basecrack', 9 | license='MIT', 10 | packages=[ 11 | 'src' 12 | ], 13 | py_modules=[ 14 | 'basecrack' 15 | ], 16 | install_requires=[ 17 | 'argparse', 18 | 'colorama', 19 | 'termcolor', 20 | 'pathlib', 21 | 'anybase32', 22 | 'base36', 23 | 'base58', 24 | 'pybase62', 25 | 'base91', 26 | 'pybase100', 27 | 'exifread', 28 | 'opencv-python', 29 | 'pytesseract' 30 | ], 31 | python_requires='>=3.0.0', 32 | entry_points={ 33 | 'console_scripts': [ 34 | 'basecrack = basecrack:main' 35 | ] 36 | } 37 | ) 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Mufeed VH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/base92.py: -------------------------------------------------------------------------------- 1 | # THE BEERWARE LICENSE (Revision 42): 2 | # wrote this file. As long as you retain this notice you 3 | # can do whatever you want with this stuff. If we meet some day, and you 4 | # think this stuff is worth it, you can buy me a beer in return 5 | # - Nathan Hwang (thenoviceoof) 6 | # 7 | # Modified from: https://github.com/thenoviceoof/base92/blob/master/python/base92/base92.py 8 | 9 | import math 10 | 11 | def base92_ord(val): 12 | num = ord(val) 13 | if val == '!': 14 | return 0 15 | elif ord('#') <= num and num <= ord('_'): 16 | return num - ord('#') + 1 17 | elif ord('a') <= num and num <= ord('}'): 18 | return num - ord('a') + 62 19 | else: 20 | raise ValueError('val is not a base92 character') 21 | 22 | def base92_decode(bstr): 23 | bitstr = '' 24 | resstr = '' 25 | if bstr == '~': 26 | return '' 27 | # we always have pairs of characters 28 | for i in range(len(bstr) // 2): 29 | x = base92_ord(bstr[2*i])*91 + base92_ord(bstr[2*i+1]) 30 | bitstr += '{:013b}'.format(x) 31 | while 8 <= len(bitstr): 32 | resstr += chr(int(bitstr[0:8], 2)) 33 | bitstr = bitstr[8:] 34 | # if we have an extra char, check for extras 35 | if len(bstr) % 2 == 1: 36 | x = base92_ord(bstr[-1]) 37 | bitstr += '{:06b}'.format(x) 38 | while 8 <= len(bitstr): 39 | resstr += chr(int(bitstr[0:8], 2)) 40 | bitstr = bitstr[8:] 41 | return resstr 42 | 43 | decode = base92_decode 44 | b92decode = base92_decode -------------------------------------------------------------------------------- /tests/tests.py: -------------------------------------------------------------------------------- 1 | from basecrack import BaseCrack 2 | import unittest 3 | 4 | class TestBase(unittest.TestCase): 5 | def test_base16(self): 6 | assert BaseCrack().decode('436865636B537472696E67')[0] == 'CheckString', "Base16 test failed." 7 | 8 | def test_base32(self): 9 | assert BaseCrack().decode('INUGKY3LKN2HE2LOM4======')[0] == 'CheckString', "Base32 test failed." 10 | 11 | def test_base36(self): 12 | assert BaseCrack().decode('45640901731484716')[0] == 'checkstring', "Base36 test failed." 13 | 14 | def test_base58(self): 15 | assert BaseCrack().decode('HiVkR1foHM1ZXjk')[0] == 'CheckString', "Base58 test failed." 16 | 17 | def test_base62(self): 18 | assert BaseCrack().decode('6ZOc3cWz3dWiylL')[0] == 'CheckString', "Base62 test failed." 19 | 20 | def test_base64(self): 21 | assert BaseCrack().decode('Q2hlY2tTdHJpbmc=')[0] == 'CheckString', "Base64 test failed." 22 | 23 | def test_base64url(self): 24 | assert BaseCrack().decode('Q2hlY2tTdHJpbmc')[0] == 'CheckString', "Base64Url test failed." 25 | 26 | def test_ascii85(self): 27 | assert BaseCrack().decode('6YL%@CK#=qBl7P')[0] == 'CheckString', "ASCII85 test failed." 28 | 29 | def test_base85(self): 30 | assert BaseCrack().decode('Luh4VYg2S`X>Ml')[0] == 'CheckString', "Base85 test failed." 31 | 32 | def test_base91(self): 33 | assert BaseCrack().decode('WXnBaseCrack 2 |

Decoder For Base Encoding Schemes

3 |

4 | version 5 | GitHub license 6 |

7 | 8 | **BaseCrack** is a tool written in Python that can decode all alphanumeric base encoding schemes. This tool can accept single user input, multiple inputs from a file, input from argument, **multi-encoded bases**, **bases in image EXIF data**, **bases on images with OCR** and decode them incredibly fast. 9 | 10 | For a basic demo, try the [Web Interface](https://basecrack.herokuapp.com/) that uses BaseCrack's [API](#api). 11 | 12 |
13 | Fun Fact! 14 |
15 | I initially made this after being fed up with lame CTF challenges with multi-encoded bases. Now some of them started doing that in Steganography challenges so I automated that too smh! 16 |
17 | 18 | ## Table of Contents 19 | - [Features](#features) 20 | - [Supported Encoding Schemes](#supported-encoding-schemes) 21 | - [Installation](#installation) 22 | - [Usage](#usage) 23 | - [API](#api) 24 | - [Contribution](#contribution) 25 | - [License](#license) 26 | 27 | ## Features 28 | 29 | - Decode multi-encoded bases of any pattern. 30 | - Decode bases in image EXIF data. 31 | - Decode bases on images with OCR detection. 32 | - Can decode multiple base encodings from a file. 33 | - Generate a wordlist/output with the decoded bases. 34 | - Predicts the type of encoding scheme. 35 | 36 | ## Supported Encoding Schemes 37 | - Base16 38 | - Base32 39 | - Base36 40 | - Base58 41 | - Base62 42 | - Base64 43 | - Base64Url 44 | - Base85 45 | - Ascii85 46 | - Base91 47 | - Base92 48 | - [Base100](https://github.com/AdamNiederer/base100) ([#14](https://github.com/mufeedvh/basecrack/issues/14)) 49 | 50 | ## Installation 51 | 52 | $ git clone https://github.com/mufeedvh/basecrack.git 53 | $ cd basecrack 54 | $ pip3 install -r requirements.txt 55 | $ python3 basecrack.py -h 56 | 57 | **NOTE:** Python3 is recommended to use! 58 | 59 | **Linux:** 60 | 61 | $ sudo apt-get update 62 | $ sudo apt-get install tesseract-ocr libtesseract-dev 63 | 64 | **MacOS:** 65 | 66 | $ brew install tesseract 67 | 68 | **Windows:** 69 | 70 | OCR Detection is implemented with [Tesseract](https://github.com/tesseract-ocr/tesseract) and Windows requires installation of the Tesseract executable. Installing the dependencies from `requirements.txt` which includes `pytesseract` should install it. If in case it doesn't, here's how you can set it up: 71 | 72 | 1. First check whether you have it installed or not in the `Program Files`/`Program Files (x86)` under the `Tesseract-OCR` directory. 73 | 2. If there is, give that path in the `config.json` and you're all set! If you don't have it, install it from [here](https://github.com/UB-Mannheim/tesseract/wiki) and set the path in `config.json`. 74 | 75 | **Tesseract Docs:** https://tesseract-ocr.github.io/ 76 | 77 | **NOTE:** I haven't completely tested this tool on Windows so if you stumble upon any issues, please [open an issue](https://github.com/mufeedvh/basecrack/issues/new). 78 | 79 | ## Usage 80 | 81 | Get a list of all the arguments: 82 | 83 | python3 basecrack.py -h 84 | 85 | To decode a single base encoding from user input: 86 | 87 | python3 basecrack.py 88 | 89 | To decode a single base encoding from argument **(-b/--base)**: 90 | 91 | python3 basecrack.py -b SGVsbG8gV29ybGQh 92 | 93 | To decode multiple base encodings from a file **(-f/--file)**: 94 | 95 | python3 basecrack.py -f file.txt 96 | 97 | **Magic Mode:** To decode multi-encoded base of any pattern **(-m/--magic)**: 98 | 99 | python3 basecrack.py --magic 100 | 101 | To input an image for **EXIF/OCR** detection mode **(-i/--image)**: 102 | 103 | python3 basecrack.py -i image.jpg (--exif/--ocr) 104 | 105 | **EXIF Data:** To decode bases in image EXIF data **(-e/--exif)**: 106 | 107 | python basecrack.py -i image.jpg --exif 108 | 109 | **OCR Base Detection:** To decode bases on image with OCR detection **(-c/--ocr)**: 110 | 111 | python basecrack.py -i image.jpg --ocr 112 | 113 | To generate a wordlist/output with the decoded bases **(-o/--output)**: 114 | 115 | python basecrack.py -f file.txt -o output-wordlist.txt 116 | 117 | ## Magic Mode 118 | 119 | Now you can **decode multi-encoded bases** of any pattern in a single shot. 120 | 121 | Have you ever stumbled upon that one lame CTF challenge that gives you an encoded string which is just encoded over and over with Base64, Base91, Base85 and so on? Just give that to BaseCrack and you're done with it! ;) 122 | 123 | Want to test it out? Just give it this input: 124 | 125 | ``` 126 | IX(Fp@nNG6ef<,*TFE]IT^zdINAb9EVbp,e<*[Nv+T8 127 | ``` 128 | 129 | and see for yourself! :) 130 | 131 | ### BaseCrack API 132 | 133 | BaseCrack can now be used as a library! Just import the `BaseCrack()` class and call the `decode()` function. 134 | 135 | See [**API**](https://github.com/mufeedvh/basecrack#api). 136 | 137 | ## API 138 | 139 | Want to use BaseCrack as a library? We got you covered! 140 | 141 | Just put `basecrack` in your project's directory and you're ready to go! 142 | 143 | **Example:** 144 | 145 | ```python 146 | # import the BaseCrack class from basecrack.py 147 | from basecrack import BaseCrack 148 | 149 | # calling the api function decode() with the encoded base 150 | result = BaseCrack().decode('c3BhZ2hldHRp') 151 | 152 | # printing the output 153 | """ 154 | result is tuple where: 155 | result[0] = DECODED STRING 156 | result[1] = ENCODING SCHEME 157 | """ 158 | print('Decoded String: {}'.format(result[0])) 159 | print('Encoding Scheme: {}'.format(result[1])) 160 | ``` 161 | 162 | **Output:** 163 | 164 | ``` 165 | Decoded String: spaghetti 166 | Encoding Scheme: Base64 167 | ``` 168 | 169 | ## Contribution 170 | 171 | Ways to contribute 172 | - Suggest a feature 173 | - Report a bug 174 | - Fix something and open a pull request 175 | - Help me document the code 176 | - Spread the word 177 | 178 | Before you open a PR, make sure everything's good by running the tests: 179 | 180 | **Unit Tests:** (Thanks [@FavasM](https://github.com/mufeedvh/basecrack/pull/8)) 181 | 182 | python3 -m unittest discover -v -s tests 183 | 184 | ## License 185 | Licensed under the MIT License, see LICENSE for more information. 186 | -------------------------------------------------------------------------------- /src/base_chain.py: -------------------------------------------------------------------------------- 1 | import anybase32 2 | import base36 3 | import base58 4 | import base62 5 | import base64 6 | import base91 7 | import src.base92 as base92 8 | import pybase100 9 | 10 | from termcolor import colored 11 | 12 | class DecodeBase: 13 | def __init__(self, encoded_base, api_call=False, image_mode=False): 14 | self.encoded_base = encoded_base 15 | self.b32_once = False 16 | self.b64_once = False 17 | self.b64_url = False 18 | self.encoding_type = [] 19 | self.results = [] 20 | 21 | # state conditions 22 | self.api_call = api_call 23 | self.image_mode_call = image_mode 24 | 25 | def decode(self): 26 | self.decode_base() 27 | return ( 28 | self.encoding_type, 29 | self.results 30 | ) 31 | 32 | def contains_replacement_char(self, res): 33 | """ 34 | `contains_replacement_char()` checks whether the decoded base 35 | contains an unknown unicode, ie: invalid character. 36 | these are replaced with 'replacement character', 37 | which is '�' and 'U+FFFD' in unicode and 38 | also checks for unicode chars after `127`. 39 | """ 40 | if u'\ufffd' in res: return True 41 | else: 42 | count = 0 43 | for char in res: 44 | if ord(char) > 127: count += 1 45 | return True if count > 0 else False 46 | 47 | def process_decode(self, decode_string, scheme): 48 | """ 49 | `process_decode()` stores the result if the encoding is valid 50 | after checks from `contains_replacement_char()` and 51 | prints the output if it isn't an API call 52 | """ 53 | encoding_type = self.encoding_type 54 | results = self.results 55 | 56 | if len(decode_string) < 3: return 57 | if not self.contains_replacement_char(decode_string): 58 | # don't repeat `base64url` when `base64` has already passed and it's not a URL 59 | if scheme == 'Base64' and '://' not in decode_string: 60 | self.b64_once = True 61 | 62 | if self.b64_once and (scheme == 'Base64URL'): 63 | return 64 | 65 | # append results to the respective lists 66 | encoding_type.append(scheme) 67 | results.append(decode_string) 68 | 69 | if not self.api_call: 70 | if self.image_mode_call: 71 | print( 72 | colored('\n[-] Attempting Base: ', 'yellow') + 73 | colored(self.encoded_base, 'red') 74 | ) 75 | 76 | print( 77 | colored('\n[>] Decoding as {}: '.format(scheme), 'blue') + 78 | colored(decode_string, 'green') 79 | ) 80 | 81 | def decode_base(self): 82 | encoded_base = self.encoded_base 83 | process_decode = self.process_decode 84 | 85 | # decoding as base16 86 | try: 87 | process_decode( 88 | base64.b16decode(encoded_base, casefold=False).decode('utf-8', 'replace'), 89 | 'Base16' 90 | ) 91 | except Exception as _: pass 92 | 93 | # decoding as base32 94 | try: 95 | process_decode( 96 | base64.b32decode( 97 | encoded_base, casefold=False, map01=None 98 | ).decode('utf-8', 'replace'), 99 | 'Base32' 100 | ) 101 | self.b32_once = True 102 | except Exception as _: pass 103 | 104 | # decoding as base32 (RFC 3548) 105 | if not self.b32_once: 106 | try: 107 | """ 108 | Base32 charset can differ based on their spec, this requires stripping 109 | the padding or changing charsets to get the correct results. 110 | By default this `anybase32` implementation follows the RFC 3548 spec. 111 | """ 112 | temp_clean_base = str.encode(encoded_base.replace('=', '')) 113 | process_decode( 114 | anybase32.decode(temp_clean_base).decode('utf-8', 'replace'), 115 | 'Base32' 116 | ) 117 | except Exception as _: pass 118 | 119 | # decoding as base36 120 | try: 121 | process_decode( 122 | base36.dumps(int(encoded_base)), 123 | 'Base36' 124 | ) 125 | except Exception as _: pass 126 | 127 | # decoding as base58 128 | try: 129 | process_decode( 130 | base58.b58decode(encoded_base.encode()).decode('utf-8', 'replace'), 131 | 'Base58' 132 | ) 133 | except Exception as _: pass 134 | 135 | # decoding as base62 136 | try: 137 | process_decode( 138 | base62.decodebytes(encoded_base).decode('utf-8', 'replace'), 139 | 'Base62' 140 | ) 141 | except Exception as _: pass 142 | 143 | # decoding as base64 144 | try: 145 | process_decode( 146 | base64.b64decode(encoded_base).decode('utf-8', 'replace'), 147 | 'Base64' 148 | ) 149 | except Exception as _: pass 150 | 151 | # decoding as base64url 152 | try: 153 | process_decode( 154 | base64.urlsafe_b64decode( 155 | encoded_base + '=' * (4 - len(encoded_base) % 4) 156 | ).decode('utf-8', 'replace'), 157 | 'Base64URL' 158 | ) 159 | except Exception as _: pass 160 | 161 | # decoding as base85 162 | try: 163 | process_decode( 164 | base64.b85decode(encoded_base).decode('utf-8', 'replace'), 165 | 'Base85' 166 | ) 167 | except Exception as _: pass 168 | 169 | # decoding as ascii85 170 | try: 171 | process_decode( 172 | base64.a85decode(encoded_base).decode('utf-8', 'replace'), 173 | 'Ascii85' 174 | ) 175 | except Exception as _: pass 176 | 177 | # decoding as base91 178 | try: 179 | process_decode( 180 | base91.decode(encoded_base).decode('utf-8', 'replace'), 181 | 'Base91' 182 | ) 183 | except Exception as _: pass 184 | 185 | # decoding as base92 186 | try: 187 | process_decode( 188 | base92.decode(encoded_base), 189 | 'Base92' 190 | ) 191 | except Exception as _: pass 192 | 193 | # decoding as base100 lol why??!! 194 | try: 195 | process_decode( 196 | pybase100.decode(encoded_base).decode(), 197 | 'Base100' 198 | ) 199 | except Exception as _: 200 | pass -------------------------------------------------------------------------------- /basecrack.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | __author__ = 'Mufeed VH' 5 | __version__ = '4.0' 6 | __email__ = 'contact@mufeedvh.com' 7 | __github__ = 'https://github.com/mufeedvh/basecrack' 8 | 9 | import os 10 | import re 11 | import sys 12 | import time 13 | import platform 14 | import json 15 | import argparse 16 | from colorama import init 17 | from termcolor import colored 18 | from pathlib import Path 19 | 20 | from src.base_chain import DecodeBase 21 | from src.messages import push_error, print_line_separator 22 | 23 | class BaseCrack: 24 | def __init__(self, output=None, magic_mode_call=False, quit_after_fail=True): 25 | self.output = output 26 | # initial bools 27 | self.api_call = False 28 | self.magic_mode_call = magic_mode_call 29 | self.image_mode_call = False 30 | self.quit_after_fail = quit_after_fail 31 | 32 | # main decode function 33 | def decode_base(self, encoded_base): 34 | if len(encoded_base) > 3: 35 | # execute decode chain 36 | encoding_type, results = DecodeBase( 37 | encoded_base, 38 | api_call = self.api_call, 39 | image_mode = self.image_mode_call 40 | ).decode() 41 | 42 | if not results and not self.api_call: 43 | if not self.image_mode_call: 44 | push_error('Not a valid encoding.') 45 | 46 | if self.quit_after_fail: 47 | quit() 48 | 49 | # print/return the results 50 | for x in range(len(results)): 51 | if not self.api_call: 52 | print( 53 | colored('\n[-] The Encoding Scheme Is ', 'blue') + 54 | colored(encoding_type[x], 'green') 55 | ) 56 | 57 | # generating the wordlist/output file with the decoded base 58 | if self.output != None: 59 | open(self.output, 'a').write(results[x]+'\n') 60 | else: 61 | return results[x].strip(), encoding_type[x] 62 | 63 | if self.image_mode_call and results: 64 | print_line_separator() 65 | else: 66 | push_error("Found no valid base encoded strings.") 67 | 68 | 69 | def decode_from_file(self, file): 70 | """ 71 | `decode_from_file()` fetches the set of base encodings from the input file 72 | and passes it to 'decode_base()' function to decode it all 73 | """ 74 | 75 | print(colored('[-] Decoding Base Data From ', 'cyan') + colored(file, 'yellow')) 76 | 77 | # check whether file exists 78 | if not Path(file).is_file(): 79 | push_error('File does not exist.') 80 | quit() 81 | 82 | with open(file) as input_file: 83 | # reading each line from the file 84 | for line in input_file: 85 | # checking if the line/base is not empty 86 | if len(line) > 1: 87 | line = line.strip() 88 | print(colored('\n[-] Encoded Base: ', 'yellow')+str(line)) 89 | 90 | if self.magic_mode_call: 91 | self.magic_mode(line) 92 | else: 93 | self.decode_base(line) 94 | 95 | print_line_separator() 96 | 97 | 98 | def decode(self, encoded_base): 99 | """ 100 | API FUNCTION 101 | ------------ 102 | the `decode()` function returns a tuple 103 | with the structure: 104 | ('DECODED_STRING', 'ENCODING SCHEME') 105 | For example: 106 | >> from basecrack import BaseCrack 107 | >> BaseCrack().decode('c3BhZ2hldHRp') 108 | ('spaghetti', 'Base64') 109 | ie: 110 | result[0] is the decoded string 111 | result[1] is the encoding scheme 112 | """ 113 | self.api_call = True 114 | 115 | # api calls returns a tuple with the decoded base and the encoding scheme 116 | return self.decode_base(encoded_base) 117 | 118 | 119 | def magic_mode(self, encoded_base): 120 | """ 121 | `magic_mode()` tries to decode multi-encoded bases of any pattern 122 | """ 123 | iteration = 0 124 | result = None 125 | encoding_pattern = [] 126 | start_time = time.time() 127 | 128 | while True: 129 | if self.decode(encoded_base) is not None: 130 | iteration += 1 131 | result = self.decode(encoded_base) 132 | decoded_string = result[0] 133 | encoding_scheme = result[1] 134 | encoding_pattern.append(encoding_scheme) 135 | 136 | print(colored('\n[-] Iteration: ', 'green')+colored(iteration, 'blue')) 137 | print(colored('\n[-] Heuristic Found Encoding To Be: ', 'yellow')+colored(encoding_scheme, 'green')) 138 | print(colored('\n[-] Decoding as {}: '.format(encoding_scheme), 'blue')+colored(decoded_string, 'green')) 139 | print(colored('\n{{<<', 'red')+colored('='*70, 'yellow')+colored('>>}}', 'red')) 140 | 141 | # setting the encoded bases and the current result for the next iteration 142 | encoded_base = decoded_string 143 | else: 144 | break 145 | 146 | if result is not None: 147 | end_time = time.time() 148 | 149 | print(colored('\n[-] Total Iterations: ', 'green')+colored(iteration, 'blue')) 150 | 151 | # show the encoding pattern in order and comma-seperated 152 | pattern = ' -> '.join(map(str, encoding_pattern)) 153 | print(colored('\n[-] Encoding Pattern: ', 'green')+colored(pattern, 'blue')) 154 | 155 | print( 156 | colored('\n[-] Magic Decode Finished With Result: ', 'green') + 157 | colored(decoded_string, 'yellow', attrs=['bold']) 158 | ) 159 | 160 | # generating the wordlist/output file with the decoded base 161 | if self.output != None: 162 | open(self.output, 'a').write(decoded_string+'\n') 163 | 164 | completion_time = str(end_time-start_time)[:6] 165 | 166 | print( 167 | colored('\n[-] Finished in ', 'green') + 168 | colored(completion_time, 'cyan', attrs=['bold']) + 169 | colored(' seconds\n', 'green') 170 | ) 171 | else: 172 | quit(colored('\n[!] Not a valid encoding.\n', 'red')) 173 | 174 | 175 | def decode_from_image(self, image, mode): 176 | """ 177 | `decode_from_image()` AKA "lame_steganography_challenge_solving_automated()" has two modes: 178 | - OCR Detection Mode: dectects base encodings in images 179 | - EXIF Data Mode: detects base encodings in an image's EXIF data 180 | """ 181 | self.image_mode_call = True 182 | 183 | # check whether file exists 184 | if not Path(image).is_file(): 185 | push_error('File does not exist.') 186 | quit() 187 | 188 | if mode == 'exif': 189 | import exifread 190 | 191 | read_image = open(image, 'rb') 192 | exif_tags = exifread.process_file(read_image) 193 | 194 | for tag in exif_tags: 195 | split_tag = str(exif_tags[tag]).split(' ') 196 | 197 | for base in split_tag: 198 | if len(base) < 3 or '\\x' in base: continue 199 | 200 | for base in base.splitlines(): 201 | if self.magic_mode_call: 202 | self.magic_mode(base) 203 | else: 204 | self.decode_base(base) 205 | elif mode == 'ocr': 206 | import cv2, pytesseract 207 | 208 | # import tesseract for windows 209 | if platform.system() == 'Windows': 210 | load_config = json.loads(open('config.json', 'r').read()) 211 | 212 | if len(load_config) > 0: 213 | # load 32/64 bit executables 214 | if sys.maxsize > 2**32: 215 | # 64 bit 216 | tesseract_path = load_config['tesseract_path']['32bit'] 217 | else: 218 | # 32 bit 219 | tesseract_path = load_config['tesseract_path']['64bit'] 220 | 221 | # raw string to treat `\` as a literal character 222 | pytesseract.pytesseract.tesseract_cmd = r'{}'.format(tesseract_path) 223 | 224 | read_image = cv2.imread(image) 225 | get_text = pytesseract.image_to_string(read_image) 226 | strings_from_img = str(get_text).replace(' ', '') 227 | 228 | # cleaning the detected string with valid base chars for accurary 229 | base = re.sub('[^A-Za-z0-9+/=@]', '', strings_from_img) 230 | 231 | if self.magic_mode_call: self.magic_mode(base) 232 | else: self.decode_base(base) 233 | 234 | 235 | # print a banner to look cool 236 | def banner(): 237 | banner = ''' 238 | ██████╗ █████╗ ███████╗███████╗ ██████╗██████╗ █████╗ ██████╗██╗ ██╗ 239 | ██╔══██╗██╔══██╗██╔════╝██╔════╝██╔════╝██╔══██╗██╔══██╗██╔════╝██║ ██╔╝ 240 | ██████╔╝███████║███████╗█████╗ ██║ ██████╔╝███████║██║ █████╔╝ 241 | ██╔══██╗██╔══██║╚════██║██╔══╝ ██║ ██╔══██╗██╔══██║██║ ██╔═██╗ 242 | ██████╔╝██║ ██║███████║███████╗╚██████╗██║ ██║██║ ██║╚██████╗██║ ██╗ 243 | ╚═════╝ ╚═╝ ╚═╝╚══════╝╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ v4.0 244 | ''' 245 | print(colored(banner, 'red')+colored('\n\t\tpython basecrack.py -h [FOR HELP]\n', 'green')) 246 | 247 | def main(): 248 | banner() 249 | 250 | # setting up argparse module to accept arguments 251 | parser = argparse.ArgumentParser() 252 | parser.add_argument('-b', '--base', help='Decode a single encoded base from argument.') 253 | parser.add_argument('-f', '--file', help='Decode multiple encoded bases from a file.') 254 | parser.add_argument('-m', '--magic', help='Decode multi-encoded bases in one shot.', action='store_true') 255 | parser.add_argument('-i', '--image', help='Decode base encodings from image with OCR detection or EXIF data.') 256 | parser.add_argument('-c', '--ocr', help='OCR detection mode.', action='store_true') 257 | parser.add_argument('-e', '--exif', help='EXIF data detection mode. (default)', action='store_true') 258 | parser.add_argument('-o', '--output', help='Generate a wordlist/output with the decoded bases, enter filename as the value.') 259 | args = parser.parse_args() 260 | 261 | if args.output: 262 | print( 263 | colored('\n[>] ', 'yellow') + 264 | colored('Enabled Wordlist Generator Mode :: ', 'green') + 265 | colored(args.output+'\n', 'blue') 266 | ) 267 | 268 | """ 269 | decodes base encodings from file if argument is given 270 | else it accepts a single encoded base from user 271 | """ 272 | if args.file: 273 | if args.magic: 274 | BaseCrack( 275 | output=args.output, 276 | magic_mode_call=True 277 | ).decode_from_file(str(args.file)) 278 | else: 279 | BaseCrack(output=args.output).decode_from_file(str(args.file)) 280 | 281 | elif args.base: 282 | print(colored('[-] Encoded Base: ', 'yellow')+colored(str(args.base), 'red')) 283 | 284 | if args.magic: 285 | BaseCrack().magic_mode(str(args.base)) 286 | else: 287 | BaseCrack().decode_base(str(args.base)) 288 | 289 | elif args.image: 290 | print(colored('[-] Input Image: ', 'yellow')+colored(str(args.image), 'red')) 291 | 292 | if args.ocr: 293 | mode = 'ocr' 294 | elif args.exif: 295 | mode = 'exif' 296 | # default 297 | else: 298 | mode = 'exif' 299 | 300 | if args.magic: 301 | BaseCrack( 302 | output=args.output, magic_mode_call=True, quit_after_fail=False 303 | ).decode_from_image(str(args.image), mode) 304 | else: 305 | BaseCrack( 306 | quit_after_fail=False 307 | ).decode_from_image(str(args.image), mode) 308 | 309 | else: 310 | if sys.version_info >= (3, 0): 311 | encoded_base = input(colored('[>] Enter Encoded Base: ', 'yellow')) 312 | else: 313 | encoded_base = raw_input(colored('[>] Enter Encoded Base: ', 'yellow')) 314 | 315 | if args.magic: 316 | BaseCrack().magic_mode(encoded_base) 317 | else: 318 | BaseCrack().decode_base(encoded_base) 319 | 320 | if args.output: 321 | print( 322 | colored('\n[-] Output Generated Successfully > ', 'green') + 323 | colored(args.output+'\n', 'yellow') 324 | ) 325 | 326 | if __name__ == '__main__': 327 | init() 328 | main() 329 | --------------------------------------------------------------------------------