Skip Ribbon Commands
Skip to main content

Ondrej Sevecek's English Pages

:

Engineering and troubleshooting by Directory Master!
MCM: Directory
February 05
Once again, how to enable Secure Boot in UEFI BIOS configuration

I have already covered all the steps in a previous article about UEFI Secure Boot configuration and Windows 2016 installation from USB flash drive. Here I will just repeat what are the necessary steps to undertake in the UEFI BIOS in order to have the Secure Boot enabled in Windows 2016 or Windows 10. I have just experienced another motherboard which taught me it once again (it was Gigabyte H170-D3H motherboard with the original F4 and later with F20 and later with F21 BIOS update):

Basic requirements

  •  CSM disabled - the compatibilitu support mode (CSM) must be disabled or it would allow nonUEFI boot media and boot loaders to be started which would effectively make the secure boot a nonsense
  • require Administrator password to enter BIOS - this is another requirement as well. Without having the BIOS configuration password protected, secure boot is again without a logic
  • Windows 8/10 Features setting enabled - you have to enable either the Windows 8/10 or the Windows 8/10 WHQL setting for the Windows 8/10 Features configuration option (you will find it on the BIOS tab). For me, both options worked to boot into the Secure Boot. I was not able to find any documentation about any differences in the two of them. So select whichever you like more :-)
  • Secure Boot enabled - sure you have to change the setting to enabled :-) it is not enable by default
  • Intel TXT - if the option is not present in the BIOS at all, it seems like it is supported automatically. I didn't need to do anything regarding this so called trusted execution technology.

The crucial thing to enable the Secure Boot

You must always Provision Factory Default keys! Even if you have just received your machine from manufacturing, you have to do it yourself. This cannot be done if the Secure Boot Mode is set to Standard. So the crucial technique is to first enable the Customized mode for secure boot, then provision the factory default keys manually and only then switch back to the Standard mode:

  1. switch the Attempt Secure Boot to Enabled
  2. switch the Secure Boot Mode to Customized - it enables the Key Management submenu
  3. go into the Key Management sub menu
  4. switch the Provision Factory Default keys to Enabled
  5. go back up
  6. switch the Secure Boot Mode to Standard

And you are all done.

January 05
Display password (password reveal button) keyboard shortcut

We have the password reveal button (aka show password button) in most password entry GUIs since Windows 8. It is the small eye icon showing at the end of password entry edit boxes which, when you mouse-click onto it, reveals the currenlty typed password which would normally be hidden under the stars or dots. People like to lookup the value in order to prevent failed password attempts especially when the computer is configured with several national keyboards or just to be sure. Internet Explorer have had the button included since its version 8 regardless of Windows version.

Is there a keyboard shortcut that would allow you to display the password as you type instead of leaving the keyboard and scrabble around for the mouse? Moreover we server oriented geeks sometimes do not even have mouse available at all.

How to display the password using a keyboard shortcut

Yes there is. Only since Windows 10 and Windows Server 2016. But finally.

Alt-F8

On the other hand, secure corporate environments may, according to some information security standards such as the ISO/IEC/EN/CSN 27001/27002, need to disable the password reveal button completelly. As people get accustomed to always showing their password for prior confirmation, they may forget about others watching. There are also survailence cameras etc.

How to disable the password reveal button

There is a Group Policy setting to disable this option available ever since the button exists (Windows 8 and latter). You will find it in a GPO (Group Policy Object) exactly here:

Computer Configuration
  Policies
    Administrative Templates
      Windows Components
        Credential User Interface
          Do not display the password reveal button

and

Computer Configuration
  Policies
    Administrative Templates
      Windows Components
        Internet Explorer
          Security Features
            Do not display the password reveal button

If you want to disable the show password button both in general user interface and in Internet Explorer you simply Enable both settings. It applies to the new Edge browser as well.

December 29
How to create UEFI bootable flash drive with installation media of Windows Server 2016

You may want to install Windows Server 2016 directly on a fully UEFI enabled system in order to be able to enforce the Secure Boot and make use of features such as Device Guard (Credential Guard) or the Hyper-V isolation and TPM virtual smart cards.

To have Secure Boot propagated the whole way up to a fully booted operating system, you have to clean install directly with all the UEFI support enabled (I have already covered some of it in a previous post about Secure Boot in Windows 10). On my current platform it does not work even if I only leave the CSM (compatibility support mode enabled) so what I need is a fully UEFI and Secure Boot enabled installation media which was not required on my previous trials with an older hardware and Windows 10. I plan installing from a pen flash drive. As it turns out, there some challenges though.

Requirements and challenges

