4

I have started exploring COM objects, initially I got to it from UAC bypass methods but I guess my question is general. The general question is: How can I get from (elevated) CLSID in the registry to calling the functions in the COM interface?. Explanation of the research I performed and my specific questions next. I will use IFileOperation COM (CLSID = 3AD05575-8857-4850-9277-11B85BDB8E09) and ICMLuaUtil COM (CLSID = 3E5FC7F9-9A51-4367-9063-A120244FBEC7) as an example in this post.

COM objects are listed in the registry under HKEY_CLASSES_ROOT\CLSID. The default value has the COM name and the server DLL file.

Now, in order to list the interfaces associated with it I used oleview, for IFileOperation it showed all the interfaces but for ICMLuaUtil it showed only the .

Even after I get the interface name, I have to get the function list and signatures, this can be extracted from the idl file. A great answer presented here shows how to get from an interface name to its function signatures by examining the example code and searching for idl file with the same name as the imports in the sdk folder. I have tried applying similar approach with the two interfaces with partial success.

  1. For IFileOperation, the example source has 4 includes neither of which has exact same idl file in the sdk folder. So i search the content of all the files in the folder for the word IFileOperation which resulted in getting the desired file: shobjidl_core.
  2. Unfortunately I couldn't do the same thing with ICMLuaUtil as I don't know the interface name, I tried several name variations but got nothing.

Other methods I tried are:

  1. Extracting all files from the DLLs I found in the registry (nothing there)
  2. COMViewer should present additional information but it didn't work on my Windows 10 machine (didn't start), even with compatibility mode.

My question is how do I get from the (sometimes undocumented) COM objects to concrete interface definition so I could use it in my code? The UACMe project has a concrete definition of the interface for ICMLuaUtil, so there must be a method to obtain it. What are the guidelines and steps to obtain those function signatures?

4
  • Hi and welcome to RE.SE! I'm confused. One of the sentences trails off just like . And furthermore why do you think you need an interface name? IIDs/CLSIDs/GUIDs is all that COM will ever care about. It all starts with IUnknown and both the documentation (MSDN) as well as several books explain in great detail how to instantiate COM objects. However, those are all non-RCE topics. Whether there is an interface definition (IDL or otherwise) solely depends on the author. After all COM is meant to allow black-box objects to interact with each other. Only caller and callee need to know.
    – 0xC0000022L
    Commented Nov 22, 2018 at 20:50
  • @0xC0000022L thanks for taking the time to understand the question. I do realize that functions which handle COMs (CoCreateInstance, CoGetObject..) require IIDs/CLSIDs only. Let me narrow the question, for ICMLuaUtil, where I got no interface name/idl file, how can I get the IID and how to figure out the method signatures for it? it was done before, and I wish to learn how to reproduce the results (github.com/hfiref0x/UACME/blob/…)
    – Anton.P
    Commented Nov 23, 2018 at 7:43
  • @Anton.P Do you have the basic knowledge of C++ class, constructor, destructor, inheritance? I want to add some info.
    – Biswapriyo
    Commented Nov 25, 2018 at 13:44
  • @Biswapriyo yes I am familiar with C++ (know it well and familiar with the concepts you listed), and I would be glad to get more info
    – Anton.P
    Commented Nov 25, 2018 at 13:46

3 Answers 3

5

Now, this can't be a full-fledged answer from A to Z, unfortunately. But for COM there are only a few steps that are really specific to COM.

The project you linked clearly has reverse-engineered the interfaces (elvint.h). What this means first and foremost is that the names of the interfaces needn't necessarily match what Microsoft called them (in your case they match, though). However, if the binaries implementing the respective COM interfaces or their proxy DLL are official system files, PDB files can be fetched for them and even though Microsoft has severely cut down on public symbols, this often allows you to assign more meaningful names to functions/methods.

Of course the author(s) of the linked project already knew what they were looking for and it seems to be system components that somehow "magically" are able to circumvent the typical UAC elevation prompt even though one should expect them to show one.

Now say they found that the Connection Manager configuration (related to RAS) contained such functionality, they'd now go ahead and perform an action that you'd expect to require elevation but which doesn't. Then they'd look up the DLLs loaded into the process and either find an InProc server or a proxy leading them to an OutProc server.

Failing the lookup step they could simply survey all the system DLLs, looking for the typical COM bunch of exported functions (the first being the most obvious):

  • DllGetClassObject
  • DllCanUnloadNow
  • DllAddRef
  • DllRelease

... and then inspect the version info resource. With the pefile module this becomes a simple Python scripting job. It's basically that first step you always have with RCE: intelligence gathering. It's not strictly RCE (as in sifting through or manipulating disassembly) but it's an intrinsic part of all the RCE jobs I've done so far.

But in your case you already used oleview.exe and therefore knew the most important information:

