Normal view

There are new articles available, click to refresh the page.
Before yesterdayDecoder's Blog

Hands off my (MS) cloud services!

By: Decoder
6 April 2021 at 15:08

Ok, this title is deliberately provocative, but the goal of this post is just to share some (as usual) “quick & dirty” tricks with all of you concerned about securing your Microsoft’s O365/Exchange/AzureAD online instances.

If you are facing the problem of having one or more services exposed on Microsoft cloud and want to have a clearer view of security configurations, it’s no easy to find the right path despite there are a ton of online resources about this subject.

Since I’m definitely not an expert, I often asked my friend @anformato for help and he always gave me useful tips. So when I later proposed him to write a post together on this topic in order to share “real life” experiences, he enthusiastically accepted.

In this first part we will talk about the Azure Active Directory “ecosystem”.

So let’s start from the beginning!

Azure Active Directory

Probably you have your on premise Active Directory Domain/Forest replicated with your online Azure AD instance, right?

Don’t replicate everything

You should carefully evaluate which objects you really need to synchronize and avoid to replicate everything. Remember “less is more”, and the more you replicate, the more you enlarge a possible attack surface.

Synchronize only the necessary objects (like users,) who really need to access online services. It’s also good practice not replicating administrators and other high privileged users. Azure AD admins should not be shared with on premise admins and vice versa (more on this in the dedicated chapter)

Replication can be easily configured with the “Azure Active Connect” tool:

Remember to perform this task before the first synchronization, otherwise it will be painful to remove unnecessary objects replicated to Azure AD (I had to open a case with MS support…)

Limit Administrative Access

Reducing the number of persistent Global Administrators is really a good practice.
Since only another GA can reset a global admin’s password, you should have at least 2 Global Admin users. (Microsoft recommends do not have more than 4/5 GAs, do you really need more than 5 GA’s?)

Azure AD roles allow you to grant granular permissions to your admins, providing you a way to implement the principle of least privilege. There are a bunch of predefined roles which you should definitely take in consideration for delegating administrative tasks.

A good starting point to understand Role Based Access control is here.

 
You should not use account with admin roles to manage administrative tasks, instead create dedicated accounts for each user with administrative privileges; those account should be cloud only account with no ties to on-premises Active Directory.
 
If your tenant has Azure Active Directory P2 licenses, a good practice is using Privileged Identity Management. It allows to implement just-in-time privileged access to Azure resources and Azure AD with approval flows.
 
All admin users must obviously use MFA, better with MS Authenticator App instead of using SMS or phone call as second authenticator factor. You should definitely avoid using personal account to manage M365 or Azure resources.
 
Last but not least: In order to prevent being accidentally locked out of your Azure Active Directory, have in place a procedure to manage emergency access. Ask yourself what will happen if service is currently unavailable because of network issue or IdP outage, or natural disaster emergency, or other GA users are not available?
Some procedures to manage emergency can be found here.

Restrict access to Azure Portal

By default, every Azure AD user can access the portal even without specific roles. This means they can access the entire directory and get information about AD settings including users, groups, and so on. You might argue, ok a standard Active Directory user can also access all this information on the on premise site, why should I care? Remember, your Azure AD is normally exposed all over the internet, so if the user is compromised it would be a juicy option for an attacker, do you agree? We will talk about it later..

Keep in mind that restricting access to portal does not block access to the various Azure API’s

Third party tools such as @_dirkjan fantastic “roadrecon“, which interacts with some “undocumented” MS Graph API , allow you to dump the whole directory for later investigations and analysis

Restrict access to MSOnline powershell module

This module provides a lot of powershell cmdlets for administering Azure AD. Even if it’s now deprecated, it’s still available and a standard user can by default access the MSOL service and run lot of “get-msol*” commands.

So the question is: how can we stop this? Well, the answer is very simple, as as an admin launch this command in an MSOL session:

This will prevent the possibility for a standard user (not admin) to read other user’s information and in fact inhibit most of the cmdlets:

And also block “roadrecon” tools:

..and AADinternals tools too:

This will also somehow “stop” the dangerous phishing technique via “device code authentication” as described here.

But keep in mind that it will not inhibit other operations, such as sending an email on behalf the victim once obtained the access token. For preventing such types of attacks you should implement dedicated “Conditional access policies”, available with Premium subscriptions.

Back to us, what could be the side effect by setting this flag? To be honest, it’s not so clear. Googling around you can find some old posts stating that if “UsersPermissionToReadOtherUsersEnabled is set to false in Azure Active Directory (AAD), users are unable to add external/internal members in Microsoft Teams”.

We were unable to reproduce this misbehavior with Teams and there is no official note from MS about this. It seems that this problem has been resolved.

You can also completely block access to all MSOL cmdlets, including admins, with the AzureADPreview module:

Know what you are doing because it seems that there is not the possibility to add exception (for example: admin users).

Restrict access to AzureAd powershell module

Given that this module will be the replacement of the MSOL module, if you already disabled the permissions in MSOL cmdlet, the API won’t be available also in AzureAD:

You can even restrict access to the Azure AD powershell by assigning roles, as described here

$appId = "1b730954-1685-4b74-9bfd-dac224a7b894" #azuread

$sp = Get-AzureADServicePrincipal -Filter "appId eq '$appId'"

if (-not $sp) { $sp = New-AzureADServicePrincipal -AppId $appId}

$user = Get-AzureADUser -objectId <upn> #permit only to this user(s) to access Azure Ad PS

New-AzureADServiceAppRoleAssignment -ObjectId $sp.ObjectId -ResourceId $sp.ObjectId -Id ([Guid]::Empty.ToString()) -PrincipalId $user.ObjectId

If user is not explicitly permitted, he cannot access:

Following the same procedure, you can also restrict access to MS Graph module (appid: 14d82eec-204b-4c2f-b7e8-296a70dab67e)

Disable Legacy Authentication

Disabling Legacy Authentication in a must-do if you are thinking about how to reduce your attack surface.

Legacy Authentication refers to all protocols that use the unsecure Basic Authentication mechanism and if you don’t block legacy authentication your MFA strategy won’t be effective as expected.

Among legacy authentication protocols are:

  • Exchange ActiveSync (EAS)
  • Exchange Web Services (EWS)
  • IMAP4
  • MAPI over HTTP (MAPI/HTTP)
  • RPC over HTTP
  • POP3
  • Authenticated SMTP
  • ….

Modern authentication is based on the Active Directory Authentication Library (ADAL) and OAuth 2.0 which support multi-factor authentication and interactive sign-in.

But blocking legacy authentication in your directory is easier to say than done .. you need to evaluate and understand the potential impacts before!

Starting from analyzing Azure AD sign-ins logs could be a good starting point.

  • Navigate to the Azure portal > Azure Active Directory > Sign-ins.
  • Add the Client App column if it is not shown by clicking on Columns > Client App.
  • Add filters > Client App > select all of the legacy authentication protocols. Select outside the filtering dialog box to apply your selections and close the dialog box

Unfortunately there is no magic “recipe”, you should avoid using applications which don’t support modern authentication 😦

For example, if you are still using the unsupported Outlook/Office 2010, it’s really time to upgrade to a newer version.

Blocking legacy authentication using Azure AD Conditional Access

The best way to block legacy authentication to Azure AD is through Conditional Access. Keep in mind it requires at least Azure Active Directory P1 licenses.

You can directly and indirectly block legacy authentication with CA policies and include/exclude specific users and groups as shown here

Consider to enable your blocking policy in Report-only mode to monitor impacts before changing the state of the policy from “Report-Only” to “On”.

Blocking legacy authentication service-side

If you don’t have Azure AD P1 or P2 licenses, the good news is that nothing is lost! Legacy authentication can be blocked service-side or resource-side.

Exchange Online

The easiest way is just to disable the protocols which by default use legacy authentication like Pop3, Imap, …

This can be done via exchange management powershell:

Get-CASMailbox -Filter {ImapEnabled -eq "true" -or PopEnabled -eq "true" } | Set-CASMailbox -ImapEnabled $false -PopEnabled $false

With this commands you will disable Imap and Pop3 for all mailbox users. Once done, you can enable only a specific subset of users who really need to use these protocols.

You can also set a global policy which will disable these protocols for all new mailboxes:

Get-CASMailboxPlan -Filter {ImapEnabled -eq "true" -or PopEnabled -eq "true" } | set-CASMailboxPlan -ImapEnabled $false -PopEnabled $false

The problem with this setting could be if you don’t want to block protocols that can do legacy and modern authentication. Exchange Online has a feature named authentication policies that you can use to block legacy authentication per protocol.

To manage authentication policy in Microsoft 365 Admin Center:

  • Navigate to the Microsoft 365 admin center
  • Settings > Org Setting
  • Navigate to Modern Authentication

