Monday, March 5, 2012

Windows Web Security: Why you DON'T run IIS AppPools with user accounts that have elevated rights to a domain.

Title:  Windows Web Security:  Why you DON'T run IIS AppPools with user accounts that have elevated rights to a domain.

Description:  Windows AppPool account passwords are exposable as clear-text to a local Administrator of an IIS server.

Vulnerability:  If your IIS machine is compromised and an AppPool running on that machine is running with a domain account that has elevated user rights, the attacker simply needs to run a PowerShell command to see the username and password of an account with domain credentials.  This also applies in an administratively segregrated environment where you have an Administrator of an IIS server who should not have domain-level rights.

Requirements:  at least IIS 7/Windows 2008 R2 if not before.  I haven't tested this on previous versions but should still be testable via the root\MicrosoftIISv2 namespace.

Proof of Concept:


You can see the AppPool Name as well as the configured domain username and password.  This was run using a local Administrator account on an IIS server.

Wednesday, February 15, 2012

Windows Service Security Descriptors: MOF Changes

Back to Windows Service Security Descriptors Project


// Author:  Cameron Wilson
// Date:  9/27/2011
// Description:  A custom class created to inventory Windows Service Vulnerabilities

[ SMS_Report(TRUE), 
 SMS_Group_Name("Service Vulnerabilities"), 
 SMS_Class_ID("Custom|ServiceVulnerabilities|1.0"),
 SMS_Namespace  (False),
 Namespace      ("\\\\\\\\localhost\\\\root\\\\Custom") ]
 
class ServiceVulnerabilities : SMS_Class_Template
{
 [SMS_Report(TRUE),key] uint32 ServiceID;
 [SMS_Report(TRUE) ] string   ServiceName;
 [SMS_Report(TRUE) ] string   ServiceDisplayName;
 [SMS_Report(TRUE) ] string   AccessMask;
 [SMS_Report(TRUE) ] string   AceFlags;
 [SMS_Report(TRUE) ] string   AceType;
 [SMS_Report(TRUE) ] string   Trustee;
};

Windows Service Security Descriptors

Project:  Windows Service Security Descriptors

Overview:  A project aimed at tracking and mitigating the vulnerabilities in ACLs on Windows services in your network.

Requirements:
  1. Windows Clients - Windows Vista+
  2. Microsoft SMS/SCCM 2003+
Why Should I Care?:
  1. Windows services run with the NT Authority\System account, unless otherwise specified.
  2. The application of Windows service ACLs is left up to the vendor who installs the service.
  3. Many Windows services (especially driver services) are poorly written so that the service allows Everyone, Full Control access.
  4. The exploitation of vulnerable service ACLs is very common part of a hacker's escalation path in your network.
In a nutshell, any normal user account that is on a system with this vulernable ACL on a service can very easily elevate their credentials to that of a local Administrator.  This is a key part of the escalation path because once a hacker has local Administrator access, they can run/install any program they want on a system to start stealing domain-level user credentials.

Scope:

This project is nothing more than addressing one facet of a single part of a hacker's entire escalation path.  Some might argue that this is more effort than its worth and depending on your network's security requirements, and for some, it absolutely is.  However, if your goal is a high security environment or you simply want to prevent a hacker from penetrating your network by any and all means, this information would be of interest to you.

How it Works:
  1. Host
    • A script runs on each host that queries the Windows services for ACL information.  The data is filtered, formatted, and then written to a custom WMI namespace and class.  The SMS/SCCM client accounts for the new class and sends the data up to the SMS/SCCM server with the frequency of its own hardware inventory.
  2. SMS/SCCM Server
    • The SMS_DEF.MOF file must be updated to tell the SCCM clients to start accounting for the new WMI class in the new namespace. 
Now that the information is being aggregated by the SMS/SCCM client natively, you can leverage SMS/SCCM collections, reports, advertisements, etc. to:
  1. Provide reports on vulnerable services in your network.
  2. Create collections of "like" services with the same vulnerability.
  3. Use advertisements to fix vulnerable services throughout your network in a targetted way. 
