From 5f78b56d56da4000c73fe4867a5ca7a8e7e43e83 Mon Sep 17 00:00:00 2001 From: Dillon Franke Date: Sun, 26 Dec 2021 13:01:02 -0800 Subject: [PATCH] Added functionality for JWT tokens sent within GET request parameters --- README.md | 3 ++- jwt_tool.py | 42 +++++++++++++++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a63e08f..12a070c 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,8 @@ All modes now allow for sending the token directly to an application. You need to specify: * target URL (-t) * a request header (-rh) or request cookies (-rc) that are needed by the application (***at least one must contain the token***) -* (optional) any POST data (where the request is a POST) +* (optional) any POST data, (-pd) where the request is a POST +* (optional) any GET data, (-gd) where the request is a GET (include characters such as `?` and `&` in target URL) * (optional) any additional jwt_tool options, such as modes or tampering/injection options * (optional) a *canary value* (-cv) - a text value you expect to see in a successful use of the token (e.g. "Welcome, ticarpi") An example request might look like this (using scanning mode for forced-errors): diff --git a/jwt_tool.py b/jwt_tool.py index 423f287..3f1e06e 100644 --- a/jwt_tool.py +++ b/jwt_tool.py @@ -124,6 +124,7 @@ def createConfig(): 'scanMode': '', 'reqMode': '', 'postData': '', + 'getData': '', 'resCode': '', 'resSize': '', 'resContent': ''} @@ -133,9 +134,11 @@ def createConfig(): cprintc("Make sure to set the \"httplistener\" value to a URL you can monitor to enable out-of-band checks.", "cyan") exit(1) -def sendToken(token, cookiedict, track, headertoken="", postdata=None): +def sendToken(token, cookiedict, track, headertoken="", postdata=None, getdata=None): if not postdata: postdata = config['argvals']['postData'] + if not getdata: + getdata = config['argvals']['getData'] url = config['argvals']['targetUrl'] headers = {'User-agent': config['customising']['useragent']+" "+track} if headertoken: @@ -146,12 +149,16 @@ def sendToken(token, cookiedict, track, headertoken="", postdata=None): if config['services']['proxy'] == "False": if postdata: response = requests.post(url, data=postdata, headers=headers, cookies=cookiedict, proxies=False, verify=False) + elif getdata: + response = requests.get(url + getdata, headers=headers, cookies=cookiedict, proxies=False, verify=False) else: response = requests.get(url, headers=headers, cookies=cookiedict, proxies=False, verify=False) else: proxies = {'http': 'http://'+config['services']['proxy'], 'https': 'http://'+config['services']['proxy']} if postdata: response = requests.post(url, data=postdata, headers=headers, cookies=cookiedict, proxies=proxies, verify=False) + elif getdata: + response = requests.get(url + getdata, headers=headers, cookies=cookiedict, proxies=proxies, verify=False) else: response = requests.get(url, headers=headers, cookies=cookiedict, proxies=proxies, verify=False) if int(response.elapsed.total_seconds()) >= 9: @@ -218,6 +225,10 @@ def jwtOut(token, fromMod, desc=""): else: posttoken = [config['argvals']['postdata'],0] + if config['argvals']['headerloc'] == "getdata": + gettoken = p.subn(token, config['argvals']['getdata'], 0) + else: + gettoken = [config['argvals']['getdata'],0] try: cookiedict = parse_dict_cookies(cookietoken[0]) @@ -227,11 +238,11 @@ def jwtOut(token, fromMod, desc=""): # Check if token was included in substitution - if cookietoken[1] == 1 or headertoken[1] == 1 or posttoken[1]: - resData = sendToken(token, cookiedict, logID, headertoken[0], posttoken[0]) + if cookietoken[1] == 1 or headertoken[1] == 1 or posttoken[1] or gettoken: + resData = sendToken(token, cookiedict, logID, headertoken[0], posttoken[0], gettoken[0]) else: if config['argvals']['overridesub'] == "true": - resData = sendToken(token, cookiedict, logID, headertoken[0], posttoken[0]) + resData = sendToken(token, cookiedict, logID, headertoken[0], posttoken[0], gettoken[0]) else: cprintc("[-] No substitution occurred - check that a token is included in a cookie/header in the request", "red") # cprintc(headertoken, cookietoken, "cyan") @@ -1830,6 +1841,8 @@ def printLogo(): help="request cookies to send with the forged HTTP request") parser.add_argument("-rh", "--headers", action="append", help="request headers to send with the forged HTTP request (can be used multiple times for additional headers)") + parser.add_argument("-gd", "--getdata", action="store", + help="text string that contains all the data to be sent in a GET request") parser.add_argument("-pd", "--postdata", action="store", help="text string that contains all the data to be sent in a POST request") parser.add_argument("-cv", "--canaryvalue", action="store", @@ -1898,7 +1911,7 @@ def printLogo(): pass findJWT = "" if args.targeturl: - if args.cookies or args.headers or args.postdata: + if args.cookies or args.headers or args.postdata or args.getdata: jwt_count = 0 jwt_locations = [] @@ -1914,8 +1927,12 @@ def printLogo(): jwt_count += 1 jwt_locations.append("post data") + if args.getdata and re.search('eyJ[A-Za-z0-9_\/+-]*\.eyJ[A-Za-z0-9_\/+-]*\.[A-Za-z0-9._\/+-]*', str(args.getdata)): + jwt_count += 1 + jwt_locations.append("get data") + if jwt_count > 1: - cprintc("Too many tokens! JWT in more than one place: cookie, header, POST data", "red") + cprintc("Too many tokens! JWT in more than one place: cookie, header, POST data, or GET data", "red") exit(1) if args.cookies: @@ -1942,10 +1959,19 @@ def printLogo(): cprintc("Invalid postdata formatting", "red") exit(1) + if args.getdata: + try: + if re.search('eyJ[A-Za-z0-9_\/+-]*\.eyJ[A-Za-z0-9_\/+-]*\.[A-Za-z0-9._\/+-]*', str(args.getdata)): + config['argvals']['headerloc'] = "getdata" + except: + cprintc("Invalid getdata formatting", "red") + exit(1) + searchString = " | ".join([ str(args.cookies), str(args.headers), - str(args.postdata) + str(args.postdata), + str(args.getdata) ]) try: @@ -2020,6 +2046,8 @@ def printLogo(): config['argvals']['headervalue'] = str(args.headervalue) if args.postdata: config['argvals']['postData'] = args.postdata + if args.getdata: + config['argvals']['getData'] = args.getdata if args.canaryvalue: config['argvals']['canaryvalue'] = args.canaryvalue if args.noproxy: