Closed AzAgarampur closed 3 years ago
Hello,
sure.
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
Thanks. Does this function has a symbol name? I can't find it using pattern bytes in 17763.
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()
.
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.
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.
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.
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.
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?
No, they have different pattern. Yours works good with 19041/42.
Ok, thanks for clarifying. I will update my code to match shell32.dll
on those versions.
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.
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
?
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.
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)
.
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
?
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.
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
What about calling UserAssocSet
with UASET_CLEAR (0) as first param? If I not mistaken it calls _DeleteUserChoice
which should do removal.
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.
Probably it will just delete it. I didn't tried yet. This enum is from WXP leak.
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.
I've checked
UserAssocSet(UASET_CLEAR)
. It call results in deletion ofUserChoice
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 inSystemSettings.Handlers.dll
). In 19041, 19042shell32!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.
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.
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
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.
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.
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.
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.
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.
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.
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)) {
arObjectInternal->lpVtbl->SetProgIdAsDefault
function as starting point, disassembly it and locate _SetAssociation
call.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.
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)?
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 >:(
I will release this on next Monday.
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?
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.
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).
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.
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.