Go into your BIOS (now called UEFI) and make sure you have:

  • CSM (compatibility support mode) disabled - this prevents booting anything else than correctly digitally signed UEFI Secure Boot operating system, in our case the Windows 2016 setup from the installation media.
  • all legacy OpROMs disabled
  • Administrator password for entering the BIOS enforced - without admin password Secure Boot does not work
  • Secure Boot enabled

The installation media based on USB pen flash drive must meet the following criteria:

  • be GPT (GUID partition table) formated - we cannot use MBR style harddisk format, UEFI requires the newer format called GPT
  • have a single partition formated with FAT32 - unless you are extremelly lucky, you cannot use NTFS. The UEFI BIOS needs to be able to read the contents of the partition and kind of logically they understand FAT32 only. You cannot create more partitions on the USB flash drive, because it is advertised as a removable media into operating system and thus it prevents you from creating more than a single partition. Some USB flash drives may have the option to flip the "removable bit" (also called RMB), but it is always kind of a hack for hours long fun during long winter nights.

And here comes the problem. Windows 2016 installation contains INSTALL.WIM file in the sources folder which is more than 4.3 GB long. Unfortunatelly FAT32 file system can accomodate files of size up to 4 GB only. So you cannot put such a big file on FAT32 while you cannot use NTFS for the source partition.

So we have to split the install.wim file into two .swm files with DISM command line utility and it will make do.

The procedure

  1. Obtain the Windows 2016 installation ISO and extract the files from it.
  2. Split the sources\install.wim file to several .swm files using the now built-in DISM tool:
    dism /Split-Image /ImageFile:sources/install.wim /SWMFile:sources/install.swm /FileSize:4000
    
  3. It will create at least two install.swm and install2.swm files, or even more of them, if you specified a smaller file size or have had a bigger original install.wim image.
  4. Delete the original sources\install.wim file from the sources folder and keep there or copy there the swm files that you just produced in the previous step
  5. Obtain a pen USB flash drive that you want to use for the installation media
  6. Start DISKPART command line as Administrator
  7. Identify your flash drive with the following command (in my case it showed as disk number 3):
    list disk
    
  8. Select the disk, clean it, convert to GPT and create the empty partition:
    list disk
    select disk 3
    clean
    convert gpt
    create partition primary
    format fs=fat32 quick
    assign
    
  9. Copy all the installation source files containing the split swm files which your prepared previously into the newly formated flash drive. You have to copy all the files from the ISO, including the sources, boot and efi folders as well

And go install, it should work :-) Note that such a drive should be displayed in the UEFI BIOS as a boot option. If it is not, the UEFI BIOS didn't recognize the drive or didn't recognize it can boot from it and it will not boot anyway.

December 18
The best way how to start PowerShell PS1 scripts

If you have a PS1 script file, it is sometimes difficult to start it. You can always right-click it and select Run with PowerShell. But this may fail without much information displayed if the computer's effective execution policy prevents the script from running. When you create a scheduled task or other kind of scheduled job, you also have to explicitly call the powershell.exe with the script file parameter instead of just typing the name of the PS1 file.

This is the reason for which I always create and distribute another BAT file with all my PS1 scripts. I always create the two files with the same name, because then you do not need to specify the exact name of the PS1 file inside the calling BAT file:

@ECHO OFF
powershell -NoLogo -ExecutionPolicy Bypass -File "%~d0%~p0%~n0.p1" %*
exit /B %errorlevel%

The batch file calls PS1 with the same name as is its own filename. It also passes all its own command line parameters down to the powershell script. Finally it returns or forwards the exit code obtained from the powershell script.

I usually do not need to synchronize current directory to the parent folder of the script files, but if you like it, you can improve the batch slightly in this way:

@ECHO OFF
cd /D "%~d0%~p0"
powershell -NoLogo -ExecutionPolicy Bypass -File "%~d0%~p0%~n0.p1" %*
exit /B %errorlevel%
December 18
PowerShell script to automatically correct the client list of DNS servers configured on network interfaces

Sometimes you have a requirement to change statically configured IP addresses of DNS servers (DNS resolvers) which are configured on network interfaces (NICs) of your computers. If you configure your servers or even workstations with a static list of DNS server addresses, you would have to go to all of them manually and change the IP addresses one by one. I have just hit an environment where all machines are configured statically and what if we rather change the hundreds of configurations automatically?

I created a simple PowerShell script which detects the network adapters that need reparations and sets the correct DNS server search order

