diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4bfe59e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +config/config.ini diff --git a/README.md b/README.md index 5f278b9..6ea7937 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,18 @@ Please [refer to the documentation](https://razorpay.com/docs/payments/dashboard ## Usage ### Query all possible UPI addresses for the provided phone number -`upi-recon.py ` +`upi-recon.py -p ` ### Query all possible UPI addresses for the provided phone number using a specified number of threads -`upi-recon.py -t 5` +`upi-recon.py -p -t 5` ### Query a single UPI address for the provided phone number using a provided suffix -`upi-recon.py -s ` +`upi-recon.py -p -s ` +### Query all possible UPI addresses for the provided Gmail address +`upi-recon.py -g ` + ## Contributions Contributions are welcome. Feature wishlist: +- [x] Introduce support for Google Pay addresses - [ ] Introduce support for more API providers - [ ] Introduce support for wordlist based address discovery - [ ] Improve argument parsing code diff --git a/config/config.ini b/config/config.ini deleted file mode 100644 index 400f249..0000000 --- a/config/config.ini +++ /dev/null @@ -1,2 +0,0 @@ -[main] -api_key_id = key_goes_here \ No newline at end of file diff --git a/config/config.ini.example b/config/config.ini.example new file mode 100644 index 0000000..d33b16c --- /dev/null +++ b/config/config.ini.example @@ -0,0 +1,2 @@ +[main] +api_key_id = your_key_goes_here \ No newline at end of file diff --git a/upi-recon.py b/upi-recon.py index 0c16f87..feec95b 100644 --- a/upi-recon.py +++ b/upi-recon.py @@ -2,9 +2,9 @@ # -*- coding: utf-8 -*- import argparse +import os import requests import concurrent.futures -import signal from time import sleep from random import uniform as rand @@ -24,13 +24,15 @@ # Author: Karan Saini (@squeal) # URL: https://github.com/qurbat/upi-recon - # Usage: upi-recon.py [query all possible UPI addresses] - upi-recon.py -t 5 [query all possible UPI addresses with specified number of threads] - upi-recon.py -s [query a single UPI address with a specific suffix] + # Usage: upi-recon.py (query all possible UPI addresses) + upi-recon.py -t 5 (query all possible UPI addresses with specified number of threads) + upi-recon.py -s (query a single UPI address with a specific suffix) + upi-recon.py -g (query common Google Pay UPI addresses for specified google account) """ upi_suffix_dict = ['airtel', 'airtelpaymentsbank', 'apl', 'abfspay', 'allbank', 'andb', 'aubank', 'axis', 'albk', 'allahabadbank', 'apb', 'axisb', 'axisbank', 'axisgo', 'barodampay', 'barodapay', 'bandhan', 'birla', 'boi', 'cbin', 'cboi', 'centralbank', 'cnrb', 'dlb', 'eazypay', 'ezeepay', 'fbl', 'federal', 'freecharge', 'cmsidfc', 'csbcash', 'csbpay', 'cub', 'dbs', 'dcb', 'denabank', 'equitas', 'finobank', 'hdfcbank', 'hdfcbankjd', 'hsbc', 'ibl', 'icici', 'idbi', 'idbibank', 'idfcbank', 'icicibank', 'idfc', 'idfcnetc', 'ikwik', 'imobile', 'indianbank', 'indus', 'jkb', 'karurvysyabank', 'kaypay', 'kbl', 'kmb', 'kmbl', 'kotak', 'kvb', 'kvbank', 'lime', 'mahb', 'myicici', 'obc', 'okaxis', 'okhdfcbank', 'okicici', 'oksbi', 'paytm', 'payzapp', 'pingpay', 'pnb', 'pockets', 'rajgovhdfcbank', 'rbl', 'rmhdfcbank', 'sbi', 'sib', 'ubi', 'uboi', 'uco', 'unionbank', 'unionbankofindia', 'united', 'upi', 'utbi', 'ybl', 'yesbank', 'yesbankltd', 'indbank', 'indianbk', 'iob', 'jsbp', 'karb', 'lvb', 'lvbank', 'psb', 'purz', 'sc', 'scb', 'scbl', 'scmobile', 'srcb', 'synd', 'syndbank', 'syndicate', 'tjsb', 'vijayabank', 'vijb', 'vjb'] +gpay_suffix_dict = ['okicici', 'oksbi', 'okaxis', 'okhdfcbank'] def address_discovery(vpa, api_url): try: @@ -51,7 +53,6 @@ def address_discovery(vpa, api_url): # argument definition parser = argparse.ArgumentParser(description='fetch UPI addresses and associated information for a given phone number') # primary arguments - parser.add_argument('phone', type=str, help='phone number to query UPI addresses for') parser.add_argument('-t', '--threads', type=int, default=None, help='number of threads to use for parallel address discovery') parser.add_argument('-q', '--quiet', default=False, action='store_true', help='suppress banner') # group arguments @@ -60,9 +61,15 @@ def address_discovery(vpa, api_url): group_2 = parser.add_mutually_exclusive_group() group_2.add_argument('-a', '--all', default=True, action='store_true', help='query all suffixes') group_2.add_argument('-s', '--suffix', type=str, help='query a specific suffix') + group_3 = parser.add_mutually_exclusive_group() + group_3.add_argument('phone', type=str, nargs='?', help='phone number to query UPI addresses for') + group_3.add_argument('-g', '--gpay', type=str, nargs='?', help='enter gmail address to query Google Pay UPI addresses for') # parse arguments arguments = parser.parse_args() # check the configuration + if not os.path.exists('config/config.ini'): + print('[!] config/config.ini not found! please create the config file\n[!] you may refer to config/config.ini.example for help') + exit(1) config = ConfigParser() config_file = 'config/config.ini' config.read(config_file) @@ -76,17 +83,34 @@ def address_discovery(vpa, api_url): # set variables and normalize input API_URL = 'https://api.razorpay.com/v1/payments/validate/account?key_id=' api_key_id = config.get('main', 'api_key_id') - phone = arguments.phone[2:] if arguments.phone[0:2] == '91' and len(arguments.phone) > 10 else arguments.phone + if not arguments.gpay and not arguments.phone: + print('[!] please enter a phone number or gmail address') + exit(1) + if arguments.gpay and not arguments.phone: + email = arguments.gpay[:-10] if arguments.gpay.endswith('@gmail.com') else arguments.gpay + phone = '8888888888' + elif arguments.phone and arguments.gpay: + print('[!] please enter either a phone number or a gmail address') + exit(1) + elif arguments.phone: + phone = arguments.phone[2:] if arguments.phone[0:2] == '91' and len(arguments.phone) > 10 else arguments.phone + if len(phone) != 10: + print('[!] please enter a valid 10 digit phone number') + exit(1) # check if api_key_id is correct if api_key_id and not api_key_id[0:3] == 'rzp': quit('[!] invalid api_key_id') if not phone.isdigit(): - quit('[!] phone number must be numeric') + quit('[!] phone number must be numeric. use -g to query Google Pay addresses') # informational header print('[i] starting at ' + datetime.now().strftime("%d/%m/%Y %H:%M:%S")) # do the thing - if arguments.suffix: # query one + if arguments.suffix and arguments.gpay: + print('[!] cannot use suffix and gpay at the same time. please specify only one.') + exit(1) + + elif arguments.suffix: arguments.all = False suffix = arguments.suffix[1:] if arguments.suffix[0] == '@' else arguments.suffix print('[i] querying UPI addresses for phone number ' + phone) @@ -94,6 +118,26 @@ def address_discovery(vpa, api_url): print('[i] finished at ' + datetime.now().strftime("%d/%m/%Y %H:%M:%S")) exit(1) + elif arguments.gpay: + if arguments.threads: + print('[i] querying Google Pay UPI addresses for ' + email + '@gmail.com with ' + str(arguments.threads) + ' threads') + with concurrent.futures.ThreadPoolExecutor(max_workers=arguments.threads) as executor: + for suffix in gpay_suffix_dict: + try: + executor.submit(address_discovery, email + '@' + suffix, API_URL + api_key_id) + sleep(rand(0.1, 0.2)) + except KeyboardInterrupt as e: + print('\n[!] interrupted! stopping threads...') + exit(1) + print ('[i] finished at ' + datetime.now().strftime("%d/%m/%Y %H:%M:%S")) + exit(1) + else: + print('[i] querying Google Pay UPI addresses for ' + email + '@gmail.com') + for suffix in track(gpay_suffix_dict): + address_discovery(email + '@' + suffix, API_URL + api_key_id) + print('[i] finished at ' + datetime.now().strftime("%d/%m/%Y %H:%M:%S")) + exit(1) + elif arguments.all and not arguments.threads: # query all with no concurrency print('[i] querying UPI addresses for phone number ' + phone) for suffix in track(upi_suffix_dict): @@ -101,8 +145,8 @@ def address_discovery(vpa, api_url): print('[i] finished at ' + datetime.now().strftime("%d/%m/%Y %H:%M:%S")) exit(1) - elif arguments.threads: # query all with threading - print('[i] querying UPI addresses for phone number ' + phone) + elif arguments.threads and not arguments.gpay: # query all with threading + print('[i] querying UPI addresses for phone number ' + phone + ' with ' + str(arguments.threads) + ' threads') with concurrent.futures.ThreadPoolExecutor(max_workers=arguments.threads) as executor: for suffix in upi_suffix_dict: try: @@ -111,5 +155,7 @@ def address_discovery(vpa, api_url): except KeyboardInterrupt: print('\n[!] interrupted! stopping threads...') exit(1) - print('[i] finished at ' + datetime.now().strftime("%d/%m/%Y %H:%M:%S")) + finally: + print('[i] finished at ' + datetime.now().strftime("%d/%m/%Y %H:%M:%S")) + exit(1) exit(1)