Decipher a message

As part of the PowerShell Summit, there’s a scripting competition also known as the “iron scripter” contest.

This year, one of the challenges was published on this page https://ironscripter.us/a-challenge-from-the-dark-faction/

There’s a message encoded with the Caesar Cipher on this page: http://bit.ly/DarkFactionMessage which leads to this text file



You can find more details on the Caesar Cipher on this wikipedia page.

About my solution: It’s clear that % is the space character. I needed to know if a character is upper or lower case and I did a substitution to the left in the ascii table by 5 or 37. Plus in some cases, I had to adjust the character without using a substitution (or I just simply failed to find the common denominator)

My quick and dirty solution is the following:

It looks like this:

The original text can be found on this page: https://devops-collective-inc.gitbook.io/the-monad-manifesto-annotated/what-is-monad

Advertisements

Quick tip: how to add REG_NONE value

  • Context

A colleague of mine sent me a message saying that I need to add a reg_none value.

Although the referenced article talks about REG_SZ (String Value) – How to suppress the AutoDiscover redirect warning in Outlook – I can only see REG_NONE values under HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\AutoDiscover\RedirectServers

  • Problem

I started to innocently do the following but it failed:

$v = 'autodiscover-s.outlook.com'
$p = 'HKCU:\Software\Microsoft\Office\16.0\Outlook\AutoDiscover\RedirectServers'
Set-ItemProperty -Value $null -Type None -Path $p -Name $v -Force -Verbose
Set-ItemProperty -Value '' -Type None -Path $p -Name $v -Force -Verbose

I ended with the following error messages:
Set-ItemProperty : Value cannot be null.
Set-ItemProperty : The type of the value object did not match the specified RegistryValueKind or the object could not be properly converted


I almost gave up and started to look for an acceptable answer on the web.

  • Solution

The best answer I found is on this page: https://stackoverflow.com/questions/47530823/set-reg-none-value-via-powershell
Kudos to Mathias R. Jessen. He has the solution and the relevant answer! Awesome!

It’s true, the value is a null byte array and it’s revealed by using the Get-Member cmdlet:

$v = 'autodiscover-s.outlook.com'
$p = 'HKCU:\Software\Microsoft\Office\16.0\Outlook\AutoDiscover\RedirectServers'
Get-ItemProperty -Path $p -Name $v | gm

The solution is:

$v = 'autodiscover-s.outlook.com'
$p = 'HKCU:\Software\Microsoft\Office\16.0\Outlook\AutoDiscover\RedirectServers'
Set-ItemProperty -Value ([byte[]]@()) -Type None -Path $p -Name $v -Force -Verbose

Quick tip: avoid error 0x800f0954

  • Context:

I was comparing features of Windows between branches and got this error when I run the Get-WindowsCapability cmdlet:
Get-WindowsCapability : Get-WindowsCapability failed. Error code = 0x800f0954

I also tried

DISM.exe /online /get-capabilities

If you open the dism.log file as suggested, it doesn’t help in this case:

  • Cause:

It’s not obvious unless you start reading the help of dism.exe or use the tab completion of the Get-WindowsCapability cmdlet and see that there are more parameters than those documented online, like /Source and /LimitAccess

If your computer is not allowed to contact Windows Update or uses a WSUS server that has not activated the UpdateCategories named ‘Windows 10 Feature On Demand (FOD)’ and approved the capability source files for FOD

You may notice in the WindowsUpdate.log that when you don’t use the /LitmitAcces that you’ve some lines like this:
ComApi *END * Search ClientId = TrustedInstaller FOD Enumerate, Updates found = 0, ServiceId…

And if I open the CBS.log file suggested in the dism.log file, I can see that 0x800f0954 means CBS_E_INVALID_WINDOWS_UPDATE_COUNT_WSUS

  • Solution:

Just use the /LimitAccess as suggested 😀

Get-WindowsCapability -Online -LimitAccess

