Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added functionality for JWT tokens sent within GET request parameters #57

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
42 changes: 35 additions & 7 deletions jwt_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ def createConfig():
'scanMode': '',
'reqMode': '',
'postData': '',
'getData': '',
'resCode': '',
'resSize': '',
'resContent': ''}
Expand All @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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])
Expand All @@ -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")
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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 = []

Expand All @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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:
Expand Down