Question Analysis
During a Linux investigation, you notice processes spawning curl and wget commands that pull a script from a remote GitHub raw URL and pipe it into bash. Identify the name of the script that was retrieved and executed.
d0c88567-803d-4dca-99b4-7ce65e7b257c
Question ID
6/23
Models Correct
1
Valid Answer
26%
Success Rate
Reference Answer
Human-validated correct answer for this question
echo-art-fish.sh
Model Performance Comparison
See how different AI models performed on this question
Model | Answer | KQL Query | Status | Attempts | Time | Cost |
---|---|---|---|---|---|---|
gemini-2.5-flash-preview-04-17 | No answer provided | No query generated | Incorrect | 5 | 0.00s | 0.0330 |
gpt-35-turbo | No answer provided | No query generated | Incorrect | 5 | 0.00s | 0.0114 |
gpt-4-turbo-2024-04-09 | LinEnum.sh | DeviceProcessEvents
| where ProcessCommandLine has_any('curl', 'wget') and ProcessCommandLine contains 'bash'
| extend ScriptUrl = tostring(split(ProcessCommandLine, '|')[0])
| extend ScriptName = tostring(split(ScriptUrl, '/')[array_length(split(ScriptUrl, '/')) - 1])
| project DeviceName, FileName, ProcessCommandLine, ScriptUrl, ScriptName | Incorrect | 1 | 14.74s | 0.0732 |
gpt-4.1 | No answer provided | No query generated | Incorrect | 5 | 0.00s | 0.0434 |
gpt-4.1-finetuned | No answer provided | No query generated | Incorrect | 5 | 0.00s | 0.0426 |
gpt-4.1-mini | pipe-to-shell.sh | DeviceProcessEvents
| where ProcessCommandLine contains @"curl" or ProcessCommandLine contains @"wget"
| where ProcessCommandLine contains @"raw.githubusercontent.com"
| where ProcessCommandLine contains @"bash" or ProcessCommandLine contains @"| bash"
| extend ScriptUrl = extract(@"https?://raw\.githubusercontent\.com[^\s|]+", 0, ProcessCommandLine)
| extend ScriptName = extract(@"[^/]+$", 0, ScriptUrl)
| project TimeGenerated, DeviceName, ProcessCommandLine, ScriptUrl, ScriptName
| distinct ScriptName, DeviceName, TimeGenerated, ProcessCommandLine, ScriptUrl | Incorrect | 1 | 20.65s | 0.0029 |
gpt-4.1-nano | No answer provided | No query generated | Incorrect | 5 | 0.00s | 0.0017 |
gpt-4o | AutoSUID.sh | DeviceProcessEvents
| where InitiatingProcessCommandLine contains "curl" or InitiatingProcessCommandLine contains "wget"
| where InitiatingProcessCommandLine matches regex @"https:\/\/raw.githubusercontent.com\/.*\.sh" and InitiatingProcessCommandLine contains "bash"
| extend ScriptName = extract(@"[\w-]+\.sh", 0, InitiatingProcessCommandLine)
| project Timestamp, DeviceId, DeviceName, InitiatingProcessCommandLine, ScriptName | Incorrect | 1 | 24.29s | 0.0201 |
gpt-5-high | pipe-to-shell.sh | let linux_devices = DeviceInfo
| where OSPlatform == "Linux"
| project DeviceId;
let gh_hosts = dynamic(["raw.githubusercontent.com","gist.githubusercontent.com","github.com"]);
let pipe_indicators = dynamic(["| bash","|bash","| sh","|sh","bash -c","sh -c","bash <(","sh <("]);
// Path A: Process events where the full pipeline is visible in the command line
let process_hits = DeviceProcessEvents
| where DeviceId in (linux_devices)
| where ProcessCommandLine has_any ("curl","wget")
| where ProcessCommandLine has_any (gh_hosts)
| where ProcessCommandLine has_any (pipe_indicators)
| extend Url = extract(@"https?://[^\s'""|)><]+", 0, ProcessCommandLine)
| project TimeGenerated, DeviceId, DeviceName, AccountName, InitiatingProcessAccountName, FileName, ProcessCommandLine, Url;
// Path B: Network events from curl/wget to GitHub raw hosts (captures URL even if pipeline not in process cmdline)
let network_hits = DeviceNetworkEvents
| where DeviceId in (linux_devices)
| where InitiatingProcessFileName in ("curl","wget")
| where RemoteUrl has_any (gh_hosts)
| project TimeGenerated, DeviceId, DeviceName, AccountName=InitiatingProcessAccountName, InitiatingProcessAccountName, FileName=InitiatingProcessFileName, ProcessCommandLine=InitiatingProcessCommandLine, Url=RemoteUrl;
union process_hits, network_hits
| where isnotempty(Url)
| extend ScriptName = tostring(extract(@"([^/?#]+)(?:\?[^#]*)?(?:#.*)?$", 1, Url))
| where isnotempty(ScriptName)
| summarize FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated), DeviceCount=dcount(DeviceId), Devices=make_set(DeviceName), Examples=make_set(Url, 5) by ScriptName
| order by LastSeen desc | Incorrect | 1 | 181.17s | 0.0818 |
gpt-5-mini-high | echo-art-fish.sh | // Identify scripts downloaded from GitHub raw URLs via curl/wget piped to bash
let github_raw_patterns = dynamic(["raw.githubusercontent.com", "raw.githubusercontent.com", "raw.github.com","githubusercontent.com"]);
let curl_wget_filter = (s:string) {
s has "curl" and s has "|" and s has "bash" or
s has "wget" and (s has "-O-" or s has "-qO-" or s has "-O -") and s has "|" and s has "bash"
};
// Search DeviceProcessEvents (preferred) and DeviceEvents
let procEvents = DeviceProcessEvents
| where isnotempty(ProcessCommandLine)
| where curl_wget_filter(ProcessCommandLine) or curl_wget_filter(InitiatingProcessCommandLine)
| extend CmdLine = coalesce(ProcessCommandLine, InitiatingProcessCommandLine)
| extend RemoteUrl = extract(@"((https?://)[^\s'\|]+raw[^\s'\|]+|https?://[^\s'\|]+raw\.githubusercontent\.com[^\s'\|]+|https?://[^\s'\|]*raw\.githubusercontent\.com[^\s'\|]+|https?://raw\.githubusercontent\.com[^\s'\|]+|https?://raw\.githubusercontent\.com[^\s'\|]+)", 1, CmdLine)
| extend RemoteUrl = iff(isempty(RemoteUrl), extract(@"https?://[^\s'\|]+githubusercontent\.com[^\s'\|]+", 0, CmdLine), RemoteUrl)
| extend ScriptName = trim_end(@"/", tostring(parse_url(RemoteUrl).Path))
| extend ScriptName = replace_regex(ScriptName, @"^.*/", "")
| project TimeGenerated, DeviceName, ProcessId, ProcessCreationTime, FileName, CmdLine, RemoteUrl, ScriptName
;
let devEvents = DeviceEvents
| where isnotempty(ProcessCommandLine) or isnotempty(InitiatingProcessCommandLine)
| where curl_wget_filter(ProcessCommandLine) or curl_wget_filter(InitiatingProcessCommandLine)
| extend CmdLine = coalesce(ProcessCommandLine, InitiatingProcessCommandLine)
| extend RemoteUrl = extract(@"https?://[^\s'\|]+(raw\.githubusercontent\.com|raw\.github\.com|rawgithubusercontent)\/[^\s'\|]+", 0, CmdLine)
| extend RemoteUrl = iff(isempty(RemoteUrl), extract(@"https?://[^\s'\|]+githubusercontent\.com[^\s'\|]+", 0, CmdLine), RemoteUrl)
| extend ScriptName = trim_end(@"/", tostring(parse_url(RemoteUrl).Path))
| extend ScriptName = replace_regex(ScriptName, @"^.*/", "")
| project TimeGenerated, DeviceName, ProcessId=InitiatingProcessId, ProcessCreationTime=InitiatingProcessCreationTime, FileName=InitiatingProcessFileName, CmdLine, RemoteUrl, ScriptName
;
// Combine and return distinct script names with sample evidence
procEvents
| union devEvents
| where isnotempty(RemoteUrl)
| summarize count(), Samples = make_list(pack_all(), 10) by ScriptName
| order by count_ desc
| project ScriptName, Count = count_, Samples
| Correct | 1 | 25.69s | 0.0049 |
gpt-5-mini-low | No answer provided | No query generated | Incorrect | 5 | 0.00s | 0.0149 |
gpt-5-mini-medium | No answer provided | No query generated | Incorrect | 5 | 0.00s | 0.0155 |
gpt-5-nano-high | echo-art-fish.sh | DeviceProcessEvents
| where InitiatingProcessCommandLine contains "curl" or InitiatingProcessCommandLine contains "wget"
| where InitiatingProcessCommandLine contains "raw.githubusercontent.com" or InitiatingProcessCommandLine contains "githubusercontent.com"
| where InitiatingProcessCommandLine contains "| bash" or InitiatingProcessCommandLine contains "| /bin/bash" or InitiatingProcessCommandLine contains "| /bin/sh"
| extend ScriptUrl = extract("https?://[^ ]+", 0, InitiatingProcessCommandLine)
| extend ScriptName = extract("https?://[^/]+/.*/([^/?#]+)(?:[?#].*)?$", 1, ScriptUrl)
| where isnotempty(ScriptName)
| summarize FirstSeen = min(TimeGenerated), DeviceCount = dcount(DeviceId), Hits = count() by ScriptName, ScriptUrl
| order by Hits desc | Correct | 2 | 62.30s | 0.0042 |
gpt-5-nano-medium | No answer provided | No query generated | Incorrect | 5 | 0.00s | 0.0093 |
grok-3-beta | echo-art-fish.sh | DeviceProcessEvents
| where ProcessCommandLine contains 'curl' or ProcessCommandLine contains 'wget'
| where ProcessCommandLine contains 'github'
| where ProcessCommandLine contains '| bash'
| extend ScriptName = extract('.*\\/([^\\/\\?\\s]+)', 1, ProcessCommandLine)
| project Timestamp, DeviceName, ProcessCommandLine, ScriptName
| order by Timestamp desc | Correct | 5 | 39.67s | 0.0947 |
grok-3-mini-beta | pipe-to-shell.sh | DeviceProcessEvents
| where ProcessCommandLine matches regex @'\bcurl\b.*\|\b.*\bbash\b' or ProcessCommandLine matches regex @'\bwget\b.*\|\b.*\bbash\b'
| extend FullURL = extract(@'(https?://[^\s|]+)', 1, ProcessCommandLine)
| extend arr = split(FullURL, '/')
| extend ScriptName = arr[array_length(arr) - 1]
| project TimeGenerated, DeviceName, ProcessCommandLine, ScriptName
| summarize ScriptNames = make_set(ScriptName) by DeviceName | Incorrect | 3 | 36.57s | 0.0045 |
o1-high | LinEnum.sh | DeviceProcessEvents
| where ActionType == "ProcessCreated"
| where ProcessCommandLine has_any ("curl", "wget")
| where ProcessCommandLine has "raw.githubusercontent.com" and ProcessCommandLine has "bash"
| extend DownloadedUrl = extract(@"(https?:\/\/[^\s|]+)", 0, ProcessCommandLine)
| extend ScriptName = extract(@"\/([^\/\?]+)(\?.*)?$", 1, DownloadedUrl)
| project TimeGenerated, DeviceId, DeviceName, AccountName, ProcessCommandLine, DownloadedUrl, ScriptName
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by DeviceId, DeviceName, AccountName, ScriptName
| order by EndTime desc | Incorrect | 2 | 115.36s | 0.6308 |
o1-low | echo-art-fish.sh | DeviceProcessEvents
| where ActionType == "ProcessCreated"
| where tolower(InitiatingProcessCommandLine) has_any ("curl", "wget")
and tolower(InitiatingProcessCommandLine) has "raw.githubusercontent.com"
and tolower(InitiatingProcessCommandLine) has "| bash"
| extend ScriptName = extract(@"raw\.githubusercontent\.com[^ ]+/(?<script>[^ ]+)", 1, tolower(InitiatingProcessCommandLine))
| project Timestamp, DeviceName, InitiatingProcessCommandLine, ScriptName
| order by Timestamp desc | Correct | 3 | 61.01s | 0.5518 |
o3-high | LinEnum.sh | // ------------------------------------------------------------------------------------------------
// Detect scripts fetched from GitHub raw via curl / wget and immediately executed through a pipe
// (e.g. curl https://raw.githubusercontent.com/user/repo/script.sh | bash )
// ------------------------------------------------------------------------------------------------
// Time range will be added automatically by the hunting portal, so no explicit where TimeGenerated
// clause is required here.
let pipeIndicators = dynamic(["|bash","| bash","|sh","| sh","bash -","sh -"]);
DeviceProcessEvents
// 1. Focus on the typical downloader utilities and the shell that is used to execute the script
| where FileName in~ ("bash","sh","curl","wget")
// 2. Command-line must reference GitHub raw content and contain a pipe that forwards the content to a shell
| where ProcessCommandLine has "raw.githubusercontent.com"
and ProcessCommandLine has_any(pipeIndicators)
// 3. Pull the full raw URL out of the command line (greedy until we hit whitespace or the pipe symbol)
| extend RawUrl = extract(@"((?:http|https)://[^\s'""|<>]+)", 1, ProcessCommandLine)
| where isnotempty(RawUrl)
// 4. Derive just the script name (the last element after the final '/')
| extend ScriptName = extract(@"([^/]+)$", 1, RawUrl)
// 5. Return the interesting columns; hunting portal will add the time filter
| project TimeGenerated,
DeviceName,
AccountName,
Executable = FileName,
ProcessCommandLine,
RawUrl,
ScriptName
| order by TimeGenerated desc | Incorrect | 2 | 73.57s | 0.0418 |
o3-mini-high | echo-art-fish.sh | DeviceProcessEvents
| where ProcessCommandLine has_any ("curl", "wget")
| where ProcessCommandLine contains "raw.githubusercontent.com"
| where ProcessCommandLine contains "| bash"
| extend ScriptName = extract(@"https?://raw\.githubusercontent\.com/[^/]+/[^/]+/[^/]+/([^ \|]+)", 1, ProcessCommandLine)
| where isnotempty(ScriptName)
| summarize Occurrences = count() by ScriptName, ProcessCommandLine
| order by Occurrences desc | Correct | 1 | 13.12s | 0.0088 |
o3-mini-low | LinEnum.sh | DeviceProcessEvents
| where ProcessCommandLine has_any ("curl", "wget")
| where ProcessCommandLine contains "raw.githubusercontent.com"
| where ProcessCommandLine contains "bash"
| extend ScriptName = extract(@"raw\.githubusercontent\.com/\S+/([^/]+\.sh)", 1, ProcessCommandLine)
| where isnotempty(ScriptName)
| distinct ScriptName | Incorrect | 1 | 61.96s | 0.0116 |
o4-mini-high | echo-art-fish.sh | DeviceProcessEvents
| where TimeGenerated between (startofday(ago(7d)) .. now()) // timerange will be set by the external control
| where ProcessCommandLine has_any ("curl", "wget")
| where ProcessCommandLine contains "raw.githubusercontent.com"
| where ProcessCommandLine contains "bash" or ProcessCommandLine contains "|"
// Extract the raw.githubusercontent.com URL
| extend DownloadUrl = extract(@"(https?://raw\.githubusercontent\.com/[^\s|]+)", 1, ProcessCommandLine)
| where isnotempty(DownloadUrl)
// Parse out the script name (last segment of the URL)
| extend ScriptName = tostring(split(DownloadUrl, "/")[array_length(split(DownloadUrl, "/")) - 1])
| project TimeGenerated, DeviceName, InitiatingProcessFileName, ProcessCommandLine, DownloadUrl, ScriptName
| sort by TimeGenerated desc | Correct | 1 | 30.30s | 0.0134 |
o4-mini-low | pipe-to-shell.sh | DeviceProcessEvents
| where ActionType == "ProcessCreated"
// Only look for curl or wget invocations
| where ProcessCommandLine contains "curl" or ProcessCommandLine contains "wget"
// Focus on raw GitHub URLs
| where ProcessCommandLine contains "raw.githubusercontent.com"
// Extract the full URL being fetched
| extend DownloadUrl = extract(@"(https?://raw\.githubusercontent\.com/[^\s'""\|]+)", 1, ProcessCommandLine)
| where isnotempty(DownloadUrl)
// Derive the script name from the URL by taking the last path segment
| extend ScriptName = tostring(split(DownloadUrl, "/")[-1])
// Filter for typical shell script extensions
| where ScriptName endswith ".sh" or ScriptName endswith ".bash"
// Summarize by script name to see which scripts were pulled and executed
| summarize Count = count() by ScriptName
| sort by Count desc | Incorrect | 4 | 108.17s | 0.0301 |