Intro
The Local Security Authority (Lsa) in Windows is designed to manage a Systems sec policy, auditing, logging users on to the system and storing private data such as Service Account Passwords, Cached Password hashes, FTP and Web-User Passwords, Remote Access Service (RAS) dial-up account Names and Passwords and Computer Account passwords for domain Access.
The LSA Secrets are stored under the HKLM:\Security\Policy\Secrets key. This key contains additional sub-keys that store encrypted Secrets. The HKLM:\Security\Policy\Secrets key is not accessible from regedit or other tools by default, but you can access it by running as SYSTEM.
Each Secret contains five values:
· CurrVal – Current Encrypted Value
· CupdTime – Last Update Time
· OldVal – Old Value
· OupdTime Old Update Time
SecDesc – Security Descriptor
P/Invoke
Windows PowerShell V 2.0 includes a Cmdlet, Add-Type, which is used to add a Microsoft .NET Framework type (a class) to a Windows PowerShell session. It’s also possible to call native Windows APIs in Windows PowerShell.
If you want to learn how to call the Native Windows APIs, check out PInvoke.net. Pinvok.net is a wiki that allows developers to share P/Invoke signatures, user-defined types, and any other info related to calling Win32 and other unmanaged APIs from managed code (C# VB.NET and PowerShell, Yaay).
As for this particular little example we’ll check out advapi32 and the LsaRetrievePrivateData function. The function is described here.
PowerShell
As mentioned earlier, you can’t access the HKLM:\Security\Policy\Secrets key as a User, however, you can access it as SYSTEM. A simple way of running PowerShell as NT AUTHORITY\SYSTEM is by using psexec.exe.
PS > .\PsExec.exe -i -s powershell.exe
PS > whoami
nt authority\system
Step two is to use the sample code from P/Invoke in PowerShell. Thanks to the Add-Type CmdLet, we can simply place the C# sample code in a variable and pass it to the CmdLet using the MemberDefinition parameter.
$signature = @"
[StructLayout(LayoutKind.Sequential)]
public struct LSA_UNICODE_STRING
{
public UInt16 Length;
public UInt16 MaximumLength;
public IntPtr Buffer;
}
[StructLayout(LayoutKind.Sequential)]
public struct LSA_OBJECT_ATTRIBUTES
{
public int Length;
public IntPtr RootDirectory;
public LSA_UNICODE_STRING ObjectName;
public uint Attributes;
public IntPtr SecurityDescriptor;
public IntPtr SecurityQualityOfService;
}
public enum LSA_AccessPolicy : long
{
POLICY_VIEW_LOCAL_INFORMATION = 0x00000001L,
POLICY_VIEW_AUDIT_INFORMATION = 0x00000002L,
POLICY_GET_PRIVATE_INFORMATION = 0x00000004L,
POLICY_TRUST_ADMIN = 0x00000008L,
POLICY_CREATE_ACCOUNT = 0x00000010L,
POLICY_CREATE_SECRET = 0x00000020L,
POLICY_CREATE_PRIVILEGE = 0x00000040L,
POLICY_SET_DEFAULT_QUOTA_LIMITS = 0x00000080L,
POLICY_SET_AUDIT_REQUIREMENTS = 0x00000100L,
POLICY_AUDIT_LOG_ADMIN = 0x00000200L,
POLICY_SERVER_ADMIN = 0x00000400L,
POLICY_LOOKUP_NAMES = 0x00000800L,
POLICY_NOTIFICATION = 0x00001000L
}
[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
public static extern uint LsaRetrievePrivateData(
IntPtr PolicyHandle,
ref LSA_UNICODE_STRING KeyName,
out IntPtr PrivateData
);
[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
public static extern uint LsaStorePrivateData(
IntPtr policyHandle,
ref LSA_UNICODE_STRING KeyName,
ref LSA_UNICODE_STRING PrivateData
);
[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
public static extern uint LsaOpenPolicy(
ref LSA_UNICODE_STRING SystemName,
ref LSA_OBJECT_ATTRIBUTES ObjectAttributes,
uint DesiredAccess,
out IntPtr PolicyHandle
);
[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
public static extern uint LsaNtStatusToWinError(
uint status
);
[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
public static extern uint LsaClose(
IntPtr policyHandle
);
[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
public static extern uint LsaFreeMemory(
IntPtr buffer
);
"@
Add-Type -MemberDefinition $signature -Name LSAUtil -Namespace LSAUtil
In the example above we store the Sample Code from P/Invoke in a variable and then use the Add-Type CmdLet to Add it to our PowerShell Session.
Now for the tricky part. You can access the Lsa Secrets for Service Accounts using the NT AUTHORITY\SYSTEM account but you can’t decrypt them. To decrypt the Values you have to own them. How to solve this? The simplest way is to use the reg.exe command.
In this example i’ll use the SC_OSearch14 key (SharePoint 2010 Timer) and create a temporary key where i’ll copy each of the represented values described above.
"CurrVal","OldVal","OupdTime","CupdTime","SecDesc" | ForEach-Object {
$copyFrom = "HKLM\SECURITY\Policy\Secrets\_SC_OSearch14\" + $_
$copyTo = "HKLM\SECURITY\Policy\Secrets\MySecret\" + $_
$regCopy = reg COPY $copyFrom $copyTo /s /f
}
Next, I’ll create three objects holding the objectAtrtibutes, localSystem and secretName.
$objectAttributes = New-Object LSAUtil.LSAUtil+LSA_OBJECT_ATTRIBUTES
$objectAttributes.Length = 0
$objectAttributes.RootDirectory = [IntPtr]::Zero
$objectAttributes.Attributes = 0
$objectAttributes.SecurityDescriptor = [IntPtr]::Zero
$objectAttributes.SecurityQualityOfService = [IntPtr]::Zero
# localSystem
$localsystem = New-Object LSAUtil.LSAUtil+LSA_UNICODE_STRING
$localsystem.Buffer = [IntPtr]::Zero
$localsystem.Length = 0
$localsystem.MaximumLength = 0
# Secret Name
$secretName = New-Object LSAUtil.LSAUtil+LSA_UNICODE_STRING
$secretName.Buffer = [System.Runtime.InteropServices.Marshal]::StringToHGlobalUni("MySecret")
$secretName.Length = [Uint16]("MySecret".Length * [System.Text.UnicodeEncoding]::CharSize)
$secretName.MaximumLength =
[Uint16](("MySecret".Length + 1) * [System.Text.UnicodeEncoding]::CharSize)
With the objects at hand i can go ahead and retrieve the Lsa Policy Handle.
$lsaPolicyHandle = [IntPtr]::Zero
[LSAUtil.LSAUtil+LSA_AccessPolicy]$access =
[LSAUtil.LSAUtil+LSA_AccessPolicy]::POLICY_GET_PRIVATE_INFORMATION
$lsaOpenPolicyHandle =
[LSAUtil.LSAUtil]::LSAOpenPolicy(
[ref]$localSystem,
[ref]$objectAttributes,
$access,
[ref]$lsaPolicyHandle
)
$lsaNtStatusToWinError = [LSAUtil.LSAUtil]::LsaNtStatusToWinError($ntsResult)
If the LsaOpenPolicy function works out, it returns ’0′, otherwise you’ll have a nice error. A good tip is to check the output.
if($lsaOpenPolicyHandle -ne 0) {
Write-Warning "lsaOpenPolicyHandle Windows Error Code: $lsaOpenPolicyHandle"
}
Next, we retrieve the Private Data using the LsaRetrievePrivateData function and close the LsaPolicyHandle.
$privateData = [IntPtr]::Zero
$ntsResult =
[LSAUtil.LSAUtil]::LsaRetrievePrivateData(
$lsaPolicyHandle,
[ref]$secretName,
[ref]$privateData
)
$lsaClose = [LSAUtil.LSAUtil]::LsaClose($lsaPolicyHandle)
Again, it’s a good idea to check the exit code from the LsaRetrievePrivateData function.
if($lsaNtStatusToWinError -ne 0) {
Write-Warning "lsaNtsStatusToWinError: $lsaNtStatusToWinError"
}
Next step is to convert the output to a managed object and then convert it to a string.
[LSAUtil.LSAUtil+LSA_UNICODE_STRING]$secretData =
[LSAUtil.LSAUtil+LSA_UNICODE_STRING][System.Runtime.InteropServices.marshal]::PtrToStructure(
$privateData,
[LSAUtil.LSAUtil+LSA_UNICODE_STRING]
)
[string]$value = [System.Runtime.InteropServices.marshal]::PtrToStringAuto($secretData.Buffer)
$value = $value.SubString(0, ($secretData.Length / 2))
$freeMemory = [LSAUtil.LSAUtil]::LsaFreeMemory($privateData)
At this point, you should have a Password in clear text. To find the account associated with the ‘_SC_OSearch14′ Service Account you can simply use WMI as demonstrated below.
$serviceName = "_SC_OSearch14" -Replace "^_SC_"
$service = Get-WmiObject -Query "SELECT StartName FROM Win32_Service WHERE Name = '$serviceName'"
$account = $service.StartName
Last step is to return the Account and Password as demonstrated below.
New-Object PSObject -Property @{
Account = $account;
SETEC_ASTRONOMY = $value
}
Account SETEC_ASTRONOMY
------- ---------------
POWERSHELL\spAdmin Password1
Visit http://research.truesec.com/ to download the code demonstrated above.
/Niklas
Enable LENOVO TPM Security Chip (and other stuff) from a TS
I have some customers who run strictly Lenovo Computers (laptops and Desktops). On a lot of these computers the security Chip has been disabled or is in Inactive mode, thus not allowing the use of Bitlocker. I just finished messing around with activating the TPM Chip in the BIOS From a Task sequence on those LENOVO computers, and once all the minor obstacles were figured out, it turned out to be quiet easy.
The first thing I wanted to do was to check if the TPM chis was already Active, and if not, Activate it. This is actually real simple on a LENOVO laptop, as this can all be done using WMI. LENOVO has been kind enough to supply the scripts needed to do this, along with some .pdf guides ( Get the scripts HERE).
Once you have the scripts, the once you need are ListAll.vbs and SetConfig.vbs. The ListAll script, will list the status of all WMI configurable settings in the BIOS. Just open an elevated command prompt and run the ListAll.vbs, and remember use cscript.exe when you execute the script, or you will get like 50 message boxes…
cscript.exe ListAll.vbs
In the picture below (from a LENOVO W520), you can see that the Security chip is Inactive, and needs to be activated for Bitlocker to work. (you will also notice that the computer is running in AHCI mode, and that Virtualization is disabled along with a lot of other stuff.. these settings can be changed as well if necessary)

To change the settings we utilize the script SetConfig.vbs. Just place it in your scripts package (or create one), and then in your Task Sequence add a Run Command line STEP Like shown in the Picture

Command line: Cscript.exe SetConfig.vbs SecurityChip Active
It’s important that the settings you want to change are written exactly as they appear in the ListAll output, as they are case sensitive, or the script will fail. This means that Active is correct but active is NOT.
That’s basically it. The TPM chip will now be activated during the next reboot, which off cause means that you must add a reboot before enabling Bitlocker.
If you want it real neat and pretty, you could make an option on the step to not run if the Chip is already active. It’s not necessary to do so, but it will at least give you some info on whether or not the chip was enabled in the SMSTS.log.

Make an If NONE option, and in that if statement, add a Query WMI (as shown a bow), In that Query change the Namespace from root\cimv2 to root\wmi and in the WQL Query write:
Select * FROM Lenovo_BiosSetting WHERE CurrentSetting = 'SecurityChip,Active'
Now the script will only run if the status of the Security Chip is different from Active.
One last thing I ran into was on Lenovo T510 and T410, where this didn’t work. Turned out it was because of an error in the BIOS… Once I flashed the bios with the newest version, it worked like a charm… Should you need a guide on how to do an SCCM unattended BIOS update I have written a guide on my blog http://blog.coretech.dk/mip/
/Michael
Bonus post from our PKI expert Hasain Alshakarti
It is not about having spare time and it is not about just testing but it is all about what is supposed to work in the Windows platform but it does not or it is not clear enough to understand and use. Let me give you one example: It all started with a question about how to manually perform a scripted certificate request using enrollment agents at the Microsoft TechNet forums for Windows Server Security http://social.technet.microsoft.com/Forums/en-US/winserversecurity. My previous experience tells that the certreq.exe can be used in such situation and that was my given answer but it all ended in a non-working scenario with one of a kind error message that said "Certificate Request Processor: An attempt was made to perform an initialization operation when initialization has already been completed. 0x800704df (WIN32: 1247)". This type of errors triggers my curiosity and gets me going until I manage to have a working solution or gets knocked out temporarily until I find my way and sometimes with a custom written tool to share with the community :) Read more about this specific topic and other topics on my blog at http://secadmins.