Wouldn’t it be nice to have a dism command (and its equivalent PowerShell cmdlet) to know what sources it queries and/or will use?
Wouldn’t it be nice to have an official documentation that tells how to configure FOD in WSUS next to this one
(btw, WSUS is mentioned only once on a page next to above one and says the following 😦

)
If you read this article and know the answer to these 2 questions, please ping me in the comments below.

Applocker and PowerShell: how do they tightly work together?

  • Goal: Make sure to understand how Applocker and PowerShell work together.
  • Background

Let’s suppose you’re familiar with Applocker.
(that’s not a requirement to read this post 🙂 )
Anyway, please keep in mind the following that appears in the official online documentation:

Source: https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/applocker/applocker-architecture-and-components

A script is run

Before a script file is run, the script host (for example. for .ps1 files the script host is PowerShell) invokes AppLocker to verify the script. AppLocker invokes the Application Identity component in user-mode with the file name or file handle to calculate the file properties. The script file then is evaluated against the AppLocker policy to verify that it is allowed to run. In each case, the actions taken by AppLocker are written to the event log.

Source: https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/applocker/security-considerations-for-applocker
Source:https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/applocker/understand-applocker-policy-design-decisions

AppLocker can only control VBScript, JScript, .bat files, .cmd files, and Windows PowerShell scripts. It does not control all interpreted code that runs within a host process, for example, Perl scripts and macros. Interpreted code is a form of executable code that runs within a host process. For example, Windows batch files (*.bat) run within the context of the Windows Command Host (cmd.exe). To control interpreted code by using AppLocker, the host process must call AppLocker before it runs the interpreted code, and then enforce the decision returned by AppLocker. Not all host processes call into AppLocker and, therefore, AppLocker cannot control every kind of interpreted code, such as Microsoft Office macros.

Source:https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/applocker/using-event-viewer-with-applocker

