Vollgar: 6 Scripts to Help Review Your SQL Servers

Vollgar: 6 Scripts to Help Review Your SQL Servers

Last week, Guardicore released information about a newly discovered attack that uses SQL Servers to compromise servers and networks. Here’s a link; I’d really encourage you to read it. The attack known as Vollgar uses a simple brute force attack to gain access to SQL Servers exposed to the internet. It then uses the elevated permissions of the compromised logins (sysadmin or serveradmin) to modify the capabilities of SQL Server and extended its access.

Guardicore has released a PowerShell script that examines servers to determine if they’ve been infected. We’ve safely run the scripts on SQL Servers in our lab environment and for many of our clients.

If you routinely apply regular updates to your servers, practice the Principle of Least Privilege, regularly change critical passwords, have stringent password complexity requirements, and don’t expose your SQL Servers directly to the internet, the likelihood of a brute force attack succeeding is greatly reduced.

Here are six scripts that can help determine your level of potential exposure.

Who Has sysadmin or serveradmin Privileges?

The Vollgar attack is a brute force attack that attempts to guess the password for SQL Logins with elevated privileges. To be successful it needs logins that can execute sp_configure to change server-level settings. This are implicitly held by the sysadmin and serveradmin fixed server roles.

So, the first step in determining your exposure to Vollgar is to discover the members of the sysadmin and serveradmin roles. The following script will show you the members of each role.

USE master; 
GO

EXEC sp_helpsrvrolemember
	'sysadmin';

EXEC sp_helpsrvrolemember
	'serveradmin';

In my sample database, the following is returned.

Another approach to retrieving the same information in one consolidated result set is to use the following script.

--list of logins that are members of the sysadmin or serveradmin roles
SELECT SP1.[name] AS 'Login',
	SP2.[name] AS 'ServerRole'
FROM sys.server_principals AS SP1
	JOIN sys.server_role_members AS SRM 
		ON SP1.principal_id = SRM.member_principal_id
	JOIN sys.server_principals AS SP2 
		ON SRM.role_principal_id = SP2.principal_id
WHERE SP2.[name] IN ('sysadmin', 'serveradmin')
ORDER BY SP2.[name],
	 SP1.[name];

As expected, this script produces the same results.

Of course, it’s best practice to only grant the minimum rights required by each login, a practice known as least privilege. If these queries return more logins than absolutely necessary, it’s time to review your security practices.

Who has Passwords that Do Not Expire and without Password Complexity Requirements?

Having a complex password and changing it regularly is part of the basic blocking and tackling of security. Passwords like “Password123”, “Qwerty”, and “Puddles!” can be cracked in very short order using tools freely available on the web. And if these passwords never expire, users have no reason to change them regularly, making them even more of a liability.

For Windows Integrated Authentication, password complexity and expiration is handled at the network domain level. For SQL logins, these are enforced inside of SQL Server.

To find active SQL logins (e.g. not disabled) that do not require a basic level of complexity and are set to not expired, run the following script.

--Active SQL Logins where passwords do not expire
--and do not have complexity requirements 
SELECT name, 
	type_desc, 
	create_date, 
	modify_date, 
	default_database_name
FROM sys.sql_logins
WHERE is_expiration_checked = 0
	 AND is_disabled = 0 
	 AND is_policy_checked = 0 ; 

In my sample system, the script produces the following list.

Regularly changing passwords creates a moving target for potential attackers. If you have SQL logins that do not expire and do not have minimum complexity requirements, consider turning these features on for all your logins.

Putting some of the above queries together will give us a list of all active SQL logins that are members of the sysadmin or serveradmin fixed server roles along with whether their logins adhere to password complexity and expiration policies.

--list of SQL logins that are members of the sysadmin or serveradmin roles
SELECT SP1.[name] AS 'Login',
	SP2.[name] AS 'ServerRole',
	CASE l.is_disabled WHEN 1 THEN 'No' ELSE 'Yes' END AS Is_Enabled,
	CASE l.is_expiration_checked WHEN 1 THEN 'Yes' ELSE 'No' End AS Pwd_Expires,
	CASE l.is_policy_checked WHEN 1 THEN 'Yes' ELSE 'No' END AS Pwd_Complexity_Reqs
