urlscan.io

Use urlscan.io integration to perform scans on suspected URLs and see their reputation.

Data Enrichment & Threat Intelligence · URLScan.io

Details

IDurlscan.io
ProviderUrlscan io
CategoryData Enrichment & Threat Intelligence
From Version5.0.0
Docker Imagedemisto/python3:3.12.8.3720084
Supported ModulesAgentix XSIAM

README

Use urlscan.io integration to perform scans on suspected urls and see their reputation.

Configure urlscan.io on Cortex XSOAR

  1. Navigate to Settings > Integrations > Servers & Services.
  2. Search for urlscan.io.
  3. Click Add instance to create and configure a new integration instance.
    • Name: a textual name for the integration instance.
    • Server URL (e.g. https://urlscan.io/api/v1/ )
    • API Key (needed only for submitting URLs for scanning)
    • Scan Visibility: Determines the visibility level of the scan. This will override the 'public submissions' setting.
    • Source Reliability. Reliability of the source providing the intelligence data. (The default value is C - Fairly reliable)
    • Scan Country. Specify which country the scan should be performed from. If you omit this value, urlscan will try to do automatic country detection based on the TLD of the URL, GeoIP information of the server and of the user.
    • Trust any certificate (not secure)
    • Use system proxy settings
    • URL Threshold. Minimum number of positive results from urlscan.io to consider the URL malicious.
    • User Agent: User Agent used during scans with this integration.
  4. Click Test to validate the URLs, token, and connection.

Commands

You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook. After you successfully execute a command, a DBot message appears in the War Room with the command details.

  1. Search for indicators: urlscan-search
  2. (Deprecated) Submit a URL: urlscan-submit
  3. Submit a URL (specify the "using" argument): url

1. Search for indicators


Search for an indicator that is related to previous urlscan.io scans.

Notice: Submitting indicators using this command might make the indicator data publicly available. See the vendor’s documentation for more details.

Base Command

urlscan-search

Input
Argument Name Description Required
searchParameter Enter a parameter to search as a string (IP, File name, sha256, url, domain) Required
searchType Allows querying multiple search parameters Optional
 
Context Output
Path Description
URLScan.URL Bad URLs found
URLScan.Domain Domain of the URL scanned
URLScan.ASN ASN of the URL scanned
URLScan.IP IP of the url scanned
URLScan.ScanID Scan ID for the URL scanned
URLScan.ScanDate Latest scan date for the URL
URLScan.Hash SHA-256 of file scanned
URLScan.FileName Filename of the file scanned
URLScan.FileSize File size of the file scanned
URLScan.FileType File type of the file scanned
 
Command Example

!urlscan-search searchParameter=8.8.8.8

!urlscan-search searchType=advanced searchParameter="filename:logo.png AND date:>now-24h"

!urlscan-search searchType=raw searchParameter="q=meta%3Asearchhit.search.04eb755f-468d-4421-ab86-210a01ee1bdd&datasource=hostnames&search_after="

2. (Deprecated) Submit a URL directly to urlscan.io


Submits a URL to urlscan.io.

This command is deprecated, but will still work if it is used in a playbook.

Base Command

urlscan-submit

Input
Argument Name Description Required
url URL to scan Required
timeout How many seconds to wait to the scan id result. Default is 30 seconds. Optional
public Will the submission be public or private Optional
useragent User Agent used to perform scans Optional
scan_visibility The submission visibility. If specified, overrides the 'public' parameter Optional
 
Context Output
Path Description
URLScan.URLs URLs related to the scanned URL
URLScan.RelatedIPs IPs related to the scanned URL
URLScan.RelatedASNs ASNs related to the scanned URL
URLScan.Countries Countries associated with the scanned URL
URLScan.relatedhashes IOCs found for the scanned URL
URLScan.Subdomains Associated subdomains for the url scanned
URLScan.ASN ASN of the URL scanned
URLScan.Data URL of the file found
URLScan.Malicious.Vendor Vendor reporting the malicious indicator for the file
URLScan.Malicious.Description Description of the malicious indicator
URLScan.File.Hash SHA256 of file found
URLScan.File.FileName File name of file found
URLScan.File.FileType File type of the file found
URLScan.File.Hostname URL where the file was found
URLScan.Certificates Certificates found for the scanned URL
 
Command Example

!urlscan-submit url=http://www.github.com/

3. Submit a URL (specify using urlscan.io)


Submit a URL to scan and specify the using argument as urlscan.io.

Base Command

url

Input
Argument Name Description Required
url URL to scan Required
timeout How many seconds to wait for the scan ID result. Default is 30 seconds. Optional
public Whether the submission will be public or private Optional
retries Number of retries if the API rate limit is reached. This argument is optional, but if you specify this argument, you need to specify the wait argument.  Optional
wait Time interval (in seconds) between retries, if the API rate limit is reached. This argument is optional, but if you specify the retries argument, you need to specify this argument. Optional
useragent User Agent used to perform scans Optional
scan_visibility The submission visibility. If specified, overrides the 'public' parameter Optional
use_url_as_name Whether to use the URL as the screenshot name. Default is false which sets screenshot name to screenshot.png Optional
 
Context Output
Path Description
URLScan.URLs URLs related to the scanned URL
URLScan.RelatedIPs IPs related to the URL scanned
URLScan.RelatedASNs ASNs related to the scanned URL
URLScan.Countries Countries associated with the scanned URL
URLScan.relatedhashes IOCs found for the scanned URL
URLScan.Subdomains Associated sub-domains for the scanned URL
URLScan.ASN ASN of the scanned URL
URLScan.Data URL of the file found
URLScan.Malicious.Vendor Vendor reporting the malicious indicator for the file
URLScan.Malicious.Description Description of the malicious indicator
URLScan.File.Hash SHA-256 of file found
URLScan.File.FileName File name of file found
URLScan.File.FileType File type of the file found
URLScan.File.Hostname URL where the file was found
URLScan.Certificates Certificates found for the scanned URL
URLScan.RedirectedURLS Redirected URLs from the URL scanned
URLScan.EffectiveURL  Effective URL of the original URL
URL.ASN  The URL ASN.
URL.FeedRelatedIndicators.value  Indicators that are associated with the URL.
URL.FeedRelatedIndicators.type  The type of the indicators that are associated with the URL.
URL.Geo.Country  The URL country.
URL.ASOwner The URL AS owner.
URL.Tags Tags that are associated with the URL.
 
Command Example

!url url=http://www.github.com/ using="urlscan.io"

 

Configuration parameters

  • creds_apikey
  • scan_visibility — Scan Visibility (required)
  • integrationReliability — Source Reliability (required)
  • country — Scan Country
  • url_threshold — URL Threshold. Minimum number of positive results from urlscan.io to consider the URL malicious.
  • useragent — User Agent
  • create_relationships — Create relationships
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • is_public — Enable public submissions by default.
  • apikey — API Key (only required for scanning URLs)

Commands (7)

  • url

    Submits a URL to scan.

  • urlscan-get-http-transaction-list Deprecated

    Returns the HTTP transaction list for the specified URL. Do not use this command in conjunction with the urlscan-get-http-transactions script.

  • urlscan-get-result-page Deprecated

    Returns the results page for the specified UUID.

  • urlscan-poll-uri Deprecated

    Polls the urlscan service regarding the results of the specified URI.

  • urlscan-search

    Search for an indicator that is related to former urlscan.io scans.

  • urlscan-submit Deprecated

    Deprecated. Use the url command instead.

  • urlscan-submit-url-command Deprecated

    Submits a URL to retrieve its UUID.

import json
import time
from threading import Thread

import demistomock as demisto  # noqa: F401
import pytest
from CommonServerPython import *  # noqa: F401
from pytest_mock import MockerFixture

RETURN_ERROR_TARGET = "UrlScan.return_error"
SCAN_URL = "https://urlscan.io/api/v1/scan/"
RESULT_URL = "https://urlscan.io/api/v1/result/"


@pytest.mark.parametrize("continue_on_blacklisted_urls", [(True), (False)])
def test_continue_on_blacklisted_error_arg(mocker, requests_mock, continue_on_blacklisted_urls):
    from UrlScan import BLACKLISTED_URL_ERROR_MESSAGES, Client, http_request

    return_error_mock = mocker.patch(RETURN_ERROR_TARGET)
    response_json = {
        "status": 400,
        "message": "Scan prevented ...",
        "description": BLACKLISTED_URL_ERROR_MESSAGES[0],
    }
    args = {"continue_on_blacklisted_urls": continue_on_blacklisted_urls}
    data = {"url": "www.test.com"}
    requests_mock.post(SCAN_URL, status_code=400, json=response_json)
    mocker.patch.object(demisto, "args", return_value=args)
    client = Client()

    response = http_request(client, "POST", "scan/", json=json.dumps(data))
    if continue_on_blacklisted_urls:
        assert return_error_mock.call_count == 0
    else:
        assert response[0].get("is_error") is True
        assert ("The submitted domain is on our blacklist. For your own safety we did not perform this scan...") in response[
            0
        ].get("error_string")


def thread_target():
    from UrlScan import Client, format_results

    client = Client()
    try:
        format_results(client, "uuid", "")
    except Exception:
        pass


def test_endless_loop_on_failed_response(requests_mock, mocker):
    """
    Given
    - Some uuid
    When
    - Running format results on it
    Then
    - Assert it does not enter an endless loop
    """
    mocker.patch(RETURN_ERROR_TARGET)

    with open("./test_data/capitalne.json") as f:
        response_data = json.loads(f.read())
    requests_mock.get(RESULT_URL + "uuid", status_code=200, json=response_data)
    thread = Thread(target=thread_target)
    thread.start()
    time.sleep(10)
    assert not thread.is_alive(), "format_results method have probably entered an endless loop"


def test_urlscan_submit_url(requests_mock, mocker):
    """
    Given
    - Two URLs which are rate limited
    When
    - running the !url command
    Then
    - Assert the items are scheduled and the metrics are correct.
    """
    import CommonServerPython
    from UrlScan import Client, urlscan_submit_command

    response_json = {"is_error": True}
    args = {"url": "https://something.com,https://somethingelse.com"}
    requests_mock.post(SCAN_URL, status_code=429, json=response_json, headers={"X-Rate-Limit-Reset-After": "123"})
    mocker.patch.object(demisto, "args", return_value=args)
    mocker.patch.object(CommonServerPython, "is_demisto_version_ge", return_value=True)
    mocker.patch.object(ScheduledCommand, "raise_error_if_not_supported")

    client = Client()

    response = urlscan_submit_command(client=client)
    scheduled_command = response[0].scheduled_command
    scheduled_command_args = scheduled_command._args
    assert scheduled_command_args["polling"] is True
    assert scheduled_command_args["url"] == ["https://something.com", "https://somethingelse.com"]
    assert scheduled_command._next_run == "123"
    assert scheduled_command._items_remaining == 2

    metrics = response[1]
    assert metrics.execution_metrics == [{"Type": "QuotaError", "APICallsCount": 2}]


def test_urlscan_search_only_found(mocker: MockerFixture):
    """
    Given:
        Client, execution metrics and empty command results
    When:
        urlscan_search_only is called with a url that has a result
    Then:
        - Execution metrics success is incremented by 1
        - The command_results is empty
    """
    from UrlScan import Client, urlscan_search_only

    client = Client()
    command_results = []
    execution_metrics = ExecutionMetrics()
    url = "http://example.com"
    mocker.patch(
        "UrlScan.urlscan_search",
        return_value={"results": [{"task": {"uuid": "123"}, "page": {"url": "http://example.com"}}]},
    )
    mocker.patch("UrlScan.format_results")

    urlscan_search_only(client, url, command_results, execution_metrics)

    assert execution_metrics.success == 1
    assert len(command_results) == 0


def test_urlscan_search_only_not_found(mocker: MockerFixture):
    """
    Given:
        Client, execution metrics and empty command results
    When:
        urlscan_search_only is called with a url that has no result
    Then:
        - No results message is added to command_results
        - Execution metrics is unchanged
    """
    from UrlScan import Client, urlscan_search_only

    client = Client()
    command_results = []
    execution_metrics = ExecutionMetrics()
    url = "http://example.com"
    mocker.patch("UrlScan.urlscan_search", return_value={"results": []})

    urlscan_search_only(client, url, command_results, execution_metrics)

    assert execution_metrics.success == 0
    assert len(command_results) == 1
    assert command_results[0].readable_output.startswith("No results found for")


def test_urlscan_search_only_error(mocker: MockerFixture):
    """
    Given:
        Client, execution metrics and empty command results
    When:
        urlscan_search_only is called with a url that return an error
    Then:
        - Error message is added to command_results
        - Execution metrics general error is incremented by 1
    """
    from UrlScan import Client, urlscan_search_only

    client = Client()
    command_results = []
    execution_metrics = ExecutionMetrics()
    url = "http://example.com"

    mocker.patch(
        "UrlScan.urlscan_search",
        return_value={"is_error": True, "error_string": "Test error"},
    )

    urlscan_search_only(client, url, command_results, execution_metrics)

    assert execution_metrics.general_error == 1
    assert "Test error" in command_results[0].readable_output


def test_format_results_check_lists(mocker):
    from UrlScan import Client, format_results

    client = Client()

    with open("./test_data/capitalne.json") as f:
        response_data = json.loads(f.read())

    mocker.patch("UrlScan.urlscan_submit_request", return_value=(response_data, "", ""))
    mocker.patch.object(demisto, "results", return_value="")
    command_results_inputs = mocker.patch("UrlScan.CommandResults")

    format_results(client, "uuid", "")
    outputs = command_results_inputs.call_args[1]["outputs"]["URLScan(val.URL && val.URL == obj.URL)"]
    assert outputs.get("links") == ["http://capitalne.com/home", "http://capitalne.com/about", "https://urlscan.io/"]