Information shown in cmlua.dll for ICMLuaUtil

... which is why I found your comment confusing:

Unfortunately I couldn't do the same thing with ICMLuaUtil as I don't know the interface name, I tried several name variations but got nothing.

Now having found cmlua.dll to be the DLL to implement our (COM) object of interest, we can go ahead and load that into IDA (or another disassembler) and we will get prompted to load the debug symbol from Microsoft's symbol servers.

And once the symbols are loaded we get to see this:

excerpt of IDA's understanding of the cmlua.dll functions with debug symbols applied

Looks familiar?

To get the IDL you need to match the vtable of the CCMLuaUtil class to the interface pointer its DLL returns when asked for that specific class. Looking into DllGetClassObject will enable you to figure out the IID.

Now from here on this is going to be the same old grunt job of making sense of the disassembly (or the pseudo code if you can afford the decompiler plugin).


Now if we get lucky we can save a whole lot of time if the COM server (typically a DLL) in question contains a TYPELIB resource, which is the compiled version of an IDL and therefore the best starting point to get to an IDL quickly. For cmlua.dll we're out of luck, however.

Furthermore if you had no symbols like with Windows components, you would have to dig yourself into the disassembly starting from DllGetClassObject which is well-known and well-documented and should be trivial to understand even for the layperson. Going from there and equipped with the knowledge that the three first methods of any COM interface are inherited from IUnknown you'd then try to make sense of the implementation of each method (apart from those well-known first three) and assign hopefully meaningful names to the functions and their parameters. That is: the whole task becomes a bit more tedious without debug symbols, but not at all impossible.

1
  • 1
    I can't even imagine a better answer than this one. Thank you so much. I tried loading the DLL with IDA and indeed after obtaining the symbols I see the signatures. There is a lot of information I need to parse, try out, and apply on my research. Thanks!!
    – Anton.P
    Commented Nov 23, 2018 at 11:55
3

well the answers out there are good this is just an addition to show how you could arrive using windbg in command line

dbh is an utility in windbg installation that can load any binary and provide a lot of static information using it and the command line version of windbg cdb.exe you can get the methods in two commands (notice the method names are demangled)

C:\>dbh c:\Windows\System32\cmlua.dll  "x CCM*" | grep -i vf
    11            1002d58 :   CCMLuaUtil::`vftable'

C:\>cdb -c "dps cmlua.dll+2d58" -z c:\Windows\System32\cmlua.dll

Microsoft (R) Windows Debugger Version 10.0.16299.15 X86
Loading Dump File [c:\Windows\System32\cmlua.dll]


cmlua!_DllMainCRTStartup:
100061e7 8bff            mov     edi,edi

0:000> cdb: Reading initial command 'dps cmlua.dll+2d58'

10002d58  100042ad cmlua!CCMLuaUtil::QueryInterface
10002d5c  10004e82 cmlua!CCMLuaUtil::AddRef
10002d60  10004279 cmlua!CCMLuaUtil::Release
10002d64  10004346 cmlua!CCMLuaUtil::SetRasCredentials
10002d68  10004401 cmlua!CCMLuaUtil::SetRasEntryProperties
10002d6c  100044dd cmlua!CCMLuaUtil::DeleteRasEntry
10002d70  10004573 cmlua!CCMLuaUtil::LaunchInfSection
10002d74  100045e1 cmlua!CCMLuaUtil::LaunchInfSectionEx
10002d78  10004630 cmlua!CCMLuaUtil::CreateLayerDirectory
10002d7c  1000466e cmlua!CCMLuaUtil::ShellExec
10002d80  10004690 cmlua!CCMLuaUtil::SetRegistryStringValue
10002d84  10004701 cmlua!CCMLuaUtil::DeleteRegistryStringValue
10002d88  100055da cmlua!CCMLuaUtil::DeleteRegKeysWithoutSubKeys
10002d8c  10004767 cmlua!CCMLuaUtil::DeleteRegTree
10002d90  100048cc cmlua!CCMLuaUtil::ExitWindowsFunc
10002d94  10005c72 cmlua!CCMLuaUtil::AllowAccessToTheWorld
10002d98  100048d9 cmlua!CCMLuaUtil::CreateFileAndClose
10002d9c  1000560f cmlua!CCMLuaUtil::DeleteHiddenCmProfileFiles
10002da0  1000492a cmlua!CCMLuaUtil::CallCustomActionDll
10002da4  10004b6c cmlua!CCMLuaUtil::RunCustomActionExe
10002da8  10004c2c cmlua!CCMLuaUtil::SetRasSubEntryProperties
10002dac  10004d0e cmlua!CCMLuaUtil::DeleteRasSubEntry
10002db0  10004da7 cmlua!CCMLuaUtil::SetCustomAuthData
10002db4  10005cdb cmlua!CCMLuaUtil::`vector deleting destructor'
10002db8  00000000
10002dbc  10009138 cmlua!hProxyDll+0x10
10002dc0  10009188 cmlua!hProxyDll+0x60
10002dc4  00000000
10002dc8  69727453
10002dcc  6343676e
10002dd0  706f4368
10002dd4  20784579
0:000>

