USTA Account Takeover Prevention
Collects compromised credentials sourced from stealer malware attacks, helping organizations identify potential account takeovers and enhance their security posture. Provided by PRODAFT.
Data Enrichment & Threat Intelligence · USTAv4 Cyber Threat Intelligence Platform
Details
| ID | USTA Account Takeover Prevention |
|---|---|
| Provider | PRODAFT |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 6.10.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
USTAv4 Account Takeover Prevention is designed to collect compromised credentials sourced from stealer malware attacks, helping organizations identify potential account takeovers and enhance their security posture. Provided by PRODAFT.
This integration was integrated and tested with version 4.1.0 of USTAv4 Account Takeover Prevention.
Configure USTAv4 Account Takeover Prevention in Cortex
- Navigate to Settings > Integrations > Servers & Services.
- Search for USTAv4 Account Takeover Prevention.
-
Click Add instance to create and configure a new integration instance.
Parameter Description Required Your server URL True API Key The API Key to use for connection True Fetch incidents by status False Trust any certificate (not secure) False Use system proxy settings False Fetch incidents False First Fetch Time The time range to consider for the initial data fetch. Warning: Fetching a large time range may cause performance issues! True - 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.
usta-atp-search-username
Search for compromised credentials by username
Base Command
usta-atp-search-username
Input
| Argument Name | Description | Required |
|---|---|---|
| username | Username to search. | Required |
| page_size | Number of result that should appear on each page. | Optional |
| page | 1-indexed page number to get a particular page of results. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| USTA.AccountTakeoverPrevention.id | Number | The ID of the alert |
| USTA.AccountTakeoverPrevention.username | String | The username of the compromised credential |
| USTA.AccountTakeoverPrevention.password | String | The password of the compromised credential |
| USTA.AccountTakeoverPrevention.url | String | The URL of the compromised credential |
| USTA.AccountTakeoverPrevention.is_corporate | Boolean | Whether the compromised credential is corporate |
| USTA.AccountTakeoverPrevention.created | String | The creation date of the compromised credential |
| USTA.AccountTakeoverPrevention.victim_detail.ip | String | The IP address of the victim |
| USTA.AccountTakeoverPrevention.victim_detail.country | String | The country of the victim |
| USTA.AccountTakeoverPrevention.victim_detail.phone_number | String | The phone number of the victim |
| USTA.AccountTakeoverPrevention.victim_detail.computer_name | String | The computer name of the victim computer |
| USTA.AccountTakeoverPrevention.victim_detail.victim_os | String | The OS of the victim computer |
| USTA.AccountTakeoverPrevention.victim_detail.language | String | The language of the victim computer |
| USTA.AccountTakeoverPrevention.victim_detail.memory | String | The memory of the victim computer |
| USTA.AccountTakeoverPrevention.victim_detail.cpu | String | The CPU of the victim computer |
| USTA.AccountTakeoverPrevention.victim_detail.gpu | String | The GPU of the victim computer |
| USTA.AccountTakeoverPrevention.victim_detail.malware | String | The family of the malware that infected the victim computer |
| USTA.AccountTakeoverPrevention.victim_detail.infection_date | String | The infection date of the victim computer |
Command Example
!usta-atp-search-username username=user123456 page_size=1 page=1
Context Example
{
"USTA" : {
"AccountTakeoverPrevention": [
{
"id": 1234567,
"status": "open",
"username": "user123456",
"password": "******",
"url": "https://example.com/login",
"is_corporate": "False",
"created": "2024-11-18T00:00:00.000000Z",
"victim_detail": {
"username": "anonymous",
"ip": "0.0.0.0",
"country": "Unknown",
"phone_number": "N/A",
"computer_name": "DESKTOP-XXXXX",
"victim_os": "OS x64",
"language": "N/A",
"memory": "XXXX MB",
"cpu": "Generic CPU",
"gpu": "Generic GPU",
"malware": "Unknown",
"infection_date": "N/A",
"created": "2024-11-18T00:00:00.000000Z"
}
}
]
}
}
Configuration parameters
url— Your server URL (required)api_key— API Key (required)status— Fetch incidents by statusmax_fetch— Maximum number of alerts per fetchinsecure— Trust any certificate (not secure)proxy— Use system proxy settingsisFetch— Fetch incidentsincidentFetchInterval— Incidents Fetch IntervalincidentType— Incident typefirst_fetch— First Fetch Time (required)
Commands (1)
-
usta-atp-search-usernameSearch for compromised credentials by username.
"""Base Integration for Cortex XSOAR - Unit Tests file Pytest Unit Tests: all funcion names must start with "test_" More details: https://xsoar.pan.dev/docs/integrations/unit-testing MAKE SURE YOU REVIEW/REPLACE ALL THE COMMENTS MARKED AS "TODO" You must add at least a Unit Test function for every XSOAR command you are implementing with your integration """ import json import demistomock as demisto # noqa: F401 import pytest from USTAAccountTakeoverPrevention import ( Client, check_module, compromised_credentials_search_command, convert_to_demisto_severity, create_paging_header, fetch_incidents, main, ) def util_load_json(path): with open(path, encoding="utf-8") as f: return json.loads(f.read()) def test_check_module(mocker): """Tests test_module command function. Checks the output of the command function with the expected output. """ mock_response = util_load_json("test_data/auth_success_response.json") client = Client(base_url="", verify=False, headers={}, proxy=False) mocker.patch.object(client, "check_auth", return_value=mock_response) response = check_module(client) assert response == "ok" @pytest.mark.parametrize( "username, mock_response_file, expected_output_file", [ ( "user1", "test_data/compromised_credentials_search_response.json", "test_data/compromised_credentials_search_response.json", ), ("user2", "test_data/search_empty_response.json", "test_data/search_empty_response.json"), ], ) def test_compromised_credentials_search_command(mocker, username, mock_response_file, expected_output_file): """Tests compromised_credentials_search command function with multiple test cases. Checks the output of the command function with the expected output. No mock is needed here because the compromised_credentials_search_command does not call any external API. """ mock_response = util_load_json(mock_response_file) expected_output = util_load_json(expected_output_file) _count = len(mock_response.get("results", [])) client = Client(base_url="", verify=False, headers={}, proxy=False) mocker.patch.object(client, "compromised_credentials_search_api_request", return_value=mock_response) response = compromised_credentials_search_command(client, {"username": username}) assert response.readable_output.startswith(f"Showing {_count} results") assert response.outputs == expected_output def test_fetch_incidents(mocker): """Tests fetch_incidents command function. Checks the output of the command function with the expected output. No mock is needed here because the fetch_incidents_command does not call any external API. """ mock_response = util_load_json("test_data/compromised_credentials_fetch_incidents_response.json") expected_output = util_load_json("test_data/compromised_credentials_fetch_incidents_expected_output.json") client = Client(base_url="", verify=False, headers={}, proxy=False) mocker.patch.object(client, "compromised_credentials_api_request", return_value=mock_response) next_run, incidents = fetch_incidents(client=client, max_results=100, last_run={}, first_fetch_time="3 days") assert incidents == expected_output assert len(incidents) == 1 assert next_run["last_ids"] == [7668573] def test_subsequent_run(mocker): """ Given: - A last run with a last fetch time and list of last incident IDs When: - Fetch incidents is called with the last run - First fetch time is provided Then: - Returned incidents should have occurred after last fetch - Number of returned incidents should match max results - Next run should have new updated last incident IDs """ last_run = {"last_fetch": "2021-02-01T00:00:00Z", "last_ids": [1, 2, 3]} first_fetch = last_run.get("last_fetch") mock_response = util_load_json("test_data/compromised_credentials_fetch_incidents_response.json") expected_output = util_load_json("test_data/compromised_credentials_fetch_incidents_expected_output.json") client = Client(base_url="", verify=False, headers={}, proxy=False) mocker.patch.object(client, "compromised_credentials_api_request", return_value=mock_response) next_run, incidents = fetch_incidents(client=client, max_results=3, last_run=last_run, first_fetch_time=first_fetch) assert len(incidents) == 1 assert incidents[0]["occurred"] > first_fetch assert incidents == expected_output assert next_run["last_ids"] == [7668573] @pytest.mark.parametrize( "hello_world_severity, expected_xsoar_severity", [("low", 1), ("medium", 2), ("high", 3), ("critical", 4), ("unknown", 0)] ) def test_convert_to_demisto_severity(hello_world_severity, expected_xsoar_severity): """ Given: - A string represents a HelloWorld severity. When: - Running the 'convert_to_demisto_severity' function. Then: - Verify that the severity was correctly translated to a Cortex XSOAR severity. """ assert convert_to_demisto_severity(hello_world_severity) == expected_xsoar_severity def test_convert_to_demisto_severity_invalid(): """ Given: - An invalid HelloWorld severity. When: - Running the 'convert_to_demisto_severity' function. Then: - Verify that the function raises a ValueError. """ with pytest.raises(KeyError): convert_to_demisto_severity("invalid") def test_create_paging_header(): """ Given: - A number of results, page number and page size. When: - Running the 'create_paging_header' function. Then: - Verify that the function returns the correct paging header. """ results_num = 10 page = 2 size = 5 expected_output = "Showing 10 results, Size=5, from Page 2\n" assert create_paging_header(results_num, page, size) == expected_output def test_compromised_credentials_search_api_request(mocker): """ Given: - A client and a status. When: - Running the 'compromised_credentials_search_api_request' function. Then: - Verify that the function returns the correct response. """ mock_response = util_load_json("test_data/compromised_credentials_search_response.json") client = Client(base_url="", verify=False, headers={}, proxy=False) mocker.patch.object(client, "_http_request", return_value=mock_response) response = client.compromised_credentials_search_api_request(status=1, start="2021-02-01T00:00:00Z", size=100) assert response == mock_response def test_main_search_cmd(mocker): """ Given: - A command to execute. When: - Running the main function. Then: - Verify that the correct command function is called with the correct arguments. """ mocker.patch.object( demisto, "params", return_value={ "url": "https://example.com", "api_key": "API_KEY", "insecure": True, "proxy": False, "first_fetch": "3 days", "status": "open", "max_fetch": 50, }, ) Client(base_url="", verify=False, headers={}, proxy=False) mocker.patch.object(demisto, "args", return_value={"username": "user1"}) mocker.patch.object(demisto, "command", return_value="usta-atp-search-username") mocker.patch.object(demisto, "results") mocker.patch.object(demisto, "setLastRun") mocker.patch.object(demisto, "incidents") mocker.patch.object(Client, "check_auth") mocker.patch.object( Client, "compromised_credentials_search_api_request", return_value=util_load_json("test_data/compromised_credentials_search_response.json"), ) main() demisto.results.assert_called_once() demisto.setLastRun.assert_not_called() demisto.incidents.assert_not_called() def test_main_fetch_incidents_cmd(mocker): mocker.patch.object( demisto, "params", return_value={ "url": "https://example.com", "api_key": "API_KEY", "insecure": True, "proxy": False, "first_fetch": "3 days", "status": "open", "max_fetch": 50, }, ) Client(base_url="", verify=False, headers={}, proxy=False) mock_response = util_load_json("test_data/compromised_credentials_fetch_incidents_response.json") mocker.patch.object(demisto, "command", return_value="fetch-incidents") mocker.patch.object(Client, "compromised_credentials_api_request", return_value=mock_response) mocker.patch.object(demisto, "results") mocker.patch.object(demisto, "setLastRun") mocker.patch.object(demisto, "incidents") main() demisto.incidents.assert_called_once() demisto.results.assert_not_called() demisto.setLastRun.assert_called_once()