Installation Aspects:
  1. Security_Descriptor_Service_WMI.vbs
    • This script creates a custom WMI namespace and class and then populates that namespace and class with the data from the vulnerable services on the host.  This script should run at system startup or through an SCCM package.  I'd highly recommend the SCCM package over a startup script because of the integrated ability to enforce that the script runs and it runs with any kind of frequency.  A startup script requires the machine be rebooted before it will run.
  2. SMS_DEF.MOF Modifications
    • Append the mof file definitions to this file so the SMS/SCCM clients will start aggregating this information up to the SMS/SCCM database.  If you have multiple site servers, these changes must be done to each SMS_DEF.MOF file on each server.
Example Report:

Windows Service Security Descriptors: Client Script

Back to Windows Service Security Descriptors Project


' Author:  Cameron L. Wilson (thepip3r)
' Date:   9/25/2011
' Description:  Track vulnerable services on Windows systems
' References:
' - Win32_ACE:    http://msdn.microsoft.com/en-us/library/aa394063.aspx
' - Win32_SecurityDescriptor: http://msdn.microsoft.com/en-us/library/aa394402.aspx
' - Win32_Service:   http://msdn.microsoft.com/en-us/library/aa394418(v=VS.85).aspx




' ****************************************************************************************************************************************
' Begin:  Create Custom WMI Namespace/Class
' ****************************************************************************************************************************************

Const wbemCimtypeSint16 = 2
Const wbemCimtypeSint32 = 3
Const wbemCimtypeReal32 = 4
Const wbemCimtypeReal64 = 5
Const wbemCimtypeString = 8
Const wbemCimtypeBoolean = 11
Const wbemCimtypeObject = 13
Const wbemCimtypeSint8 = 16
Const wbemCimtypeUint8 = 17
Const wbemCimtypeUint16 = 18
Const wbemCimtypeUint32 = 19
Const wbemCimtypeSint64 = 20
Const wbemCimtypeUint64 = 21
Const wbemCimtypeDateTime = 101
Const wbemCimtypeReference = 102
Const wbemCimtypeChar16 = 103


Set objLocator = CreateObject("WbemScripting.sWbemLocator") 
Set objSvc = objLocator.ConnectServer(".", "root") 
Set objNamespace = objSvc.Get("__namespace") 
Set objCustomNameSpace = objNamespace.SpawnInstance_ 

objCustomNamespace.name = "custom" ' Create the "Custom" namespace
objCustomNamespace.Put_() 

Set objWMI = GetObject("winmgmts:root\custom") 
Set objClass = objWMI.Get()

strClass = "ServiceVulnerabilities"

' Look for existing custom classes on this host and wipe them out if they exist
On Error Resume Next
Set objServiceVulnerabilities = objWMI.Get(strClass)

If Err.Number = 0 Then
 objServiceVulnerabilities.Delete_
End If
On Error Goto 0

' Create the custom class with all of its properties
objClass.Path_.Class = strClass 
objClass.Properties_.add "ServiceID", wbemCimtypeUint32 
objClass.Properties_("ServiceID").Qualifiers_.add "key", true 
objClass.Properties_.add "ServiceName", wbemCimtypeString 
objClass.Properties_.add "ServiceDisplayName", wbemCimtypeString 
objClass.Properties_.add "Trustee", wbemCimtypeString 
objClass.Properties_.add "AccessMask", wbemCimtypeUint32 
objClass.Properties_.add "AceType", wbemCimtypeUint32
objClass.Properties_.add "AceFlags", wbemCimtypeUint32

objClass.Put_() 

' Apply a pre-defined security string to the new namespace/class
strSD = "1,0,4,128,148,0,0,0,164,0,0,0,0,0,0,0,20,0,0,0,2,0,128,0,4,0,0,0,0,18,24,0,63,0,6,0,1,2,0,0,0,0,0,5,32,0,0,0,32,2,0,0,0,18,20,0,19,0,0,0,1,1,0,0,0,0,0,5,20,0,0,0,0,18,20,0,19,0,0,0,1,1,0,0,0,0,0,5,19,0,0,0,0,18,20,0,19,0,0,0,1,1,0,0,0,0,0,5,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,0,0,0,0,5,32,0,0,0,32,2,0,0,1,2,0,0,0,0,0,5,32,0,0,0,32,2,0,0" 
arrSD = Split(strSD,",") 