# Note: we will reconfigure all the NICs that currently
#       contain at least one of the $currentPossibleDNSs DNS server IPs 
#       among the list of its configured DNS servers. We do not touch
#       any other NICs to be on the safe side agains WiFis and VPNs
$currentPossibleDNSs = @('10.10.0.18', '10.10.0.11')

# Note: we will configure the NICs with exactly the following
#       list of DNS server IPs which gets reset to this list in effect
$newDNS = @('10.10.0.12', '10.10.0.11', '10.10.0.15')

#
#

[object[]] $nics = gwmi win32_networkadapterconfiguration | ? { $_.DNSServerSearchOrder.Count -gt 0 }

foreach ($oneNic in $nics) {

  [bool] $matches = $false

  foreach ($oneCurrentDNS in $currentPossibleDNSs) {
    
    if ($oneNic.DNSServerSearchOrder -contains $oneCurrentDNS) {

      $matches = $true
      break
    } 
  }

  if ($matches) {

    Write-Host ('One found NIC: ip = {0} | {1} | dns = {2}' -f ($oneNic.IPAddress -join ','), $oneNic.Description, ($oneNic.DNSServerSearchOrder -join ','))

    $res = $oneNic.SetDNSServerSearchOrder($newDNS)
    Write-Host ('Reconfigured: {0}' -f $res.ReturnValue)

    if ($res.ReturnValue -ne 0) {

      throw ('Cannot reconfigure search order on NIC: #{0} | error = {1}' -f $oneNic.InterfaceIndex, $res.ReturnValue)
    }
  }
}

You can either run the script from PowerShell command line manually or you can as well assing it to the computer as an Immediate Task by using the Group Policy Preferences - Scheduled Tasks feature.

December 17
How to match domain names in NPS logins

If you implement custom Connection Request Policy in an NPS server (network policy server) you may want to forward authentication requests to a remote RADIUS server group. You may base the forwarding decision on a number of request attributes comming from the RADIUS client (such as a VPN gateway or a WiFi access point) as well as those passed through from its access client (the actual VPN client or WiFi client).

One of the attributes that you can check is the user name or user login. You may want to match user login names against domain names and forward the RADIUS requests for authentication to different remote RADIUS server groups. When using the user name attribute for connection request policy matching, you specify a regular expression (regex) to match the domain name. The following are examples of how to do it depending on the format of the login used:

what login regex
match a NetBIOS domain name followed by backslash domainA\kamil ^domainA\\.+
match a FQDN domain name preceded by the at@ sign kamil@domainB.com .+@domainB\.com$
longer fully qualified domain name kamil@ad.domainB.local .+@ad\.domainB.\local$
both NetBIOS and DNS domain names domainA\kamil or kamil@domainA.local (^domainA\\.+)|(.+@domain.\local$)

Note that the carret ^ chacter means begin of the string while the dollar sign $ means end of the string, dot-plus .+ means at least a single character and the dot and backslash must be escaped with another backslash. You can always verify the functionality from powershell just like in the following examples:

'domainA\kamil' -match '^domainA\.+'
'domainXXX\kamil' -match '^domainA\.+'
'kamil@domainB.com' match '.+@domainB\.com$'

Wish you happy time with your NPS :-)

November 30
Two different Azure/Office365 PowerShell modules for IT professionals

I was just trying to Connect-MsolService which requires installation of some PowerShell module. There is a wide confusion even in google which of the modules or which of their versions should I download. It took me some time, so to clarify it for others, here it goes:

If you want to use the Connect-MsolService cmdlet to connect to Office365 (aka Microsoft online services - MSOL) and manage the Office 365 accounts and domains, you need the following two downloads:

  • Microsoft Online Services Sign-in Assistant for IT Professionals RTW:
    • which is in fact the msoidcli_64.msi package
    • after installation it appears as Microsoft Online Services Sign-In Assistant in the Programs and Features control panel
    • and represents itself as a Windows service called msoidsvc aka Microsoft Online Services Sign-In Assistant
  • Windows Azure Active Directory Module for Windows PowerShell version 1.0:
    • which is in fact the AdministrationConfig.msi package
    • also called Azure AD Module for PowerShell aka Office 365 Module for PowerShell aka Msol Module for PowerShell aka AAD Powershell aka Azure Active Directory Connection aka Azure AD Module for PowerShell
    • after installation it appears as Windows Azure Active Directory Module for Windows PowerShell in the Programs and Features control panel
    • this one has already some newer versions available (1.1.166.0 as the newest release version and 2.0 as a public preview). These are updated and mentioned in the following article Microsoft Azure Active Directory PowerShell Module Version Release History but do not bother

This is the only method currently available for direct download which can be used to manage Office365 and use the MSOL powershell command lets.

