def list_kb(scan_id): """ List vulnerabilities stored in the KB (for a specific scan) Filters: * /scans/0/kb/?name= returns only vulnerabilities which contain the specified string in the vulnerability name. (contains) * /scans/0/kb/?url= returns only vulnerabilities for a specific URL (startswith) If more than one filter is specified they are combined using AND. :return: A JSON containing a list of: - KB resource URL (eg. /scans/0/kb/3) - The KB id (eg. 3) - The vulnerability name - The vulnerability URL - Location A - Location B """ scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') data = [] for finding_id, finding in enumerate(kb.kb.get_all_findings()): if matches_filter(finding, request): data.append(finding_to_json(finding, scan_id, finding_id)) return jsonify({'items': data})
def exception_creator(scan_id): """ Mostly for testing, but have fun playing ;) :return: None, just create a new exception in the exception handler for this scan, helps me with the unittest process. """ scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') current_status = FakeStatus(None) current_status.set_running_plugin('phase', 'plugin') current_status.set_current_fuzzable_request('phase', 'http://www.w3af.org/') try: raise Exception('unittest') except Exception, exception: exec_info = sys.exc_info() enabled_plugins = '' scan_info.w3af_core.exception_handler.write_crash_file = lambda x: x scan_info.w3af_core.exception_handler.handle(current_status, exception, exec_info, enabled_plugins)
def get_traffic_details(scan_id, traffic_id): """ The HTTP request and response associated with a vulnerability, usually the user will first get /scans/1/kb/3 and from there (if needed) browse to this resource where the HTTP traffic is available :param scan_id: The scan ID :param traffic_id: The ID of the request/response :return: HTTP request and response in base64 format """ scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') history_db = HistoryItem() try: details = history_db.read(traffic_id) except DBException: msg = 'Failed to retrieve request with id %s from DB.' abort(404, msg) return data = {'request': b64encode(details.request.dump()), 'response': b64encode(details.response.dump())} return jsonify(data)
def scan_log(scan_id): """ :param scan_id: The scan ID to retrieve the scan :return: The scan log contents, paginated using 200 entries per page """ scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') if scan_info.output is None: abort(404, 'Scan output not found') page = request.args.get('page', None) _id = request.args.get('id', None) if page is not None and not page.isdigit(): abort(400, 'Invalid page number') if _id is not None and not _id.isdigit(): abort(400, 'Invalid log id') if page is not None and _id is not None: abort(400, 'Can only paginate using one of "page" or "id"') next, next_url, log_entries = paginate_logs(scan_id, scan_info, page, _id) return jsonify({ 'next': next, 'next_url': next_url, 'entries': log_entries })
def scan_log(scan_id): """ :param scan_id: The scan ID to retrieve the scan :return: The scan log contents, paginated using 200 entries per page """ scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') if scan_info.output is None: abort(404, 'Scan output not found') page = request.args.get('page', None) _id = request.args.get('id', None) if page is not None and not page.isdigit(): abort(400, 'Invalid page number') if _id is not None and not _id.isdigit(): abort(400, 'Invalid log id') if page is not None and _id is not None: abort(400, 'Can only paginate using one of "page" or "id"') next, next_url, log_entries = paginate_logs(scan_id, scan_info, page, _id) return jsonify({'next': next, 'next_url': next_url, 'entries': log_entries})
def get_traffic_details(scan_id, traffic_id): """ The HTTP request and response associated with a vulnerability, usually the user will first get /scans/1/kb/3 and from there (if needed) browse to this resource where the HTTP traffic is available :param scan_id: The scan ID :param traffic_id: The ID of the request/response :return: HTTP request and response in base64 format """ scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') history_db = HistoryItem() try: details = history_db.read(traffic_id) except DBException: msg = 'Failed to retrieve request with id %s from DB.' abort(404, msg) return data = { 'request': b64encode(details.request.dump()), 'response': b64encode(details.response.dump()) } return jsonify(data)
def list_kb(scan_id): """ List vulnerabilities stored in the KB (for a specific scan) Filters: * /scans/0/kb/?name= returns only vulnerabilities which contain the specified string in the vulnerability name. (contains) * /scans/0/kb/?url= returns only vulnerabilities for a specific URL (startswith) If more than one filter is specified they are combined using AND. :return: A JSON containing a list of: - KB resource URL (eg. /scans/0/kb/3) - The KB id (eg. 3) - The vulnerability name - The vulnerability URL - Location A - Location B """ scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') data = [] for finding_id, finding in enumerate(kb.kb.get_all_findings()): if matches_filter(finding, request): data.append(finding_to_json(finding, scan_id, finding_id, True)) return jsonify({'items': data})
def decorated(*args, **kwargs): if not 'PASSWORD' in app.config: # Auth was not enabled at startup return f(*args, **kwargs) auth = request.authorization if not auth or not check_auth(auth.username, auth.password): abort(401, 'Could not verify access. Please specify a username and' ' password for HTTP basic authentication.') return f(*args, **kwargs)
def decorated(*args, **kwargs): if not 'PASSWORD' in app.config: # Auth was not enabled at startup return f(*args, **kwargs) auth = request.authorization if not auth or not check_auth(auth.username, auth.password): abort( 401, 'Could not verify access. Please specify a username and' ' password for HTTP basic authentication.') return f(*args, **kwargs)
def get_url_list(scan_id): """ A list with all the known URLs by this scanner :param scan_id: The scan ID :return: URLs in a list """ scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') data = [str(u) for u in kb.kb.get_all_known_urls()] return jsonify({'items': data})
def scan_status(scan_id): """ :param scan_id: The scan ID :return: The scan status """ scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') exc = scan_info.exception status = scan_info.w3af_core.status.get_status_as_dict() status['exception'] = exc if exc is None else str(exc) return jsonify(status)
def list_kb(scan_id): """ List vulnerabilities stored in the KB (for a specific scan) Filters: * /scans/0/kb/?name= returns only vulnerabilities which contain the specified string in the vulnerability name. (contains) * /scans/0/kb/?url= returns only vulnerabilities for a specific URL (startswith) If more than one filter is specified they are combined using AND. :return: A JSON containing a list of: - KB resource URL (eg. /scans/0/kb/3) - The KB id (eg. 3) - The vulnerability name - The vulnerability URL - Location A - Location B """ scanData = scanGetWithScanId(scan_id) if scanData != None and scanData.scanResult != None: return jsonify({'items': scanData.scanResult}) scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') data = [] print 'hostname', urlparse(scanGetUrl(scan_id)).hostname hostname = urlparse(scanGetUrl(scan_id)).hostname for finding_id, finding in enumerate(kb.kb.get_all_findings()): if finding.get_url() == None: continue; if matches_filter(finding, request) and urlparse(finding.get_url().url_string).hostname==hostname: data.append(finding_to_json(finding, scan_id, finding_id)) for id, scan_info in SCANS.iteritems(): if scan_info is None: continue target_urls = scan_info.target_urls status = scan_info.w3af_core.status.get_simplified_status() errors = True if scan_info.exception is not None else False if (errors == False and scan_id == id and status == 'Stopped'): scanData.scanResult = data return jsonify({'items': data})
def require_json(): """ Helper to let users know that they should submit the content-type header https://github.com/andresriancho/w3af/issues/13323 :return: abort() if an error occured """ if request.method in {'GET', 'HEAD', 'DELETE'}: return if request.mimetype != 'application/json': abort(400, NO_HEADER) json_data = request.get_json(silent=True) if json_data is None: abort(400, INVALID_JSON)
def get_kb(scan_id, vulnerability_id): """ The whole information related to the specified vulnerability ID :param vulnerability_id: The vulnerability ID to query :return: All the vulnerability information """ scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') for finding_id, finding in enumerate(kb.kb.get_all_findings()): if vulnerability_id == finding_id: return jsonify( finding_to_json(finding, scan_id, finding_id, detailed=True)) abort(404, 'Not found')
def get_fuzzable_request_list(scan_id): """ A list with all the known fuzzable requests by this scanner :param scan_id: The scan ID :return: Fuzzable requests (serialized as base64 encoded string) in a list """ scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') data = [] for fuzzable_request in kb.kb.get_all_known_fuzzable_requests(): data.append(b64encode(fuzzable_request.dump())) return jsonify({'items': data})
def get_kb(scan_id, vulnerability_id): """ The whole information related to the specified vulnerability ID :param vulnerability_id: The vulnerability ID to query :return: All the vulnerability information """ scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') for finding_id, finding in enumerate(kb.kb.get_all_findings()): if vulnerability_id == finding_id: return jsonify(finding_to_json(finding, scan_id, finding_id, detailed=True)) abort(404, 'Not found')
def scan_pause(scan_id): """ Pause a scan :param scan_id: The scan ID to pause :return: Empty result if success, 403 if the current state indicates that the scan can't be paused. """ scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') if not scan_info.w3af_core.can_pause(): abort(403, 'Scan can not be paused') scan_info.w3af_core.pause() return jsonify({'message': 'Success'})
def scan_delete(scan_id): """ Clear all the scan information :param scan_id: The scan ID to stop :return: Empty result if success, 403 if the current state indicates that the scan can't be cleared. """ scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') if not scan_info.w3af_core.can_cleanup(): abort(403, 'Scan is not ready to be cleared') scan_info.w3af_core.cleanup() SCANS[scan_id] = None return jsonify({'message': 'Success'})
def get_exception_details(scan_id, exception_id): """ The whole information related to the specified exception ID :param exception_id: The exception ID to query :return: All the vulnerability information """ scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') all_exceptions = scan_info.w3af_core.exception_handler.get_all_exceptions() for i_exception_id, exception_data in enumerate(all_exceptions): if exception_id == i_exception_id: return jsonify(exception_to_json(exception_data, scan_id, exception_id, detailed=True)) abort(404, 'Not found')
def scan_stop(scan_id): """ Stop a scan :param scan_id: The scan ID to stop :return: Empty result if success, 403 if the current state indicates that the scan can't be stopped. """ scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') if not scan_info.w3af_core.can_stop(): abort(403, 'Scan can not be stop') t = Process(target=scan_info.w3af_core.stop, name='ScanStopThread', args=()) t.daemon = True t.start() return jsonify({'message': 'Stopping scan'})
def list_exceptions(scan_id): """ List all exceptions found during a scan :return: A JSON containing a list of: - Exception resource URL (eg. /scans/0/exceptions/3) - The exceptions id (eg. 3) - Exception string - Exception file name - Exception line number """ scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') data = [] all_exceptions = scan_info.w3af_core.exception_handler.get_all_exceptions() for exception_id, exception_data in enumerate(all_exceptions): data.append(exception_to_json(exception_data, scan_id, exception_id)) return jsonify({'items': data})
def start_scan(): """ Starts a new w3af scan Receive a JSON containing: - A list with the target URLs - The profile (eg. the content of fast_scan.pw3af) :return: A JSON containing: - The URL to the newly created scan (eg. /scans/1) - The newly created scan ID (eg. 1) """ if not request.json or not 'scan_profile' in request.json: abort(400, 'Expected scan_profile in JSON object') if not request.json or not 'target_urls' in request.json: abort(400, 'Expected target_urls in JSON object') scan_profile = request.json['scan_profile'] target_urls = request.json['target_urls'] # # First make sure that there are no other scans running, remember that this # REST API is an MVP and we can only run one scan at the time (for now) # scan_infos = SCANS.values() if not all([si is None for si in scan_infos]): abort( 400, 'This version of the REST API does not support' ' concurrent scans. Remember to DELETE finished scans' ' before starting a new one.') # # Before trying to start a new scan we verify that the scan profile is # valid and return an informative error if it's not # scan_profile_file_name, profile_path = create_temp_profile(scan_profile) w3af_core = w3afCore() try: w3af_core.profiles.use_profile(scan_profile_file_name, workdir=profile_path) except BaseFrameworkException, bfe: abort(400, str(bfe))
def start_scan(): """ Starts a new w3af scan Receive a JSON containing: - A list with the target URLs - The profile (eg. the content of fast_scan.pw3af) :return: A JSON containing: - The URL to the newly created scan (eg. /scans/1) - The newly created scan ID (eg. 1) """ if not request.json or not 'scan_profile' in request.json: abort(400, 'Expected scan_profile in JSON object') if not request.json or not 'target_urls' in request.json: abort(400, 'Expected target_urls in JSON object') scan_profile = request.json['scan_profile'] target_urls = request.json['target_urls'] # # First make sure that there are no other scans running, remember that this # REST API is an MVP and we can only run one scan at the time (for now) # scan_infos = SCANS.values() if not all([si is None for si in scan_infos]): abort(400, 'This version of the REST API does not support' ' concurrent scans. Remember to DELETE finished scans' ' before starting a new one.') # # Before trying to start a new scan we verify that the scan profile is # valid and return an informative error if it's not # scan_profile_file_name, profile_path = create_temp_profile(scan_profile) w3af_core = w3afCore() try: w3af_core.profiles.use_profile(scan_profile_file_name, workdir=profile_path) remove_temp_profile(scan_profile_file_name) except BaseFrameworkException, bfe: abort(400, str(bfe))
def scan_delete(scan_id): """ Clear all the scan information :param scan_id: The scan ID to stop :return: Empty result if success, 403 if the current state indicates that the scan can't be cleared. """ scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') if scan_info.w3af_core is None: abort(400, 'Scan state is invalid and can not be cleared') if not scan_info.w3af_core.can_cleanup(): abort(403, 'Scan is not ready to be cleared') scan_info.cleanup() SCANS[scan_id] = None return jsonify({'message': 'Success'})
scan_profile_file_name, profile_path = create_temp_profile(scan_profile) w3af_core = w3afCore() try: w3af_core.profiles.use_profile(scan_profile_file_name, workdir=profile_path) except BaseFrameworkException, bfe: abort(400, str(bfe)) finally: remove_temp_profile(scan_profile_file_name) # # Now that we know that the profile is valid I verify the scan target info # if target_urls is None or not len(target_urls): abort(400, 'No target URLs specified') for target_url in target_urls: try: URL(target_url) except ValueError: abort(400, 'Invalid URL: "%s"' % target_url) target_options = w3af_core.target.get_options() target_option = target_options['target'] try: target_option.set_value([URL(u) for u in target_urls]) w3af_core.target.set_options(target_options) except BaseFrameworkException, bfe: abort(400, str(bfe))
def start_scan(): """ Starts a new w3af scan Receive a JSON containing: - A list with the target URLs - The profile (eg. the content of fast_scan.pw3af) :return: A JSON containing: - The URL to the newly created scan (eg. /scans/1) - The newly created scan ID (eg. 1) """ if not request.json or 'scan_profile' not in request.json: abort(400, 'Expected scan_profile in JSON object') if 'target_urls' not in request.json: abort(400, 'Expected target_urls in JSON object') scan_profile = request.json['scan_profile'] target_urls = request.json['target_urls'] # # First make sure that there are no other scans running, remember that this # REST API is an MVP and we can only run one scan at the time (for now) # scan_infos = SCANS.values() if not all([si is None for si in scan_infos]): abort( 400, 'This version of the REST API does not support' ' concurrent scans. Remember to DELETE finished scans' ' before starting a new one.') # # Before trying to start a new scan we verify that the scan profile is # valid and return an informative error if it's not # # scan_profile_file_name, profile_path = create_temp_profile(scan_profile) # w3af_core = w3afCore() # # try: # w3af_core.profiles.use_profile(scan_profile_file_name, # workdir=profile_path) # except BaseFrameworkException, bfe: # abort(400, str(bfe)) # # Now that we know that the profile is valid I verify the scan target info # if not len(target_urls): abort(400, 'No target URLs specified') for target_url in target_urls: try: URL(target_url) except ValueError: abort(400, 'Invalid URL: "%s"' % target_url) # target_options = w3af_core.target.get_options() # target_option = target_options['target'] # try: # target_option.set_value([URL(u) for u in target_urls]) # w3af_core.target.set_options(target_options) # except BaseFrameworkException, bfe: # abort(400, str(bfe)) # # Finally, start the scan in a different thread # scan_id = get_new_scan_id() scan_info_setup = Event() args = (target_urls, scan_profile, scan_info_setup) t = Process(target=start_scan_helper, name='ScanThread', args=args) t.daemon = True t.start() # Wait until the thread starts scan_info_setup.wait() return jsonify({ 'message': 'Success', 'id': scan_id, 'href': '/scans/%s' % scan_id }), 201
def list_url(scan_id): scan_info = get_scan_info_from_id(scan_id) if scan_info is None: abort(404, 'Scan not found') return jsonify({'items': CrawlUrls().get()})
def start_scan(): """ Starts a new w3af scan Receive a JSON containing: - A list with the target URLs - The profile (eg. the content of fast_scan.pw3af) :return: A JSON containing: - The URL to the newly created scan (eg. /scans/1) - The newly created scan ID (eg. 1) """ if not request.json or 'scan_profile' not in request.json: abort(400, 'Expected scan_profile in JSON object') if 'target_urls' not in request.json: abort(400, 'Expected target_urls in JSON object') scan_profile = request.json['scan_profile'] target_urls = request.json['target_urls'] # # First make sure that there are no other scans running, remember that this # REST API is an MVP and we can only run one scan at the time (for now) # scan_infos = SCANS.values() if not all([si is None for si in scan_infos]): abort(400, 'This version of the REST API does not support' ' concurrent scans. Remember to DELETE finished scans' ' before starting a new one.') # # Before trying to start a new scan we verify that the scan profile is # valid and return an informative error if it's not # # scan_profile_file_name, profile_path = create_temp_profile(scan_profile) # w3af_core = w3afCore() # # try: # w3af_core.profiles.use_profile(scan_profile_file_name, # workdir=profile_path) # except BaseFrameworkException, bfe: # abort(400, str(bfe)) # # Now that we know that the profile is valid I verify the scan target info # if not len(target_urls): abort(400, 'No target URLs specified') for target_url in target_urls: try: URL(target_url) except ValueError: abort(400, 'Invalid URL: "%s"' % target_url) # target_options = w3af_core.target.get_options() # target_option = target_options['target'] # try: # target_option.set_value([URL(u) for u in target_urls]) # w3af_core.target.set_options(target_options) # except BaseFrameworkException, bfe: # abort(400, str(bfe)) # # Finally, start the scan in a different thread # scan_id = get_new_scan_id() scan_info_setup = Event() args = (target_urls, scan_profile, scan_info_setup) t = Process(target=start_scan_helper, name='ScanThread', args=args) t.daemon = True t.start() # Wait until the thread starts scan_info_setup.wait() return jsonify({'message': 'Success', 'id': scan_id, 'href': '/scans/%s' % scan_id}), 201
# valid and return an informative error if it's not # scan_profile_file_name, profile_path = create_temp_profile(scan_profile) w3af_core = w3afCore() try: w3af_core.profiles.use_profile(scan_profile_file_name, workdir=profile_path) except BaseFrameworkException, bfe: abort(400, str(bfe)) # # Now that we know that the profile is valid I verify the scan target info # if target_urls is None or not len(target_urls): abort(400, 'No target URLs specified') for target_url in target_urls: try: URL(target_url) except ValueError: abort(400, 'Invalid URL: "%s"' % target_url) target_options = w3af_core.target.get_options() target_option = target_options['target'] try: target_option.set_value([URL(u) for u in target_urls]) w3af_core.target.set_options(target_options) except BaseFrameworkException, bfe: abort(400, str(bfe))
def start_scan(): """ Starts a new w3af scan Receive a JSON containing: - A list with the target URLs - The profile (eg. the content of fast_scan.pw3af) :return: A JSON containing: - The URL to the newly created scan (eg. /scans/1) - The newly created scan ID (eg. 1) """ #if not request.json or not 'scan_profile' in request.json: # abort(400, 'Expected scan_profile in JSON object') if not request.json or not 'target_urls' in request.json: abort(400, 'Expected target_urls in JSON object') scan_profile = file('fast_scan.pw3af').read() target_urls = request.json['target_urls'] if (not len(target_urls)) or len(target_urls) > 1: abort(400, 'Invalid URL: "%s"' % target_url) scanResult = None for target_url in target_urls: try: URL(target_url) scanResult = scanGet(target_url) except ValueError: abort(400, 'Invalid URL: "%s"' % target_url) if scanResult != None: return jsonify({'message': 'Success', 'id': scanResult.scanId, 'href': '/scans/%s' % scanResult.scanId}), 201 # # First make sure that there are no other scans running, remember that this # REST API is an MVP and we can only run one scan at the time (for now) # scan_infos = SCANS.values() """ if not all([si is None for si in scan_infos]): abort(400, 'This version of the REST API does not support' ' concurrent scans. Remember to DELETE finished scans' ' before starting a new one.') """ # # Before trying to start a new scan we verify that the scan profile is # valid and return an informative error if it's not # #scan_profile_file_name = 'fast_scan.pw3af' #profiles_path = '../../../../../profiles/' scan_profile_file_name, profile_path = create_temp_profile(scan_profile) w3af_core = w3afCore() try: w3af_core.profiles.use_profile(scan_profile_file_name, workdir=profile_path) except BaseFrameworkException, bfe: abort(400, str(bfe))
w3af_core.profiles.use_profile(scan_profile_file_name, workdir=profile_path) except BaseFrameworkException, bfe: abort(400, str(bfe)) # # Now that we know that the profile is valid I verify the scan target info # target_options = w3af_core.target.get_options() target_option = target_options['target'] try: target_option.set_value([URL(u) for u in target_urls]) w3af_core.target.set_options(target_options) except BaseFrameworkException, bfe: abort(400, str(bfe)) # # Finally, start the scan in a different thread # scan_id = get_new_scan_id() scan_info_setup = Event() args = (target_urls, scan_profile, scan_info_setup) t = Process(target=start_scan_helper, name='ScanThread', args=args) t.daemon = True scanAddId(scan_id, target_urls[0]) t.start() # Wait until the thread starts scan_info_setup.wait()