Set objSS = objWMI.Get("__SystemSecurity=@") 
objSS.SetSD(arrSD)

' ****************************************************************************************************************************************
' End:  Create Custom WMI Namespace/Class
' ****************************************************************************************************************************************




' ****************************************************************************************************************************************
' Begin:  Query Service Security Descriptors for User-Defined Criteria
' ****************************************************************************************************************************************

Const SE_DACL_PRESENT = &h4  
Const ACCESS_ALLOWED_ACE_TYPE = &h0  
Const ACCESS_DENIED_ACE_TYPE  = &h1   
 
strExclusions = "BUILTIN\Administrators, NT SERVICE\TrustedInstaller, NT AUTHORITY\SYSTEM, NT SERVICE\IPBusEnum, BUILTIN\Domain Administrators"
arrExclusions = Split(strExclusions, ",")
 
strComputer = "."  
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate, (Security)}!\\" & strComputer & "\root\cimv2")   
Set colServices =  objWMIService.ExecQuery("Select * from Win32_Service")   

Set objData = objWMI.Get(strClass) 
Set objInstance = objData.SpawnInstance_ 

y = 0
For Each objService in colServices    
 
 WScript.Echo objService.Name

 iReturn = objService.GetSecurityDescriptor( objSD )   
 
 If ( iReturn <> 0 ) Then   
  WScript.Echo "Could not get security descriptor: " & iReturn
 End If  
 
 ' Extract the security descriptor flags   
 intControlFlags = objSD.ControlFlags  
 
 If intControlFlags AND SE_DACL_PRESENT Then 
  WScript.Echo "   Control Flags:" & vbTab & intControlFlags
  ' Get the ACE entries from security descriptor    
  colACEs = objSD.DACL  

  For Each objACE in colACEs  
   strTrustee = objACE.Trustee.Domain & "\" & objACE.Trustee.Name

   If Trim(strTrustee) = "\" Then
    strTrustee = objACE.Trustee.SIDString
   End If
   
   ' Make sure the ACEType is an "allow", make sure the accessmask gives the group some kind of "write" permissions, and the trustee group isn't predefined as "good"
   If objACE.AceType = ACCESS_ALLOWED_ACE_TYPE And objACE.AccessMask >= 262144 And Not InArray(arrExclusions, strTrustee) Then 
    ' Get all the trustees and determine which have access to the service
    strTrustees = strTrustees & strTrustee & vbCrLf
    strAccessMasks = strAccessMasks & objACE.AccessMask & vbCrLf
    strAceFlags = strAceFlags & objACE.AceFlags & vbCrLf
    strAceTypes = strAceTypes & objACE.AceType & vbCrLf
    
    WScript.Echo "   Trustee:" & vbTab & strTrustee
    WScript.Echo "   Trustee SID:" & vbTab & objACE.Trustee.SIDString
    WScript.Echo "   Trustee SIDLength:" & vbTab & objACE.Trustee.SIDLength
    WScript.Echo "   Trustee SID:"
    For Each strSIDpart in objACE.Trustee.SID
     strSID = strSID & strSIDPart
    Next
    WScript.Echo "     " & vbTab & strSID
    WScript.Echo "   Access Mask:" & vbTab & objACE.AccessMask
    WScript.Echo "   Ace Flags:" & vbTab & objACE.AceFlags
    WScript.Echo "   Ace Types:" & vbTab & objACE.AceType
    
   ElseIf objACE.AceType = ACCESS_DENIED_ACE_TYPE Then     
    ' Don't care about Denies
   End If  
  Next
  
  If strTrustees <> "" Then
   WScript.Echo "   Class Index:" & vbTab & y
   
   arrTrustees = Split(strTrustees, vbCrLf)
   arrAccessMasks = Split(strAccessMasks, vbCrLf)
   arrAceFlags = Split(strAceFlags, vbCrLf)
   arrAceTypes = Split(strAceTypes, vbCrLf)
   
   ' Loop through the found ACE entries and write those to our custom class
   For x=0 to (Ubound(arrTrustees)-1)
    objInstance.ServiceID = y
    objInstance.ServiceName = objService.Name
    objInstance.ServiceDisplayName = objService.DisplayName
    objInstance.Trustee = arrTrustees(x)
    objInstance.AccessMask = arrAccessMasks(x)
    objInstance.AceType = arrAceTypes(x) 
    objInstance.AceFlags = arrAceFlags(x)
    
    objInstance.Put_() 
    y = y + 1
   Next
   
   strTrustees = ""
   strAccessMasks = ""
   strAceFlags = ""
   strAceTypes = ""
  End If

  
 Else   
  ' No DACL found; we don't care about the security descriptor.
  WScript.Echo "   Control Flags(F):" & vbTab & intControlFlags
 End If  