If you want to manage other Azure services, you will need to download Azure Powershell from github. This comes in various versions, such as 1.7.0 or 2.2.0 or 3.2.0. It does NOT need the Msol Services Sign-In Assistant and does NOT contain the Connect-MsolService cmdlet so that I assume it cannot manage Office 365 at all.

October 23
Error 314 when creating new NLB cluster

If you get error 314 or error message Provider load failure on Windows Server 2016 when creating a new NLB cluster using GUI or using the New-NlbCluster cmdlet, note that you must have IPv6 enabled in order to create the cluster. You cannot even disable just the IPv6 tunnel adapters with registry value DisableComponents = 0x1. According to my testing, the DisabledComponents value must not be present in registry with any value.

August 29
Enable the Backup (or any other) privilege for a PowerShell script

The Backup privilege (SeBackupPrivilege, also sometimes called the Backup user right) is in fact very powerful. If enabled for a process or thread it automatically gives the generic read permission to any resource operation. It should be rather called read all privilege. Even if some object permissions explicitly deny the read permission, if the access is attempted with the backup privilege enabled, the access is granted implicitly.

This may be very useful when scripting some administrative tasks which need to read data not accessible even to Administartors by default. Such as enumerating NTFS system disk files and thus avoiding the anoying access denied errors on some of the system folders.

Similarly, if you enable Restore privilege (the SeRestorePrivilege), it would give you the generic write permission implicitly on any object which may allow you to overwrite virtually anything in the system.

function Implement-AdjustPrivilege ()
{
  $win32api = @'

using System;
using System.Runtime.InteropServices;

namespace Sevecek.Win32API
{
  [StructLayout(LayoutKind.Sequential)]
  public struct LUID
  {
    public UInt32 LowPart;
    public Int32 HighPart;
  }

  [StructLayout(LayoutKind.Sequential)]
  public struct LUID_AND_ATTRIBUTES
  {
    public LUID Luid;
    public UInt32 Attributes;
  }

  [StructLayout(LayoutKind.Sequential)]
  public struct TOKEN_PRIVILEGES
  {
    public UInt32 PrivilegeCount;
    public LUID Luid;
    public UInt32 Attributes;
  }

  public class Privileges
  {
    public const UInt32 DELETE = 0x00010000;
    public const UInt32 READ_CONTROL = 0x00020000;
    public const UInt32 WRITE_DAC = 0x00040000;
    public const UInt32 WRITE_OWNER = 0x00080000;
    public const UInt32 SYNCHRONIZE = 0x00100000;
    public const UInt32 STANDARD_RIGHTS_ALL = (
                                                READ_CONTROL |
                                                WRITE_OWNER |
                                                WRITE_DAC |
                                                DELETE |
                                                SYNCHRONIZE
                                            );
    public const UInt32 STANDARD_RIGHTS_REQUIRED = 0x000F0000u;
    public const UInt32 STANDARD_RIGHTS_READ = 0x00020000u;

    public const UInt32 SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001u;
    public const UInt32 SE_PRIVILEGE_ENABLED = 0x00000002u;
    public const UInt32 SE_PRIVILEGE_REMOVED = 0x00000004u;
    public const UInt32 SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000u;

    public const UInt32 TOKEN_QUERY = 0x00000008;
    public const UInt32 TOKEN_ADJUST_PRIVILEGES = 0x00000020;

    public const UInt32 TOKEN_ASSIGN_PRIMARY = 0x00000001u;
    public const UInt32 TOKEN_DUPLICATE = 0x00000002u;
    public const UInt32 TOKEN_IMPERSONATE = 0x00000004u;
    public const UInt32 TOKEN_QUERY_SOURCE = 0x00000010u;
    public const UInt32 TOKEN_ADJUST_GROUPS = 0x00000040u;
    public const UInt32 TOKEN_ADJUST_DEFAULT = 0x00000080u;
    public const UInt32 TOKEN_ADJUST_SESSIONID = 0x00000100u;
    public const UInt32 TOKEN_READ = (
                                      STANDARD_RIGHTS_READ |
                                      TOKEN_QUERY
                                   );
    public const UInt32 TOKEN_ALL_ACCESS = (
                                            STANDARD_RIGHTS_REQUIRED |
                                            TOKEN_ASSIGN_PRIMARY |
                                            TOKEN_DUPLICATE |
                                            TOKEN_IMPERSONATE |
                                            TOKEN_QUERY |
                                            TOKEN_QUERY_SOURCE |
                                            TOKEN_ADJUST_PRIVILEGES |
                                            TOKEN_ADJUST_GROUPS |
                                            TOKEN_ADJUST_DEFAULT |
                                            TOKEN_ADJUST_SESSIONID
                                         );

    [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
    public static extern IntPtr GetCurrentProcess();

    [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
    public static extern IntPtr GetCurrentThread();

    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid);

    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, UInt32 BufferLengthInBytes, IntPtr PreviousStateNull, IntPtr ReturnLengthInBytesNull);

    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle);

    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool OpenThreadToken(IntPtr ThreadHandle, UInt32 DesiredAccess, bool OpenAsSelf, out IntPtr TokenHandle);

    [DllImport("ntdll.dll", EntryPoint = "RtlAdjustPrivilege")]
    public static extern int RtlAdjustPrivilege(
                UInt32 Privilege,
                bool Enable,
                bool CurrentThread,
                ref bool Enabled
                );

    [DllImport("Kernel32.dll", SetLastError = true)]
    public static extern bool CloseHandle(IntPtr handle);

    //
    //

    private static LUID LookupPrivilege(string privilegeName)
    {
      LUID privilegeValue = new LUID();
      
      bool res = LookupPrivilegeValue(null, privilegeName, out privilegeValue);

      if (!res)
      {
        throw new Exception("Error: LookupPrivilegeValue()");
      }

      return privilegeValue;
    }

    //
    //

    public static void AdjustPrivilege(string privilegeName, bool enable)
    {
      IntPtr accessToken = IntPtr.Zero;
      bool res = false;

      try
      {
        LUID privilegeValue = LookupPrivilege(privilegeName);

        res = OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, false, out accessToken);
        
        if (!res)
        {
          res = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out accessToken);

          if (!res)
          {
            throw new Exception("Error: OpenProcessToken()");
          }
        }

        TOKEN_PRIVILEGES tokenPrivileges = new TOKEN_PRIVILEGES();
        tokenPrivileges.PrivilegeCount = 1;
        tokenPrivileges.Luid = privilegeValue;

        if (enable)
        {
          tokenPrivileges.Attributes = SE_PRIVILEGE_ENABLED;
        }
        else
        {
          tokenPrivileges.Attributes = 0;
        }

        res = AdjustTokenPrivileges(accessToken, false, ref tokenPrivileges, (uint)System.Runtime.InteropServices.Marshal.SizeOf(tokenPrivileges), IntPtr.Zero, IntPtr.Zero);
        
        if (!res)
        {
          throw new Exception("Error: AdjustTokenPrivileges()");
        }
      }

      finally
      {
        if (accessToken != IntPtr.Zero)
        {
          CloseHandle(accessToken);
          accessToken = IntPtr.Zero;
        }
      }
    }
  }
}

