diff --git a/detections/experimental/network/detect_dga_domains_using_pretrained_model_in_dsdl.yml b/detections/experimental/network/detect_dga_domains_using_pretrained_model_in_dsdl.yml index 65d30d977a..4237449026 100644 --- a/detections/experimental/network/detect_dga_domains_using_pretrained_model_in_dsdl.yml +++ b/detections/experimental/network/detect_dga_domains_using_pretrained_model_in_dsdl.yml @@ -1,25 +1,33 @@ name: Detect DGA domains using pretrained model in DSDL -id: 92e24f32-9b9a-4060-bba2-2a0eb3bf3493 +id: 92e24f32-9b9a-4060-bba2-2a0eb31f3493 version: 1 -date: '2022-09-15' +date: "2023-01-18" author: Abhinav Mishra, Kumar Sharad and Namratha Sreekanta, Splunk type: Anomaly datamodel: - Network_Resolution -description: 'The following analytic uses a pretrained deep learning model to detect Domain Generation Algorithm (DGA) generated domains. The model is trained independently and is then made available for download. One of the prominent indicators of a domain being DGA generated is if the domain name consists of unusual character sequences or concatenated dictionary words. Adversaries often use clever techniques to obfuscate machine generated domain names as human generated. Predicting DGA generated domain names requires analysis and building a model based on carefully chosen features. The deep learning model we have developed uses the domain name to anlayze patterns of character sequences along with carefully chosen custom features to predict if a domain is DGA generated. The model takes a domain name consisting of second-level and top-level domain names as input and outputs a dga_score. Higher the dga_score, the more likely the input domain is a DGA domain. The threshold for flagging a domain as DGA is set at 0.5.' -search: '| tstats `security_content_summariesonly` values(DNS.answer) as IPs min(_time) as firstTime max(_time) as lastTime from datamodel=Network_Resolution by DNS.src, DNS.query -| `drop_dm_object_name("DNS")` | rename query AS domain | fields IPs, src, domain, firstTime, lastTime | apply pretrained_dga_model_dsdl | rename pred_dga_proba AS dga_score | where dga_score>0.5 | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | table src, domain, IPs, firstTime, lastTime, dga_score | `detect_dga_domains_using_pretrained_model_in_dsdl_filter`' -how_to_implement: " -* Steps to deploy DGA detection model into Splunk App DSDL. This detection depends on the Splunk app for Data Science and Deep Learning which can be found here - https://splunkbase.splunk.com/app/4607/ and the Network Resolution datamodel which can be found here - https://splunkbase.splunk.com/app/1621/. The detection uses a pre-trained deep learning model that needs to be deployed in DSDL app. Follow the steps for deployment here - https://github.com/splunk/security_content/wiki/How-to-deploy-pre-trained-Deep-Learning-models-for-ESCU. -* Download the artifacts .tar.gz file from the link - https://seal.splunkresearch.com/pretrained_dga_model_dsdl.tar.gz -* Download the pretrained_dga_model_dsdl.ipynb Jupyter notebook from https://github.com/splunk/security_content/notebooks -* Login to the Jupyter Lab for pretrained_dga_model_dsdl container. This container should be listed on Containers page for DSDL app. -* Below steps need to be followed inside Jupyter lab - * Upload the pretrained_dga_model_dsdl.tar.gz file into app/model/data path using the upload option in the jupyter notebook. - * Untar the artifact pretrained_dga_model_dsdl.tar.gz using tar -xf app/model/data/pretrained_dga_model_dsdl.tar.gz -C app/model/data - * Upload pretrained_dga_model_dsdl.pynb into Jupyter lab notebooks folder using the upload option in Jupyter lab - * Save the notebook using the save option in jupyter notebook. - * Upload pretrained_dga_model_dsdl.json into notebooks/data folder." +description: The following analytic uses a pre trained deep learning model to detect Domain Generation Algorithm (DGA) generated domains. The model is trained independently and is then made available for download. One of the prominent indicators of a domain being DGA generated is if the domain name consists of unusual character sequences or concatenated dictionary words. Adversaries often use clever techniques to obfuscate machine generated domain names as human generated. Predicting DGA generated domain names requires analysis and building a model based on carefully chosen features. The deep learning model we have developed uses the domain name to analyze patterns of character sequences along with carefully chosen custom features to predict if a domain is DGA generated. The model takes a domain name consisting of second-level and top-level domain names as input and outputs a dga_score. Higher the dga_score, the more likely the input domain is a DGA domain. The threshold for flagging a domain as DGA is set at 0.5. +search: "| tstats `security_content_summariesonly` values(DNS.answer) as IPs min(_time) as firstTime max(_time) as lastTime from datamodel=Network_Resolution by DNS.src, DNS.query | `drop_dm_object_name(DNS)` | rename query AS domain | fields IPs, src, domain, firstTime, lastTime | apply pretrained_dga_model_dsdl | rename pred_dga_proba AS dga_score | where dga_score>0.5 | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | table src, domain, IPs, firstTime, lastTime, dga_score | `detect_dga_domains_using_pretrained_model_in_dsdl_filter`" + +how_to_implement: 'Steps to deploy DGA detection model into Splunk App DSDL.\ + This detection depends on the Splunk app for Data Science and Deep Learning which can be found here - https://splunkbase.splunk.com/app/4607/ and the Network Resolution datamodel which can be found here - https://splunkbase.splunk.com/app/1621/. The detection uses a pre-trained deep learning model that needs to be deployed in DSDL app. Follow the steps for deployment here - https://github.com/splunk/security_content/wiki/How-to-deploy-pre-trained-Deep-Learning-models-for-ESCU.\ +* Download the artifacts .tar.gz file from the link `https://seal.splunkresearch.com/pretrained_dga_model_dsdl.tar.gz`\ + +* Download the pretrained_dga_model_dsdl.ipynb Jupyter notebook from `https://github.com/splunk/security_content/notebooks`\ + +* Login to the Jupyter Lab for pretrained_dga_model_dsdl container. This container should be listed on Containers page for DSDL app.\ + +* Below steps need to be followed inside Jupyter lab \ + +* Upload the pretrained_dga_model_dsdl.tar.gz file into `app/model/data` path using the upload option in the jupyter notebook.\ + +* Untar the artifact `pretrained_dga_model_dsdl.tar.gz` using `tar -xf app/model/data/pretrained_dga_model_dsdl.tar.gz -C app/model/data`\ + +* Upload `pretrained_dga_model_dsdl.pynb` into Jupyter lab notebooks folder using the upload option in Jupyter lab\ + +* Save the notebook using the save option in jupyter notebook.\ + +* Upload `pretrained_dga_model_dsdl.json` into `notebooks/data` folder.' known_false_positives: False positives may be present if domain name is similar to dga generated domains. references: - https://attack.mitre.org/techniques/T1568/002/ @@ -54,7 +62,7 @@ tags: - DE.CM observable: - name: domain - type: URL Domain + type: URL role: - Attacker - name: src diff --git a/detections/experimental/network/detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.yml b/detections/experimental/network/detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.yml new file mode 100644 index 0000000000..1380562fee --- /dev/null +++ b/detections/experimental/network/detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.yml @@ -0,0 +1,116 @@ +name: Detect suspicious DNS TXT records using pretrained model in DSDL +id: 92f65c3a-968c-11ed-a1eb-0242ac120002 +version: 1 +date: "2023-01-15" +author: Abhinav Mishra, Kumar Sharad and Namratha Sreekanta, Splunk +type: Anomaly +datamodel: +- Network_Resolution +description: The following analytic uses a pre trained deep learning model to + detect suspicious DNS TXT records. The model is trained independently and is + then made available for download. The DNS TXT records are categorized into + commonly identified types like email, verification, http using regular + expressions https://www.tide-project.nl/blog/wtmc2020/. The TXT records that + do not match regular expressions for well known types are labeled as 1 for + "unknown/suspicious" and otherwise 0 for "not suspicious". The deep learning + model we have developed uses DNS TXT responses to analyze patterns of character + sequences to predict if a DNS TXT is suspicious or not. The higher the + pred_is_unknown_proba, the more likely the DNS TXT record is suspicious. + The threshold for flagging a domain as suspicious is set at 0.5. +search: '| tstats `security_content_summariesonly` count min(_time) as firstTime + max(_time) as lastTime from datamodel=Network_Resolution where + DNS.message_type=response AND DNS.record_type=TXT by DNS.src DNS.dest + DNS.answer DNS.record_type + | `drop_dm_object_name("DNS")` + | rename answer as text + | fields firstTime, lastTime, message_type,record_type,src,dest, text + | apply detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl + | rename pred_is_unknown_proba as is_suspicious_score + | where is_suspicious_score > 0.5 + | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)` + | rename src as "Source IP", dest as "Destination IP", text as "DNS Answer", record_type as "DNS Record Type" + | table "Source IP","Destination IP","DNS Answer","DNS Record Type", firstTime, lastTime,is_suspicious_score + | `detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl_filter`' + +how_to_implement: 'Steps to deploy detect suspicious DNS TXT records model into Splunk App + DSDL. This detection depends on the Splunk app for Data Science and Deep + Learning which can be found here - https://splunkbase.splunk.com/app/4607/ + and the Network Resolution datamodel which can be found here - + https://splunkbase.splunk.com/app/1621/. The detection uses a pre-trained + deep learning model that needs to be deployed in DSDL app. Follow the steps + for deployment here - `https://github.com/splunk/security_content/wiki/How-to-deploy-pre-trained-Deep-Learning-models-for-ESCU`.\ + + * Download the `artifacts .tar.gz` file from the link - TODO update link here= + Download the `detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.ipynb` + Jupyter notebook from https://github.com/splunk/security_content/notebooks\ + + * Login to the Jupyter Lab assigned for + detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl container. + This container should be listed on Containers page for DSDL app.\ + + * Below steps need to be followed inside Jupyter lab \ + + * Upload the detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.tar.gz file + into `app/model/data` path using the upload option in the jupyter notebook.\ + + * Untar the artifact detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.tar.gz using `tar -xf app/model/data/detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.tar.gz -C app/model/data`\ + + * Upload detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.pynb into Jupyter lab notebooks folder using the upload option in Jupyter lab \ + + * Save the notebook using the save option in jupyter notebook. \ + + * Upload `detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.json` into `notebooks/data` folder.' +known_false_positives: False positives may be present if DNS TXT record contents + are similar to benign DNS TXT record contents. +references: +- https://attack.mitre.org/techniques/T1071/004/ +- https://unit42.paloaltonetworks.com/dns-tunneling-how-dns-can-be-abused-by-malicious-actors/ +- https://en.wikipedia.org/wiki/TXT_record +tags: + analytic_story: + - DNS Hijacking + - Suspicious DNS Traffic + - Command and Control + asset_type: Endpoint + cis20: + - CIS 8 + - CIS 12 + - CIS 13 + confidence: 90 + context: + - Source:Endpoint + - Stage:Command And Control + impact: 50 + kill_chain_phases: + - Command & Control + message: A suspicious DNS TXT response was detected on host $src$ , kindly review. + mitre_attack_id: + - T1568.002 + nist: + - PR.DS + - PR.PT + - DE.AE + - DE.CM + observable: + - name: answer + type: Other + role: + - Attacker + - name: src + type: Hostname + role: + - Victim + product: + - Splunk Enterprise + - Splunk Enterprise Security + - Splunk Cloud + required_fields: + - _time + - DNS.message_type + - DNS.record_type + - DNS.src + - DNS.dest + - DNS.answer + risk_score: 45 + security_domain: network diff --git a/lookups/__mlspl_detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.mlmodel b/lookups/__mlspl_detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.mlmodel new file mode 100644 index 0000000000..5b3968aaba --- /dev/null +++ b/lookups/__mlspl_detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.mlmodel @@ -0,0 +1,2 @@ +algo,model,options +MLTKContainer,"{""__mlspl_type"": [""mltkc.MLTKContainer"", ""MLTKContainer""], ""dict"": {""endpoint_url"": ""https://localhost:54270"", ""out_params"": {""params"": {""mode"": ""stage"", ""algo"": ""detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl""}, ""args"": [""is_unknown"", ""text""], ""target_variable"": [""is_unknown""], ""feature_variables"": [""text""], ""model_name"": ""detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl"", ""algo_name"": ""MLTKContainer"", ""mlspl_limits"": {""handle_new_cat"": ""default"", ""max_distinct_cat_values"": ""100"", ""max_distinct_cat_values_for_classifiers"": ""100"", ""max_distinct_cat_values_for_scoring"": ""100"", ""max_fit_time"": ""600"", ""max_inputs"": ""100000"", ""max_memory_usage_mb"": ""4000"", ""max_model_size_mb"": ""30"", ""max_score_time"": ""600"", ""use_sampling"": ""true""}, ""kfold_cv"": null}, ""feature_variables"": [""text""], ""target_variable"": ""is_unknown""}}","{""params"": {""mode"": ""stage"", ""algo"": ""detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl""}, ""args"": [""is_unknown"", ""text""], ""target_variable"": [""is_unknown""], ""feature_variables"": [""text""], ""model_name"": ""detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl"", ""algo_name"": ""MLTKContainer"", ""mlspl_limits"": {""handle_new_cat"": ""default"", ""max_distinct_cat_values"": ""100"", ""max_distinct_cat_values_for_classifiers"": ""100"", ""max_distinct_cat_values_for_scoring"": ""100"", ""max_fit_time"": ""600"", ""max_inputs"": ""100000"", ""max_memory_usage_mb"": ""4000"", ""max_model_size_mb"": ""30"", ""max_score_time"": ""600"", ""use_sampling"": ""true""}, ""kfold_cv"": null}" diff --git a/lookups/__mlspl_detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.yml b/lookups/__mlspl_detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.yml new file mode 100644 index 0000000000..6d1d90cfd5 --- /dev/null +++ b/lookups/__mlspl_detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.yml @@ -0,0 +1,4 @@ +description: Detect suspicious DNS txt records using Pretrained Model in DSDL +filename: __mlspl_detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.mlmodel +name: __mlspl_detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl +case_sensitive_match: 'false' \ No newline at end of file diff --git a/notebooks/detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.ipynb b/notebooks/detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.ipynb new file mode 100644 index 0000000000..c6f011a8b5 --- /dev/null +++ b/notebooks/detect_suspicious_dns_txt_records_using_pretrained_model_in_dsdl.ipynb @@ -0,0 +1,772 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Deep Learning model to detect suspicious DNS TXT records " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook uses a pre-trained deep learning model to predict whether a DNS TXT record is suspicious or not." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "While DNS TXT records initially were supposed to only hold descriptive text, its use and format have been subject to changes and debates over the years. This makes TXT records ideal candidates for malicious and unintended practices which include adding large records to create more efficient DNS amplification attacks, or creating a command and control channel for malware. \n", + "\n", + "The model is trained independently and is then made available for download. The DNS TXT records are categorized into commonly identified types like email, verification, http using regular expressions https://www.tide-project.nl/blog/wtmc2020/. The TXT records that do not match regular expressions for well known types are labeled as 1 for \"unknown/suspicious\" and otherwise 0 for \"not suspicious\". The deep learning model we have developed uses DNX TXT responses to analyze patters of character sequences to predict of a DNS TXT is suspicious or not. Higher the pred_is_unknown_proba, the more likely is the DNS TXT record is suspicious. The threshold for flagging a domain as suspicious is set at 0.5." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Stage 0 - import libraries\n", + "At stage 0 we define all imports necessary to run our subsequent code depending on various libraries." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "deletable": false, + "name": "mltkc_import" + }, + "outputs": [], + "source": [ + "# this definition exposes all python module imports that should be available in all subsequent commands\n", + "import numpy as np \n", + "import pandas as pd \n", + "import os\n", + "import torch\n", + "import torch.nn as nn\n", + "import torch.nn.functional as F\n", + "import torch.optim as optim\n", + "from torch.utils.data.sampler import SubsetRandomSampler\n", + "from torch.utils.data import Dataset\n", + "from torch.utils.data import DataLoader\n", + "from sklearn.feature_extraction.text import CountVectorizer\n", + "from torch.autograd import Variable\n", + "from torch.optim import lr_scheduler\n", + "from collections import Counter,OrderedDict\n", + "import pickle\n", + "\n", + "# global constants\n", + "MODEL_DIRECTORY = \"/srv/app/model/data/\"\n", + "vocab_size = 10002\n", + "embedding_dim = 64\n", + "hidden_size = 64\n", + "fc_hidden_size = 64\n", + "num_output_nodes = 1\n", + "\n", + "dropout = 0.5" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "numpy version: 1.19.2\n", + "pandas version: 1.1.3\n", + "torch version: 1.11.0\n" + ] + } + ], + "source": [ + "# THIS CELL IS NOT EXPORTED - free notebook cell for testing or development purposes\n", + "print(\"numpy version: \" + np.__version__)\n", + "print(\"pandas version: \" + pd.__version__)\n", + "print(\"torch version: \" + torch.__version__)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Stage 1 - get a data sample from Splunk\n", + "There are currently 2 ways to retrieve data from Splunk: Option 1 is to interactively pull data from Splunk into the DLTK Jupyter Lab environment. This is useful when the Splunk REST API is accessible from the Jupyter environment and a valid Splunk auth token is defined in the DLTK app. This option has advantages to quickly experiment with different Splunk SPL queries and further interactively work with the search results in Jupyter.\n", + "\n", + "### Option 1 - pull data from Splunk" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "import libs.SplunkSearch as SplunkSearch" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "8b33153fe3ca43febb2257b1791064fa", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "VBox(children=(HBox(children=(Textarea(value='| makeresults count=10 \\n| streamstats c as i \\n| eval s = i%3 \\…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "search = SplunkSearch.SplunkSearch()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
| \n", + " | _time | \n", + "feature_0 | \n", + "feature_1 | \n", + "feature_2 | \n", + "i | \n", + "s | \n", + "
|---|---|---|---|---|---|---|
| 0 | \n", + "2022-05-18T10:30:54.000+02:00 | \n", + "0.8460328145883977 | \n", + "0.32027013367041945 | \n", + "0.8921294794417918 | \n", + "1 | \n", + "1 | \n", + "
| 1 | \n", + "2022-05-18T10:30:54.000+02:00 | \n", + "0.3228773274458945 | \n", + "0.9270845879800618 | \n", + "0.6180154159665108 | \n", + "2 | \n", + "2 | \n", + "
| 2 | \n", + "2022-05-18T10:30:54.000+02:00 | \n", + "0.9652930609881878 | \n", + "0.6127289566211402 | \n", + "0.6959671885706484 | \n", + "3 | \n", + "0 | \n", + "
| 3 | \n", + "2022-05-18T10:30:54.000+02:00 | \n", + "0.9985754322260618 | \n", + "0.7212827517651021 | \n", + "0.6265083532780409 | \n", + "4 | \n", + "1 | \n", + "
| 4 | \n", + "2022-05-18T10:30:54.000+02:00 | \n", + "0.4984334879554808 | \n", + "0.4883861029520631 | \n", + "0.8144497619941831 | \n", + "5 | \n", + "2 | \n", + "
| 5 | \n", + "2022-05-18T10:30:54.000+02:00 | \n", + "0.9335020575672388 | \n", + "0.7346397545188665 | \n", + "0.03047862323001027 | \n", + "6 | \n", + "0 | \n", + "
| 6 | \n", + "2022-05-18T10:30:54.000+02:00 | \n", + "0.8374560391530395 | \n", + "0.9159072837792337 | \n", + "0.01808864250779152 | \n", + "7 | \n", + "1 | \n", + "
| 7 | \n", + "2022-05-18T10:30:54.000+02:00 | \n", + "0.8355274042114615 | \n", + "0.3842416317202151 | \n", + "0.1918810959905386 | \n", + "8 | \n", + "2 | \n", + "
| 8 | \n", + "2022-05-18T10:30:54.000+02:00 | \n", + "0.35425164457410574 | \n", + "0.11845849128440022 | \n", + "0.01499031251296401 | \n", + "9 | \n", + "0 | \n", + "
| 9 | \n", + "2022-05-18T10:30:54.000+02:00 | \n", + "0.38959994819015265 | \n", + "0.7069222498685122 | \n", + "0.930438622366637 | \n", + "10 | \n", + "1 | \n", + "