Next

' ****************************************************************************************************************************************
' End:  Query Service Security Descriptors for User-Defined Criteria
' ****************************************************************************************************************************************



' Checks for the existence of a string in an array
Function InArray(arrName, strValue)
 For Each strItem in arrName
  If Trim(LCase(strItem)) = Trim(LCase(CStr(strValue))) Then
   InArray = TRUE
   Exit Function
  End If
 Next
 InArray = FALSE 
End Function

   

Tuesday, February 7, 2012

PowerShell: Create-ComplexPassword v2

Description: Generates complex passwords based off of the criteria given. Able to create passwords of any length and any complexity given the following standard options:

  • length
  • lowercase
  • uppercase
  • numbers
  • special characters

Acknowledgements: The script should run efficiently no matter how complex of a password is requested.  The most visible limitation is that the script could take a second or two to complete an abnormally long password ( > 2000 chars) because of the way the script generates and shuffles the characters.

Logic Overview:

  • Determine supplied options
  • Build out all the requirements specified sequentially in a string.
  • Loop ($PasswordLength * 5) times to shuffle each character in the string to guarantee the password is randomized.  As an example, if the password requested is 20 characters long, the password's characters are shuffled 100 (20*5) times to produce a "randomizing" effect.

Example Syntax:

Create-ComplexPassword -PasswordLength 20 -LowerAlphas 2 -UpperAlphas 2 -Numbers 2 -SpecialCharacters 2
Create-ComplexPassword -PasswordLength 30 -SpecialCharacters 20
Create-ComplexPassword -PasswordLength 25 -LowerAlphas 2 -UpperAlphas 2

Function Syntax:

Function Create-ComplexPassword {
#  http://msdn.microsoft.com/en-us/library/60ecse8t(VS.80).aspx
Param (
        [parameter()] [ValidateNotNullOrEmpty()] [int] [ValidateScript({$_ -gt 0})] $PasswordLength = 8,
        [parameter()] [ValidateNotNullOrEmpty()] [int] [ValidateScript({$_ -ge 0})] $LowerAlphas = 0,
        [parameter()] [ValidateNotNullOrEmpty()] [int] [ValidateScript({$_ -ge 0})] $UpperAlphas = 0,
        [parameter()] [ValidateNotNullOrEmpty()] [int] [ValidateScript({$_ -ge 0})] $Numbers = 0,
        [parameter()] [ValidateNotNullOrEmpty()] [int] [ValidateScript({$_ -ge 0})] $SpecialCharacters = 0
    )
  
    If (($LowerAlphas + $UpperAlphas + $Numbers + $SpecialCharacters) -gt $PasswordLength) { 
        Throw "The specified sum of the number of upper, lower, numeric, and special characters cannot be greater than the desired length of the password."
    }
    
    # Creates random object with seed to guarantee uniqueness
 $objRnd = New-Object System.Random((Get-Random))
    
    $strPassword = $Null
    $arrSpecials = @(33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,58,59,60,61,62,63,64,91,92,93,94,95,96,123,124,125,126)
    
    # Add the specified number of lowercase values to the string
    For ($i=0; $i -lt $LowerAlphas; $i++) {
        $strPassword += [char]$objRnd.next(97,122)
    }

 # Add the specified number of uppercase values to the string
    For ($i=0; $i -lt $UpperAlphas; $i++) {
        $strPassword += [char]$objRnd.next(65,90)
    }
    
    # Add the specified number of number values to the string
    For ($i=0; $i -lt $Numbers; $i++) {
        $strPassword += [char]$objRnd.next(48,57)
    }
    
    # Add the specified number of special character values to the string
    For ($i=0; $i -lt $SpecialCharacters; $i++) {
        $strPassword += [char]$arrSpecials[$objRnd.next(0,$arrSpecials.length)]
    }
    
    # Add the remaining number of random characters for those not specified to fill the rest of the length
    For ($i = $strPassword.length; $i -lt $PasswordLength; $i++) {
  $strPassword += [char]$objRnd.next(33,126)
    }
 
 For ($i = 0; $i -lt ($PasswordLength * 5); $i++) {
  $pos = $objRnd.Next(0, ($strPassword.length - 1))
  
  $lpart = $strPassword.Substring(0,$pos)
  $hpart = $strPassword.Substring($pos + 1, ($strPassword.Length - ($pos + 1)))

  $strPassword = "$lpart$hpart" + $strPassword[$pos]
 }

 $strPassword
}