You can even more fine grain these settings by allowing / disallowing basic authentication protocols for specific users using the authentication policies cmdlets:

New-AuthenticationPolicy -Name "Allow Basic Authentication for POP3"
Set-AuthenticationPolicy -Identity "Allow Basic Authentication for POP3" -AllowBasicAuthPop
Set-user -Identity [email protected] AuthenticationPolicy "Allow Basic Authentication for Pop3"

Sharepoint Online

In order to disable legacy authentication on your Sharepoint Online tenant you can use the follwoing cmdlets:

Connect-SPOService -Url –https://-admin.sharepoint.com
Get-SPOTenant
Set-SPOTenant –LegacyAuthProtocolsEnabled $false

AppLICATIONS, Consent and Permissions.. oh my..

The variegated world of the so-called “Applications” essentially have to interact with the ecosystem of O365 services exposed through public API and it’s up to you to manage and grant the appropriate permissions requested by the Apps. It’s a complex topic and in this chapter we will give you some simple advices. If security is your concern (I bet yes), the first thing to do is is to prohibit users consent for “unknown” apps, given that it’s allowed by default (??).

Why should you do it? Because by setting a more restrictive consent (as highlighted in the screenshot) will prevent most of the well known phishing techniques by abusing “malicious apps” and Oauth2. You can find an excellent example by @merlos here.

If the user clicks on the malicious link in the phishing email, he will no more have the possibility to accept the requested permissions:

But will be presented this screen where the Organization’s Admin approval is required:

You should also disable “Users can register applications” feature 

Only users with administrative roles or a limited subset of users with “Application developer” role  should be allowed to register custom-developed applications after these are reviewed and evaluated also from a security perspective.

Conclusion

In this short we hope to have given you some useful tips on how to somehow secure your tenants. The topic is clearly much more complex and requires dedicated skills, but as usual you have to start from the basics 😉

In next post we will (hopefully) write about logging, auditing, detecting .. stay tuned!

.. and adopt Two Factor Authentication (2FA) for all your users!!

Hands off my IIS accounts!

By: Decoder
14 December 2020 at 16:03

As promised in my previous post, I will (hopefully) give you some advices on how to harden the IIS “Application Pool” accounts aka “identities”.

First of all we need to understand how IIS architecture works and how identities are managed. Therefore I suggest you to read some specific posts about this topic for example this one and this one . More about identities can be found here

Ok! now that you got all the details let’s start from the IIS worker process “w3wp”. This is the process which will effectively access the http resources we asked for and serve them. By default this process runs under the “ApplicationPoolIdentity” identity. This is nothing more than (in this case the default) a “Virtual Account”, with the name of the Application Pool Identity, which IIS creates for us. Virtual accounts are directly managed by OS, so you don’t need to set passwords.

We can create dedicated Application Pools and assign them to web sites. Given that every application pool then has his own Virtual Account, we can secure access to resources based on the single application pool identity.

This is really cool, so we are able to set our security boundaries.

Let’s take a closer look at the process:

We can observe that the AppPool identity is member of the built in “IIS_IUSRS” group and also the “SERVICE” group. Unfortunately, it has also 2 dangerous privileges: SeAssignPrimaryTokenPrivilege and SeImpersonatePrivilege, probably the most abused privileges for escalations when you are able to get code execution on a buggy web application (in the following screenshot we uploaded a webshell), do you agree?

I think that everyone who is responsible / concerned for securing IIS Web Applications would like to limit these privileges, especially if they aren’t needed. In fact, the impersonation privileges are only useful if IIS needs to impersonate the Windows user who accessed the web app (<identity impersonate=true>), otherwise who cares?

So the first question is: is it safe to remove the privileges in this context?

There is no official documentation from MS about this, but there is not reason why it should lead to problems as long as you don’t need to impersonate

Now let’s see how privileges are configured.

We need to launch the Policy editor “gpedit.msc” and go to:

Computer Configuration->Windows Settings->Security Settings->Local Policies->User Right Assignment

This is the configuration for the “Impersonate a client after Authentication” (SeImpersonatePrivilege):

Bad news, IIS_IUSRS and SERVICE group have this privilege by default, and the Application Pools Identities belongs to these groups.

For the “Replace a process level token” (SeAssignPrimaryPrivilege) we have:

Again, our Applications Pool Identities and SERVICE groups have this privilege.

We could indeed inhibit the automatic membership to “IIS_IUSRS” group by modifying the appropriate xml file following MS official documentation (side note: I was not able to perform this action, got always an HTTP error 503 if set to “true”):

But this would not solve our problem given that the Application Pools Identities belongs to SERVICE group by default and we cannot remove this group from the Application Pool. Moreover, we cannot remove Impersonation Privileges for the SERVICE group.

So what can we do?

First of all we have to forget 😦 the Virtual Accounts and assign to our Application Pool a “real” Windows Account (just a standard user with a very strong password and set to “never expires”).

Let’s see the result:

The user (web) is no more member of the SERVICE group, this is a good starting point.

He is still member of IIS_IUSRS and IIS APPPOL\MyAppPool, so in order to remove our unwanted privileges, in the Group Policy Editor we have to:

  • Remove IIS_IUSRS from “Impersonate a Client after Authentication”
  • Remove IIS APPPOL\MyAppPool from “Replace a process level token”
  • restart the IIS services and observe the result..

Yes! privileges are no longer present, and is confirmed by listing the w3wp process tokens:

Now we can be more comfortable given that we have secured these accounts, isn’t it? Well … not exactly.. there are still a lot af nasty things you can do in session 0 .. more about this maybe in a future post 😉

Hands off my service account!

By: Decoder
5 November 2020 at 20:45

Windows service accounts are one of the preferred attack surface for privilege escalation. If you are able to compromise such an account, it is quite easy to get the highest privileges, mainly due to the powerful impersonation privileges that are granted by default to services by the operating system.

Even if Microsoft introduced WSH (Windows Service Hardening), there isn’t much you can do to “lower” the power of standard Windows services, but if you need to build or deploy a third-party service you can definitely strengthen it by using WSH.

In this post I will show you some useful tips.

Make use of Virtual accounts

Obviously a service must be run with a specific account. There are some built-in Windows accounts like SYSTEM (oh no !!), Local Service, and Network Service. But there is also the ability to use Virtual Accounts (I won’t write about “Group Managed Service Accounts” in this post) which are self-managed (no need to deal with passwords) and you can grant specific permissions by setting the correct ACL’s on the resources. This allows you to isolate the service and in case of compromise, they can only access the resources you have allowed. Virtual accounts can also access network resources, but in this case they will impersonate the computer account (COMPUTERNAME$)

Configuring Services with Virtual Accounts

First of all, you don’t need to create VSAs, when the service is installed, a matching account is automatically created for you in the form:

NT SERVICE\<service name>

all you need is to assign the account to your service:

Don’t forget to leave the password field blank!

Restricting access to Virtual Accounts

Now that your service runs under a specific account and not a generic one like Local Service or Network Service , you can implement fine-grained access control on resources like files and directories:

Removing unnecessary privileges

Impersonation privileges are a nightmare, so if your service doesn’t need to impersonate, why should we grant them to this service user?

Is it possible to remove this default privilege? There is a good news, yes! We can configure the privileges directly in registry or by using the “sc.exe” command:

And these values will be written into registry:

Let’s see if the the privilege is removed:

Oh yes! The dangerous privilege has been removed and it’s no more possible to get him back, for example using @itm4n ‘s trick described in this great post.

Write Restricted Token

If you add this extra group to your service tokens, you can further limit the permissions of your service account.

This means that by default you can only read or execute resources unless you explicitly grant write access:

A great research about write restricted tokens can be found in Forshaw’s post

Conclusions

I hope that this very short post can be useful for us who must secure our Windows Sytems (Offensive sec is just a joke for me 🙂 )

In the next post I will show you how to remove impersonation privileges in other critical services… stay tuned

UPDATE

Following Forshaw’s recent blog post and hint and his post on “Sharing a Logon Session a Little Too Much“ I can confirm that you gain back your privileges with the “loopback pipe trick”. This is possible because you will get the original token stored in LSASS without stripped privileges by the Service Control Manager. So the only solution to prevent this is to associate a Write restricted Token to your service?

Well not exactly … -> https://bugs.chromium.org/p/project-zero/issues/detail?id=2194

The use of WRITE RESTRICTED is considered by Microsoft to be of primary use for mitigating ambient privilege attacks 😦

That’s all 😉

When a stupid oplock leads you to SYSTEM

By: Decoder
27 October 2020 at 17:26

As promised in my previous post, I’m going to show you the second method I found in order to circumvent CVE-2020-1317.

