2018-11-09 20:32:08 +05:30
#!/usr/bin/env python3
2020-12-06 15:06:15 +05:30
# -*- coding: utf-8 -*-
2018-11-09 20:32:08 +05:30
2021-02-07 19:43:30 +05:30
from arjun . core . colors import green , end , info , bad , good , run , res
2019-04-12 18:08:22 +05:30
2018-03-01 19:16:28 +05:30
import argparse
2022-09-11 14:36:51 +05:30
import json
2019-04-12 18:08:22 +05:30
2020-12-06 15:06:15 +05:30
from urllib . parse import urlparse
2021-02-07 19:43:30 +05:30
import arjun . core . config as mem
from arjun . core . bruter import bruter
from arjun . core . exporter import exporter
from arjun . core . requester import requester
2022-09-11 02:57:13 +05:30
from arjun . core . anomaly import define , compare
2021-03-11 05:37:23 +05:30
from arjun . core . utils import fetch_params , stable_request , random_str , slicer , confirm , populate , reader , nullify , prepare_requests , compatible_path
2020-12-06 15:06:15 +05:30
2021-02-07 19:43:30 +05:30
from arjun . plugins . heuristic import heuristic
2018-03-01 19:16:28 +05:30
2021-06-15 23:01:34 +05:30
arjun_dir = compatible_path ( mem . __file__ . replace ( compatible_path ( ' /core/config.py ' ) , ' ' ) )
2021-02-08 08:18:43 +05:30
2019-10-23 12:59:47 +05:30
parser = argparse . ArgumentParser ( ) # defines the parser
# Arguments that can be supplied
2021-06-10 13:02:44 +03:00
parser . add_argument ( ' -u ' , help = ' Target URL ' , dest = ' url ' )
parser . add_argument ( ' -o ' , ' -oJ ' , help = ' Path for json output file. ' , dest = ' json_file ' )
parser . add_argument ( ' -oT ' , help = ' Path for text output file. ' , dest = ' text_file ' )
2024-01-08 11:55:50 +07:00
parser . add_argument ( ' -oB ' , help = ' Output to Burp Suite Proxy. Default is 127.0.0.1:8080. ' , dest = ' burp_proxy ' , nargs = ' ? ' , const = ' 127.0.0.1:8080 ' )
2021-06-10 13:02:44 +03:00
parser . add_argument ( ' -d ' , help = ' Delay between requests in seconds. (default: 0) ' , dest = ' delay ' , type = float , default = 0 )
2022-09-11 02:57:13 +05:30
parser . add_argument ( ' -t ' , help = ' Number of concurrent threads. (default: 5) ' , dest = ' threads ' , type = int , default = 5 )
2022-04-04 14:47:27 +05:30
parser . add_argument ( ' -w ' , help = ' Wordlist file path. (default: {arjundir} /db/large.txt) ' , dest = ' wordlist ' , default = arjun_dir + ' /db/large.txt ' )
2022-09-11 14:36:51 +05:30
parser . add_argument ( ' -m ' , help = ' Request method to use: GET/POST/XML/JSON/HEADERS. (default: GET) ' , dest = ' method ' , default = ' GET ' )
2021-06-10 13:02:44 +03:00
parser . add_argument ( ' -i ' , help = ' Import target URLs from file. ' , dest = ' import_file ' , nargs = ' ? ' , const = True )
parser . add_argument ( ' -T ' , help = ' HTTP request timeout in seconds. (default: 15) ' , dest = ' timeout ' , type = float , default = 15 )
2023-11-16 18:00:17 +05:30
parser . add_argument ( ' -c ' , help = ' Chunk size. The number of parameters to be sent at once ' , type = int , dest = ' chunks ' , default = 250 )
2021-06-10 15:20:29 +03:00
parser . add_argument ( ' -q ' , help = ' Quiet mode. No output. ' , dest = ' quiet ' , action = ' store_true ' )
2021-06-10 13:02:44 +03:00
parser . add_argument ( ' --headers ' , help = ' Add headers. Separate multiple headers with a new line. ' , dest = ' headers ' , nargs = ' ? ' , const = True )
parser . add_argument ( ' --passive ' , help = ' Collect parameter names from passive sources like wayback, commoncrawl and otx. ' , dest = ' passive ' , nargs = ' ? ' , const = ' - ' )
parser . add_argument ( ' --stable ' , help = ' Prefer stability over speed. ' , dest = ' stable ' , action = ' store_true ' )
parser . add_argument ( ' --include ' , help = ' Include this data in every request. ' , dest = ' include ' , default = { } )
2022-04-09 21:35:06 +05:30
parser . add_argument ( ' --disable-redirects ' , help = ' disable redirects ' , dest = ' disable_redirects ' , action = ' store_true ' )
2019-10-23 12:59:47 +05:30
args = parser . parse_args ( ) # arguments to be parsed
2018-03-01 19:16:28 +05:30
2021-02-07 19:43:30 +05:30
if args . quiet :
print = nullify
print ( ''' %s _
/ _ | _ '
( | / / ( / / ) v % s
_ / % s
''' % (green, __import__( ' arjun ' ).__version__, end))
try :
from concurrent . futures import ThreadPoolExecutor , as_completed
except ImportError :
print ( ' %s Please use Python > 3.2 to run Arjun. ' % bad )
quit ( )
2020-12-06 15:06:15 +05:30
mem . var = vars ( args )
2019-04-12 18:08:22 +05:30
2020-12-06 15:26:32 +05:30
mem . var [ ' method ' ] = mem . var [ ' method ' ] . upper ( )
2023-11-16 18:00:17 +05:30
if mem . var [ ' method ' ] != ' GET ' :
mem . var [ ' chunks ' ] = 500
2020-12-06 15:06:15 +05:30
if mem . var [ ' stable ' ] or mem . var [ ' delay ' ] :
mem . var [ ' threads ' ] = 1
2022-04-04 14:47:27 +05:30
if mem . var [ ' wordlist ' ] in ( ' large ' , ' medium ' , ' small ' ) :
mem . var [ ' wordlist ' ] = f ' { arjun_dir } /db/ { mem . var [ " wordlist " ] } .txt '
2018-03-01 19:16:28 +05:30
2018-11-09 20:32:08 +05:30
try :
2021-06-15 23:01:34 +05:30
wordlist_file = arjun_dir + ' /db/small.txt ' if args . wordlist == ' small ' else args . wordlist
2021-03-11 05:37:23 +05:30
wordlist_file = compatible_path ( wordlist_file )
2021-02-07 19:43:30 +05:30
wordlist = set ( reader ( wordlist_file , mode = ' lines ' ) )
2020-12-06 15:06:15 +05:30
if mem . var [ ' passive ' ] :
host = mem . var [ ' passive ' ]
if host == ' - ' :
host = urlparse ( args . url ) . netloc
print ( ' %s Collecting parameter names from passive sources for %s , it may take a while ' % ( run , host ) )
passive_params = fetch_params ( host )
wordlist . update ( passive_params )
print ( ' %s Collected %s parameters, added to the wordlist ' % ( info , len ( passive_params ) ) )
wordlist = list ( wordlist )
2018-11-09 20:32:08 +05:30
except FileNotFoundError :
2020-12-06 15:06:15 +05:30
exit ( ' %s The specified file for parameters doesn \' t exist ' % bad )
2020-12-08 20:55:17 +05:30
if len ( wordlist ) < mem . var [ ' chunks ' ] :
2021-02-07 19:43:30 +05:30
mem . var [ ' chunks ' ] = int ( len ( wordlist ) / 2 )
if not args . url and not args . import_file :
exit ( ' %s No target(s) specified ' % bad )
2020-12-06 15:06:15 +05:30
def narrower ( request , factors , param_groups ) :
2021-02-07 19:43:30 +05:30
"""
takes a list of parameters and narrows it down to parameters that cause anomalies
returns list
"""
2021-07-13 14:18:21 +02:00
anomalous_params = [ ]
2020-12-06 15:06:15 +05:30
threadpool = ThreadPoolExecutor ( max_workers = mem . var [ ' threads ' ] )
futures = ( threadpool . submit ( bruter , request , factors , params ) for params in param_groups )
for i , result in enumerate ( as_completed ( futures ) ) :
2019-03-02 07:31:47 +05:30
if result . result ( ) :
2021-07-13 14:18:21 +02:00
anomalous_params . extend ( slicer ( result . result ( ) ) )
2022-04-04 14:47:27 +05:30
if mem . var [ ' kill ' ] :
return anomalous_params
print ( ' %s Processing chunks: %i / %-6i ' % ( info , i + 1 , len ( param_groups ) ) , end = ' \r ' )
2021-07-13 14:18:21 +02:00
return anomalous_params
2020-12-06 15:06:15 +05:30
2021-02-07 19:43:30 +05:30
2022-09-11 02:57:13 +05:30
def initialize ( request , wordlist , single_url = False ) :
2021-02-07 19:43:30 +05:30
"""
handles parameter finding process for a single request object
returns ' skipped ' ( on error ) , list on success
"""
2020-12-06 15:06:15 +05:30
url = request [ ' url ' ]
2020-12-06 23:57:06 +05:30
if not url . startswith ( ' http ' ) :
print ( ' %s %s is not a valid URL ' % ( bad , url ) )
return ' skipped '
2020-12-06 15:06:15 +05:30
print ( ' %s Probing the target for stability ' % run )
2022-04-04 14:47:27 +05:30
request [ ' url ' ] = stable_request ( url , request [ ' headers ' ] )
if not request [ ' url ' ] :
2020-12-06 15:06:15 +05:30
return ' skipped '
2019-10-23 12:59:47 +05:30
else :
2023-11-16 18:00:17 +05:30
fuzz = " z " + random_str ( 6 )
response_1 = requester ( request , { fuzz [ : - 1 ] : fuzz [ : : - 1 ] [ : - 1 ] } )
2022-09-11 02:57:13 +05:30
if single_url :
print ( ' %s Analysing HTTP response for anomalies ' % run )
2023-11-16 18:00:17 +05:30
response_2 = requester ( request , { fuzz [ : - 1 ] : fuzz [ : : - 1 ] [ : - 1 ] } )
2020-12-06 15:06:15 +05:30
if type ( response_1 ) == str or type ( response_2 ) == str :
return ' skipped '
2023-11-16 18:00:17 +05:30
# params from response must be extracted before factors but displayed later
found , words_exist = heuristic ( response_1 , wordlist )
2020-12-06 15:06:15 +05:30
factors = define ( response_1 , response_2 , fuzz , fuzz [ : : - 1 ] , wordlist )
2023-11-16 18:00:17 +05:30
zzuf = " z " + random_str ( 6 )
response_3 = requester ( request , { zzuf [ : - 1 ] : zzuf [ : : - 1 ] [ : - 1 ] } )
while factors :
reason = compare ( response_3 , factors , { zzuf [ : - 1 ] : zzuf [ : : - 1 ] [ : - 1 ] } ) [ 2 ]
if not reason :
break
factors [ reason ] = [ ]
2022-09-11 02:57:13 +05:30
if single_url :
print ( ' %s Analysing HTTP response for potential parameter names ' % run )
2020-12-06 15:06:15 +05:30
if found :
num = len ( found )
2022-09-11 14:36:51 +05:30
if words_exist :
print ( ' %s Heuristic scanner found %i parameters ' % ( good , num ) )
else :
s = ' s ' if num > 1 else ' '
print ( ' %s Heuristic scanner found %i parameter %s : %s ' % ( good , num , s , ' , ' . join ( found ) ) )
2022-09-11 02:57:13 +05:30
if single_url :
print ( ' %s Logicforcing the URL endpoint ' % run )
2020-12-06 15:06:15 +05:30
populated = populate ( wordlist )
2022-09-11 14:36:51 +05:30
with open ( f ' { arjun_dir } /db/special.json ' , ' r ' ) as f :
populated . update ( json . load ( f ) )
2020-12-08 20:55:17 +05:30
param_groups = slicer ( populated , int ( len ( wordlist ) / mem . var [ ' chunks ' ] ) )
2022-09-11 02:57:13 +05:30
prev_chunk_count = len ( param_groups )
2020-12-06 15:06:15 +05:30
last_params = [ ]
2019-10-23 12:59:47 +05:30
while True :
2020-12-06 15:06:15 +05:30
param_groups = narrower ( request , factors , param_groups )
2022-09-11 02:57:13 +05:30
if len ( param_groups ) > prev_chunk_count :
2023-11-16 18:00:17 +05:30
response_3 = requester ( request , { zzuf [ : - 1 ] : zzuf [ : : - 1 ] [ : - 1 ] } )
if compare ( response_3 , factors , { zzuf [ : - 1 ] : zzuf [ : : - 1 ] [ : - 1 ] } ) [ 0 ] != ' ' :
2022-09-29 22:22:17 +08:00
print ( ' %s Target is misbehaving. Try the --stable switch. ' % bad )
2022-09-11 02:57:13 +05:30
return [ ]
2020-12-06 15:06:15 +05:30
if mem . var [ ' kill ' ] :
return ' skipped '
param_groups = confirm ( param_groups , last_params )
2022-09-11 02:57:13 +05:30
prev_chunk_count = len ( param_groups )
2020-12-06 15:06:15 +05:30
if not param_groups :
2019-07-02 13:53:54 +05:30
break
2020-12-06 15:06:15 +05:30
confirmed_params = [ ]
for param in last_params :
reason = bruter ( request , factors , param , mode = ' verify ' )
if reason :
name = list ( param . keys ( ) ) [ 0 ]
confirmed_params . append ( name )
2022-09-11 14:36:51 +05:30
if single_url :
print ( ' %s parameter detected: %s , based on: %s ' % ( res , name , reason ) )
2020-12-06 15:06:15 +05:30
return confirmed_params
2019-04-12 18:08:22 +05:30
2021-02-07 19:43:30 +05:30
def main ( ) :
request = prepare_requests ( args )
2019-10-23 12:59:47 +05:30
2021-02-07 19:43:30 +05:30
final_result = { }
try :
if type ( request ) == dict :
# in case of a single target
2020-12-06 15:06:15 +05:30
mem . var [ ' kill ' ] = False
2021-02-07 19:43:30 +05:30
url = request [ ' url ' ]
2022-09-11 14:36:51 +05:30
these_params = initialize ( request , wordlist , single_url = True )
2020-12-06 15:06:15 +05:30
if these_params == ' skipped ' :
2021-02-07 19:43:30 +05:30
print ( ' %s Skipped %s due to errors ' % ( bad , request [ ' url ' ] ) )
2021-02-08 08:18:43 +05:30
elif these_params :
2020-12-06 15:06:15 +05:30
final_result [ url ] = { }
final_result [ url ] [ ' params ' ] = these_params
2021-02-07 19:43:30 +05:30
final_result [ url ] [ ' method ' ] = request [ ' method ' ]
final_result [ url ] [ ' headers ' ] = request [ ' headers ' ]
2022-09-11 14:36:51 +05:30
print ( ' %s Parameters found: %s ' % ( good , ' , ' . join ( final_result [ url ] [ ' params ' ] ) ) )
2022-09-11 02:57:13 +05:30
exporter ( final_result )
else :
print ( ' %s No parameters were discovered. ' % info )
2021-02-07 19:43:30 +05:30
elif type ( request ) == list :
# in case of multiple targets
2022-09-11 02:57:13 +05:30
count = 0
2021-02-07 19:43:30 +05:30
for each in request :
2022-09-11 02:57:13 +05:30
count + = 1
2021-02-07 19:43:30 +05:30
url = each [ ' url ' ]
mem . var [ ' kill ' ] = False
2022-09-11 02:57:13 +05:30
print ( ' %s Scanning %d / %d : %s ' % ( run , count , len ( request ) , url ) )
2021-02-07 19:43:30 +05:30
these_params = initialize ( each , list ( wordlist ) )
if these_params == ' skipped ' :
print ( ' %s Skipped %s due to errors ' % ( bad , url ) )
elif these_params :
final_result [ url ] = { }
final_result [ url ] [ ' params ' ] = these_params
final_result [ url ] [ ' method ' ] = each [ ' method ' ]
final_result [ url ] [ ' headers ' ] = each [ ' headers ' ]
2022-09-11 02:57:13 +05:30
exporter ( final_result )
print ( ' %s Parameters found: %s \n ' % ( good , ' , ' . join ( final_result [ url ] [ ' params ' ] ) ) )
if not mem . var [ ' json_file ' ] :
final_result = { }
continue
else :
print ( ' %s No parameters were discovered. \n ' % info )
2021-02-07 19:43:30 +05:30
except KeyboardInterrupt :
exit ( )
if __name__ == ' __main__ ' :
main ( )