'@

  if ([object]::Equals(('Sevecek.Win32API.Privileges' -as [type]), $null)) {

    Add-Type -TypeDefinition $win32api
  }
}

The above is the core function. The following code just makes use of it. Sure you must be member of local Administrators group usually or have the right granted explicitly:

Implement-AdjustPrivilege
[Sevecek.Win32API.Privileges]::AdjustPrivilege('SeBackupPrivilege', $true)

Cheers!

August 25
How to verify if current user is member of Administrators group

A common PowerShell scripter's requirement is to verify if the script is running under a member of local Administrators group. And also if the UAC feature (the user account control) is in effect to be sure the script is really running with elevated privileges (also called running as administrator).

Note that I am using the group's SID instead of a name which may differ on various localized systems.

function Is-LocalAdministratorsMember ()
{
  # Note: $false to get the Thread's access token in both cases, whether the thread is or is not impersonating
  [Security.Principal.WindowsIdentity] $currentPrincipal = [Security.Principal.WindowsIdentity]::GetCurrent($false)

  [bool] $isAdmin = $false
  [string[]] $sids = $currentPrincipal.Groups | select -Expand Value

  for ($i = 0; $i -lt $sids.Length; $i ++) {

    if ($sids[$i] -eq 'S-1-5-32-544') {

      $isAdmin = $true
      break
    }
  }

  return $isAdmin
}
1 - 10Next
 

 About this blog

 
Ondrej Sevecek 

Ondrej Sevecek is technical consultant, writer and speaker specialising in network security, PKI, identity management and Active Directory on Microsoft Windows platform. Ondrej is Microsoft Certified Master (MCM:Directory and MCSM:Directory) and the  Most Valuable Professional (MVP: Enterprise Security). He also maintains his CISA and CHFI:Computer Hacking Forensic Investigator and CEH:Certified Ethical Hacker certifications.

Ondrej is also MCT and gives lectures in the greatest of European training centers GOPAS.