Event ID: Level: Event message: Description:
8007 Error *File name* was not allowed to run. Access to *file name* is restricted by the administrator. Applied only when the **Enforce rules ** enforcement mode is set either directly or indirectly through Group Policy inheritance. The script or .msi file cannot run.
  • Some Requirements:
  • Have a computer that meets the Applocker requirement to enforce an Applocker group policy (=have an Enterprise edition of Windows)
  • Have Windows PowerShell up-to-date
  • Create the default rules for EXE,MSI,SCRIPTS,Packages and delete the rule named “(Default rule) All scripts” that has a single ‘*’ in the Path and applies for the local built-in administrators group.
  • Enforce the rules
  • Start the AppId service
  • Refresh the local group policy
  • Make sure Applocker “allow mode” (a.k.a whitelist mode) is enforced and that PowerShell runs in Constrained Language mode (even for the administrator)
  • Have the ExecutionPolicy set to “Remote Signed”
    • The (simple) case of a .bat file:

    Let’s create a .bat file that contains nothing malicious and copy it in a location where it’s allowed to run and in another location where it’s not.
    Let’s assess now how the above documentation applies!

    # LangMode? 
    $ExecutionContext.SessionState.LanguageMode
    
    # Appplocker service running?
    Get-Service -Name AppIdsvc | fl St*
    
    # Create a batch file in allowed location
    @'
    @echo off
    echo Hello world
    '@ | Out-File C:\windows\test.bat -Encoding ascii
    
    # Copy the same file in a denied location
    Copy-Item -Path C:\Windows\test.bat -Destination C:\
    
    # How Applocker would evaluate its RUN decisions
    Get-AppLockerPolicy -Effective | 
    Test-AppLockerPolicy -Path C:\test.bat -User Everyone
    Get-AppLockerPolicy -Effective | 
    Test-AppLockerPolicy -Path C:\Windows\test.bat -User Everyone
    
    # Run these 2 scripts
    C:\test.bat
    C:\Windows\test.bat
    
    # What appears in the log
    Get-WinEvent "Microsoft-Windows-AppLocker/MSI and Script" `
    -MaxEvents 100 | Where Message -match "TEST.BAT" | 
    Select -First 2
    

    We can see that the above documentation strictly applies. The .bat file in a location that is not explicitly mentioned in the rules is denied from running by default.
    The code in the .bat file isn’t executed, I get a “This program is blocked by group policy. For more information, contact your system administrator.” message instead and a related 8007 event is generated.

    Let’s have a quick look at what appears in the eventlog.
    The file %OSDRIVE%\TEST.BAT isn’t allowed to run because there’s no rule that would allow it to be executed. A DeniedbyDefault RUN decision from Applocker has a RuleId set to {00000000-0000-0000-0000-000000000000}.

    The file %WINDIR%\TEST.BAT was allowed to run because the rule named “(Default Rule) All scripts located in the Windows folder” allows it explicitly.

    We have seen so far that the events written to the eventlog, whether the file is really executed or not and the PolicyDecision(s) reported by the Test-AppLockerPolicy cmdlet are fully aligned and coherent with the official documentation.

    • The (advanced) case of a .ps1 file:

    Let’s create a .ps1 file that contains nothing malicious and copy it in a location where it’s allowed to run and in another location where it’s not.
    Let’s assess now how the above documentation applies!

    # LangMode? 
    $ExecutionContext.SessionState.LanguageMode
    
    # Appplocker service running?
    Get-Service -Name AppIdsvc | fl St*
    
    # Create a PowerShell script file in allowed location
    'echo "Hello World from .PS1 file!"' | 
    Out-File C:\Windows\test.ps1 -Encoding ascii
    
    # Copy the same file in a denied location
    Copy-Item -Path C:\Windows\test.ps1 -Destination C:\
    
    # How Applocker would evaluate its RUN decisions
    Get-AppLockerPolicy -Effective | 
    Test-AppLockerPolicy -Path C:\test.ps1 -User Everyone
    Get-AppLockerPolicy -Effective | 
    Test-AppLockerPolicy -Path C:\Windows\test.ps1 -User Everyone
    
    # Run these 2 scripts
    C:\test.ps1
    C:\Windows\test.ps1
    
    # What appears in the log
    Get-WinEvent "Microsoft-Windows-AppLocker/MSI and Script" `
    -MaxEvents 100 | Where Message -match "TEST.PS1" | 
    Select -First 4
    

    We can see in the above picture that:

  • PowerShell host is running in constrained language mode (it’s evaluated when the process is started)
  • The content of the script isn’t malicious and doesn’t contain anything that the Constrained language mode would block.
  • The PolicyDecision(s) reported that c:\test.ps1 should be denied from running by default and C:\windows\test.p1 should run because the rule named “(Default Rule) All scripts located in the Windows folder” allows it explicitly.
  • C:\test.ps1 is executed. WAIT WHAT??? That’s unexpected, isn’t it? Why?
  • C:\windows\test.ps1 is executed. That was expected.
  • There are 4 events generated for the 2 executions.
  • Although C:\test.ps1 is executed, there’s an event Id 8007 telling that it has not! This could create a false sense of insecurity. 🙄

    We have seen so far that the events written to the eventlog, whether the file is really executed or not and the PolicyDecision(s) reported by the Test-AppLockerPolicy cmdlet are not fully aligned and coherent with the official documentation.

    Before jumping to conclusions and interpreting the above behavior, let me please introduce what happens if the PowerShell host is started in “Full Language” mode.

    Please ignore for the moment the last execution in the above picture. I’ll try to explain it later on.
    We can see in the above picture that:

  • PowerShell host is running in Full language mode (The language mode to be used is evaluated when the process is started)
  • The content of the script isn’t malicious and any language element would be allowed in this language mode.
  • The PolicyDecision(s) reported that c:\test.ps1 should be denied by default and shouldn’t run because there isn’t any rule that allows it.
  • C:\test.ps1 is not executed. That’s expected. I get a message saying: “File C:\test.ps1 cannot be loaded because its operation is blocked by software restriction policies, such as those created by using Group Policy”
  • There’s a single message in the eventlog saying “%OSDRIVE%\TEST.PS1 was prevented from running.”. At the execution time of the script, the Applocker rules have been evaluated and it appears that there isn’t any rule that would explicitly allow this script to run.
      • How could the above behaviors be explained? Is it normal?

    What’s more dangerous? Full Language mode or Constrained Language mode?

    The answer is Full Language mode. You should avoid it because if an attacker executes a malicious piece of code in this mode, he’ll have full access to any language element and therefore to any Windows API.

    Constrained language mode was introduced and designed to restrict access to sensitive language elements that can be used to invoke arbitrary Windows APIs.

    In Full Language mode, the only layer of protection that would block malicious code are the Applocker rules. Therefore, you’ve the Applocker PolicyDecision(s) that (first) apply. The security applies at the filesytem level. Let’s say Applocker operates how it was initially designed to operate: in a “File Mode”.

    If you’ve Applocker rules in “Allow mode”, any PowerShell host started will use the Constrained Language mode. This is where a 2nd security layer kicks in. The protection is somehow delegated to this language mode because it enforces so many restrictions on sensitive language elements that (almost?) all malicious code is using. This 2nd security layer applies at the file content level. Let’s say it’s a “File Content Mode”.

    Remember the last line I told you to ignore? I’ll explain it:
    To be able to get a PowerShell host running in full language mode, I restored all the default Applocker rules and especially the one that uses a wildcard for administrators, ran gpupdate.exe, deleted this rule and ran gpupdate.exe again. Inside a PowerShell host running in full language mode when I called powershell.exe the language mode to be applied was reevaluated and it’s the constrained language mode that was chosen. And we know now that file system based rules were ignored.

    At each script execution, the PowerShell host queries Applocker rules.
    These rules are bypassed if the script is executed in the constrained language mode.

      • How restrictive is the Constrained Language mode?

    Here’s an example of a piece of code using a method restricted by the Constrained Language mode:

    # LangMode? 
    $ExecutionContext.SessionState.LanguageMode
    
    # Create a PowerShell script file in allowed location
    '[console]::WriteLine("Hello World from .PS1 file!!!")' | 
    Out-File C:\Windows\test.ps1 -Encoding ascii
    
    # Copy the same file in a denied location
    Copy-Item -Path C:\Windows\test.ps1 -Destination C:\
    
    # How Applocker would evaluate its RUN decisions
    Get-AppLockerPolicy -Effective | 
    Test-AppLockerPolicy -Path C:\test.ps1 -User Everyone
    Get-AppLockerPolicy -Effective | 
    Test-AppLockerPolicy -Path C:\Windows\test.ps1 -User Everyone
    
    # Run these 2 scripts
    C:\test.ps1
    C:\Windows\test.ps1
    
    # What appears in the log
    Get-WinEvent "Microsoft-Windows-AppLocker/MSI and Script" `
    -MaxEvents 100 | Where Message -match "TEST.PS1" | 
    Select -First 4
    

    Why is there a difference between C:\test.ps1 and C:\windows\test.ps1?

    When C:\test.ps1 is executed, no Applocker rule that would allow it to run is found.
    The contrained language mode kicks in, the file is executed. The contrained language mode does its job line by line and restricts what’s not permitted.

    When C:\Windows\test.ps1 is executed. There’s an Applocker rule that allows it.
    The full language mode is selected, the file is executed. In full language mode nothing is restricted.

    If the test.ps1 script contains both allowed and sensitive language elements, here’s what happens:

    # LangMode? 
    $ExecutionContext.SessionState.LanguageMode
    
    # Create a PowerShell script file in allowed location
    @'
    echo "Hello World from .PS1 file!"
    [console]::WriteLine("Only works in FullLanguage mode")
    echo "Reaching this line in the code?"
    '@ | 
    Out-File C:\test.ps1 -Encoding ascii
    
    # Run the script
    C:\test.ps1
    

    The code that doesn’t call sensitive language element succeeds whatever its location in the script.

    The 2nd line with the sensitive language element fails and I get a message saying that “Cannot invoke method. Method invocation is supported only on core types in this language mode.”

    Let’s imagine that this piece of code is malicious. It would have failed to achieve its purpose and deliver its payload.

      • How is the language mode evaluated?

    When PowerShell.exe is started, it first tries to execute a random script (PS1) created in the %TEMP% folder.

    Then it tries to load a random module (PSM1) created in the %TEMP% folder.

    The content of these 2 files looks like this

      • What did we learn so far?

    Constrained language mode is designed to operate at the file content level while Applocker is at the file system level.

    Constrained language mode does a good job at restricting sensitive language elements and will prevent attackers to access all the Windows API. It reduces the attack surface and makes PowerShell a less attractive tool to attackers. Constrained language mode is not your only friend. There’s also the Antimalware Scan Interface (a.k.a. AMSI) but that’s another subject.

    What did we learn from a forensics perspective?

    You cannot rely on the “Microsoft-Windows-AppLocker/MSI and Script” event log to know what run if you’ve Applocker ‘allow mode’/’whitelist mode’/’lockdown mode’ turned on that makes PowerShell run in Constrained language mode.

    What’s the solution in this case?

    You need to have PowerShell logging turned on.

      • What can be seen if PowerShell logging is enabled?

    A C:\transcripts folder was created, logging enabled in the local computer policy and the computer local policy refreshed with gpupdate.exe.

    Here’s the minimum logging settings:

    When the ScriptBlockLogging is enabled, the following events in the Microsoft-Windows-PowerShell/Operational log can be seen:


    Unfortunately, it doesn’t tell you what succeeded and failed to run. It just shows what was run. Note also there’s no indication about the language mode.

    With the (Over-the-shoulder) Transcription enabled, we can both see what succeeded and failed to run:

    • Conclusion

    I hope that the official documentation of Applocker will be modified to clarify how Applocker really works with PowerShell scripts. The documentation should also reflect the changes introduced by the constrained language mode.

    I hope that the Applocker eventlog stops saying it has blocked a file and starts saying instead that it’s operating at the file content level and delegated the security to the constrained language mode.

    I hope that the transcription log and the scriptblock logging events include an additional property telling us what language mode was used to execute the code.

    Have fun! PowerShell Rock! 😎

    Quick update on PSGallery publishing

    I’ve recently published an update to my Autoruns module on the PowerShell Gallery but I encountered some error messages.

    The first one was:

    Publish-PSArtifactUtility : Failed to publish module ‘AutoRuns’: ‘Failed to process request. ‘A client version ‘4.1.0’ or higher is required to be able to publish packages. Install the latest version of NuGet client or install the latest version of PowerShellGet module using the instructions provided at https://aka.ms/installing-powershellget. Please contact cgadmin@microsoft.com to get more details.’.
    The remote server returned an error: (400) Bad Request..’.
    At C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\1.0.0.1\PSModule.psm1:1227 char:17

    As you can see I was still using the 1.0.0.1 version of the PowerShellGet module 😦
    I updated the module to the 2.0.4 version using the instructions provided in the error message: https://aka.ms/installing-powershellget

    Then I had 2nd error message:
    NuGet.exe upgrade is required to continue
    This version of PowerShellGet requires minimum version ‘4.1.0’ of NuGet.exe to publish an item to the NuGet-based repositories. NuGet.exe must be available in ‘C:\ProgramData\Microsoft\Windows\PowerShell\PowerShellGet\’ or ‘C:\Users\local-user\AppData\Local\Microsoft\Windows\PowerShell\PowerShellGet\’, or under one of the paths specified in PATH environment variable value.
    NuGet.exe can be downloaded from https://aka.ms/psget-nugetexe.
    For more information, see https://aka.ms/installing-powershellget .
    Do you want PowerShellGet to upgrade to the latest version of NuGet.exe now?

    It seems I missed the first lines about Nuget.exe in the instructions provided on https://aka.ms/installing-powershellget . My bad.
    Notice that PowerShell message is very helpful. I had a nuget.exe in one of these paths but it was tool old. I downloaded a version 4.9.x in that same path and was able to publish the module successfully.

    Where are the publishing requirements, again?

    When were these new Nuget.exe and PowerShellGet module requirements introduced?

    Well, it’s explained in the following blog post from the PowerShell Team on September 12th, 2018

    Most important: Publishers must update to PowerShellGet module version 1.6.8 or higher to publish to the PowerShell Gallery. Older versions of PowerShellGet are fine for find, save, install, and update functions, but not for publishing.
    […]
    The previous versions of PowerShell Gallery and PowerShellGet were based on older versions of NuGet. With this change we are aligning much more closely with the current state of the NuGet server and client. Many of the changes listed above – including the account and API key management – came directly from the NuGet updates. Another feature NuGet implemented is the ability to delete a package they have published accidentally, within the first hour after publishing.

    The related documentation was also updated on September 12th, 2018

    Quick tip: Renew a certificate used by WSUS

    I failed to update the certificate of a WSUS sever before it expired 😦
    Here’s what error message I got when I tried to use the cmdlet: Get-WsusServer : The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.

    Here’s what I did to replace my expired certificate by a valid one:

    
    # Create a self signed certificate
    $SelfSignedHT = @{
     DnsName = "$($env:COMPUTERNAME).myfqdn".ToLower()
     CertStoreLocation = "Cert:\LocalMachine\My"
    }
    New-SelfSignedCertificate @SelfSignedHT
    
    # Retrieve it from its store
    $cert = Get-ChildItem -Path Cert:\LocalMachine\My -SSLServerAuthentication | 
    Where { $_.NotAfter -gt (Get-Date) }
    
    # Export the public key
    Export-Certificate -Cert $cert -Type CERT -FilePath "~/documents/cert.$($cert.Thumbprint).cer"
    
    # Import the public key in the Root CA store
    Import-Certificate -FilePath "~/documents/cert.$($cert.Thumbprint).cer" -CertStoreLocation Cert:\LocalMachine\Root
    
    # View what certificate is being used (will show you the previous thumbprint)
    (Get-WebBinding  -Protocol https).certificateHash
    
    # Update it
    (Get-WebBinding  -Protocol https).AddSslCertificate(
    "$($cert.Thumbprint)","My"
    )
    
    # Check that the new cert is being used
    (Get-WebBinding  -Protocol https).certificateHash
    

    I was able to use the Get-WsusServer immediately after switching to the valid new certificate in the same console where it previously failed 😀

    Next step: distribute the exported public key to client computers using a GPO

    Remoting and registry

    • Issue

    I’ve recently encountered a weird issue while using Windows PowerShell Remoting.

    Using a the following

    New-PSSession -ComputerName RemoteServer
    

    failed with the following message:
    New-PSSession : Requested registry access is not allowed.
    + FullyQualifiedErrorId : PSSessionOpenFailed

    and

    icm -ComputerName RemoteServer -Authentication Kerberos { 1 }
    

    failed with the following message:
    Requested registry access is not allowed.
    + FullyQualifiedErrorId : PSSessionStateBroken

    Sorry, what’s the link between Remoting and the registry?

    • Solution

    Well, it appears that I cannot open a logon session on the remote server as well.
    Remoting isn’t the culprit. Neither is the version of PowerShell nor the version of the operating system as I suspected initially.
    I could also use remoting properly when I provided other credentials in the same shell where it previously failed.

    It appears that the User profile service tells us what’s missing:

    Windows cannot load classes registry file.
    DETAIL – The system cannot find the file specified.

    Yes, I can see that there’s no AppData\Local\Microsoft\Windows\UsrClass.dat in the profile.

    I deleted the “corrupted” profile and the issue went away 🙂

    • How to better detect this issue?

    On the client (source shell), WSMAN has some difficulties to delete the shell (that never opened, I presume)

    On the server (target server),

    You can use the following code to detect it on the remote server.

    Get-WinEvent -FilterHashtable @{ 
        LogName = "Windows PowerShell" ;
        Level = 2 ;
        Id = 103 
    }
    

    Windows PowerShell remoting rocks when the registry doesn’t fail 😉 Have fun 😎