Monday, September 12, 2011

VBScript Function: Split a string, character by character

Custom function to give VBS the ability to split a string, character by character.



Function SplitByCharacter(strString)
 Dim arrTemp()
 iLen = Len(strString)-1
 redim arrTemp(iLen)
 For iCounter = 0 to iLen
  arrTemp(iCounter) = Mid(strString, iCounter + 1,1)
 Next

 SplitByCharacter = arrTemp
End Function

Wednesday, July 13, 2011

SCCM/SMS Client Uninstall Script

Forgive the sloppy code but I'm just posting this in response to a post on TechNet in regards to someone trying to mass uninstall the SCCM client.

This script was hastily/sloppily written to address a major problem we had migrating from SMS 2003 to SCCM 2007 where the clients didn't like being reassinged to a new site. The fix action ended up being a myraid of things:

  1. Hung 'ccmexec' service due to the process not terminating
  2. The uninstall didn't fully complete
  3. Duplicate GUID problems
  4. ...etc

You may have to change some parts around:

On Error Resume Next

Set objShell = CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")

strOutputFile = "CCMOutputFile.txt"
strComputerFile = "computers.txt"
strSMSCfgFile = "c$\Windows\smscfg.ini"
strSMSDelCertString = "c:\temp\ccmdelcert.exe"
strSMSUninstallString = "c:\windows\system32\ccmsetup\ccmsetup.exe /uninstall"
strSMSCleanString = "c:\temp\ccmclean.exe /all /q"
strSCCMServer = "SCCMSERVERNAME"
strSMSServiceName = "ccmexec"

iComputersSuccess = 0
iComputersOffline = 0
iComputersTotal = 0



arrComputers = Split(getTextFile(strComputerFile), vbCrLf)