now dbh has a switch -d which would output mangled names and you can leverage that switch to print the parameters to the Methods

C:\>echo off

for /F %i in ('dbh -d c:\Windows\System32\cmlua.dll  "x *CCM*"  ^| awk "{print $4}"') do  dbh c:\windows\system32\cmlua.
dll undec %i

?Release@CCMLuaUtil@@UAGKXZ =
public: virtual unsigned long __stdcall CCMLuaUtil::Release(void)

??_ECCMLuaUtil@@UAEPAXI@Z =
public: virtual void * __thiscall CCMLuaUtil::`vector deleting destructor'(unsigned int)

??0CCMLuaUtil@@QAE@XZ =
public: __thiscall CCMLuaUtil::CCMLuaUtil(void)

?AddRef@CCMLuaUtil@@UAGKXZ =
public: virtual unsigned long __stdcall CCMLuaUtil::AddRef(void)

?CreateFileAndClose@CCMLuaUtil@@UAGJPBGKKKK@Z =
public: virtual long __stdcall CCMLuaUtil::CreateFileAndClose(unsigned short const *,unsigned long,unsigned long,unsigne
d long,unsigned long)

?DeleteHiddenCmProfileFiles@CCMLuaUtil@@UAGJPBG@Z =
public: virtual long __stdcall CCMLuaUtil::DeleteHiddenCmProfileFiles(unsigned short const *)

??_GCCMLuaUtil@@UAEPAXI@Z =
public: virtual void * __thiscall CCMLuaUtil::`scalar deleting destructor'(unsigned int)

?SetRasSubEntryProperties@CCMLuaUtil@@UAGJPBG0KPAPAGK@Z =
public: virtual long __stdcall CCMLuaUtil::SetRasSubEntryProperties(unsigned short const *,unsigned short const *,unsign
ed long,unsigned short * *,unsigned long)

?QueryInterface@CCMLuaUtil@@UAGJABU_GUID@@PAPAX@Z =
public: virtual long __stdcall CCMLuaUtil::QueryInterface(struct _GUID const &,void * *)

?CCMLuaUtil_CreateInstance@@YGJABU_GUID@@PAPAX@Z =
long __stdcall CCMLuaUtil_CreateInstance(struct _GUID const &,void * *)

?ExitWindowsFunc@CCMLuaUtil@@UAGJXZ =
public: virtual long __stdcall CCMLuaUtil::ExitWindowsFunc(void)

?CreateLayerDirectory@CCMLuaUtil@@UAGJPBG@Z =
public: virtual long __stdcall CCMLuaUtil::CreateLayerDirectory(unsigned short const *)

?LaunchInfSectionEx@CCMLuaUtil@@UAGJPBG0K@Z =
public: virtual long __stdcall CCMLuaUtil::LaunchInfSectionEx(unsigned short const *,unsigned short const *,unsigned lon
g)

?ShellExec@CCMLuaUtil@@UAGJPBG00KK@Z =
public: virtual long __stdcall CCMLuaUtil::ShellExec(unsigned short const *,unsigned short const *,unsigned short const
*,unsigned long,unsigned long)

?DeleteRasEntry@CCMLuaUtil@@UAGJPBG0@Z =
public: virtual long __stdcall CCMLuaUtil::DeleteRasEntry(unsigned short const *,unsigned short const *)

?DeleteRegistryStringValue@CCMLuaUtil@@UAGJHPBG0@Z =
public: virtual long __stdcall CCMLuaUtil::DeleteRegistryStringValue(int,unsigned short const *,unsigned short const *)

