#!/usr/bin/env python __version__ = '0.3-dev' print "\n \033[92m" print " ___ _ _____ __ " print " / _ \ |__ ___ _ __ ___ \_ \_ __ / _| ___ __ _ __ _ " print " / /_)/ '_ \ / _ \| '_ \ / _ \ / /\/ '_ \| |_ / _ \ / _` |/ _` |" print " / ___/| | | | (_) | | | | __/\/ /_ | | | | _| (_) | (_| | (_| |" print " \/ |_| |_|\___/|_| |_|\___\____/ |_| |_|_| \___/ \__, |\__,_|" print " |___/ " print " PhoneInfoga Ver. %s " % __version__ print " Coded by Sundowndev " print "\033[94m\n" import requests import sys import hashlib import json import argparse from bs4 import BeautifulSoup import re import phonenumbers from phonenumbers import carrier from phonenumbers import geocoder from phonenumbers import timezone parser = argparse.ArgumentParser(description= "Advanced information gathering tool for phone numbers (https://github.com/sundowndev/PhoneInfoga) version %s" % __version__, usage='%(prog)s -n [options]') parser.add_argument('-n', '--number', metavar='number', type=str, help='The phone number to scan (E164 or international format)') parser.add_argument('-i', '--input', metavar="input_file", type=file, help='Phone number list to scan (one per line)') parser.add_argument('-o', '--output', metavar="output_file", type=file, help='Output to save scan results') parser.add_argument('-s', '--scanner', metavar="scanner", default="all", type=str, help='The scanner to use') parser.add_argument('--osint', action='store_true', help='Use OSINT reconnaissance') parser.add_argument('-u', '--update', action='store_true', help='Update the tool & databases') args = parser.parse_args() # If any param is passed, execute help command if not len(sys.argv) > 1: parser.print_help() if args.update: print 'update' sys.exit() scanners = ['any', 'all', 'numverify', 'ovh', 'whosenumber', 'freecarrier', '411'] code_info = '\033[97m[*] ' code_warning = '\033[93m(!) ' code_result = '\033[1;32m[+] ' code_error = '\033[91m[!] ' def saveToOutput(output): print 'save' def localScan(number): print code_info + 'Running local scan...' PhoneNumber = dict(); FormattedPhoneNumber = number.replace("\n", "").replace("-", "").replace(" ", "") try: PhoneNumberObject = phonenumbers.parse(FormattedPhoneNumber, None) except: return False else: if not phonenumbers.is_valid_number(PhoneNumberObject): return False PhoneNumber['full'] = phonenumbers.format_number(PhoneNumberObject, phonenumbers.PhoneNumberFormat.E164).replace('+', '') PhoneNumber['countryCode'] = phonenumbers.format_number(PhoneNumberObject, phonenumbers.PhoneNumberFormat.INTERNATIONAL).split(' ')[0] PhoneNumber['number'] = phonenumbers.format_number(PhoneNumberObject, phonenumbers.PhoneNumberFormat.E164).replace(PhoneNumber['countryCode'], '') print code_result + 'Local format: (0)' + PhoneNumber['number'] print code_result + 'Country code: ' + PhoneNumber['countryCode'] print code_result + 'Location: %s' % geocoder.description_for_number(PhoneNumberObject, "en") print code_result + 'Carrier: %s' % carrier.name_for_number(PhoneNumberObject, 'en') print code_result + 'Area: %s' % geocoder.description_for_number(PhoneNumberObject, 'en') #print '\033[1;32m[+] Timezone: %s, %s' % (timezone.time_zones_for_number(PhoneNumberObject)[0],timezone.time_zones_for_number(PhoneNumberObject)[1]) #print code_info + 'This is most likely a landline, or a fixed VoIP.' if phonenumbers.is_possible_number(PhoneNumberObject): print code_info + 'The number is valid and possible.' else: print code_warning + 'The number is valid but might not be possible.' return PhoneNumber def numverifyScan(PhoneNumber): if not args.scanner == 'numverify' and not args.scanner == 'all': return -1 print code_info + 'Running Numverify.com scan...' requestSecret = '' resp = requests.get('https://numverify.com/') soup = BeautifulSoup(resp.text, "html5lib") for tag in soup.find_all("input", type="hidden"): if tag['name'] == "scl_request_secret": requestSecret = tag['value'] break; apiKey = hashlib.md5() apiKey.update(PhoneNumber + requestSecret) apiKey = apiKey.hexdigest() headers = { 'host': "numverify.com", 'connection': "keep-alive", 'content-length': "49", 'accept': "application/json, text/javascript, */*; q=0.01", 'origin': "https://numverify.com", 'x-requested-with': "XMLHttpRequest", 'user-agent': "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36", 'content-type': "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW", 'referer': "https://numverify.com/", 'accept-encoding': "gzip, deflate, br", 'accept-language': "en-US,en;q=0.9,fr;q=0.8,la;q=0.7,es;q=0.6,zh-CN;q=0.5,zh;q=0.4", 'cache-control': "no-cache" } response = requests.request("GET", "https://numverify.com/php_helper_scripts/phone_api.php?secret_key=" + apiKey + "&number=" + PhoneNumber, data="", headers=headers) if response.content == "Unauthorized" or response.status_code != 200: print(code_error + "An error occured while calling the API (bad request or wrong api key).") sys.exit() data = json.loads(response.content) if data["valid"] == False: print(code_error + "Error: Please specify a valid phone number. Example: +6464806649") sys.exit() InternationalNumber = '('+data["country_prefix"]+')' + data["local_format"] print(code_result + "Number: (%s) %s") % (data["country_prefix"],data["local_format"]) print(code_result + "Country: %s (%s)") % (data["country_name"],data["country_code"]) print(code_result + "Location: %s") % data["location"] print(code_result + "Carrier: %s") % data["carrier"] print(code_result + "Line type: %s") % data["line_type"] def ovhScan(country, number): if not args.scanner == 'ovh' and not args.scanner == 'all': return -1 print code_info + 'Running OVH scan...' querystring = {"country":country} headers = { 'accept': "application/json", 'cache-control': "no-cache" } response = requests.request("GET", "https://api.ovh.com/1.0/telephony/number/detailedZones", data="", headers=headers, params=querystring) data = json.loads(response.content) def whosenumberScan(countryCode, number): if not args.scanner == 'whosenumber' and not args.scanner == 'all': return -1 print code_info + 'Running Whosenumber scan...' print 'https://whosenumber.info/' + countryCode + number def repScan(countryCode, number): if not args.scanner == '411' and not args.scanner == 'all': return -1 print code_info + 'Running 411.com scan...' print 'https://www.411.com/phone/%s-%s' % (countryCode,number) def freecarrierlookupScan(countryCode, number): if not args.scanner == 'freecarrier' and not args.scanner == 'all': return -1 print code_info + 'Running freecarrierlookup.com scan...' payload = "phonenum=%s&cc=%s" % (number,countryCode) headers = { 'host': "freecarrierlookup.com", 'connection': "keep-alive", 'content-length': "48", 'accept': "application/json, text/javascript, */*; q=0.01", 'origin': "https://freecarrierlookup.com", 'x-requested-with': "XMLHttpRequest", 'user-agent': "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36", 'content-type': "application/x-www-form-urlencoded", 'referer': "https://freecarrierlookup.com/", 'accept-encoding': "gzip, deflate, br", 'accept-language': "en-US,en;q=0.9,fr;q=0.8,la;q=0.7,es;q=0.6,zh-CN;q=0.5,zh;q=0.4", 'cookie': "PHPSESSID=cdifm9u3ch2mqscdnj2pjqjfuq", 'cache-control': "no-cache", 'postman-token': "c81a7bb0-f338-c2e5-5f32-e1b94726cce5" } response = requests.request("POST", "https://freecarrierlookup.com/getcarrier.php", data=payload, headers=headers) print response.content data = json.loads(response.content) if not data["status"] == "success": print code_error + '0 result found.' return -1 soup = BeautifulSoup(response.content, "html5lib") tags = soup.find_all("p") print code_result + 'Phone Number: ' + tags[0].string.replace('<\/p>\\n <\/div>\\n', '') print code_result + 'Carrier: ' + tags[1].string.replace('<\/p>\\n <\/div>\\n', '') print code_result + 'Is Wireless:' print code_result + 'SMS Gateway Address: ' print code_result + 'MMS Gateway Address: ' def scanNumber(number): print "\033[1m\033[93m[!] ---- Fetching informations for %s ---- [!]" % number print code_info + 'Parsing informations...' PhoneNumber = localScan(number) if not PhoneNumber: print(code_error + "Error: number " + number + " is not valid. Skipping.") sys.exit() numverifyScan(PhoneNumber['full']) ovhScan('fr', PhoneNumber['full']) # TODO: replace 1st parameter to be dynamic #freecarrierlookupScan(PhoneNumber['countryCode'], PhoneNumber['number']) #whosenumberScan(PhoneNumber['countryCode'], PhoneNumber['number']) #repScan(PhoneNumber['countryCode'], PhoneNumber['number']) print '\n' # Verify scanner option if not args.scanner in scanners: print(code_error + "Error: scanner doesn't exists.") sys.exit() if args.number: scanNumber(args.number) elif args.input: for line in args.input.readlines(): scanNumber(line) if args.output: args.output.write("Hello World") args.output.close()