hfiref0x / UACME

Defeating Windows User Account Control
BSD 2-Clause "Simplified" License
6.25k stars 1.31k forks source link

UAC bypass with Shell protocol hijack and Security Center CPL abuse #87

Closed AzAgarampur closed 3 years ago

AzAgarampur commented 3 years ago

Hello,

I would like to help add something to this repo once again. If you are willing to take a look I'd be more than happy to share the code and explain how it works.

Thank you.

hfiref0x commented 3 years ago

Hello,

sure.

AzAgarampur commented 3 years ago

Thanks for your response.

This method does two things. It hijacks the https protocol handler and uses autoelevated COM interface IWscAdmin. The COM interface calls ShellExecuteW with an https://... parameter. The hijacked handler is cmd.exe, or any other program, is then launched with administrative privileges.

A lot of the code is for hijacking the default https protocol handler. Since Windows 8 the system that sets the protocol settings is encrypted and verified with a hash. So we cannot change it manually any more. Most of the code is loading the .dll files that contain this mechanism, so we can set the custom handler using the internal functions and encrypt it so windows will recognize our custom handler.

here is the code: https://github.com/AzAgarampur/byeintegrity3-uac

hfiref0x commented 3 years ago

Thanks. Does this function has a symbol name? I can't find it using pattern bytes in 17763.

AzAgarampur commented 3 years ago

Yeah, in Win. 10 function is ?SetUserAssoc@@YAJW4UASET@@PEBG1W4ASSOC_MAKE_DEFAULT_FLAGS@@@Z in SystemSettings.Handlers.dll. Undecorated symbol name is: int __stdcall __high SetUserAssoc().

hfiref0x commented 3 years ago

I found these functions with your patterns in w8.1 and w10 (19042) but I could not find anything like that in 17763 which is strange.

17763 doesn't even import ConvertSidToStringSidW which is used by func in 19042.

Same in 14393.

hfiref0x commented 3 years ago

Oh it seems in 14393/17763 this routine is inside shell32.dll named UserAssocSet which is similar to w8.1. Need to check 19041/18362/18363 to figure out if it the same.

AzAgarampur commented 3 years ago

17763 doesn't even import ConvertSidToStringSidW which is used by func in 19042.

Same in 14393.

Hmm, seems peculiar... I think they are still using windows 8.1 classic shell library.

Oh it seems in 14393/17763 this routine is inside shell32.dll named UserAssocSet which is similar to w8.1.

Yeah this seems to be the same function as in windows 8.1.

hfiref0x commented 3 years ago

This change is likely introduced in 19041. I've checked 18362/63 they have it in shell32.dll. Your pattern is working well for 19041 too.

Edit: maybe 1st commit for test tomorrow.

AzAgarampur commented 3 years ago

So just to make sure, the ones you found in shell32.dll on 14393/17763/19041/18362/18363 has the same signature as SIGNATURE_NT6X defined in the code?

hfiref0x commented 3 years ago

No, they have different pattern. Yours works good with 19041/42.

AzAgarampur commented 3 years ago

Ok, thanks for clarifying. I will update my code to match shell32.dll on those versions.

hfiref0x commented 3 years ago

From what I see here, shell32.dll and SystemSettings.Handlers.dll has identical functions basically copy-pasted in 19041/19042. What is the difference?

See in shell32 UserAssocSet -> SetUserAssoc

SystemSettings.Handles UserAssocSet -> SetUserAssoc

except this one in SystemSettings seems have an extra flag as 4 param.

AzAgarampur commented 3 years ago

Hmm, on Windows 10 19042 SystemSettings.Handlers.dll calls UserAssocSet -> SetUserAssoc. On Windows 8, shell32.dll is UserAssoc only and has a few different function calls within the function.

In which OS version do you see shell32.dll be UserAssocSet -> SetUserAssoc?

hfiref0x commented 3 years ago

Its in 19041/19042. I'm asking this because in process of looking for stable patterns to find this function. Is there is no difference between shell32 and systemsettings dll implementation it could be easier to search and call it from shell32 instead.

Additionally I've found it prototype in MS WXP leak.

STDAPI UserAssocSet(UASET set, LPCWSTR pszExt, LPCWSTR pszSet) looks valid for everything except recent Win10 versions.

AzAgarampur commented 3 years ago

Ooh, ok I see what you're saying now. My apologies for not understanding before, I get it now.

OK so on Windows 10 19041/19042, The shell32.dll UserAssocSet -> SetUserAssoc is the same as W10 systemsettings.handlers.dll, and does the same thing we want. However, it takes one more parameter, as we know. According to the symbol file, the prototype with the 4th parameter combined with the one you just posted is going to be:

STDAPI UserAssocSet(UASET set, LPCWSTR pszExt, LPCWSTR pszSet, ASSOC_MAKE_DEFAULT_FLAGS dwFlags).

hfiref0x commented 3 years ago

I've pushed preliminary version to dev branch. In my tests it work on Win7/Win8.1, Win10 17763 but does not work on 19042. This code uses shell32!UserAssocSet routine so it either not working properly or called in a wrong way on 19042. Need more investigation. If in the end of trials it won't work from shell32 I will switch to SystemSettings.Handlers.dll and use it instead. Also what is the preferred way to unregister this shell protocol? Is it enough to remove registry key from HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Shell\Associations\UrlAssociations?

hfiref0x commented 3 years ago

As I expected there is no difference between shell32 and SystemSettings.Handlers versions of UserAssocSet usage. Looks like example of typical MS bloatware dev. This one commit should work on 19041/42. I've not yet tested it with 18362/63 as I currently don't have required image, will try tomorrow.

AzAgarampur commented 3 years ago

Also what is the preferred way to unregister this shell protocol? Is it enough to remove registry key from HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Shell\Associations\UrlAssociations?

Well we first have to delete the ProgId class entry @ HKCU\SOFTWARE\Classes\PROG_ID. If we do this, the next time http:// is invoked, OpenWith.exe asks which app should be used.

Or we can back up the old value ProgId at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice, and then call UserAssocSet with the saved ProgId to restore it to how it was before the attack. If we do this we still need to delete old ProgId class entry at HKCU\SOFTWARE\Classes\PROG_ID

hfiref0x commented 3 years ago

What about calling UserAssocSet with UASET_CLEAR (0) as first param? If I not mistaken it calls _DeleteUserChoice which should do removal.

AzAgarampur commented 3 years ago

Hmm, if it works that's great -- I have not tried it yet. Will it revert to factory defaults or will it save users preferred choice? For example, ProgId could be ChromeHTML if user has set Google Chrome as default app to open https. Will using UserAssocSet with UASET_CLEAR (0) put it back to ChromeHTML or will it revert it back to system default MSHTML, (or whatever)?

Also did you get the UASET enum from the XP source or from symbol files? Just wondering.

hfiref0x commented 3 years ago

Probably it will just delete it. I didn't tried yet. This enum is from WXP leak.

hfiref0x commented 3 years ago

Oh, it seems more complicated than I thought.

I think in 18362, 18363 shell32!UserAssocSet has 4 parameters (just like in SystemSettings.Handlers.dll). In 19041, 19042 shell32!UserAssocSet has 3 parameters again.

However I don't have Win10 1904 or 1909 installed to test this.

Edit:

I've checked UserAssocSet(UASET_CLEAR). It call results in deletion of UserChoice subkey for given protocol name. I'm okay with that. Tomorrow I will install 1909 version and method here.

AzAgarampur commented 3 years ago

I've checked UserAssocSet(UASET_CLEAR). It call results in deletion of UserChoice subkey for given protocol name. I'm okay with that. Tomorrow I will install 1909 version and method here.

👍

I think in 18362, 18363 shell32!UserAssocSet has 4 parameters (just like in SystemSettings.Handlers.dll). In 19041, 19042 shell32!UserAssocSet has 3 parameters again.

Ugh, either lazy or cheap Windows devs keep screwing everything up... I'll wait to see the final results you yield before I change my code so we don't get too confused.

hfiref0x commented 3 years ago

UserAssocSet under 18362/18363 require fourth parameter to be set to 2.

Ugh, either lazy or cheap Windows devs keep screwing everything up.

I would rather say each w10 release is somewhat unfinished so they are always moving thing back and forth later.

hfiref0x commented 3 years ago

As you can see below it is all very different for each Windows release, I could not find any stable pattern that can work on anything except current build. Maybe there is a documented way to register this type of shell extension?

Normally, I would load symbols for shell32 and extract required function UserAssocSet address through it symbol via DebugAPI, but UACMe cannot use that as this require either embedding debug components (symsrv/dbghelp) or implementing pdb download and it parsing from scratch.

Library: shell32.dll

20241 Symbol: SetAssociationFromAssociationsXml

Pattern: 4C 8B C6 49 8B D6 8B C8 (call e8)

Code:

.text:000000018040C7F6 4C 8B C6                 mov     r8, rsi
.text:000000018040C7F9 49 8B D6                 mov     rdx, r14
.text:000000018040C7FC 8B C8                    mov     ecx, eax
.text:000000018040C7FE E8 35 28 14 00           call    UserAssocSet