Prerequisites

  1. Domain user has access to a domain joined Windows machine
  2. Domain user must be able to create a subdirectory under “Datastore\0” which is theoretically no more possible. But as we will see there are at least two method to bypass the limitation.

First Method: “Domain Group Policy” abuse

We will implement a Domain User Group Policy which produces “files”, typically script files such as Logon/Logoff scripts or configuration preferences xml files. This is a very common scenario in AD enterprise domains

Steps to reproduce

  • In this case we are going to configure user preferences under “User Configuration\Preferences\Windows Setting” in Group Policy Management. But it’s just an example, logon/logoff scripts will work too.
  • On the Domain Controller create a Domain preferences policy (for example “Ini Files”)

  • Login with domain user credentials on a domain joined computer
  • Check if the “Group Policy Cache” has been saved under the directory:
    • C:\programdata\microsoft\GrouPolicy\users\<SID>\Datastore (the entire directory, subdirectories and files are read-only for users after CVE-2020-1317)
    • If not, perform a “gpupdate /force” via command prompt
  • We can observe that the preference “inifiles.xml” has been created  
  • Now we will place an exclusive “oplock” [1] on this file:

  • On a new command prompt, we will force a new “gpupdate /force”. Oplock will be triggered and policy processing hangs:
  • Here comes the fun part(!), when Group Policy Caching is processed, the “gpsvc” service, running as SYSTEM, lists all the files and folders starting from the local “DataStore” root directory and performs several “SetSecurity” operations on subfolders and files. The first “SetSecurity” will always grant “Full control” to the current user, the second one only read access. This is the first run and we should have full control over the “Datastore” folders. In a new command prompt, we will try to create a directory under “Datastore\0” folder:
  • Once created, we disable inheritance and remove permissions to SYSTEM and Administrator Group. Why? Because when we release the “oplock”, the “second run” will occur during which the domain user will be granted read only access. Given that the process is running as SYSTEM, this operation will be denied. This can be observed in following procmon screenshot:
  • Now we have a directory with full control where we can place a symlink via \RPC Control to a SYSTEM protected file… and, just as an example, alter the contents of “printconfig.dll”, start an XPS Print job and gain a SYSTEM shell, as described in previous blog posts [3]

Second Method: redirect Windows Error Processing “ReportQueue” directory

In this case we are going to redirect the “C:\ProgramData\Microsoft\Windows\WER\ReportQueue” folder to the “Datastore\0” directory. This folder is normally empty if no error reporting process is running.

Steps to reproduce

  • Create the junction. In this case we are using CreateMountPoint.exe[3] , we can also use the built-in “mklink /j “ command but we need to remove the directory before

  • Ensure that optional diagnostics data etc..  has been turned on in Settings->Privacy->Diagnostics&feedback
  • Launch “Feedback Hub” and report a problem. Remember to check “Save a local copy…”
  • You will observe that the redirected “ReportQueue” directory will be populated. As soon as this happens, perform a “gpupdate /force”
  • Once the update has finished, you will be able to create a directory under the “Datastore\0” folder. At this point, you can proceed with same technique explained before

Conclusions:

I think to have successfully demonstrated that even after CVE-2020-1317 bug fix, there were still several possibilities to abuse junctions and Set Security calls in group policy client in order to perform an elevation of privilege starting from a low privileged domain user.

That’s all 😉

When ntuser.pol leads you to SYSTEM

By: Decoder
24 October 2020 at 16:32

This is a super short writeup following my previous post . My last sentence was a kind of provocation because I already knew that there were at least 2 “bypasses” for CVE-2020-1317.

I did not submit them because I totally disagree with recent MSRC changes in their policies, so when I discovered that they were fixed with latest security updates I decided to share my findings.

So here we are, this is the first one (next to come hopefully soon). I won’t dig too much in details. Let’s start from Group Policy caching again. This “feature” is enabled by default but can be turned off and in Windows Servers it’s disabled by default.

When GP Caching is disabled a new privilege escalation is possible (more on this later). If certain policies are configured, the file “ntuser.pol” is written in c:\programdata\microsoft\grouppolicy\users\<sid>.

The domain user has Full Control (!) over this directory and on ntuser.pol too. The file has hidden and system attributes but we can of course remove them..

Let’s see what happens with procmon..

This is interesting!

The file ntuser.pol is renamed to tempntuser.pol by the group policy service running as SYSTEM. Got it? At the end “tempntuser.pol” is deleted:

So we can obviously perform an “arbitrary delete” by abusing our so loved symlinks via “\RPC Control” but the rename operation can permit us to write a file (with filename and contents under our control) with SYSTEM privileges and that’s a real EoP! We also need to inhibit the file deletion, otherwise our file won’t exist any more at the end of the process.

The procedure is rather simple, I think that this simple batch file is more than sufficient 🙂

test.dll” is our “source” file that we will put in a directory where we have full control, because during move operations, the file maintains his source ACL

The “poc.exe” simply waits until the file is created in our target directory and then places an oplock in order to prevent the deletion (which will fail because of sharing violations)

And at the very end we have our file written in System32 with full control over it!!

But why do we have full control under the <SID> directory? Well, what I observed is that when group policy caching is enabled, a “Datastore” sub-directory is created and correct permissions are then set.. (see also my previous post)

That’s all 😉

Abusing Group Policy Caching

By: Decoder
23 September 2020 at 20:20

In this post I will show you how I discovered a severe vulnerability in the so-called “Group Policy Caching” which was fixed (among other GP vulnerabilities) in CVE-2020-1317

A standard domain user can perform, via the “gpsvc” service,  arbitrary file overwrite with SYSTEM privileges  by altering behavior of “Group Policy Caching”.

Cool, isn’t it?

The “Group Policy caching” feature is  configurable since Windows 2012/8, here you can learn more about it: https://specopssoft.com/blog/things-work-group-policy-caching/

For our goal, all you need is just a domain user and at least the “Default Domain” policy with no special configuration and no special settings in “Group Policy Caching” are needed. Only default configurations 😉

The “Group Policy caching files” are stored under subfolders:

  • C:\users\<domain_user>\AppData\Local\GroupPolicy\datastore\0

Each time a standard domain user performs ” a gpupdate /force”, the directory is created (if it doesn’t exist) and then populated depending on the group policies which are applied.

The domain user has full control over the “Datastore” directory but only read access under the “0” subfolder.

This can be easilly overriden, if we just rename the “Datastore” folder and create a new “Datastore” folder with the “0” subfolder.

Now let’s create a file in the “0” folder:

and then run a “gpupdate /force” watching what happens in procmon:

As we can see, the file is opened without impersonation (SYSTEM) and 2 SetSecurity calls are made. The resulting perms on the test file are:

Very strange, we lost the full control over the file, probably the second SetSecurity call, but the first one?

Let’s move a step forward, now we create a junction under the “0” folder pointing to a protected directory, for example: “c:\users\administrator\documents”

Note: We obviously need to rename the actual “Datastore” folder and create a new one with the “0” subfolder given that now we have again only read access

Again, in procmon let’s see what happens with a new “gpupdate /force”

A first SetSecurity call is done on “desktop.ini” and then the processing stops due to an access denied on “MyMusic”.

What will be the resulting perms of “desktop.ini”.. ? Guess what, full control for our user!

Now we have a clear vision of the whole picture:

When Group Policy Caching is processed, the “gpsvc” service, running as SYSTEM, lists all the files and folders starting from the local “DataStore” root directory and performs several “SetSecurity” operations on subfolders and files. The first “SetSecurity” will always grant “Full control” to the current user, the second one only read access.

Being able to obtain full control over a SYSTEM protected file (during the first SetSecurity call) EoP should be super-easy, don’t you agree?

For example, we can alter the contents of “printconfig.dll”, start an XPS Print job and gain a SYSTEM shell, as described in my post: https://decoder.cloud/2019/11/13/from-arbitrary-file-overwrite-to-system/

To achieve my goal, I created a very simple POC, here the relevant parts:

When the targetfile is accessed, an “opportunistic lock” is set and after the new junction is moved to “\RPC Control” which will in fact stop the processing and the second SetSecurity call.

Our domain user now has full control over the file!

Microsoft fixed this security “bug” by simply moving the “Group Policy” folders under c:\programdata\microsoft\grouppolicy\users\<sid> which is readonly for standard users….

That’s all 🙂

gp0

decoderblogblog

The impersonation game

By: Decoder
30 May 2020 at 15:23

I have to admit, I really love Windows impersonation tokens! So when it comes to the possibility to “steal” and/or impersonate a token I never give up!

This is also another chapter of the never ending story of my loved “JuicyPotato”.

 So, here we are (refer to my previous posts in order to understand how we arrived a this point):

  • You cannot specify a custom port for OXID resolver address in latest Windows versions
  • If you redirect the OXID resolution requests to a remote server on port 135 under your control and the forward the request to your local Fake RPC server, you will obtain only an ANONYMOUS LOGON.
  • If you resolve the OXID Resolution request to a fake “RPC Server”, you will obtain an identification token during the IRemUnkown2 interface query.

