Detection rules › Splunk

PowerShell Script Block With URL Chain

Status
production
Severity
medium
Group by
Guid, Name, Opcode, Path, ScriptBlockId, ScriptBlockText, dest, process_id, signature, signature_id, user_id, vendor_product
Author
Steven Dick
Source
github.com/splunk/security_content

The following analytic identifies suspicious PowerShell script execution via EventCode 4104 that contains multiple URLs within a function or array. It leverages PowerShell operational logs to detect script blocks with embedded URLs, often indicative of obfuscated scripts or those attempting to download secondary payloads. This activity is significant as it may signal an attempt to execute malicious code or download additional malware. If confirmed malicious, this could lead to code execution, further system compromise, or data exfiltration. Review parallel processes and the full script block for additional context and related artifacts.

Known false positives

  • No false positives have been identified at this time.

MITRE ATT&CK coverage

Telemetry coverage

Rule body

name: PowerShell Script Block With URL Chain
id: 4a3f2a7d-6402-4e64-a76a-869588ec3b57
version: 12
creation_date: '2023-07-11'
modification_date: '2026-05-13'
author: Steven Dick
status: production
type: TTP
description: The following analytic identifies suspicious PowerShell script execution via EventCode 4104 that contains multiple URLs within a function or array. It leverages PowerShell operational logs to detect script blocks with embedded URLs, often indicative of obfuscated scripts or those attempting to download secondary payloads. This activity is significant as it may signal an attempt to execute malicious code or download additional malware. If confirmed malicious, this could lead to code execution, further system compromise, or data exfiltration. Review parallel processes and the full script block for additional context and related artifacts.
data_source:
    - Powershell Script Block Logging 4104
search: '`powershell` EventCode=4104 ScriptBlockText IN ("*http:*","*https:*") | regex ScriptBlockText="(\"?(https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*))\"?(?:,|\))?){2,}" | rex max_match=20 field=ScriptBlockText "(?<url>https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*))" | fillnull | stats count min(_time) as firstTime max(_time) as lastTime by dest signature signature_id user_id vendor_product EventID Guid Opcode Name Path ProcessID ScriptBlockId ScriptBlockText | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `powershell_script_block_with_url_chain_filter`'
how_to_implement: The following analytic requires PowerShell operational logs to be imported. Modify the powershell macro as needed to match the sourcetype or add index. This analytic is specific to 4104, or PowerShell Script Block Logging.
known_false_positives: No false positives have been identified at this time.
references:
    - https://www.mandiant.com/resources/blog/tracking-evolution-gootloader-operations
    - https://thedfirreport.com/2022/05/09/seo-poisoning-a-gootloader-story/
    - https://attack.mitre.org/techniques/T1059/001/
finding:
    title: A suspicious powershell script used by $user_id$ on host $dest$ contains URLs in an array, this is commonly used for malware.
    entity:
        field: user_id
        type: user
        score: 50
intermediate_findings:
    entities:
        - field: dest
          type: system
          score: 50
          message: A suspicious powershell script used by $user_id$ on host $dest$ contains URLs in an array, this is commonly used for malware.
analytic_story:
    - Malicious PowerShell
    - Hellcat Ransomware
asset_type: Endpoint
mitre_attack_id:
    - T1059.001
    - T1105
product:
    - Splunk Enterprise
    - Splunk Enterprise Security
    - Splunk Cloud
category: endpoint
security_domain: endpoint

Stages and Predicates

Stage 1: search

`powershell` EventCode=4104 ScriptBlockText IN ("*http:*","*https:*")

Stage 2: regex

| regex ScriptBlockText="(\"?(https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*))\"?(?:,|\))?){2,}"

Stage 3: rex

| rex max_match=20 field=ScriptBlockText "(?<url>https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*))"

Stage 4: fillnull

| fillnull

Stage 5: stats

| stats count min(_time) as firstTime max(_time) as lastTime by dest signature signature_id user_id vendor_product EventID Guid Opcode Name Path ProcessID ScriptBlockId ScriptBlockText

Stage 6: search

| `security_content_ctime(firstTime)`

Stage 7: search

| `security_content_ctime(lastTime)`

Stage 8: search

| `powershell_script_block_with_url_chain_filter`

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
EventCodeeq
  • 4104 corpus 269 (splunk 269)
field:"EventID" kind:eq value:"4104"
ScriptBlockTextin
  • "*http:*"
  • "*https:*"
field:"ScriptBlockText" kind:in
ScriptBlockTextregex_match
  • "(\"?(https?:\/\/(?:www.)?[-a-zA-Z0-9@:%._+~#=]{1,256}.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&\/=]*))\"?(?:,|))?){2,}"
field:"ScriptBlockText" kind:regex_match