Number of params: 4 (last should be set to 2)

19042 Symbol: SetAssocationfromAssociationsXml

Pattern: 4C 8B C7 48 8B D6 41 8B C9 (call e8)

Code:

.text:00000001800A417E 4C 8B C7                 mov     r8, rdi
.text:00000001800A4181 48 8B D6                 mov     rdx, rsi
.text:00000001800A4184 41 8B C9                 mov     ecx, r9d
.text:00000001800A4187 E8 18 01 00 00           call    UserAssocSet

Number of params: 3

19041 Symbol: SetAssocationfromAssociationsXml

Pattern: 44 8B C9 4C 8B C6 49 8B D7 (call e8)

Code:

.text:00000001800A3033 44 8B C9                 mov     r9d, ecx
.text:00000001800A3036 4C 8B C6                 mov     r8, rsi
.text:00000001800A3039 49 8B D7                 mov     rdx, r15
.text:00000001800A303C E8 0B FF FF FF           call    UserAssocSet

Number of params: 3

18363 Symbol: SetAssocationfromAssociationsXml

Pattern: 4C 8B C6 44 8B C9 49 8B D7 (call e8)

Code:

.text:0000000180081AE3 4C 8B C6                 mov     r8, rsi
.text:0000000180081AE6 44 8B C9                 mov     r9d, ecx
.text:0000000180081AE9 49 8B D7                 mov     rdx, r15  
.text:0000000180081AEC E8 13 FE FF FF           call    UserAssocSet

Number of params: 4 (last should be set to 2)

18362 Symbol: SetAssocationfromAssociationsXml

Pattern: 44 8B C9 4C 8B C6 49 8B D7 (call e8)

Code:

.text:000000018005ED4F 44 8B C9                  mov     r9d, ecx
.text:000000018005ED52 4C 8B C6                  mov     r8, rsi
.text:000000018005ED55 49 8B D7                  mov     rdx, r15
.text:000000018005ED58 E8 B7 00 00 00            call    UserAssocSet

Number of params: 4 (last should be set to 2)

17763 Symbol: SetAssocationfromAssociationsXml

Pattern: 4C 8B C6 44 8B C9 49 8B D7 (call e8)

Code:

.text:000000018002D8DB 4C 8B C6                  mov     r8, rsi
.text:000000018002D8DE 44 8B C9                  mov     r9d, ecx
.text:000000018002D8E1 49 8B D7                  mov     rdx, r15
.text:000000018002D8E4 E8 C3 35 00 00            call    UserAssocSet

Number of params: 4

14393 Symbol: _SetAssociation

Pattern: 48 6B C0 4E B9 02 00 00 00 4C 03 C0 (call e8)

Code:

.text:00000001802FC537 48 6B C0 4E                imul    rax, 4Eh
.text:00000001802FC53B B9 02 00 00 00             mov     ecx, 2
.text:00000001802FC540 4C 03 C0                   add     r8, rax
.text:00000001802FC543 E8 D4 8D FF FF             call    UserAssocSet

Number of params: 3

9600 Symbol: _SetAssociation

Pattern: 4C 8B C6 48 8B D3 B9 02 00 00 00 (call e8)

Code:

.text:000000018059B5AA 4C 8B C6                    mov     r8, rsi
.text:000000018059B5AD 48 8B D3                    mov     rdx, rbx
.text:000000018059B5B0 B9 02 00 00 00              mov     ecx, 2
.text:000000018059B5B5 E8 A6 9C D1 FF              call    UserAssocSet

Number of params: 3

7601 Symbol: CAssocHandler::MakeDefault

Pattern: 4C 8B 43 40 48 8B 53 38 B9 01 00 00 00 (call e8)

Code:

.text:000007FF78B24703 4C 8B 43 40                 mov     r8, [rbx+40h]
.text:000007FF78B24707 48 8B 53 38                 mov     rdx, [rbx+38h]
.text:000007FF78B2470B B9 01 00 00 00              mov     ecx, 1
.text:000007FF78B24710 E8 53 F4 FF FF              call    UserAssocSet

Number of params: 3

AzAgarampur commented 3 years ago

I would rather say each w10 release is somewhat unfinished so they are always moving thing back and forth later.

Valid point.

require fourth parameter to be set to 2.

Looks like the enum values keep changing.

AzAgarampur commented 3 years ago

Well, we can eliminate Windows 7 code because we can set association manually through the registry. Windows 8 seems the same so that's fine.

Maybe there is a documented way to register this type of shell extension?

