Skip to content

Commit 55d8963

Browse files
committed
add CTI uplifts public
1 parent 8802318 commit 55d8963

File tree

1 file changed

+181
-74
lines changed
  • artifacts/definitions/Windows/Forensics

1 file changed

+181
-74
lines changed

artifacts/definitions/Windows/Forensics/Lnk.yaml

+181-74
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ description: |
2121
- SusArgRegex: Regex for suspicious strings in Arguments.
2222
- SusHostnameRegex: Regex for suspicious TrackerData Hostname.
2323
- CheckHostnameMismatch: Compare TrackerData.MachineID with Hostname (noisy in many networks)
24+
- VmPrefixMAC: Regex to match known Virtual Machine MacAddress prefix in TrackerData.
25+
- RiskyExe: Regex target exe to flag as risky.
26+
2427
2528
List of fields targeted by filter regex:
2629
@@ -40,19 +43,38 @@ description: |
4043
4144
Windows.Forensics.Lnk also will highlight suspicious lnk attributes in a Suspicious field.
4245
43-
* Large Size - default over 20000 bytes
44-
* Startup Path - path with \Startup\
46+
* Large Size - Check for large size, default over 20000 bytes
47+
* Startup Path - Path with \Startup\
48+
* Zeroed Headers - Check for ShellHeader items zeroed.
49+
* Hidden window - Check for ShellLinkHeader.ShowCommand as SHOWMINNOACTIVE
50+
* Target Changed path - Check LNK TargetPath different to PropertyStore path.
51+
* Target Changed size - Check LNK ShellLinkHeader.FileSize different to PropertyStore size.
52+
* Risky target - Checks several LNK target paths to the RiskyExe regex.
53+
* WebDAV - Checks for NetworkProviderType = WNNC_NET_DAV
54+
* Line break in StringData.Name
55+
* Suspicious argument size - large sized arguments over 250 characters as default
4556
* Environment variable script - environment vatiable with a common script configured (bat|cmd|ps1|js|vbs|vbe|py)
57+
* Environment variable script
4658
* No Target with environmant variable - environment variable only execution
47-
* Suspicious argument size - large sized arguments over 250 characters as default
48-
* Arguments have ticks - ticks are common in malicious LNK files
49-
* Arguments have environment variables - environment variables (%|\$env:) are common in malicious LNKs
50-
* Arguments have rare characters - looks for specific rare characters that may indicate obfuscation (\?|\!|\~|\@)
51-
* Arguments have leading space malicious LNK files may have a many leading spaces to obfuscate some tools
59+
* Suspicious hostname - some common malicious hostnames
60+
* Hostname mismatch - if selected will compare trackerdata hostname to machine name (lots of FPs)
61+
* Created in VM - Check TrackerData MacAddress for known VM prefix
62+
* Local Admin- check PropertyStore for indications LNK created by local admin UID 500
63+
* Cyrillic Language - check PropertyStore for Cyrillic strings
64+
* Chinese Language - check PropertyStore for Chinese strings
65+
* Korean Language - check PropertyStore for Korean strings
66+
* Persian Language - check PropertyStore for Persian strings
67+
* Vietnamese Language - check PropertyStore for Vietnamese strings
68+
* CodePage - checks for existance of a ExtraData code page setting. Rare enough to report on - 936:Simplified Chinese, 949:Korean, 950:Traditional Chinese
69+
* Has Overlay - check for overlay and extra data attached to LNK
70+
* Long Base64 - check for a long base64 blog over 20 decoded characters
71+
* Arguments have ticks - ticks are common in malicious LNK files
72+
* Arguments have environment variables - environment variables (%|\$env:) are common in malicious LNKs
73+
* Arguments have rare characters - looks for specific rare characters that may indicate obfuscation (\?|\!|\~|\@)
74+
* Arguments have leading space - malicious LNK files may have a many leading spaces to obfuscate some tools
5275
* Arguments have http strings - LNKs are reguarly used as a download cradle - https?://
76+
* Arguments have UNC strings
5377
* Suspicious arguments - some common malicious arguments observed in field (with mind to False positive)
54-
* Suspicious hostname - some common malicious hostnames
55-
* Hostname mismatch - if selected will compare trackerdata hostname to machine name (lots of FPs)
5678
5779
5880
reference:
@@ -83,13 +105,24 @@ parameters:
83105
type: int
84106
- name: SusArgRegex
85107
description: Regex for suspicious strings in Argumetns.
108+
type: regex
86109
default: \\AppData\\|\\Users\\Public\\|\\Temp\\|comspec|&cd&echo| -NoP | -W Hidden | [-/]decode | -e.* (JAB|SUVYI|SQBFAFgA|aWV4I|aQBlAHgA)|start\s*[\\/]b|\.downloadstring\(|\.downloadfile\(|iex
87110
- name: SusHostnameRegex
88111
description: Regex for suspicious TrackerData Hastname.
112+
type: regex
89113
default: ^(Win-|Desktop-|Commando$)
90114
- name: CheckHostnameMismatch
91115
description: Compare TrackerData.MachineID with Hostname (noisy in many networks)
92116
type: bool
117+
- name: VmPrefixMAC
118+
description: VM MacAddress prefix regex to compate to LNK TrackerData.
119+
type: regex
120+
default: ^(00:50:56|00:0C:29|00:05:69|00:1C:14|08:00:27|52:54:00|00:21:F6|00:14:4F|00:0F:4B|00:15:5D)
121+
- name: RiskyExe
122+
description: Regex target exe to flag as risky.
123+
type: regex
124+
default: ^\\(cmd|powershell|cscript|wscript|rundll32|regsvr32|mshta|wmic|netsh)\.exe$
125+
93126

94127
export: |
95128
LET Profile = '''
@@ -681,7 +714,7 @@ export: |
681714
"EnvironmentVariable": 0xA0000001,
682715
"Console": 0xA0000002,
683716
"TrackerData": 0xA0000003,
684-
"ConsoleFE": 0xA0000004,
717+
"CodePage": 0xA0000004,
685718
"SpecialFolder": 0xA0000005,
686719
"Darwin": 0xA0000006,
687720
"IconEnvironment": 0xA0000007,
@@ -861,13 +894,17 @@ export: |
861894
["__DataBlockSize",0,"uint32"],
862895
["__MachineID", 16, "String"],
863896
["MachineID", 0, "Value",{ "value": "x=>if(condition= x.__MachineID=~'[^ -~]+', then=Null, else=x.__MachineID )" }],
864-
["MacAddress", 0, "Value",{ "value": "x=>if(condition=x.MachineID,then=split(string=x.Droid[1],sep='-')[-1])" }],
897+
["MacAddress", 0, "Value",{ "value": "x=>if(condition=x.MachineID,then=strip(suffix=':',string=regex_replace(source=split(string=x.FileDroid,sep='-')[-1],re='.{2}',replace='$0:')))" }],
898+
["__CreationTimeHex", 0, "Value",{ "value": "x=>if(condition=x.MachineID,then='0x' + x.FileDroid[15:18] + x.FileDroid[9:13] + x.FileDroid[0:8] )" }],
899+
["CreationTime", 0, "Value",{ "value": "x=>timestamp(epoch=int(int=( int(int=x.__CreationTimeHex) - 0x01B21DD213814000) / 10000))" }],
865900
["__Droid0", 32, "GUID"],
866901
["__Droid1", 48, "GUID"],
867-
["Droid", 0, "Value",{"value": "x=>if(condition=x.MachineID,then=(x.__Droid0.Value,x.__Droid1.Value))" }],
868902
["__DroidBirth0", 64, "GUID"],
869903
["__DroidBirth1", 80, "GUID"],
870-
["DroidBirth", 0, "Value",{ "value": "x=>if(condition=x.MachineID,then=(x.__DroidBirth0.Value, x.__DroidBirth0.Value))" }],
904+
["VolumeDroid", 0, "Value",{"value": "x=>if(condition=x.MachineID,then=x.__Droid0.Value)" }],
905+
["VolumeDroidBirth", 0, "Value",{ "value": "x=>if(condition=x.MachineID,then=x.__DroidBirth0.Value)" }],
906+
["FileDroid", 0, "Value",{"value": "x=>if(condition=x.MachineID,then=x.__Droid1.Value)" }],
907+
["FileDroidBirth", 0, "Value",{ "value": "x=>if(condition=x.MachineID,then=x.__DroidBirth1.Value)" }],
871908
]],
872909
#0xA0000004
873910
["ConsoleFEDataBlock", 0x0000000C, [
@@ -1316,12 +1353,24 @@ export: |
13161353
)
13171354
13181355
LET ShowExtraData(Parsed) = to_dict(item={
1319-
SELECT BlockClass as _key,
1320-
if(condition= Data.DataValue,
1321-
then= Data.DataValue, else= Data) as _value
1356+
SELECT if(condition= BlockClass=~'^0x',
1357+
then= 'Overlay',
1358+
else= BlockClass ) as _key,
1359+
if(condition= Data.DataValue,
1360+
then= Data.DataValue, else=
1361+
if(condition= NOT BlockClass =~ '^0x',
1362+
then= Data,
1363+
else= dict(
1364+
Header=format(format='0x%x',args=read_file(filename=OSPath, offset=Offset,length=4)),
1365+
Offset=Offset,
1366+
Length=len(list=read_file(filename=OSPath, offset=Offset)),
1367+
Entropy=entropy(string=read_file(filename=OSPath,offset=Offset)),
1368+
Magic=magic(accessor='data',path=read_file(filename=OSPath,offset=Offset))
1369+
))) as _value
13221370
FROM foreach(row=Parsed.ExtraData)
13231371
})
1324-
1372+
1373+
13251374
sources:
13261375
- query: |
13271376
LET hostname <= if(condition=CheckHostnameMismatch,
@@ -1337,87 +1386,145 @@ sources:
13371386
profile=Profile, struct="ShellLinkHeader") AS Parsed
13381387
FROM targets
13391388
1340-
LET parsed = SELECT
1341-
dict(OSPath=OSPath, Size=Size,
1389+
LET parsed = SELECT
1390+
dict(OSPath=OSPath, Size=Size,
13421391
Mtime=Mtime,Btime=Btime) as SourceFile,
1343-
ShowHeader(Parsed=Parsed) as ShellLinkHeader,
1344-
Parsed.LinkInfo as LinkInfo,
1345-
ShowLinkTarget(Parsed=Parsed) as LinkTarget,
1346-
Parsed.StringData as StringData,
1347-
ShowExtraData(Parsed=Parsed) as ExtraData,
1348-
property_store(data=Parsed) as PropertyStore
1349-
FROM lnk_files
1392+
ShowHeader(Parsed=Parsed) as ShellLinkHeader,
1393+
Parsed.LinkInfo as LinkInfo,
1394+
ShowLinkTarget(Parsed=Parsed) as LinkTarget,
1395+
Parsed.StringData as StringData,
1396+
ShowExtraData(Parsed=Parsed) as ExtraData,
1397+
property_store(data=Parsed) as PropertyStore,
1398+
Parsed
1399+
FROM lnk_files
1400+
1401+
-- Several dynamic functions to check propertystore for anormalities
1402+
LET find_uid(propertystore) = SELECT regex_replace(source=Value,re='''S-1-5-\d{2}-\d+-\d+-\d+-''',replace='') as Value
1403+
FROM propertystore WHERE Description = 'SID'
1404+
LET find_oldpath(propertystore) = SELECT Value FROM propertystore WHERE Description = 'ParsingPath'
1405+
LET find_oldsize(propertystore) = SELECT Value FROM propertystore WHERE Description = 'System.Size'
13501406
1351-
LET results = SELECT SourceFile,
1407+
LET results = SELECT SourceFile,
13521408
ShellLinkHeader,
13531409
LinkInfo,
13541410
LinkTarget,
13551411
StringData,
13561412
if(condition=PropertyStore,
13571413
then= ExtraData + dict(PropertyStore=PropertyStore),
1358-
else= ExtraData ) as ExtraData
1359-
FROM parsed
1360-
WHERE if(condition= IocRegex,
1361-
then= format(format='%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\s%s',
1362-
args=[
1363-
StringData.TargetPath,
1364-
StringData.Name,
1365-
StringData.RelativePath,
1366-
StringData.WorkingDir,
1367-
StringData.Arguments,
1368-
StringData.IconLocation,
1369-
LinkTarget.LinkTarget,
1370-
ExtraData.TrackerData.MachineID,
1371-
ExtraData.TrackerData.MacAddress,
1372-
join(array=PropertyStore.Value,sep='\n')
1373-
]) =~ IocRegex,
1374-
else= True)
1375-
AND NOT if(condition= IgnoreRegex,
1376-
then= format(format='%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\s%s',
1377-
args=[
1378-
StringData.TargetPath,
1379-
StringData.Name,
1380-
StringData.RelativePath,
1381-
StringData.WorkingDir,
1382-
StringData.Arguments,
1383-
StringData.IconLocation,
1384-
LinkTarget.LinkTarget,
1385-
ExtraData.TrackerData.MachineID,
1386-
ExtraData.TrackerData.MacAddress,
1387-
join(array=PropertyStore.Value,sep='\n')
1388-
]) =~ IgnoreRegex,
1414+
else= ExtraData ) as ExtraData,
1415+
find_uid(propertystore=PropertyStore)[0].Value as UID,
1416+
find_oldpath(propertystore=PropertyStore)[0].Value as OldPath,
1417+
find_oldsize(propertystore=PropertyStore)[0].Value as OldSize
1418+
FROM parsed
1419+
WHERE if(condition= IocRegex,
1420+
then= format(format='%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\s%s',
1421+
args=[
1422+
StringData.TargetPath,
1423+
StringData.Name,
1424+
StringData.RelativePath,
1425+
StringData.WorkingDir,
1426+
StringData.Arguments,
1427+
StringData.IconLocation,
1428+
LinkTarget.LinkTarget,
1429+
ExtraData.TrackerData.MachineID,
1430+
ExtraData.TrackerData.MacAddress,
1431+
join(array=PropertyStore.Value,sep='\n')
1432+
]) =~ IocRegex,
1433+
else= True)
1434+
AND NOT if(condition= IgnoreRegex,
1435+
then= format(format='%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\s%s',
1436+
args=[
1437+
StringData.TargetPath,
1438+
StringData.Name,
1439+
StringData.RelativePath,
1440+
StringData.WorkingDir,
1441+
StringData.Arguments,
1442+
StringData.IconLocation,
1443+
LinkTarget.LinkTarget,
1444+
ExtraData.TrackerData.MachineID,
1445+
ExtraData.TrackerData.MacAddress,
1446+
join(array=PropertyStore.Value,sep='\n')
1447+
]) =~ IgnoreRegex,
13891448
else= False)
13901449
1450+
LET sus_cli(data) = dict(
1451+
`Arguments have ticks` = data=~'''\^|\`''',
1452+
`Arguments have environment variables` = data=~'''\%|\$env:''',
1453+
`Arguments have rare characters` = data=~'''\?\!\~\@''',
1454+
`Arguments have leading space` = data=~ '^ ',
1455+
`Arguments have http strings` = data=~'''(http|ftp)s?://''',
1456+
`Arguments have UNC strings` = data=~'''\\\\''',
1457+
`Suspicious arguments` = data=~SusArgRegex
1458+
)
1459+
1460+
-- find largest base64 blob over 10 characters
1461+
LET find_b64(data) = SELECT *
1462+
FROM if(condition=data,
1463+
then={
1464+
SELECT Base64, len(list=Base64) as Length
1465+
FROM parse_records_with_regex(accessor='data',file=data, regex='''(?P<Base64>[A-Za-z0-9+/]{10,}={0,2})''')
1466+
ORDER BY Length DESC LIMIT 1
1467+
},
1468+
else=null )
1469+
1470+
13911471
LET add_suspicious = SELECT *, dict(
13921472
`Large Size` = SourceFile.Size > SusSize,
13931473
`Startup Path` = SourceFile.OSPath =~ '''\\Startup\\''',
1394-
`Environment variable script` = ExtraData.EnvironmentVariable =~ '''\.(bat|cmd|ps1|js|vbs|vbe|py)$''',
1395-
`No Target with environmant variable` = ExtraData.EnvironmentVariable AND StringData.Arguments AND NOT (StringData.TargetPath OR StringData.RelativePath),
1474+
`Zeroed Headers` = ( ShellLinkHeader.FileSize=0 or ShellLinkHeader.CreationTime=0),
1475+
`Hidden window` = ShellLinkHeader.ShowCommand = 'SHOWMINNOACTIVE',
1476+
`Target Changed path` = lowcase(string=LinkInfo.Target.Path) != lowcase(string=OldPath) AND OldPath,
1477+
`Target Changed size` = ( ShellLinkHeader.FileSize - OldSize != 0 ) AND ShellLinkHeader.FileSize AND OldSize,
1478+
`Risky target` = StringData.TargetPath =~ RiskyExe || LinkInfo.Target.Path =~ RiskyExe || LinkTarget.LinkTarget =~ RiskyExe,
1479+
`WebDAV` = LinkInfo.Target.RelativeLink.NetworkProviderType = 'WNNC_NET_DAV',
1480+
`Line break in StringData.Name` = StringData.Name =~ '''\n''',
13961481
`Suspicious argument size` = len(list=StringData.Arguments) > SusArgSize,
1397-
`Arguments have ticks` = StringData.Arguments=~'''\^''',
1398-
`Arguments have environment variables` = StringData.Arguments=~'''\%|\$env:''',
1399-
`Arguments have rare characters` = StringData.Arguments=~'''\?\!\~\@''',
1400-
`Arguments have leading space` = StringData.Arguments =~ '^ ',
1401-
`Arguments have http strings` = StringData.Arguments =~'''https?://''',
1402-
`Suspicious arguments` = StringData.Arguments =~ SusArgRegex,
1482+
`Environment variable script` = ExtraData.EnvironmentVariable =~ '''\.(bat|cmd|ps1|js|vbs|vbe|py)$''',
1483+
`No Target with environment variable` = ExtraData.EnvironmentVariable AND StringData.Arguments AND NOT (StringData.TargetPath OR StringData.RelativePath),
14031484
`Suspicious hostname` = ExtraData.TrackerData.MachineID AND SusHostnameRegex AND ExtraData.TrackerData.MachineID=~SusHostnameRegex AND NOT lowcase(string=ExtraData.TrackerData.MachineID)=~lowcase(string=hostname[0].Hostname),
1404-
`Hostname mismatch` = CheckHostnameMismatch AND ExtraData.TrackerData.MachineID AND NOT lowcase(string=ExtraData.TrackerData.MachineID)=~lowcase(string=hostname[0].Hostname)
1405-
) as Suspicious
1485+
`Hostname mismatch` = CheckHostnameMismatch AND ExtraData.TrackerData.MachineID AND NOT lowcase(string=ExtraData.TrackerData.MachineID)=~lowcase(string=hostname[0].Hostname),
1486+
`Created in VM` = ExtraData.TrackerData.MacAddress =~ VmPrefixMAC,
1487+
`Local Admin` = UID='500',
1488+
`Cyrillic Language` = format(format='%s%s',args=[LinkTarget,ExtraData])=~ '''[\x{0400}-\x{04FF}]''',
1489+
`Chinese Language` = format(format='%s%s',args=[LinkTarget,ExtraData])=~ '''[\x{4E00}-\x{9FCC}]''',
1490+
`Korean Language` = format(format='%s%s',args=[LinkTarget,ExtraData])=~ '''[\x{3131}-\x{314e}|\x{314f}-\x{3163}|\x{ac00}-\x{d7a3}]''',
1491+
`Persian Language` = format(format='%s%s',args=[LinkTarget,ExtraData])=~ '''[\x{0600}-\x{06FF}]''',
1492+
`Vietnamese Language` = format(format='%s%s',args=[LinkTarget,ExtraData])=~ '''[\x{0102}\x{0103}\x{0110}\x{0111}\x{01A0}\x{01A1}\x{01AF}\x{01B0}\x{1EA0}-\x{1EF9}]''',
1493+
`CodePage` = ExtraData.CodePage,
1494+
`Has Overlay` = if(condition=ExtraData.Overlay, then=True)
1495+
) as Suspicious,
1496+
regex_replace(source=base64decode(string=find_b64(data=StringData.Arguments)[0].Base64),re='''[^ -~\s]''',replace='') as ArgumentsDecoded,
1497+
sus_cli(data=StringData.Arguments) as SuspiciousCli
14061498
FROM results
14071499
WHERE if(condition=SuspiciousOnly,
1408-
then= join(array=Suspicious) =~ ':true',
1500+
then= join(array=Suspicious) =~ ''':(true|0x|\d)''' OR join(array=SuspiciousCli) =~ ''':(true|0x|\d)''' OR len(list=ArgumentsDecoded) > 20,
14091501
else= True )
14101502
1503+
LET add_suspiciousb64 = SELECT *,
1504+
if(condition= len(list=ArgumentsDecoded) > 20, then = dict(`Long Base64`=True) + sus_cli(data=ArgumentsDecoded)) as SuspiciousCliB64
1505+
FROM add_suspicious
1506+
14111507
LET upload_results = SELECT *,
14121508
upload(file=SourceFile.OSPath) as UploadedLnk
1413-
FROM add_suspicious
1509+
FROM add_suspiciousb64
14141510
14151511
-- finally return rows and remove suspicious attributes that are not true
1416-
SELECT *,
1417-
to_dict(item={SELECT * FROM items(item=Suspicious) WHERE _value = True}) as Suspicious
1512+
SELECT
1513+
SourceFile,
1514+
ShellLinkHeader,
1515+
LinkInfo,
1516+
LinkTarget,
1517+
if(condition= SuspiciousCliB64,
1518+
then= to_dict(item=StringData) + dict(`DecodedBase64`=ArgumentsDecoded),
1519+
else = StringData) as StringData,
1520+
ExtraData,
1521+
to_dict(item={SELECT * FROM items(item=Suspicious) WHERE _value }) +
1522+
to_dict(item={SELECT * FROM items(item=SuspiciousCli) WHERE _value }) +
1523+
to_dict(item={SELECT * FROM items(item=SuspiciousCliB64) WHERE _value })
1524+
as Suspicious
14181525
FROM if(condition=UploadLnk,
14191526
then= upload_results,
1420-
else= add_suspicious )
1527+
else= add_suspiciousb64 )
14211528
14221529
column_types:
14231530
- name: SourceFile.Mtime
@@ -1429,4 +1536,4 @@ column_types:
14291536
- name: ShellLinkHeader.AccessTime
14301537
type: timestamp
14311538
- name: ShellLinkHeader.WriteTime
1432-
type: timestamp
1539+
type: timestamp

0 commit comments

Comments
 (0)