The following image should hopefully describe the “big picture”

 

But why this identification token and not an impersonation one? And are we sure that we will always get only an identification token? We need at least an impersonation token in order to elevate our privileges!

But first of all we need to understand who/what is generating this behavior. A network capture of the NTLM authentication could help:  

The verdict is clear: in the first NTLM message, the client sets the “Identify” flag, which means that the server should not impersonate the client.

Side Note: Don’t even try to change this flag (remember, we are doing kind of MITM attack in local NTLM Authentication, so we could alter the value), the client would not answer to our NTLM type 2 message!

But why does the client activate this flag? Well, probably it’s all in this RPC API client call setup:

RPC_STATUS RpcBindingSetAuthInfoExA( 
RPC_BINDING_HANDLE Binding, 
RPC_CSTR ServerPrincName, 
unsigned long AuthnLevel, 
unsigned long AuthnSvc, 
RPC_AUTH_IDENTITY_HANDLE AuthIdentity, 
unsigned long AuthzSvc, 
RPC_SECURITY_QOS *SecurityQos 
);

More precisely in this structure:

typedef struct _RPC_SECURITY_QOS {
unsigned long Version; 
unsigned long Capabilities; 
unsigned long IdentityTracking; 
unsigned long ImpersonationType; 
} RPC_SECURITY_QOS, *PRPC_SECURITY_QOS;

The client can define the desidered impersonation level before connecting to server. At this point we can assume that this is the default:

ImpersonationType=RPC_C_IMP_LEVEL_IDENTIFY

for the COM security settings when svchost services starts the DLL inside svchost.exe.

This is also controlled in a registry key (thanks to @tiraniddo for remembering me this)  located in HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost where the impersonation level can be defined overriding the default value.

In the following screen shot we can observe that all services belonging to “print” group will call the CoInitializeSecurity function with Impersonation level =3 (Impersonate)

And which service belongs to this group?

It’s the “PrintNotify” service, which can be instantiated and accessed by the following DCOM server:

Ok, let’s see what happens on a fully patchted Win10 1909 if we try to initialize this object via the IStorageTrigger, redirect to our fake OXID resolver and then intercept he authentication callbacks.

The first request with ANONYMOUS logon is relative to the redircted OXID resolution request. The two subsequent are generated by the IremUnknown2 queries, and this time we have impersonation tokens from SYSTEM!

And according to the registry configuration, we can see that the Impersonation parameter is read from the registry:

But wait, there is still another problem:

The SERVICE user’s group (which is typically the users we will “target” because they have impersonation privileges) cannot start/launch the sevice/dll and does not belong to the INTERACTIVE group.

Again, no way? Well not exactly.

With my modified JuicyPotato – juicy_2 (see previous screenshot) running as “NT AUTHORITY\Local Service”, I tested all CLISD’s (7268 in my Windows 10 1909) in order to find something else and this is the result:

There were 2 other CLSIDS which impersonate LOCAL SYSTEM:

  1. {90F18417-F0F1-484E-9D3C-59DCEEE5DBD8} which is hosted by the obsolete ActiveX Installer service:


This service belongs the “AxInstSVGroup” group, but surprisingly this group is not listed in the registry, so I would expect an Identification token! This means that the previous assumption is not always true, the impersonation level is not exclusively controlled via registry configuration…

2. {C41B1461-3F8C-4666-B512-6DF24DE566D1} This one does not belongs to a specific service but is related to an Intel Graphics Driver “IntelCpHeciSvc.exe” which hosts a DCOM server.




Clearly, the presence of this driver depends on the HW configuration of your machine.

The other CLISD’s impersonates the interactive user connected to first Session:

{354ff91b-5e49-4bdc-a8e6-1cb6c6877182 }- SpatialAudioLicenseServerInteractiveUser

{38e441fb-3d16-422f-8750-b2dacec5cefc}- ComponentPackageSupportServer

{f8842f8e-dafe-4b37-9d38-4e0714a61149} – CastServerInteractiveUser

So if you have impersonation privileges and an admin is connected interactively you could obtain his token and impersonate.

I did the same test on the CLSID’s of a Windows 2019 Server but this time was not able to get a SYSTEM impersonation token. The ActiveX Installer service is disabled by default on this OS. The only valid CLSID’s were the 3 relative to interactive user.

Well that’s all for now, the Impersonation game never ends 😉

.. and thanks to @splinter_code

Source code can be found here

Capture.PNG

decoderblogblog

No more JuicyPotato? Old story, welcome RoguePotato!

By: Decoder
11 May 2020 at 14:55

 

After the hype we (@splinter_code and me) created with our recent tweet , it’s time to reveal what we exactly did in order to get our loved JuicyPotato kicking again… (don’t expect something disruptive 😉 )

We won’t explain how the *potato exploits works, how it was fixed etc etc, there is so much literature about this and google is always your best friend!

Our starting point will be the “RogueWiRM” exploit.

In fact, we were trying to bypass the OXID resolver restrictions when we came across this strange behavior of BITS. But what was our original idea?

“(…) we found a solution by doing some redirection and implementing our own “Oxid Resolver” . The good news is that yes, we got a SYSTEM token! The bad one: it was only an identification token, really useless

Short recap

  • You try to initialize a DCOM object identified by a particular CLSID from an “IStorage Object” via standard marshalling (OBJREF_STANDARD)
  • The “IStorage” obect contains, among other things, the OXID, OID, and IPID parameters needed for the OXID resolution in order to get the needed bindings to connect to our DCOM object interfaces. Think about it like sort of DNS query
  • From “Inside COM+: Base Services”: The OXID Resolver is a service that runs on every machine that supports COM+. It performs two important duties:
    • It stores the RPC string bindings that are necessary to connect with remote objects and provides them to local clients.
    • It sends ping messages to remote objects for which the local machine has clients and receives ping messages for objects running on the local machine. This aspect of the OXID Resolver supports the COM+ garbage collection mechanism.
  • OXID resolver is part of “rpcss” service and runs on port 135. Starting from Windows 10 1809 & Windows Server 2019, its no more possible to query the OXID resolver on a port different than 135
  • OXID resolutions are normally authenticated.  We are only interested in the authentication part, which occurs via NTLM in order to steal the token, so we can specify in our case for OXID, OID and IPID just 32 random bytes.
  • If you specify a remote OXID resolver, the request will be processed with an “ANONYMOUS LOGON“.

So what was our idea in our previous experiments?

  • Instruct the DCOM server to perform a remote OXID query by specifying a remote IP in the binding strings instead of “127.0.0.1”. This is done in the “stringbinding” structure
  • On the remote IP where is running a machine under our control, setup a “socat” listener (or whatever you prefer) for redirecting the OXID resolutions requests to a “fake” OXID RPC Server, created by us and  listening on a port different from 135. The fake server can run on the same Windows machine we are trying to exploit, but not necessary.
  • The fake OXID RPC server implements the “ResolveOxid2” server procedure
  • This function will resolve the client (ANONYMOUS LOGON) query  by returning the  RPC bindings which will point to another “fake” RPC server listening on a dedicated port under our control hosted on the victim machine.
  • The DCOM server will connect to the RPC server in order to perform the IRemUnkown2 interface call. By connecting tho the second RPC Server, an “Autentication Callback” is triggered and if we have SE_IMPERSONATE_NAME privileges, we can impersonate the caller via RpcImpersonateClient() call
  • And yes, it worked! We got a SYSTEM token coming from BITS service in this case, but unfortunately it was an Identification token, really useless.

The good news

So we abandoned the research until some days ago, when two interesting exploits/bugs had been published:

  1. Sharing a Logon Session a Little Too Much published by @tiraniddo . You can also find in this blog  the  post along with a working POC in order to demonstrate how it was possible for the NETWORK SERVICE Account to elevate privileges (bug/feature?)
  2. The marvellous “Print Spoofer” exploit published by @itm4n. In this case (and for our scenario) not for the exploit itself but for the “bug” in the named pipe path validation checks.

On a late evening of some days ago, @splinter_code sent me a message:

(@splinter_code) “If we resolve the fake OXID request by providing an RPC binding with  our named pipe instead of the TCP/IP sequence, we should in theory be able to impersonate the NETWORK SERVICE Account and after that… SYSTEM is just a step beyond…”

(me) “Yeah, sounds good, let’s give it a try!”

And guess what? IT WORKED!!!

Let’s dig into the details.

The “fake” OXID Resolver

First of all a  little bit of context on how the OXID resolution works is required to understand the whole picture.
This is how the flow looks like, taken from msdn :

OXID resolution sequence