I'm not aware of any such thing, as Microsoft designed it this way specifically because they wanted the only way for the associations to be changed is via the settings/control panel interface.

I'll be looking for some more ideas.

hfiref0x commented 3 years ago

Btw what about this? https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-shcreateassociationregistration

There is some Chinese blogpost about UserAssocSet https://www.cnblogs.com/h2zZhou/p/6654424.html

This one use some internal interface

HRESULT SetAppAsDefault(const std::wstring & app_name) {

    IApplicationAssociationRegistration * pAAR;

    HRESULT hr = CoCreateInstance(

        CLSID_ApplicationAssociationRegistration,

        NULL,

        CLSCTX_INPROC,

        __uuidof(IApplicationAssociationRegistration),

        (void ** ) & pAAR);

    if (SUCCEEDED(hr)) {

        IApplicationAssociationRegistrationInternal * Paari = NULL;

        HRESULT hr = pAAR - > QueryInterface(

            __uuidof(IApplicationAssociationRegistrationInternal),

            (void ** ) & pAARI);

        if (SUCCEEDED(hr)) {

            hr = pAARI - > SetAppAsDefault(

                L "Goole Chrome",

                L "http",

                AT_URLPROTOCOL);

            hr = pAARI - > SetAppAsDefault(

                L "Goole Chrome",

                L "https",

                AT_URLPROTOCOL);

            pAARI - > Release();

        }

    }

    return hr;

}

It seems this internal interface unavailable in recent Win10 versions released since 2018.

AzAgarampur commented 3 years ago

Hmm I messed around with the interface and here's what I found:

It only succeeds if the program trying to be set as default is Microsoft Edge. Otherwise, it returns E_FAIL. This is because the binary of the calling setup is not Microsoft signed. So I created a DLL to use with rundll32.exe. Now it does not return E_FAIL, but it simply returns E_ACCESSDENIED. This is because the calling process is trying to set an association that is not Microsoft edge.

So basically if you aren't trying to set either an AppX or Microsoft Edge association, it will not let you do so. The documentation even says:

Note As of Windows 8, the only functionality of this interface that is supported is QueryCurrentDefault.

Which is a lie, because we know it will also work if we are trying to set default as Microsoft Edge.

AzAgarampur commented 3 years ago

On the other hand, instead of trying to use the correct UserAssocSet, SetUserAssoc, whatever, how about we go one level deeper and use _SetUserChoiceAndHash? I presume there are only minor differences between Windows versions.

Working versions:

Works on Windows 10 19041 & 19042.

Signature: { 0x48, 0x8B, 0xC4, 0x55, 0x56, 0x57, 0x48, 0x8B, 0xEC, 0x48, 0x81, 0xEC, 0x80, 0x00, 0x00, 0x00, 0x48, 0xC7, 0x45, 0xC0, 0xFE, 0xFF, 0xFF, 0xFF, 0x48, 0x89, 0x58, 0x18, 0x48, 0x8B, 0xF2, 0x48, 0x8B, 0xD9 };

Prototype: typdef HRESULT (__fastcall* _SetUserChoiceAndHash)(PWSTR fileName, PWSTR progId);

Works on Windows 10 18363.

Signature: { 0x48, 0x89, 0x5C, 0x24, 0x08, 0x55, 0x56, 0x57, 0x48, 0x8B, 0xEC, 0x48, 0x83, 0xEC, 0x70, 0x48, 0x83, 0x65, 0xD0, 0x00 };

Prototype: typdef HRESULT (__fastcall* _SetUserChoiceAndHash)(PWSTR fileName, PWSTR progId);

Works on Windows 10 17134

Signature: { 0x48, 0x89, 0x5C, 0x24, 0x18, 0x55, 0x56, 0x57, 0x48, 0x8B, 0xEC, 0x48, 0x81, 0xEC, 0x80, 0x00, 0x00, 0x00 };

Prototype: typdef HRESULT (__fastcall* _SetUserChoiceAndHash)(PWSTR fileName, PWSTR progId);

Works on Windows 10 16299

Signature: { 0x48, 0x89, 0x5C, 0x24, 0x18, 0x55, 0x56, 0x57, 0x48, 0x8B, 0xEC, 0x48, 0x81, 0xEC, 0x80, 0x00, 0x00, 0x00 };

Prototype typdef HRESULT (__fastcall* _SetUserChoiceAndHash)(PWSTR fileName, PWSTR progId);

Windows 10 15063 -> I can't get symbol files working for some reason but I'm pretty sure it will work and has the same signature as the rest of the Windows 10 16299 and higher versions.

