A LNK Between Browsers: Hunting Methodologies and Extension Abusing Actors
Jared Wilson
May 01, 2023
20 min read
Threat Research
TTPs
Two pillars in sleight of hand magic are User Initiated Action, where the target needs to believe their actions are their own, and Hidden Action, the trick needs to be concealed behind something ordinary and nonthreatening. Mandiant became aware of a chain of adversary methodologies that leverage these two pillars to achieve persistence.
The user executes an LNK shortcut file that, unbeknownst to them, has been tampered with.
The modified LNK shortcut file executes a legitimate browser, hiding the malicious extension.
If the technical sleight of hand is successful, the adversary will achieve persistence by means of malicious Chromium-based browser extensions.
While hunting this methodology Mandiant identified BRAINSTORM, a rust-based dropper, which ultimately led to RILIDE, a chromium-based extension first publicly reported by SpiderLabs. Careful investigation identified that the email and cryptocurrency theft ecosystem of RILIDE is larger than reported. This research will dissect the relevant adversary methodologies, discuss the identified malware families abusing this methodology, and include numerous detection opportunities to expand the defender’s hunting and detection repertoire.
The Connection from LNK to Extension
The LNK File
Files with the extension .lnk are colloquially known as LNK files but are officially known as Shell Link Binary Files and follow a standardized format. LNK files contain information that points a user’s interaction to another data object on the system. In many instances this is transparent to an end user. A Windows user may click on the Google Chrome icon in the Start Menu and Chrome opens. What is not shown to the user is that they are executing an LNK file with properties that point to the actual Chrome executable.
Mandiant has reported on many adversaries and malware families abusing LNK files including: FIN7, UNC1151, KEGTAP, FIN13, and APT29 (twice).
The CRX File
A CRX file is a collection of files archived together into a single package that can be used as an extension in Chromium-based browsers. Extensions enhance the browsing experience by adding features and functionality to the browser. Many browsers have an extension store where a user can review and install them into their browser all through the browser itself; this is generally accepted as a safer practice because the company owning the browser software performs analysis on the extensions themselves attempting to identify malicious extensions.
However, depending on the implemented security settings, browsers will allow for manual loading of CRX files or unpacked extensions. Packed extensions (CRX files) are a single file with a .crx extension, conversely an unpacked extension is a directory containing the extension files.
Throughout 2022 Mandiant has observed multiple financially motivated threat actors distributing and/or expressing interest in leveraging malicious browser extensions in their operations.
Abusing Both LNK and CRX
While Mandiant has previously reported on the abuse of LNK and CRX files separately, this recently observed adversary methodology has been using both filetypes within a chain of events and the bridging data-point is the --load-extension switch in Chromium-based browsers.
The --load-extension switch allows the source to specify a target directory to load as an extension. This gives malware the opportunity to start a new browser window with their malicious extension loaded.
This functionality is present on Chromium-based browsers and multiple example commands can be found in Figure 1.
Figure 1: Example Commands to Load an Extension on Multiple Chromium-based Browsers
Mandiant investigated several compromises involving LNK and extension abuse methodology in 2023. The impacted organizations extended across a broad scope of sectors, including the semiconductor, business marketing, financial investment, and telecom industries.
The following three sections dive deep into separate investigations performed on malware families utilizing both LNK abuse and extension installing to achieve persistence with RILIDE.
Investigation 1
TradingView Desktop is a charting platform and a social network for traders and investors. This software allows users to track and view cryptocurrency market changes. As a software that is used in the finance industry with the capability of a cryptocurrency focus, it is a reasonable target to masquerade as for actors with goals of stealing cryptocurrency. Users willing to track cryptocurrency may be more likely to trade, allowing RILIDE a potential vector for cryptocurrency theft.
The file TradeVlewDesktop_v4-94406.zip is a TradingView Desktop masquerading set of files. The sample is a compressed directory that contains 457 different files. The file of interest in the zipped file is TradeVlewDesktop_x64.exe, a NodeJS-based downloader. After execution, TradeVlewDesktop_x64.exe reaches out to the Telegram masquerading URL hxxp://telegromcn[.]org/soft/analytics/extension[.]exe to download dropper extension.exe, which Mandiant tracks as BRAINFOG.
BRAINFOG is a Node.JS packaged binary dropper which drops RILIDE along with Visual Basic scripts to delete all Chrome LNKs and replace them with LNK files to force the execution of RILIDE. RILIDE is a Chromium-based extension that monitors the URLs visited by victims, screenshots their browser tab views, and injects remote JavaScript into select websites. RILIDE targets the theft of email and cryptocurrency details, falling inline with the targeted audience, the finance sector.
chrome.vbsto delete all LNK files with “Chrome” in it
wtf.vbsto create a new LNK using the --load-extension switch to force the loading of RILIDE browser extension at execution.
After the user loads Chrome via the replaced LNK shortcut file, RILIDE runs in the background as the infected browser loads and manipulates web pages. During initial execution, it fetches a machine identifier and a list of targeted domains from the command and control's (C2) API endpoint /api/machine/init; this list is re-fetched every five minutes.
Figure 2: The domains this variant of RILIDE is monitoring for from /api/machine/init
When any HTML document has been completely parsed the DOMContentLoaded event will be delivered to the target function, loadScript. The loadScript function will download a list of key-values pairs which include a name and a path. The name is the domain related to the traffic of interest. If the browsing domain matches one of the monitored domains, the JavaScript in the path value will be accessed and the resulting file injected into the website for execution.
Figure 3: Domains and URIs listed at the /api/machine/get-urls endpoint of the Adversary C2
Returns a list of details about victims. This includes…
Victim IP address
Victim country
Variant reference (Google, TradingView, etc)
When it was added
When it was last observed communicating to the C2
/api/machine/init
Returns a machine ID and the list of domains it monitors for
/api/machine/get-urls
Returns the URI path to the JS script to inject
Investigation 2
Previously highlighted in the SpiderLabs blog, the GitHub user gulantin was identified as having numerous GitHub repositories storing RILIDE samples. Furthermore, Mandiant suspects this may have been a method for delivery.
A file named Blanks, tracked by Mandiant as BRAINLINK, was downloaded from the raw.githubusercontent.com URL on the gulantin github (hxxp://raw.githubusercontent[.]com/gulantin/blanks/main/blanks_online.exe).
BRAINLINK is an Advanced Installer compiled dropper which drops a CAB file that contains the RILIDE extension files along with PowerShell scripts to create new shortcuts forcing the execution of RILIDE.
Mandiant’s research of RILIDE identified that the background JavaScript file includes a domain variable set to the C2 domain for each malware version. In this investigation the RILIDE sample used the domain ashgrrwt[.]click.
Figure 5: RILIDE C2 domain variable defanged
const domain = "https://ashgrrwt.click"
RILIDE C2 Infrastructure Hunt
This C2 infrastructure provided interesting overlaps between numerous other domains. While the Admin, Billing, and Technical WHOIS details for the ashgrrwt[.]click domain were redacted for privacy, the registrant organization was not. The registrant organization, Kruglova LTD, was associated with 11 other websites.
Figure 6: RILIDE C2 domain infrastructure graph
The domains identified follow the overarching theme to these campaigns: Cryptocurrency Exchange Platforms (FinAndy/TradingView) and Finance/Banking.
Investigation 3
Researching the RILIDE ecosystem led to the identification of an open directory at 146.70.79[.]75 which included two BRAINSTORM samples (0a4f321c903a7fbc59566918c12aca09 and 34eea751fcbf4ee8d44977adb4742d93) and numerous other malicious samples. BRAINSTORM is a Rust-based dropper which drops RILIDE and updates Google Chrome, Brave, and Microsoft Edge LNK files to force the execution of RILIDE. Mandiant is tracking the activity related to this open directory as UNC4553.
Figure 7: UNC4553 Open Directory
The open directory IP shown in Figure 7 (146.70.79[.]75) has previously resolved to nch-software[.]info and panger-top[.]click. Further solidifying the connection from the IP to the domains, there is evidence connecting the URI patterns in the open directory to the URI patterns for these domains.
The open directory contained the two BRAINSTORM samples, two PUFFPASTRY samples, a suspected incomplete PUFFPASTRY sample, and two XLL samples.
PUFFPASTRY is a backdoor written in Visual-basic. PUFFPASTRY can download, upload, delete, and execute files. Additionally, PUFFPASTRY can self-terminate and enumerate system information including Anti-virus details. C2 communications occur over standard HTTP/HTTPS.
An XLL add-in is an Excel add-in file with the file extension .xll. An XLL file is a type of dynamic link library (DLL) file that can only be opened by Excel. It is not exactly clear what the intention of some of these files are given they appear to be in staging or templates.
Preventing the malicious extensions (RILIDE and others) to be loaded by the browser is the linchpin to stopping the actor’s methodology. With the inability to install the extension, further damage or exfiltration of sensitive data is prevented.
ExtensionInstallSources – Use strings with wildcards to identify where extensions can be installed from.
If there is a concern that Chrome LNKs have been manipulated or as a reoccurring security measure, users can run a user-level Chrome Settings Reset. When executed, numerous Chrome profile settings will change to the default on all devices where you're signed in. This will reset all of the default Windows-provided Chrome LNKs (i.e. Quick Launch LNKs) removing the --load-extension parameter that the actor is using to force the loading of the malicious extension.
Following a defense in depth strategy, implementing the detections documented in the Detection Opportunities section will help cover any prevention gaps.
Detection Opportunities
The Detection Opportunities section will be broken into two detection directions and will encompass a list of rules that will be expanded on in Appendix A.
Detecting Methodologies – Larger chance of detecting legitimate files or tactics that overlap with the methodology.
Detecting Malware Families – More targeted approach to detect the specific families themselves.
Detecting Methodologies
Detection Engine
Detection Title
Detection Description
YARA
M_Hunting_Embedded_Chromium_CRX_1
Detect non-CRX files with extension equities
YARA
M_Hunting_Embedded_Chromium_CRXandLNK_1
Detect non-CRX files with extension and LNK equities
YARA
M_Hunting_AdvancedInstaller_LNK_1
Detect Advanced Installer files that drop LNKs to known locations
YARA
M_Hunting_LNKEngine_LoadExtension_1
Detect LNKs that are loading an extension in a chromium browser
YARA
M_Hunting_LNKEngine_LoadExtension_Temp_1
Detect LNKs that are loading an extension from the AppData directory
YARA
M_Hunting_ArchiveEngine_CAB_Extension_1
Detect CAB files that include potential extension files
YARA-L
M_Hunting_FileWrite_Manifest_Temp_1
Detect events where a manifest.json file is being written to the Temp directory
YARA-L
M_Hunting_Process_Chromium_LoadExtension_1
Detect events where Chrome is starting with the --load-extension parameter with the value in the appdata temp directory
YARA-L
M_Hunting_FileWrite_Chrome_LNK_1
Detect events where an LNK is written to known used locations from a process that is in user directory space
YARA-L
M_Hunting_FileWrite_CRXandLNK_1
Detect events where a process writes a zip or CRX and an LNK within 2 minutes of each other
YARA-L
M_Hunting_FileWrite_ManifestandLNK_1
Detect events where a process writes a manifest and LNK file within 2 minutes of each other
VT Grep
M_Hunting_FileWrite_ManifestandChromeLNK_1
Detect samples that write both a Chrome LNK and a manifest file at execution
Detecting Malware Families
Detection Engine
Detection Title
Detection Description
YARA
M_Hunting_RILIDE_InjectJS_1
Detects the JavaScript files that RILIDE injects
YARA
M_Hunting_RILIDE_InjectJS_2
Detects the JavaScript files that RILIDE injects
YARA
M_Hunting_RILIDE_InjectJS_3
Detects the JavaScript files that RILIDE injects
YARA
M_Win_BRAINLINK_AdvancedInstaller_1
Detects BRAINLINK
YARA
M_Utility_RILIDE_Manifest_1
Detects RILIDE Manifest files
YARA
M_Utility_RILIDE_JS_1
Detects RILIDE JavaScript files
YARA
M_Utility_RILIDE_Background_1
Detects the RILIDE background JavaScript files
YARA
M_Hunting_RILIDE_CRX_1
Detects RILIDE CRX files
YARA
M_Win_BRAINSTORM_1
Detects BRAINSTORM
YARA
M_Win_BRAINFOG_1
Detects BRAINFOG
YARA
M_Hunting_BRAINFOG_1
Detects suspected BRAINFOG samples
Conclusion
While hunting for this chain of adversary methodologies, Mandiant uncovered numerous new malware families and investigations. The adversary’s effort to remain undetected by chaining methodologies has come with mixed results, as many of the samples discussed in this post have very low detection ratings. However, with this new research, detecting these methodologies should be more accessible and further expand the defender’s hunting and detection repertoire.
Acknowledgments
This content would not have been possible without the assistance of Connor McLaughlin, Pepe Torrijos, Matthew Dunwoody, Anders Vejlby, and Nick Simonian.
Appendix A: Detections
rule M_Hunting_Embedded_Chromium_CRX_1
{
meta:
author = "Mandiant"
md5 = "f1c21a69ed9f85e12d58ef0f5ac5c9b1"
description = "Hunting for non-CRX files with extension equities"
strings:
$a1 = "_metadata" ascii
$a2 = "manifest.json" ascii
$a3 = "verified_contents.json" ascii
$s1 = "_locales" ascii
$s2 = "messages.json" ascii
$f = /[a-z0-9A-Z_-]+\.(html|htm|css|js)/ ascii
$pk = {50 4B 03 04}
condition:
(((uint16(0) == 0x5A4D) and uint32(uint32(0x3C)) == 0x00004550) or (uint32(0) == 0x464c457f) or (uint32(0) == 0xfeedface or uint32(0) == 0xcefaedfe or uint32(0) == 0xfeedfacf or uint32(0) == 0xcffaedfe or uint32(0) == 0xcafebabe or uint32(0) == 0xbebafeca or uint32(0) == 0xcafebabf or uint32(0) == 0xbfbafeca)) and
(((2 of ($a*)) and $f) or ((1 of ($a*)) and ($f or (1 of ($s*))))) and
$pk and
(#pk >1) and
(for any i in (1..#pk) : ($a2 at @pk[i]+30)) and
(for any j in (1..#pk) : ($f at @pk[j]+30))
}
rule M_Hunting_Embedded_Chromium_CRXandLNK_1
{
meta:
author = "jared.wilson"
md5 = "f1c21a69ed9f85e12d58ef0f5ac5c9b1"
description = "Hunting for non-CRX files with extension equities and common strings for Google Chrome LNKs"
strings:
$a1 = "_metadata" ascii
$a2 = "manifest.json" ascii
$a3 = "verified_contents.json" ascii
$s1 = "_locales" ascii
$s2 = "messages.json" ascii
$f = /[a-z0-9A-Z_-]+\.(html|htm|css|js)/ ascii
$pk = {50 4B 03 04}
$load = "--load-extension" ascii wide
$lnk1 = "Google Chrome.lnk" ascii wide
condition:
(((uint16(0) == 0x5A4D) and uint32(uint32(0x3C)) == 0x00004550) or (uint32(0) == 0x464c457f) or (uint32(0) == 0xfeedface or uint32(0) == 0xcefaedfe or uint32(0) == 0xfeedfacf or uint32(0) == 0xcffaedfe or uint32(0) == 0xcafebabe or uint32(0) == 0xbebafeca or uint32(0) == 0xcafebabf or uint32(0) == 0xbfbafeca)) and
(((2 of ($a*)) and $f) or ((1 of ($a*)) and ($f or (1 of ($s*))))) and
$pk and
(#pk >1) and
(for any i in (1..#pk) : ($a2 at @pk[i]+30)) and
(for any j in (1..#pk) : ($f at @pk[j]+30)) and
($load or $lnk1)
}
rule M_Hunting_AdvancedInstaller_LNK_1
{
meta:
author = "Mandiant"
md5 = "2782af385665c765807ed887d4bacf36"
description = "Hunting for Advanced Installer files that drop LNKs to known locations."
(uint32(0) == 0x0000004c) and filesize < 50KB and all of them
}
rule M_Hunting_ArchiveEngine_CAB_Extension_1
{
meta:
author = "Mandiant"
description = "Looking for CAB containing what is suspected to be the files that make up an extension"
md5 = "de283dfb9c88dbb6d455ca4b31c57240"
strings:
$f1 = "manifest.json" nocase
$f2 = ".js" nocase
$f3 = ".htm" nocase
$f4 = ".png" nocase
condition:
filesize < 1MB and uint32be(0) == 0x4D534346 and ($f1 in (uint32(16) .. uint32(16) + 256)) and ($f2 in (uint32(16) .. uint32(16) + 256)) and ($f3 in (uint32(16) .. uint32(16) + 256)) and ($f4 in (uint32(16) .. uint32(16) + 256))
}
rule M_Hunting_FileWrite_Manifest_Temp_1
{
meta:
author = "Mandiant"
md5 = "f1c21a69ed9f85e12d58ef0f5ac5c9b1"
description = "Hunting for cases where a process writes a Chrome CRX manifest file to the appdata temp directory."
severity = "Medium"
events:
$e.metadata.event_type = "FILE_CREATION"
($e.target.file.names = "manifest.json" OR $e.target.file.full_path = /manifest\.json$/)
(((($e1.principal.file.file_type = "FILE_TYPE_CRX") or ($e1.target.file.full_path = /\.zip$/)) and (($e2.principal.file.file_type = "FILE_TYPE_LNK") or ($e2.target.file.full_path = /\.lnk$/))) or (((($e1.principal.file.file_type = "FILE_TYPE_LNK") or ($e1.target.file.full_path = /\.lnk$/)) and (($e2.principal.file.file_type = "FILE_TYPE_CRX") or ($e2.target.file.full_path = /\.zip$/)))))
match:
$md5 over 1m
condition:
$e1 and $e2
}
rule M_Hunting_FileWrite_ManifestandLNK_1 {
meta:
author = "Mandiant"
md5 = "f1c21a69ed9f85e12d58ef0f5ac5c9b1"
description = "Hunting for cases where a process writes a CRX Manifest file and an LNK within 1 minute of each other."
((($e1.target.file.full_path = /\\manifest\.json$/) and (($e2.principal.file.file_type = "FILE_TYPE_LNK") or ($e2.target.file.full_path = /\.lnk$/))) or (((($e1.principal.file.file_type = "FILE_TYPE_LNK") or ($e1.target.file.full_path = /\.lnk$/)) and ($e2.target.file.full_path = /\\manifest\.json$/))))
match:
$md5 over 1m
condition:
$e1 and $e2
}
//M_Hunting_FileWrite_ManifestandChromeLNK_1
tag:peexe and ((behaviour_files:"C:\\Users\\Public\\Desktop\\Google Chrome.lnk" and behaviour_files:"C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Google Chrome.lnk") or (behaviour_files:"C:\\Users\\Public\\Desktop\\Google Chrome.lnk" and behaviour_files:"C:\\Users\\user\\AppData\\Roaming\\Microsoft\\Internet Explorer\\Quick Launch\\Google Chrome.lnk") or (behaviour_files:"C:\\Users\\Public\\Desktop\\Google Chrome.lnk" and behaviour_files:"C:\\Users\\user\\AppData\\Roaming\\Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar\\Google Chrome.lnk") or (behaviour_files:"C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Google Chrome.lnk" and behaviour_files:"C:\\Users\\user\\AppData\\Roaming\\Microsoft\\Internet Explorer\\Quick Launch\\Google Chrome.lnk") or (behaviour_files:"C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Google Chrome.lnk" and behaviour_files:"C:\\Users\\user\\AppData\\Roaming\\Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar\\Google Chrome.lnk") or (behaviour_files:"C:\\Users\\user\\AppData\\Roaming\\Microsoft\\Internet Explorer\\Quick Launch\\Google Chrome.lnk" and behaviour_files:"C:\\Users\\user\\AppData\\Roaming\\Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar\\Google Chrome.lnk")) and behaviour_files:".zip" and behaviour_files:"manifest.json"
rule M_Hunting_RILIDE_InjectJS_1
{
meta:
author = "Mandiant"
md5 = "9fe5b99b20bc91995b81eddd917bff50"
description = "Hunting for the code that RILIDE injects"
$a2 = "Authorize New Device You recently attempted to sign in to your Bybit account from a new device or location. As a security measure, we require additional"
$a3 = "Please check your withdrawal address carefully."
$a4 = "Verification Code Of Withdrawal"
$a6 = "Withdrawal Verification Code"
$a7 = "Verification Code Of Authorization"
$a8 = "initiate this withdrawal or the address is"
$a9 = "Authorize New Device You recently attempted to sign in to your OKX account from a new device or location. As a security measure, we require additional"
$a10 = "Confirm your new withdrawal address"
$a11 = "A new withdrawal address was just added to your account."