If IsArray(arrComputers) Then
 For Each strComputer In arrComputers
  strComputer = Trim(strComputer)
  If strComputer <> "" Then 
   WScript.Echo "Pinging " & strComputer & " to see if it is online."
   If Ping(strComputer) Then
    WScript.Echo "Binding to " & strComputer & " via WMI."
    Set objWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
    If Err.Number <> 0 Then
     displayError("Binding error to WMI on:  " & strComputer)
     Err.Clear
    End If
    
    If Not objWMI Is Nothing Then
    
     WScript.Echo "Copying pertinent files to c:\temp"
     If Not objFSO.FileExists("\\" & strComputer & "\c$\" & strSMSUninstallString) Then
      objFSO.CopyFile "\\" & strSCCMServer & "\SCCM Client Tools\*", "\\" & strComputer & "\c$\temp\", true
     End If
     
     killProcess strComputer, "ccmexec.exe"

     Set objWMIProcess = GetObject("winmgmts:\\" & strComputer & "\root\cimv2:Win32_Process") 
     
     WScript.Echo "Running delcert process on remote host."
     If objFSO.FileExists("\\" & strComputer & "\c$\temp\ccmdelcert.exe") Then
      spawnProcess strComputer, strSMSDelCertString
     Else
      WScript.Echo "Unable to locate delcert executable for execution."
     End If
     
     WScript.Echo "Running ccmsetup /uninstall process on remote host."
     If objFSO.FileExists("\\" & strComputer & "\c$\windows\system32\ccmsetup\ccmsetup.exe") Then
      spawnProcess strComputer, strSMSUninstallString
     Else
      WScript.Echo "Unable to locate ccmsetup executable for execution."
     End If
     
     WScript.Echo "Running ccmclean process on remote host."
     If objFSO.FileExists("\\" & strComputer & "\c$\temp\ccmclean.exe") Then
      spawnProcess strComputer, strSMSCleanString
     Else
      WScript.Echo "Unable to locate ccmclean executable for execution."
     End If
           
     iComputersSuccess = iComputersSuccess + 1
    Else    
     WScript.Echo "Error binding to WMI using the syntax:  winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2"
    End If
   Else
    iComputersOffline = iComputersOffline + 1
   End If
   iComputersTotal = iComputersTotal + 1
  End If
  If iComputersTotal Mod 5 = 0 Then
   WScript.Echo ""
   WScript.Echo "STATUS UPDATE:"
   WScript.Echo vbTab & "Hosts Successfully Uninstalled:  " & iComputersSuccess
   WScript.Echo vbTab & "Hosts Offline:  " & iComputersOffline
   WScript.Echo vbTab & "Total Hosts Evaluated:  " & iComputersTotal
   WScript.Echo ""
  Else
   WScript.Echo ""
  End If
 Next
End If



' ***********************************************************************************

Sub displayError(strMessage)
    'Display custom message and information from VBScript Err object.

    strError = VbCrLf & "ERROR:  " & strMessage & VbCrLf & _
      "Number (dec) : " & Err.Number & VbCrLf & _
      "Number (hex) : &H" & Hex(Err.Number) & VbCrLf & _
      "Description  : " & Err.Description & VbCrLf & _
      "Source       : " & Err.Source
    Err.Clear
    WScript.Echo strError

End Sub

Function Ping(strComputer)
 Dim objPing, strPing

 Set objPing = objShell.Exec("ping -n 1 -w 2000 " & strComputer & "")
 
 strPing = objPing.StdOut.ReadAll()
 
 If Instr(strPing, "Reply") <> 0 Then
  Ping = True
 Else
  Ping = False
 End If
End Function


Function getTextFile(strFilePath)
 If Not objFSO.FileExists(strFilePath) Then
  WScript.Echo "Could not locate text file:  " & strFilePath
  WScript.Quit
 End If
 
 Set objTextFile = objFSO.OpenTextFile(strFilePath, 1)
 strTemp = objTextFile.ReadAll
 objTextFile.Close
 
 getTextFile = strTemp
End Function

Sub killProcess(strComputer, strProcess)
 Set objSWbemLocator = CreateObject("WbemScripting.SWbemLocator") 
 objSWbemLocator.Security_.privileges.addasstring "sedebugprivilege", true
 Set objWMIService = objSWbemLocator.ConnectServer(strComputer, "root\CIMV2")
 Set colProcesses = objWMIService.ExecQuery("Select * From Win32_Process Where Name = '" & strProcess & "'")
 
 If colProcesses.Count = 0 Then
  log("Process not found to be running:  " & strProcess & vbCrLf)
 ElseIf colProcesses.Count > 0 Then
  For Each objProcess in colProcesses
   intReturn = objProcess.Terminate()
   log("Attempting to kill process (" & strProcess & ") returned error code:  " & intReturn & vbCrLf)
  Next
 Else
  log(".Count method returned an unexpected value while attempting to kill:  " & strProcess & vbCrLf)
 End If
End Sub

Sub spawnProcess(strComputer, strExec)
 intReturn = objWMIProcess.Create(strExec)
 Select Case intReturn
  Case 0 WScript.Echo "Process creation was a success"
  Case 2 WScript.Echo "Process creation returned Access Denied"
  Case 3 WScript.Echo "There were insufficient priviledges to complete the process"
  Case 8 WScript.Echo "Unknown failure"
  Case 9 WScript.Echo "Path not found"
  Case 21 WScript.Echo "Invalid Parameter"
  Case Else WScript.Echo "Process creation returned an unrecognized parameter"
 End Select
End Sub