FROM sys.server_principals AS SP1
	JOIN sys.server_role_members AS SRM
	ON SP1.principal_id = SRM.member_principal_id
	JOIN sys.server_principals AS SP2
	ON SRM.role_principal_id = SP2.principal_id
	JOIN sys.sql_logins AS l
	ON l.principal_id = SRM.member_principal_id
WHERE SP2.[name] IN ('sysadmin', 'serveradmin')
ORDER BY SP2.[name],
	 SP1.[name];

The following results are returned on my test system.

When was a SQL Login Password Changed?

From the prior two queries, we can see that Alice and Donnie are both active members of the sysadmin fixed server role. Donnie’s password doesn’t expire and doesn’t have to meet any password complexity requirements. Of course, this is a big red flag for security. Alice’s login, on the other hand, is set to adhere to complexity and expiration requirements. That’s good.

But how long has it been since Alice actually changed her password? We can use the LOGINPROPERTY() function to help us. Note: that for the function to return meaningful information, both CHECK_POLICY and CHECK_EXPIRATION must be enabled for the login.

--when was a login's password last changed?
SELECT 'Alice' AS username,
	LOGINPROPERTY('Alice', 'PasswordLastSetTime') AS PasswordLastSetTime;

In this case, we can see that Alice last set her password on March 26, 2020.

We can use other properties in the LOGINPROPERTY() function, such as BadPasswordCount and BadPasswordTime. I wouldn’t rely too heavily on the results, though. The BadPasswordCount is reset to 0 as soon as Alice successfully logs in. And, just as importantly, it’s only relevant for those SQL Logins who have CHECK_POLICY and CHECK_EXPIRATION enabled.

--bad password attempts
SELECT name, 
	LOGINPROPERTY(name, 'BadPasswordCount') AS BadPasswordCount,
	LOGINPROPERTY(name, 'BadPasswordTime') AS BadPasswordTime
FROM sys.sql_logins 
WHERE is_expiration_checked = 1
	AND is_disabled = 0 
	AND is_policy_checked = 1; 

The results from my test system are shown below.

How to See Failed Login Attempts

Assuming your SQL Server is configured to log failed login attempts, and of course it should be, you can query the error log files using the sp_readerrorlog procedure to see the failed attempts.

EXEC sp_readerrorlog 0, 1, 'Login failed' ;

The following is returned on my test system.

Better yet, use a monitoring tool to proactively monitor failed login attempts and alert when a minimum threshold is exceeded. For our DBA as a Service clients, we provide SentryOne‘s SQLSentry monitoring tool to help with this and other events that should be monitored.

Parting Thoughts

Many years ago, I set up a test system for a writing project I was involved with. As part of the test, I set the sa password to something like “Cat123Dog!” The password met most requirements of the day – upper and lower case, at least one number and one letter, and a special symbol. “Not bad,” I thought to myself.

Then I downloaded Ophcrack, a free Windows password cracker, and released it on my unsuspecting SQL Server. Expecting the utility to run for hours, if not days, I returned to work.

A few minutes later, I decided to check on it, wanting to make sure it wasn’t hung for some reason. I was stunned. Ophcrack had already found the password! That was at least 10 years ago. I’m sure the tools of the hacker trade have gotten much better since then.

Recently, I’ve read where most breaches are a result of social engineering – someone receives an e-Card from a secret admirer, finds a thumb drive in the parking lot, or clicks an email link. “The days of brute force attacks are over,” they say.

Vollgar has proven them wrong. Basic security measures are still best practice. You owe it to yourself to make sure you’re doing it well. Here are a few links that may help.

 

3 Responses

  1. joe says:

    Updated, adding a 7th script.

  2. […] albeit illegal, business model for many. And SQL Server-specific attacks, such as MrbMiner and Vollgar, have been found in the […]

  3. […] Vollgar: 6 Scripts to Help Review Your SQL Servers […]

Leave a Reply

Your email address will not be published. Required fields are marked *