Works on Windows 10 14393

Signature: { 0x48, 0x89, 0x5C, 0x24, 0x18, 0x55, 0x56, 0x57, 0x48, 0x83, 0xEC, 0x40 };

Prototype: typdef HRESULT (__fastcall* _SetUserChoiceAndHash)(PWSTR fileName, PWSTR progId, BOOLEAN unknown0);

Works on Windows 8.1.

Signature: { 0x48, 0x89, 0x5C, 0x24, 0x18, 0x55, 0x56, 0x57, 0x48, 0x83, 0xEC, 0x40, 0x48, 0x8B, 0x05, 0x75, 0xCA, 0x75, 0x00, 0x48, 0x33, 0xC4 };

Prototype: typdef HRESULT (__fastcall* _SetUserChoiceAndHash)(PWSTR fileName, PWSTR progId, BOOLEAN unknown0);


Looks like W8.1 and Windows 10 14393 are almost or are the same signature. They both have the same prototype too. Every other Windows 10 version has the same prototype but with a few different signatures. I have not tested 17763 and 18362 because I don't have image files for those yet. I have not tested 10586 and 10240 because I don't think we need to.

hfiref0x commented 3 years ago

I also looked on this possibility, but it is not really better than calling UserAssocSet - as you see function body changes same multiple times as well as number of it parameters.

The last thing I tried - lookup UserAssocSet from the interface above.

  1. Query ApplicationAssociationRegistrationInternal interface.

Note that it has different IID's 0x1C5c9d10, 0x1225, 0x4C97, 0x8c, 0x51, 0x98, 0xE1, 0xB6, 0xF0, 0xD4, 0xE0 like in article above 0x2a848e25, 0xd688, 0x4aa3, 0x8e, 0x55, 0x0c, 0x16, 0xcb, 0x3a, 0x2d, 0xfb and new one I see from 1809

hr = CoCreateInstance(&CLSID_ApplicationAssociationRegistration,
        NULL,
        CLSCTX_INPROC,
        &IID_IApplicationAssociationRegistration,
        &arObject);

    if (SUCCEEDED(hr)) {        

        hr = arObject->lpVtbl->QueryInterface(arObject,
            &IID_IApplicationAssociationRegistrationInternal,
            &arObjectInternal);

        if (SUCCEEDED(hr)) {
  1. Take arObjectInternal->lpVtbl->SetProgIdAsDefault function as starting point, disassembly it and locate _SetAssociation call.
  2. Follow this call and find UserAssocSet inside.

However it turns out that _SetAssociation subroutine has been completely redesigned in 1903 and it doesn't have any useful code now.

This entire shell association thing have changed multiple times like if they are cannot come into conclusion what they actually want from it. I will probably leave UserAssocSet as is, it will require updates however for future Windows versions, of course if they won't break it again completely by some redesign.

AzAgarampur commented 3 years ago

I want to keep a relatively clean code base and not have to remember tons of different signatures and combinations; have you seen the internal lambda function stay relatively the same all throughout Windows versions?

SetUserChoiceAndHash__lambda_f5dab507c31cbe43228a3f2244e78e21_ (this is from 19041)?

AzAgarampur commented 3 years ago

Actually you know what let's just use the signatures you came up with for shell32.dll!UserAssocSet, it's the best choice we have.

If only Windows shell team could decide to choose something then stick to it >:(

hfiref0x commented 3 years ago

I will release this on next Monday.

AzAgarampur commented 3 years ago

If for example, the shell32 code changes again in the future, and this attack is not fixed yet, should I open a new issue here with the new details about how to update the attack or should I just leave it?

hfiref0x commented 3 years ago

Well, lets see. If this is just an update shaking some bytes in patterns required to find UserAssocSet well I usually update such stuff automatically when I test on new Windows10 release. So there is no need in ticket. However if method will be broken in part of it implementation and there still be a way to exploit things like before - feel free to open new issue.

hfiref0x commented 3 years ago

Btw, are you planning to write any article about this particular method? If no, then I can do a small blogpost about it together with 3.5.2 release (then it will be a little delayed on a couple of days).

AzAgarampur commented 3 years ago

No, so far I haven't written any "articles" about what I have found. All my findings (byeintegrity, byeintegrity2, byeintegrity3) have detailed explanations on their GitHub pages in README.md.

I don't mind waiting a few more days for the release; I think an article written by you will be cool and it would probably be of higher quality than in comparison of an article written by me.

hfiref0x commented 3 years ago

Done, + https://swapcontext.blogspot.com/2020/11/uac-bypasses-from-comautoapprovallist.html