??_7CCMLuaUtil@@6B@ =
const CCMLuaUtil::`vftable'

?LaunchInfSection@CCMLuaUtil@@UAGJPBG00H@Z =
public: virtual long __stdcall CCMLuaUtil::LaunchInfSection(unsigned short const *,unsigned short const *,unsigned short
 const *,int)

?SetCustomAuthData@CCMLuaUtil@@UAGJPBG00K@Z =
public: virtual long __stdcall CCMLuaUtil::SetCustomAuthData(unsigned short const *,unsigned short const *,unsigned shor
t const *,unsigned long)

?DeleteRasSubEntry@CCMLuaUtil@@UAGJPBG0K@Z =
public: virtual long __stdcall CCMLuaUtil::DeleteRasSubEntry(unsigned short const *,unsigned short const *,unsigned long
)

?DeleteRegTree@CCMLuaUtil@@UAGJHPBG@Z =
public: virtual long __stdcall CCMLuaUtil::DeleteRegTree(int,unsigned short const *)

?DeleteRegKeysWithoutSubKeys@CCMLuaUtil@@UAGJHPBGH@Z =
public: virtual long __stdcall CCMLuaUtil::DeleteRegKeysWithoutSubKeys(int,unsigned short const *,int)

?CallCustomActionDll@CCMLuaUtil@@UAGJPBG000PAK@Z =
public: virtual long __stdcall CCMLuaUtil::CallCustomActionDll(unsigned short const *,unsigned short const *,unsigned sh
ort const *,unsigned short const *,unsigned long *)

?SetRegistryStringValue@CCMLuaUtil@@UAGJHPBG00@Z =
public: virtual long __stdcall CCMLuaUtil::SetRegistryStringValue(int,unsigned short const *,unsigned short const *,unsi
gned short const *)

?SetRasCredentials@CCMLuaUtil@@UAGJPBG00H@Z =
public: virtual long __stdcall CCMLuaUtil::SetRasCredentials(unsigned short const *,unsigned short const *,unsigned shor
t const *,int)

?SetRasEntryProperties@CCMLuaUtil@@UAGJPBG0PAPAGK@Z =
public: virtual long __stdcall CCMLuaUtil::SetRasEntryProperties(unsigned short const *,unsigned short const *,unsigned
short * *,unsigned long)

?AllowAccessToTheWorld@CCMLuaUtil@@UAGJPBG@Z =
public: virtual long __stdcall CCMLuaUtil::AllowAccessToTheWorld(unsigned short const *)

?RunCustomActionExe@CCMLuaUtil@@UAGJPBG0PAPAG@Z =
public: virtual long __stdcall CCMLuaUtil::RunCustomActionExe(unsigned short const *,unsigned short const *,unsigned sho
rt * *)
2

This is just an extension of @0xC0000022L's answer. For this question, I will explain ICMLuaUtil COM interface below because IFileOperation COM interface is in shell32.dll, too big ;)

You mentioned the wrong CLSID. Here I provide a list of them:

GUID CLSID_CmstpLua = {3E5FC7F9-9A51-4367-9063-A120244FBEC7}
GUID IID_ICmstpLua = {6EF07F29-F9B8-4DA4-B59E-13DEA060AD60}
GUID IID_ICmstpLua2 = {AE8AFD54-5B57-4961-8A9B-12ADF23B696A}

GUID CLSID_CMLuaUtil = {3E000D72-A845-4CD9-BD83-80C07C3B881F}
GUID IID_ICMLuaUtil = {6EDD6D74-C007-4E75-B76A-E5740995E24C}

How to find the DLL file path from CLSID without using any 3rd party program? Open this registry path HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{CLSID} in RegEdit. For this case, try this command:

REG Query "HKLM\SOFTWARE\Classes\CLSID\{3E000D72-A845-4CD9-BD83-80C07C3B881F}\InprocServer32" /VE
REG Query "HKLM\SOFTWARE\Classes\Interface\{6EDD6D74-C007-4E75-B76A-E5740995E24C}" /VE

The path will be %SystemRoot%\System32\cmlua.dll. Open it in IDA (or any disassembler/decompiler) and load the PDB symbol file. Go to the functions window with Shift + F3 and search for the constructor or the destructor by typing those words in that window. Open that function in assembly view. There must be a variable like const CCMLuaUtil::vftable because you-know-why. Double click on it and you will see the layout of that COM class in assembly view.

CMLuaUtil_interface

There are many advantages of the assembly view. In the assembly view, all the methods in that class are placed in actual order but the functions windows may not (according to settings). The methods are actually placed as function pointers. For 64 bit binary there will be dq (qword 8 bytes) before every methods (in the above screenshot). The first three methods are inherited from IUnknown interface. If you are writing that class (as struct) in C, you've to include those three.

See another canonical answer for further reference. One can see a real example of this method in my repository GitHub: WslReverse where I showed the hidden COM interface of LxssManager.DLL. Also this video about Practical C++ Decompilation | Recon 2011 | Igor Skochinsky helped me a lot.

3
  • Thx for the extra information, I will of course go over it also, but I have a preliminary question, how do you know which interface relates to which CLSID? in oleview, CMLuaUtil doesn't have any interfaces associated with it
    – Anton.P
    Commented Nov 26, 2018 at 12:04
  • my question is how do you understand that IID_ICMLuaUtil is related to CLSID_CMLuaUtil? (other than the name of course which is not mendatory, e.g. in the IFileOperation COM some of the supported interfaces doesnt share its name). The commands do reveal the DLLs but I fail to understand the general approach of fining the link between them
    – Anton.P
    Commented Nov 26, 2018 at 18:20
  • @Anton.P This would be the oversimplificated answer: i.sstatic.net/ZpIRF.png
    – Biswapriyo
    Commented Nov 26, 2018 at 18:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.