Skip to content

Commit

Permalink
Adding IQY Scanner
Browse files Browse the repository at this point in the history
  • Loading branch information
phutelmyer committed Nov 3, 2023
1 parent ea46573 commit ff34f69
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 0 deletions.
5 changes: 5 additions & 0 deletions configs/python/backend/backend.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,11 @@ scanners:
priority: 5
options:
limit: 50
'ScanIqy':
- positive:
flavors:
- 'iqy_file'
priority: 5
'ScanJarManifest':
- positive:
flavors:
Expand Down
12 changes: 12 additions & 0 deletions configs/python/backend/taste/taste.yara
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,18 @@ rule excel4_file
(uint32be(0) == 0x504b0304 and $rels and $sheet and $xlsstr)
}

rule iqy_file {
meta:
description = "Detects potential IQY (Excel Web Query) files with various URL protocols"
author = "Paul Hutelmyer"
date = "2023-11-02"
strings:
$iqy_header = /^WEB\n/ nocase
$url = /(?:http|https|ftp|ftps|file|smb):\/\/\S+|\\{2}\w+\\(?:[\w$]+\\)*[\w$]+/ nocase
condition:
$iqy_header at 0 and $url
}

rule onenote_file
{
meta:
Expand Down
64 changes: 64 additions & 0 deletions src/python/strelka/scanners/scan_iqy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Description #
# This scanner is looking for iqy files used with excel.
#
# author: Tasha Taylor
# date: 10/30/2023

import re

from strelka import strelka


class ScanIqy(strelka.Scanner):
"""
Extract URLs from IQY files.
IQY files, or Excel Web Query Internet Inquire files, are typically created from a VBA Web Query output.
The following is a typical format:
WEB
1
[URL]
[optional parameters]
Additional properties can be found at: https://learn.microsoft.com/en-us/office/vba/api/excel.querytable
"""

def scan(self, data, file, options, expire_at):
try:
# Regular expression for detecting a URL-like pattern
address_pattern = re.compile(
r"\b(?:http|https|ftp|ftps|file|smb)://\S+|"
r"\\{2}\w+\\(?:[\w$]+\\)*[\w$]+",
re.IGNORECASE,
)

# Attempt UTF-8 decoding first, fall back to latin-1 if necessary
try:
data = data.decode("utf-8")
except UnicodeDecodeError:
data = data.decode("latin-1")

# Split lines to review each record separately
data_lines = data.splitlines()

addresses = set()
# For each line, check if the line matches the address pattern.
# In a typical IQY file, the "WEB" keyword is at the beginning of the file,
# and what follows is usually just one URL with optional additional parameters.
# However, because we are iterating lines anyway, lets check for additional addresses anyway.
for entry in data_lines[1:]:
match = address_pattern.search(entry)
if match:
address = match.group().strip()
if address:
addresses.add(address)

# Evaluate if any addresses were found and assign the boolean result.
self.event["address_found"] = bool(addresses)

# Send all addresses to the IOC parser.
self.add_iocs(list(addresses), self.type.url)

except UnicodeDecodeError as e:
self.flags.append(f"Unicode decoding error: {e}")
except Exception as e:
self.flags.append(f"Unexpected exception: {e}")
3 changes: 3 additions & 0 deletions src/python/strelka/tests/fixtures/test.iqy
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
WEB
1
https://github.com/target/strelka/blob/master/docs/index.html // Test case: Valid HTTPS URL
40 changes: 40 additions & 0 deletions src/python/strelka/tests/test_scan_iqy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from pathlib import Path
from unittest import TestCase, mock

from strelka.scanners.scan_iqy import ScanIqy as ScanUnderTest
from strelka.tests import run_test_scan


def test_scan_iqy(mocker):
"""
Pass: Sample event matches output of scanner.
Failure: Unable to load file or sample event fails to match.
"""

test_scan_event = {
"elapsed": mock.ANY,
"flags": [],
"address_found": True,
"iocs": [
{
"description": "",
"ioc": "github.com",
"ioc_type": "domain",
"scanner": "ScanIqy",
},
{
"description": "",
"ioc": "https://github.com/target/strelka/blob/master/docs/index.html",
"ioc_type": "url",
"scanner": "ScanIqy",
},
],
}
scanner_event = run_test_scan(
mocker=mocker,
scan_class=ScanUnderTest,
fixture_path=Path(__file__).parent / "fixtures/test.iqy",
)

TestCase.maxDiff = None
TestCase().assertDictEqual(test_scan_event, scanner_event)

0 comments on commit ff34f69

Please sign in to comment.