The client will be the RPCSS service that will try to connect to our rogue oxid resolver (server) in order to locate the binding information to connect to the specified dcom.

Consider that all the above requests done by the client in the OXID resolution sequence are authenticated (RPC Security Callback) impersonating the user running the COM behind the CLSID we choose (usually the one running as SYSTEM are of interests).
What *potato exploits does is intercepting this authentication and steal the token (well technically is a little bit more complicated because the authentication is forwarded to the local system OXID resolver that will do the real authentication and *potato will just alter some packets to steal the token… a classic MITM attack).
When this is done over the network (due to the MS fix) this will be an Anonymous Logon so quite useless for an EoP scenario.

Our initial idea was to exploit one of the OXID resolver (technically called IObjectExporter) method and forge a response that could trigger a privileged authentication to our controlled listener.

Looking at the scheme above 2 functions caught our attention:

So the first request was not of our interest because we couldn’t  specify endpoint information (that’s what we needed) so we were always answering with a response of RPC_S_OK. This is an example of what a standard response looks like:

serveralive2_pcap

So we focused in the next one (ResolveOxid2) and what we saw is something interesting that we could abuse:

resolveoxid2_pcap

Observing the above traffic, that is a standard response captured on the real OXID resolver on a windows machine, we quickly spot that we could abuse this call by forging our own ResolveOxid2 response and specify as an endpoint our controlled listener.
We can  specify endpoint information in the format ipaddress[endpoint] and also the TowerId (more on that later).

But… Why not registering the binding information we require on the system OXID resolver, getting the OID and OXID from the system and ask the unmarshalling of that?
Well as our knowledge the only way to do that is by registering a CLSID on the system and to do that you need Administrator privileges.

So our idea was to implement a whole OXID resolver that would answer always with our listener binding for any oxid oid it will receive, just a Fake OXID resolver.

Let’s analyze the function ResolveOxid2 to understand how to forge our response:

[idempotent] error_status_t ResolveOxid2(
   [in] handle_t hRpc,
   [in] OXID* pOxid,
   [in] unsigned short cRequestedProtseqs,
   [in, ref, size_is(cRequestedProtseqs)] 
     unsigned short arRequestedProtseqs[],
   [out, ref] DUALSTRINGARRAY** ppdsaOxidBindings,
   [out, ref] IPID* pipidRemUnknown,
   [out, ref] DWORD* pAuthnHint,
   [out, ref] COMVERSION* pComVersion
 );

What we need to do is to fill the DUALSTRINGARRAY ppdsaOxidBindings with our controlled listener.
Well that was a little bit tricky, but it is well documented by MS (in 1 and 2) so it was just a matter of trial and errors to forge the right offsets/sizes for the packet.

Let’s recap, we can redirect the client to our controlled endpoint (aNetworkAddr) and we can choose the protocol sequence identifier constant that identifies the protocol to be used in RPC calls (wTowerId).

There are multiple protocols supported by RPC (here a list).

In the first attempt we tried ncacn_ip_tcp, as TowerId, that allows RPC straight over TCP.
We run an RPC server (keep in mind that a “standard” user can setup and register an RPC Server) with the IRemUnknown2 interface and tried to authenticate the request in our SecurityCallback calling RpcImpersonateClient.
Returning ncacn_ip_tcp:localhost[9998] in the ResolveOxid2 response, an authentication to our RPC server (IRemUnknown2) were triggered, but unfortunately was just an identification token…

iremunkown2

 

So that was a dead end… But what about “Connection-oriented named pipesncacn_np ?

The named pipe Endpoint mapper

First of all what is the “epmapper” ? Well it’s related to the “RpcEptMapper” service. This service resolves “RPC interfaces identifiers to transport endpoints”.  Same concept as OXID Resolver, but for RPC.

The mapper runs also on TCP port 135 but can also be queried via a special named pipe “epmapper”.

This service shares the same process space as the “rpcss” service and both of them run under the NETWORK SERVICE account.  So if we are able to impersonate the account under this process we can steal the SYSTEM token (because this process hosts juicy tokens)…

The first try we gave was to return a named pipe under our control in the ResolveOxid2 response:

ncacn_np:localhost[\pipe\roguepotato]

But observing the network traffic the RPCSS still connected to the \pipe\epmapper . This is because, by protocol design, the named pipe epmapper must be used, and this is obviously a “reserved” name:

Capture

No way…

When we read the PrintSpoofer post we were impressed by clever trick of the named pipe validation path bypass (credits to @itm4n and @jonasLyk) where inserting ‘/’ in the hostname will be converted in partial path of the named pipe!

With that in mind we returned the following binding information:

ncacn_np:localhost/pipe/roguepotato[\pipe\epmapper]

And you know what? The RPCSS were trying to connect to nonexistent named pipe \roguepotato\pipe\epmapper :

named_pipe_not_found

Great! So we setup a pipe listener on \\.\pipe\roguepotato\pipe\epmapper, got the connection and impersonated the client. Then we inspected the token with TokenViewer :

token_viewer

So we had an Impersonation token of the NETWORK SERVICE account and also the LUID of the RPCSS service! 

Why that? Well, the answer was in the post mentioned above:

if you can trick the “Network Service” account to write to a  named pipe over the “network” and are able to impersonate the pipe, you can access the tokens stored in RPCSS service

All the “magic” about the fake ResolveOxid2 response is happening in this piece of code:

resolveoxid2

The Token “Kidnapper”

Perfect! Now we just needed to perform the old and well known Token Kidnapping technique to steal some SYSTEM token from the RPCSS service.

We setup a minimalist “stealer” which performs the following operations:

  • get the PID of the “rpcss” service
  • open the process, list all handles and for each handle try to duplicate it and get the handle type
  • if handle type is “Token” and token owner is SYSTEM, try to impersonate and launch a process with CreatProcessAsUser() or CreateProcessWithToken()
  • In order to get rid of the occasional “Access Denied” when trying to launch a processes in Session 0, we also setup the correct permissions for the Windows Station/Desktop 

Well, let’s unleash RoguePotato and see our SYSTEM shell popping 😀

system_privs

Final Thoughts

Probably you are a little bit confused at this point, let’s do a recap. Under which circumstances can I use this exploit to get SYSTEM privileges?

  • You need a compromised account on the victim machine with Impersonate privileges (but this is the prerequisite for all these type of exploits). A common scenario is code execution on default IIS/MSSQL configuration.
  • If you are able to impersonate NETWORK SERVICE account or if the “spooler” service is not stopped you can first try the other exploits mentioned before…
  • You need to have  a machine under your control where you can perform the redirect and this machine must be accessible on port 135 by the victim  
  • We setup a POC with 2 exe files. In fact it is also possible to launch the fake OXID Resolver in standalone mode on a Windows machine  under our control when the victim’s firewall won’t accept incoming connections.
  • Extra mile: In fact, in the old *potato exploit, an alternate way for stealing the token was just to setup a local RPC listener on dedicated port instead of forging the local NTLM Authentication,  RpcImpersonateClient() would have done the rest.

To sum it up:

flow_graph_2

You can find the source code and POC of RoguePotato here.

Final note: We didn’t bother to report this “misbehavior?” to MS because they stated that elevating from a Local Service process (with SeImpersonate) to SYSTEM is an “expected behavior”. (RogueWinRm – Disclosure Timeline)

That’ all 😉

@slpinter_code, @decoder_it

Capture

decoderblogblog

OXID resolution sequence

serveralive2_pcap

resolveoxid2_pcap

iremunkown2

Capture

named_pipe_not_found

token_viewer

resolveoxid2

system_privs

flow_graph_2

From NETWORK SERVICE to SYSTEM

By: Decoder
4 May 2020 at 15:58

In the last period, there have been several researches on how to escalate privileges by abusing generic impersonation privileges usually assigned to SERVICE accounts.

Needless to say,  a SERVICE account is required in order to abuse the privileges.

The last one, in order of time, “Printer Spoofer” is probably the most dangerous/useful because it only relies on the “Spooler” service which is enabled by default on all Windows versions.

In Windows 10 and Windows Server where WinRM is not enabled, you can use our “Rogue WinRM listener” in order  to capture a SYSTEM token.

And of course, in Windows Server versions 2016 and Windows 10 up to 1803, our Rotten/Juicy Potato is still kicking!

In this post I’m going to show you how it is possible to get SYSTEM privileges from the Network Service account, as described in Forshaw’s post “Sharing a Logon Session a Little Too Much“. I suggest you to read it before if not already done as I won’t detail the internal mechanism.

