PhishTank V2
PhishTank is a free community site where anyone can submit, verify, track, and share phishing data.
Data Enrichment & Threat Intelligence · PhishTank
Details
| ID | PhishTank V2 |
|---|---|
| Provider | Open Source |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.12.13.10325753 |
| Supported Modules | Agentix XSIAM |
README
PhishTank is a free community site where anyone can submit, verify, track and share phishing data.
This integration was integrated and tested with version 1.0.1 of PhishTank.
Configure PhishTankV2 in Cortex
| Parameter | Description | Required |
|---|---|---|
| use_https | Use HTTPS connection | False |
| Source Reliability | Reliability of the source providing the intelligence data. | B - Usually reliable |
| proxy | Use system proxy settings | False |
| insecure | Trust any certificate (not secure) | False |
| fetchIntervalHours | Database refresh interval (hours) | False |
Best Practice
When using the PhishTank V2 integration, we recommend that you use an engine to run the integration instance,
and to use different engines for different tenants.
You should open a platform feature request (FR) to request separate egress IPs for the different tenants.
Commands
You can execute these commands from the 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.
url
Checks the reputation of the supplied URLs.
Notice: Submitting indicators using this command might make the indicator data publicly available. See the vendor’s documentation for more details.
Base Command
url
Input
| Argument Name | Description | Required |
|---|---|---|
| url | A comma-separated list of URLs to check the reputation of. | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| URL.Data | String | A list of URLs with a bad reputation. |
| URL.Malicious.Vendor | String | For malicious URLs, the vendor that tagged the URL as malicious. |
| URL.Malicious.Description | String | For malicious URLs, the reason the vendor tagged the URL as malicious. |
| DBotScore.Indicator | String | The indicator that was tested. |
| DBotScore.Type | String | The indicator type. |
| DBotScore.Vendor | String | The vendor used to calculate the score. |
| DBotScore.Score | Number | The actual score. |
| DBotScore.Reliability | String | Reliability of the source providing the intelligence data. |
Command Example
!url url=hxxp://login.rakuten.co.jp.reise
Human Readable Output
PhishTankV2 Database - URL Query
Found matches for URL hxxp://login.rakuten.co.jp.reise
online phish_id submission_time target verification_time verified yes 6784982 2020-09-27T19:04:35+00:00 Other 2020-09-27T19:10:20+00:00 yes Additional details at http://www.phishtank.com/phish_detail.php?phish_id=6784982
phishtank-reload
Reload PhishTank database
Base Command
phishtank-reload
Input
There are no input arguments for this command.
Context Output
There is no context output for this command.
Command Example
!phishtank-reload
Human Readable Output
PhishTankV2 Database reloaded
Total 13181 URLs loaded
phishtank-status
Show PhishTank database status
Base Command
phishtank-status
Input
There are no input arguments for this command.
Context Output
There is no context output for this command.
Command Example
!phishtank-status
Human Readable Output
PhishTankV2 Database Status
Total 13181 URLs loaded
Last Load time Sun Oct 04 2020 09:43:01 (UTC)
Configuration parameters
username— Usernameuse_https— Use HTTPS connectionintegrationReliability— Source Reliability (required)fetchIntervalHours— Database refresh interval (number of hours to wait between each loading)proxy— Use system proxy settingsinsecure— Trust any certificate (not secure)
Commands (3)
-
phishtank-reloadReloads the PhishTank downloadable database. PhishTank database is an up to date phishing detection file (updated hourly) .
-
phishtank-statusShows the status (timestamp) of the last time that PhishTank database was loaded.
-
urlChecks the reputation of the supplied URLs.
from datetime import datetime import demistomock as demisto import pytest from CommonServerPython import DBotScoreReliability from freezegun import freeze_time from PhishTankV2 import Client, is_number, is_reload_needed, phishtank_status_command, reload, remove_last_slash, url_command def create_client( proxy: bool = False, verify: bool = False, fetch_interval_hours: str = "1", reliability: str = DBotScoreReliability.A_PLUS, username: str = "", ): return Client( proxy=proxy, verify=verify, fetch_interval_hours=fetch_interval_hours, use_https=False, reliability=reliability, username=username, ) @pytest.mark.parametrize( "number, output", [ ("True", False), ("432", True), ("str", False), ("455.55", True), ("-1", False), ("0", False), ("fd.f", False), ("1", True), ("", False), ], ) def test_is_number(number, output): """ Given: - fetchIntervalHours as integration param When: - At the beginning of the integration Then: - Returns True if fetchIntervalHours is a positive number (float) """ assert is_number(number) == output @pytest.mark.parametrize( "url, output", [(r"hxxp://www.com/", r"hxxp://www.com"), (r"hxxp://www.com", r"hxxp://www.com"), ("", "")] ) def test_remove_last_slash(url, output): """ Given: - Given url from an API response file or from integration args When: - Before operation with given url Then: - Return url without '/' in the end (if not exists return the given url) """ assert remove_last_slash(url) == output @pytest.mark.parametrize( "client,data,output", [ (Client(False, False, "2", False, DBotScoreReliability.B), {}, True), (Client(False, False, "1", False, DBotScoreReliability.A), {"list": {"id": 200}, "timestamp": 1601542800000}, False), (Client(False, False, "2", False, DBotScoreReliability.C), {"list": {"id": 200}, "timestamp": 1601542800000}, False), (Client(False, False, "0.5", False, DBotScoreReliability.B), {"list": {"id": 200}, "timestamp": 1601542800000}, True), ], ) def test_is_reloaded_needed(client, data, output): """ Given: - data as IntegrationContext When: - reload command was required Then: - Returns False if last reload occurred in the past fetch_interval_hours. True otherwise """ with freeze_time(datetime(2020, 10, 1, 10, 00, 00, 0)): assert is_reload_needed(client, data) == output FETCH_INDICATORS_PACKAGE = [ ( "http://url.example", 200, "phish_id,url,phish_detail_url,submission_time,verified,verification_time,online,target\n" "1,http://url.example1,http://url.example1,2019-10-20T23:54:13+00:00,yes,2019-10-20T23:54:13+00:00,yes,Other\n" "2,http://url.example2,http://url.example2,2019-10-20T23:54:14+00:00,yes,2019-10-20T23:54:14+00:00,yes,Target" "\n", { "http://url.example1": { "phish_id": "1", "submission_time": "2019-10-20T23:54:13+00:00", "verified": "yes", "verification_time": "2019-10-20T23:54:13+00:00", "online": "yes", "target": "Other", }, "http://url.example2": { "phish_id": "2", "submission_time": "2019-10-20T23:54:14+00:00", "verified": "yes", "verification_time": "2019-10-20T23:54:14+00:00", "online": "yes", "target": "Target", }, }, ), ( "http://url.example/", 200, "phish_id,url,phish_detail_url,submission_time,verified,verification_time,online,target\n" "1,http://url.example1,2019-10-20T23:54:13+00:00,yes,2019-10-20T23:54:13+00:00,yes,Other\n" "2,http://url.example2," "http://url.example2,2019-10-20T23:54:14+00:00,yes,2019-10-20T23:54:14+00:00,yes,Target\n", { "http://url.example2": { "phish_id": "2", "submission_time": "2019-10-20T23:54:14+00:00", "verified": "yes", "verification_time": "2019-10-20T23:54:14+00:00", "online": "yes", "target": "Target", } }, ), ( "http://url.example/", 509, "['You have exceeded the request rate limit for this method. Please see the response headers for usage stats." " For more information about rate limiting on Phishtank, please see our developer site: " "http://www.phishtank.com/developer_info.php']", {}, ), ] @pytest.mark.parametrize("url, status_code, data, expected_result", FETCH_INDICATORS_PACKAGE) def test_reload(mocker, url, status_code, data, expected_result): """ Given: - url: - on reload command : url is the end point to get all data - on url command : given url has to be checked When: - After reload or url command Then: - Returns the processed dictionary """ mocker.patch.object(Client, "get_http_request", return_value=data) client = create_client(False, False, "1", DBotScoreReliability.B) if status_code == 200 or status_code == 509: got_data = reload(client) assert got_data == expected_result else: pytest.fail() CONTEXT_LIST = [ ({}, "PhishTankV2 Database Status\nDatabase not loaded.\n"), ({}, "PhishTankV2 Database Status\nDatabase not loaded.\n"), ( { "list": { "http://url.example1": { "phish_id": "1", "submission_time": "2019-10-20T23:54:13+00:00", "verified": "yes", "verification_time": "2019-10-20T23:54:13+00:00", "online": "yes", "target": "Other", }, "http://url.example2": { "phish_id": "2", "submission_time": "2019-10-20T23:54:14+00:00", "verified": "yes", "verification_time": "2019-10-20T23:54:14+00:00", "online": "yes", "target": "Target", }, }, "timestamp": 1601969897 * 1000, }, "PhishTankV2 Database Status\nTotal **2** URLs loaded.\nLast Load time **Tue Oct 06 2020 07:38:17 (UTC)**\n", ), ] @pytest.mark.parametrize("data,expected_result", CONTEXT_LIST) @freeze_time("1993-06-17 11:00:00 GMT") def test_phishtank_status_command(mocker, data, expected_result): """ Given: - Integration context When: - After asked fot status command Then: - Returns number of loaded urls if data was loaded. - Otherwise, returns Database not loaded. """ mocker.patch.object(demisto, "getIntegrationContext", return_value=data) status = phishtank_status_command() assert status.readable_output == expected_result URL_COMMAND_LIST = [ ( # valid data , verified = yes { "phish_id": "1", "submission_time": "2019-10-20T23:54:13+00:00", "verified": "yes", "verification_time": "2019-10-20T23:54:13+00:00", "online": "yes", "target": "Other", }, ["http://url.example1"], 3, "### PhishTankV2 Database - URL Query \n#### Found matches for URL http://url.example1 \n|online" "|phish_id|submission_time|target|verification_time|verified|\n|---|---|---|---|---|---|\n| yes | 1 | " "2019-10-20T23:54:13+00:00 | Other | 2019-10-20T23:54:13+00:00 | yes |\nAdditional details at " "[http://www.phishtank.com/phish_detail.php?phish_id=1](http://www.phishtank.com/phish_detail.php?phish_id=1) \n", ), ( # no exists key verified { "phish_id": "1", "submission_time": "2019-10-20T23:54:13+00:00", "verification_time": "2019-10-20T23:54:13+00:00", "online": "yes", "target": "Other", }, ["http://url.example1"], 0, "### PhishTankV2 Database - URL Query \n#### No matches for URL http://url.example1 \n", ), ( # valid data , verified = no { "phish_id": "1", "submission_time": "2019-10-20T23:54:13+00:00", "verified": "no", "verification_time": "2019-10-20T23:54:13+00:00", "online": "yes", "target": "Other", }, ["http://url.example1"], 2, "### PhishTankV2 Database - URL Query \n#### Found matches for URL http://url.example1 \n|online" "|phish_id|submission_time|target|verification_time|verified|\n|---|---|---|---|---|---|\n| yes | 1 | " "2019-10-20T23:54:13+00:00 | Other | 2019-10-20T23:54:13+00:00 | no |\nAdditional details at " "[http://www.phishtank.com/phish_detail.php?phish_id=1](http://www.phishtank.com/phish_detail.php?phish_id=1) \n", ), ( # no data {}, ["http://url.example1"], 0, "### PhishTankV2 Database - URL Query \n#### No matches for URL http://url.example1 \n", ), ] @pytest.mark.parametrize("data,url,expected_score,expected_table", URL_COMMAND_LIST) def test_url_command(mocker, data, url, expected_score, expected_table): """ Given: - Got url to check for scores When: - After asked fot url command Then: - validating that the IOC score is as expected - validating the returned human readable """ client = create_client(False, False, "1", DBotScoreReliability.C) mocker.patch.object(demisto, "results") mocker.patch("PhishTankV2.get_url_data", return_value=(data, url[0])) command_results = url_command(client, url) # validate score output = command_results[0].to_context().get("EntryContext", {}) dbot_key = "DBotScore(val.Indicator && val.Indicator == obj.Indicator && val.Vendor == obj.Vendor && val.Type == obj.Type)" assert output.get(dbot_key, [])[0].get("Score") == expected_score assert output.get(dbot_key, [])[0].get("Reliability") == DBotScoreReliability.C # validate human readable hr_ = command_results[0].to_context().get("HumanReadable", {}) assert hr_ == expected_table @pytest.mark.parametrize("username, expected_headers", [("test", {"User-Agent": "phishtank/test"}), ("", {})]) def test_user_agent_header(mocker, username, expected_headers): """ Given: - phishtank username When: - After reload or url command Then: - validating that the User-Agent header is populated as expected """ http_request = mocker.patch.object(Client, "_http_request", return_value="") client = create_client(False, False, "1", DBotScoreReliability.B, username) reload(client) assert http_request.call_args.kwargs["headers"] == expected_headers