Created and published on 28 December 2017
Author: Sebastián Castro @r4wd3r
RID Hijacking: Maintaining access on Windows machines
The art of persistence is (and will be...) a matter of concern when successfully exploitation is achieved. Sometimes is pretty tricky to maintain access on certain environments, specially when it is not possible to execute common vectors like creating or adding users to privileged groups, dumping credentials or hashes, deploying a persistent <bind/reverse> shell, or anything that could trigger an alert on the victim. This statement ratifies why it's necessary to use discrete and stealthy techniques to keep an open door right after obtaining a high privilege access on the target.
What could be more convenient that only use OS resources in order to persist an access?
This is the new post-exploitation technique which I have named RID Hijacking.
The RID Hijacking Attack
By using only OS resources, it is possible to hijack the RID of any existing account on the victim (even the 500 Administrator Built-in Account), and assign it to another user account. This attack will:
- Assign the privileges of the hijacked account to the hijacker account, even if the hijacked account is disabled.
- Allow to authenticate with the hijacker account credentials (also remotely, depending on machine's configuration), and obtain authorized access as the hijacked user.
- Register any operation executed on the event log as the hijacked user, despite of being logged in as the hijacker.
As far as I know, this technique has not been properly documented, despite of its stunning effectiveness. So I decided to write a Metasploit module, rid_hijack , that automatizes this attack with any combination of existing accounts on the victim host. This piece of software can be found at the latest Metasploit version in post/windows/manage/rid_hijack.
This module works simply by having a meterpreter session established on a Windows victim. It will try to check privileges (and get them, if needed) and modify the registry keys associated with the specified account. There's a brief description of each parameter:
- GETSYSTEM: If true, will try to acquire the SYSTEM privileges on the victim.
- GUEST_ACCOUNT: If true, will use the victim Guest account as the hijacker account.
- RID: The RID that will be assigned to the hijacker account. This value should be owned by one existing account, the intended to be hijacked. Set to 500 by default.
- USERNAME: When set, will look for the defined user account, and treat it like the hijacker account. If GUEST_ACCOUNT, this parameter will be ignored.
- PASSWORD: When set, it will establish the hijacker account password to this value.
Testing the module
This attack has been tested on Windows XP, Windows Server 2003, Windows 8.1 and Windows 10. We will use a Windows 8.1 Pro virtual machine as a victim. There exists only one user account called user, and two built-in accounts, the Administrator account (Administrador) and the Guest Account (Invitado).
By having a meterpreter session established, we will run the module to hijack the Administrator built-in account with RID 500 and assign it to the Guest built-in account. Obviously, the Guest account does not have a password, so we need to set it one.
And then the magic becomes real... After a successful login to the machine as Guest, some cool things are easily noticeable by executing some simple commands.
- By opening a console with cmd.exe, it's possible to notice at the first glare that is running as the Administrator account.
- We are logged in as the Guest account, which is easily ascertainable by running whoami, and checking the default path.
- The Guest account still appears as member of the Guests localgroup, which will keep the attack stealthy.
- Privileged operations can be performed, such as writing to Windows protected folders like System32.
How it works?
Regardless of the version since XP
, Windows uses the Security Account Manager (SAM) to store the security descriptors of local users and built-in accounts. As is mentioned in How Security Principals Work
, every account has an assigned RID
which identifies it. Different from domain controllers, Windows workstations and servers will store most part of this data in the HKLM\SAM\SAM\Domains\Account\Users key, which requires SYSTEM privileges to be accessed.
The Names subkey contains all the local user account names, including the built-in accounts. Each of these subkeys has stored a binary value, which has defined its type attribute as the account's RID in hex format (0x1f4 = 500, 0x1f5 = 501). Having said this, we can figure that the subkeys above are mapped to the RID's of each account (one exactly)...
By looking at these subkeys, we can find some interesting REG_BINARY values named as F and V. These values contain critical data related to the security descriptors of each account, including a copy of the account's RID and the enabled/disabled flag. Stored as little-endian in the F value, the RID value is located at offset 30h, and enabled/disabled flag at offset 38h.
But how this value was found? After doing some RE on the Windows Authentication & Auhtorization architecture, it was possible to determine that the RID copy stored in the F binary is the value used by the LSASS and SRM (Security Reference Monitor) to generate the primary access token right after the first communication between the MSV1_0.dll (NTLM) and SAMSRV.dll when translating from username to security identifier (SID).
Since LSASS, which loaded the connection with the kernel-process SRM, trusts the information obtained from the SAMSRV – SAM registry hive, the access token will be created based on all the security data retrieved from the SAM, including the RID copy, which is the value used to define the security context of the owner when he logs on.
The RID hijack consists on overwriting these bytes by setting the intended RID on the F value at the mentioned offset (i.e. F4 01). Probably of an integrity issue, changing this binary will make Windows to assume the identity of the hijacker account as the hijacked one on most of the critical OS processes, giving access as was mentioned above. This can be done not only using the built-in accounts, but also with custom local users.