In short, if you can trick the “Network Service” account to write to a  named pipe over the “network” and are able to impersonate the pipe, you can access the tokens stored in RPCSS service (which is running as Network Service and contains a pile of treasures) and “steal” a SYSTEM token. This is possible because of some “weird” cheks/ assignments  in token’s Authentication ID.  The token of the caller (coming from RPCSS service) will have assigned the Authentication ID of the  service and if you impersonate this  token you will have complete access to RPCSS process, including his tokens. (because the impersonated  token belongs to group “NT Service\RpcSs “)

Side note: here you can find some other “strange” behaviors based on AuthID.

Given that the local loopback interface (127.0.0.1) is considered a network logon/access, it’s possible to exploit this (mis)behavior locally with an all-in-one tool.

The easiest way is a compromised “Network Service” account with a shell access and this will be our starting point. In this situation, we can directly write via the loopback interface to the named pipe, impersonate and access RPCSS process tokens.

Note: For testing purpose, you can impersonate the “Network Service” account using psexec from an admin shell:

  • psexec64 -i -u “NT AUTHORITY\Network Service” cmd.exe

There are many ways to accomplish this task, for example with Forshaw’s NTOBJECTMANAGER library in Powershell (keep in mind that the latest MS Defender updates marks this library as malicious!??)

But my goal was to create a standalone executable in old plain vanilla style and given that I’m very lazy, I found most of the functions needed in the old but always good incognito2 project. The source code is very useful and educational, it’s worth the study.

I reused the most relevant parts of code and did some minor changes in order to adpapt it to my needings and also to evade AV’s.

Basically this is what it does:

  • start a Named Pipe Server listening on a “random” pipe
  • start a Pipe Client thread, connect to  the random pipe via “localhost” and write some data
  • In the pipe server, once the client connects, impersonate the client (coming from “RPCSS”)
  • List tokens of all processes:Capture
  • If a SYSTEM token is available , impersonate it  and execute your shell or whatever you prefer:

Cattura

 

The “adapted” source code for my POC can be found here: https://github.com/decoder-it/NetworkServiceExploit

That’s all 😉

 

Capture

decoderblogblog

Capture

Cattura

Exploiting Feedback Hub in Windows 10

By: Decoder
28 April 2020 at 15:36

Feedback Hub is  a feature in Windows 10 which allows users to report problems or suggestions to Microsoft. It relies ond he “diagtrack” service, running as SYSTEM, or better known as “Connected User Experiences and Telemetry”

When the Feedback Hub gathers info in order to send them to MS, it does a lot of file operations, most of them performed by the SYSTEM user. It turns out that this application and the related services/executables which are run during the collection have a lot of logical bugs which can be exploited by “Directory Junctions” or Symbolic links via RPC Control.

These “bugs” could permit a malicious user to perform the following operations:

  • Arbitrary File Read (Information Disclosure)
  • Arbitrary File Overwite with contents not controlled by the Attacker (Tampering)
  • Arbitrary File Overwite/Write with contents  controlled by the Attacker (Elevation of Privilege)
  • Arbitrary File/Folder Deletion (Elevation of Privilege)

In my investigations, I was able o perform all these operations in various circumstances.

Today I’m going to show you how it is possible to perform an Arbitrary File Overwite/Write which could easily lead to EoP.  I found this issue in Windows 10 Preview up to Build v10.0.19592.1001. In Windows 10 “standard” version, the bug was much easier to exploit.

This issue was fixed in an “unintended” way in CVE-2020-0942  and sequent in latest WIP Build (10.0.19608.1006).

Prerequisites

  • Standard Windows 10 / domain user with Windows 10 computer (virtual or physical)
  • Diagnostics & Feedback has to be set to “Full” mode
    • If “Basic” was selected at the first logon this can be changed by the logged on user in settings->privacy->diagnostics& feedback by switching from “required” to “optional”

Description

When an attachment is sent via the Feedback Hub App and you choose to “Save a local copy…”, several file operations are performed by the diagtrack service, mostly using SYSTEM user privileges.

cattura.JPG

 

Here is an overview of the most significant operations.

First, diagtrack service by impersonating the current user creates a temporary random folder name diagtracktempdir<XX..X> under the “c:\Users\user\Appdata\local\temp” directory:

cattura.JPG

During the creation of the directory, the impersonated user also sets new permissions. These are not inherited from the parent folder and are very restrictive. In fact, as we can see in the following screenshot, permissions in the current directory do not include the current user:

cattura.JPG

While, for the subdirectories and files inside, the current user has some privileges.

cattura.JPG

In the next screenshot we can observe that files and folders are created without user impersonation and therefore as SYSTEM. It should also be noticed that even the file uploaded as feedback attachment (windowscoredeviceinfo.dll in this case) is now copied in the temporary folder. Additionally, all the files and folders created or copied in the temporary path will inherit these new permissions.

cattura.JPG

Once the process is complete, diagtracktempdir<XX..X> is renamed and moved into the current user FeedbackHub path. Sequent, restrictive permissions are again set on first directory of the renamed folder:

cattura.JPG

 

So the question is:

Is it still possible to abuse from special crafted “junction”?

Theoretically yes: the primary folder diagtracktempdir<XX..X> is created by the current user. Even though the permissions are sequent changed in a more restrictive way, such as granting the user only READ permissions, he could still modify because the user is the owner of the directory.

Practically, there is a race condition to win. Specifically, permissions on diagtracktempdir<XX..X> have to be changed before the subdirectories are created by SYSTEM without impersonation. In this way, the new permissions will be propagated, and the attacker will have full access on all of content.

Winning such a race conditions is hard. The time between the two events is in the order of milliseconds and you have to inject your “malicious” code for altering the permissions…

cattura.JPG

 

Nevertheless, I found a couple  solutions to win the race conditions and developing a POC/tool in VS2019 C++

Note: In order to speed up the tests, always choose “Suggest a feature” in Feedback Hub.

Only 1 Hard disk present

I tested this procedure on both physical and virtual machines. Depending on the HW, performance and current workload, I was able to perform the exploitation at first run. In some cases, it took up to 10/15 attempts.

First of all, run the “Feedback Hub” app and exit. This will create the initial directory and settings if never launched before.

This is the “Logical Flow” of my Poc/Tool:

  • Thread 1: Run a file watcher for the directory: “c:\users\<user>\AppData\Local\Packages\Microsoft.WindowsFeedbackHub_8wekyb3d8bbwe\LocalState\DiagOutputDir”
    this will intercept the creation of a directory and save the directory {GUID} useful for later
  • Thread 2: Run a file watcher for the directory “c:\users\<user>\appdata\local\temp”
    this will intercept the creation of folder diagtracktempdir<XX..X>
    • When the directory is created, change immediately the permissions, e.g.: everyone:full.
      Note: SetSecurityInfo API function is not suitable because slow (it does a lot of useless work “under the hood”), NtSetSecurityObject is faster because more “atomic”
    • We know how the final path name will look like:
      “diagtracktempdir<XX..X>\get info T0FolderPath\<{GUID}>”
  • Loop to create a “junction point” from the identified path to our target directory. Loop until the error is “path/file not found”.

If everything works fine, we should have copied our file in the destination directory. Alternatively, the loop will exit with an access denied error which means that we were too late.

The following screenshots are the PoC of how I was able to write the previous attached WindowsCoreDeviceInfo.dll in c:\windows\system32:

cattura.JPG

 

cattura.JPG

The following screenshot shows that the SetSecurity executed by the exploit happened before the creation of the directory “get info T0FolderPath

cattura.JPG

And directory successfully mounted:

cattura.JPG

Finally, file copied in target directory and EoP is just one step away 😉cattura.JPG

Two or more hard disk/partitions are present (including the possibility to mount an external USB disk)

I tested this procedure on both physical and virtual machines. In my test environment (physical and virtual machine with 2 partitions) I was able to perform the exploitation at first run. This solution is much more reliable.

Mounting an external USB disk on a physical machine can be accomplished by a standard user

First of all, run the “Feedback Hub” app and exit. This will create the initial directory and settings if never launched before.

In this scenario, we will create a junction from the directory “c:\user\<user>\documents\feedbackhub” to a directory located on another drive. This will force “reparse” operations whenever a file is opened, and this introduces delays, especially if the junction points to a different drive/partition.

When a junction is in place, the user’s “…appdata\local\temp” directory is no more used and the diagtracktempdir<XX..X> directory is directly written under the feedbackhub.

The only prerequisite is that the feedbackhub folder has to be empty, which means that that no previous Feedback Hub with Local Saving Attachments have to be done, because once the folders and  files are created, the user cannot delete them.

The following steps are required to win the race condition:

  1. Create the junction: cattura.JPG
  2. Use the junction directory instead of “…appdata\local\temp” in the C++ exploit:cattura.JPG
  3. Submit a new feedback and load a malicious DLL as attachmentcattura.JPG

Et voilà! Our dll was copied in System32 folder:

cattura.JPG

 

Conclusions

This is just  one of the still many possibilities to perform privileged file operations by abusing the generic “error reporting” functionalities in Windows 10.

If you’re hunting for for CVE’s maybe this might be worth a try. All you need is Procmon, time and patience 😉

 

POC can be downloaded here

 

 

Cattura

decoderblogblog

cattura.JPG

cattura.JPG

cattura.JPG

cattura.JPG

cattura.JPG

cattura.JPG

cattura.JPG

cattura.JPG

cattura.JPG

cattura.JPG

cattura.JPG

cattura.JPG

cattura.JPG

cattura.JPG

cattura.JPG

cattura.JPG

The strange case of “open-ssh” in Windows Server 2019

By: Decoder
19 March 2020 at 16:46

A few weeks ago I decided to install “open-ssh” on a Windows 2019 server for management purpose. The ssh server/client is based on the opensource project and MS implementation source code can be found here

Installing ssh is a very easy task, all you have to do is to install the “feature” via powershell:

ssh1

The first time you start the service, the necessary directories and files are created under the directory “c:\programdata\ssh

ssh3

 

ssh4

ssh2

A standard  “sshd_config” is created and this file is obviously readonly for  users:

ssh5

So a strange idea came in my mind: what if I created a special kind of malicious “sshd_config” file before the first launch of the open-ssh server?

As a standard user, with no special privileges, I am able to create the “ssh” directory  and write files…

And what should my “sshd_config” file contain? Well, easy: the possibility to login as an administrator with a predefined public/private key pair!

So let’s start again from the beginning…. sshd server has not yet been installed or launched for the first time.

First of all,  we need  to create a “special” sshd_config file, here the relevant parts:

StrictModes no
....
PubkeyAuthentication yes
....
Match Group administrators
     AuthorizedKeysFile c:\temp\authorized_keys
  1. StrictModes” set to “no” will bypass the owner/permissions strict checks on the “authorized_keys file”
  2. PubkeyAuthentication” set to yes will permit login via public/private keys
  3. Match Group administrators” will point to an “authorized”_keys” file generated by the user

3) is very interesting, we have the possibility to define a unique private/pubkey pair for authenticating the members of “administrators” groups..

Now we have to generate our public/private key. In this example, I will use the ssh utilities for Windows, but you can create them also on other systems (ex:Linux)

ssh1

 

Once done, we will copy the public key, in this example under c:\temp:

ssh2

Next step is creating the c:\programdata\ssh  directory and copy  the config file into it:

ssh4

At his point we have just to wait that “sshd” service will be launched, for testing we can do it on your own as an admin:

ssh5

Our config file has not been changed and we are still able to modify it!

Let’s see if it works. We are going to use OUR private/public key pair in order to login as administrator

sssh6

ssh7

 

Yes, it works! 🙂

Even if there are not so “common” preconditions, this installation/configuration  bug (which has nothing to do with the open-ssh software suite itself) could easily lead to EoP, don’t you agree?

So I informed MSRC about this, they opened a case and some days after they told me that this was already fixed with CVE-2020-757, probably as an “unintended” side effect…

Strange! My Windows 2019 server was fully patched including latest CVE’s. So I tested this on a Windows 10 machine, updated it with latest patches and actually after that I ran into an  empty “c:\programdata\ssh” folder  with correct permissions, even if open-ssh was not installed.

But why did this not happen on my Windows 2019 server?

I tested other servers as well, installed a new one from scratch and always same results, no empty c:\programdata\ssh directory!

I had a long debate about this with MSRC, basically  they were stating that they could not reproduce it and then magically, with March MS Tuesday patch, the directory was finally created with KB4538461 !

Really strange, but that’s it and given that now it’s fixed I decided to publish this post!

Stay safe, remember to keep “physical distancing” and not “social distancing” 🙂

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ssh7

💾

decoderblogblog

💾

ssh1

💾

ssh3

💾

ssh4

💾

ssh2

💾

ssh5

💾

ssh1

💾

ssh2

💾

ssh4

💾

ssh5

💾

sssh6

💾

The strange RPC interface (MS, are you trolling me?)

By: Decoder
5 February 2020 at 18:07

On a dark and stormy night, I was playing with Forshaw’s fantastic NTOBJECTMANGER library which, among the millions of things, is able to “disassemble” RPC servers  and implement Local RPC calls  in .NET.

I was looking at “interesting”  RPC servers on my Windows 10 1909 machine when my attention was caught by the “XblGameSave.dll” library.

This Dll is used by the “Xbox Live Game Service“:

xblsrv2

What is the purpose of this service? Well, I never played with Xbox nor Xbox games on Windows, but MS states that:

This service syncs save data for Xbox Live save enabled games. If this service is stopped, game save data will not upload to or download from Xbox Live.”

The service runs under the SYSTEM user context and is set to manual-triggered startup:

xblsrv

In short,  XblGameSave can be started upon a remote procedure call event.

I immediately popped up a new powershell instance as a standard user, imported  the Ntobjectmanager library and took a look at the Dll:

xbl2

Looked promising! The Dll exported a Local RPC Call “svcScheduleTaskOperation” with an Interface ID: f6c98708-c7b8-4919-887c-2ce66e78b9a0 and running as SYSTEM , maybe I could abuse  this call to  schedule a task as a privileged user?

Side note: I was able to get all these detailed information because I also specified the relative .pdb symbol file located in c:\symbols.  You can download the symbols files with the symchk.exe tool available with the Windows SDK:

symchk.exe /v c:\windows\system32\xblagamesave.dll /s 

SRV*c:\symbols\*http://msdl.microsoft.com/download/symbols

 

In order to obtain more information about the exposed interface, I created a client instance:

xbl3

The mysterious svcScheduleTaskOperation()  wanted a string as input parameter.  Next step was connect to the RPC server:

xbl4

Cool! when connecting my client I was able to trigger and start the service, so I tried to invoke the RPC call:

xbl5

 

As you can imagine, now the problem was guessing which string the function was waiting for…

The return value -2147024809  was only telling me that the parameter  was “Incorrect”. Thanks, this was a great help 😦

I hate fuzzing and bruteforcing and must admit that I’m really a noob in this field, this clearly was not the right path for me.

At this point, decompiling the Dll was no more an option! I had also the symbol file, so the odds of getting something readable and understandable by  common humans like me were founded.

xblc1This was the pseudo-C code generated by IDA, and in short, as far as I understood (I’m not that good in reversing): if input parameter was not NULL (a2), the Windows API WindowsCreateString was called and the resulting HSTRING passed to the ScheduledTaskOperation() method belonging somehow the ConnectedStorage class.

connectedstor

I obviously googled for more information with the keyword “ConnectedStorage” but surprisingly all the resulting links pointing to MS site returned a 404 error… The only way was to retrieve cached pages: https://webcache.googleusercontent.com/search?q=cache:dEN5ets6TcYJ:https://docs.microsoft.com/en-us/gaming/xbox-live/storage-platform/connected-storage/connected-storage-technical-overview

 

It seemed that the “ConnectedStorage” class, implemented in this Dll, had the following purpose:  “Store saved games and app data on Xbox One”  (and probably Windows 10 “Xbox” games too?)

My goal was not to understand the deepest and mysterious mechanisms of these classes, so I jumped to the ScheduledTaskOperation() function:

void __fastcall ConnectedStorage::Service::ScheduledTaskOperation(LPCRITICAL_SECTION lpCriticalSection, const struct ConnectedStorage::SimpleHStringWrapper *a2)
{
  const struct ConnectedStorage::SimpleHStringWrapper *v2; // rdi
  LPCRITICAL_SECTION v3; // rbx
  unsigned int v4; // eax
  const unsigned __int16 *v5; // r8
  unsigned int v6; // eax
  const unsigned __int16 *v7; // r8
  unsigned int v8; // eax
  const unsigned __int16 *v9; // r8
  unsigned int v10; // eax
  const unsigned __int16 *v11; // r8
  unsigned int v12; // eax
  const unsigned __int16 *v13; // r8
  unsigned int v14; // eax
  const unsigned __int16 *v15; // r8
  unsigned int v16; // eax
  const unsigned __int16 *v17; // r8
  unsigned int v18; // eax
  const unsigned __int16 *v19; // r8
  unsigned int v20; // eax
  const unsigned __int16 *v21; // r8
  unsigned int v22; // eax
  const unsigned __int16 *v23; // r8
  const unsigned __int16 *v24; // r8
  int v25; // [rsp+20h] [rbp-40h]
  __int64 v26; // [rsp+28h] [rbp-38h]
  LPCRITICAL_SECTION v27; // [rsp+30h] [rbp-30h]
  __int64 v28; // [rsp+38h] [rbp-28h]
  __int128 v29; // [rsp+40h] [rbp-20h]
  int v30; // [rsp+50h] [rbp-10h]
  char v31; // [rsp+54h] [rbp-Ch]
  __int16 v32; // [rsp+55h] [rbp-Bh]
  char v33; // [rsp+57h] [rbp-9h]

  v2 = a2;
  v3 = lpCriticalSection;
  v27 = lpCriticalSection;
  v28 = 0i64;
  EnterCriticalSection(lpCriticalSection);
  v26 = 0i64;
  v4 = WindowsCreateString(L"standby", 7i64, &v26);
  if ( (v4 & 0x80000000) != 0 )
    ConnectedStorage::ReportErrorAndThrow(
      (ConnectedStorage *)v4,
      (const wchar_t *)L"WindowsCreateString(str, static_cast<UINT32>(wcslen(str)), &_hstring)",
      v5);
  v25 = 0;
  v6 = WindowsCompareStringOrdinal(*(_QWORD *)v2, v26, &v25);
  if ( (v6 & 0x80000000) != 0 )
    ConnectedStorage::ReportErrorAndThrow(
      (ConnectedStorage *)v6,
      (const wchar_t *)L"WindowsCompareStringOrdinal(_hstring, right._hstring, &result)",
      v7);
  WindowsDeleteString(v26);
  if ( v25 )
  {
    v26 = 0i64;
    v8 = WindowsCreateString(L"maintenance", 11i64, &v26);
    if ( (v8 & 0x80000000) != 0 )
      ConnectedStorage::ReportErrorAndThrow(
        (ConnectedStorage *)v8,
        (const wchar_t *)L"WindowsCreateString(str, static_cast<UINT32>(wcslen(str)), &_hstring)",
        v9);
    v25 = 0;
    v10 = WindowsCompareStringOrdinal(*(_QWORD *)v2, v26, &v25);
    if ( (v10 & 0x80000000) != 0 )
      ConnectedStorage::ReportErrorAndThrow(
        (ConnectedStorage *)v10,
        (const wchar_t *)L"WindowsCompareStringOrdinal(_hstring, right._hstring, &result)",
        v11);
    WindowsDeleteString(v26);
    if ( v25 )
    {
      v26 = 0i64;
      v12 = WindowsCreateString(L"testenter", 9i64, &v26);
      if ( (v12 & 0x80000000) != 0 )
        ConnectedStorage::ReportErrorAndThrow(
          (ConnectedStorage *)v12,
          (const wchar_t *)L"WindowsCreateString(str, static_cast<UINT32>(wcslen(str)), &_hstring)",
          v13);
      v25 = 0;
      v14 = WindowsCompareStringOrdinal(*(_QWORD *)v2, v26, &v25);
      if ( (v14 & 0x80000000) != 0 )
        ConnectedStorage::ReportErrorAndThrow(
          (ConnectedStorage *)v14,
          (const wchar_t *)L"WindowsCompareStringOrdinal(_hstring, right._hstring, &result)",
          v15);
      WindowsDeleteString(v26);
      if ( !v25 )
      {
        v31 = 1;
LABEL_11:
        v30 = 4;
        _mm_storeu_si128((__m128i *)&v29, (__m128i)GUID_LOW_POWER_EPOCH);
        v33 = 0;
        v32 = 0;
        ConnectedStorage::Power::PowerChangeCallback(v3 + 4, 0i64, &v29);
        goto LABEL_12;
      }
      v26 = 0i64;
      v16 = WindowsCreateString(L"testexit", 8i64, &v26);
      if ( (v16 & 0x80000000) != 0 )
        ConnectedStorage::ReportErrorAndThrow(
          (ConnectedStorage *)v16,
          (const wchar_t *)L"WindowsCreateString(str, static_cast<UINT32>(wcslen(str)), &_hstring)",
          v17);
      v25 = 0;
      v18 = WindowsCompareStringOrdinal(*(_QWORD *)v2, v26, &v25);
      if ( (v18 & 0x80000000) != 0 )
        ConnectedStorage::ReportErrorAndThrow(
          (ConnectedStorage *)v18,
          (const wchar_t *)L"WindowsCompareStringOrdinal(_hstring, right._hstring, &result)",
          v19);
      WindowsDeleteString(v26);
      if ( !v25 )
      {
        v31 = 0;
        goto LABEL_11;
      }
      v26 = 0i64;
      v20 = WindowsCreateString(L"logon", 5i64, &v26);
      if ( (v20 & 0x80000000) != 0 )
        ConnectedStorage::ReportErrorAndThrow(
          (ConnectedStorage *)v20,
          (const wchar_t *)L"WindowsCreateString(str, static_cast<UINT32>(wcslen(str)), &_hstring)",
          v21);
      v25 = 0;
      v22 = WindowsCompareStringOrdinal(*(_QWORD *)v2, v26, &v25);
      if ( (v22 & 0x80000000) != 0 )
        ConnectedStorage::ReportErrorAndThrow(
          (ConnectedStorage *)v22,
          (const wchar_t *)L"WindowsCompareStringOrdinal(_hstring, right._hstring, &result)",
          v23);
      WindowsDeleteString(v26);
      if ( v25 )
        ConnectedStorage::ReportErrorAndThrow(
          (ConnectedStorage *)0x80070057i64,
          (const wchar_t *)L"Service::ScheduledTaskOperation called with an invalid operation type.",
          v24);
    }
  }
LABEL_12:
  LeaveCriticalSection(v3);
}


 

In short:

  • the expected input strings were “logon“, “standby“,”maintenance“, “testenter“, “testexit
  • logon“, “standby“,”maintenance” did nothing! (fake??)
  • testenter” and “testexit” called the PowerChangeCallback  method with a  parameter set to  GUID_LOW_POWER_EPOCH and a flag set  1 if  “testenter” and 0 if “testexit“. This GUID identifies  a “low power state” of the device.

The disassembled  output of the PowerChangeCallback was the following:

__int64 __fastcall ConnectedStorage::Power::PowerChangeCallback(LPCRITICAL_SECTION lpCriticalSection, __int64 a2, __int64 a3)
{
  LPCRITICAL_SECTION v3; // rdi
  int v4; // ebx
  HANDLE v5; // rcx
  __int64 v6; // rdx
  __int64 v7; // r8
  HANDLE v8; // rcx
  DWORD v10; // eax
  const unsigned __int16 *v11; // r8
  ConnectedStorage *v12; // rcx
  DWORD v13; // eax
  const unsigned __int16 *v14; // r8
  ConnectedStorage *v15; // rcx

  v3 = lpCriticalSection;
  v4 = *(_DWORD *)(a3 + 20);
  EnterCriticalSection(lpCriticalSection);
  v5 = v3[1].OwningThread;
  if ( v4 )
  {
    LOBYTE(v3[1].LockSemaphore) = 1;
    if ( !ResetEvent(v5) )
    {
      v13 = GetLastError();
      v15 = (ConnectedStorage *)((unsigned __int16)v13 | 0x80070000);
      if ( (signed int)v13 <= 0 )
        v15 = (ConnectedStorage *)v13;
      ConnectedStorage::ReportErrorAndThrow(v15, L"Event: ResetEvent failed", v14);
    }
    v8 = v3[2].OwningThread;
  }
  else
  {
    LOBYTE(v3[1].LockSemaphore) = 0;
    if ( !SetEvent(v5) )
    {
      v10 = GetLastError();
      v12 = (ConnectedStorage *)((unsigned __int16)v10 | 0x80070000);
      if ( (signed int)v10 <= 0 )
        v12 = (ConnectedStorage *)v10;
      ConnectedStorage::ReportErrorAndThrow(v12, L"Event: SetEvent failed", v11);
    }
    v8 = *(HANDLE *)&v3[3].LockCount;
  }
  if ( v8 )
    (*(void (__fastcall **)(HANDLE, __int64, __int64))(*(_QWORD *)v8 + 8i64))(v8, v6, v7);
  LeaveCriticalSection(v3);
  return 0i64;
}

This function was responsible for setting (“testenter“) and resetting (“testexit“) event objects in order to notify a waiting thread of the occurrence of the particular event ( I presume “low power change” in this case).

So back to us, what could I do with the original svcScheduleTaskOperation RPCcall ?

Probably nothing useful, maybe it has been exposed  only for testing purpose. Why did MS not implement the other functions like logon, maintenance, standby ?

And why did they call it svcScheduleTaskOperation ? Perhaps they will complete it in a future Windows release?

Mystery!

captxbl

As you can see, all legitimate commands returned 0  (success), that was a cold comfort 😦

My research sadly led to a dead end, but perhaps there are other forgotten or or leftover RPC interfaces to look for?

That’s all, for now 🙂

 

 

 

rpc

decoderblogblog

xblsrv2

xblsrv

xbl2

xbl3

xbl4

xbl5

xblc1

connectedstor

captxbl

❌
❌