Привет, Хабр!

Изучая отчеты по разбору вредоносного ПО, приходишь к выводу, что в последнее время одним из популярных методов для закрепления в системе является COM Hijacking (T1546.015), применяемый атакующими для закрепления зловредов в ОС Windows через Component Object Model. Яркий тому пример - использование COM Hijacking группировкой Fancy Bear, а также вредоносными программами,такими как Jumplump и одной из недавних - RomCom.

При этом, если рассмотреть подробнее использование RomCom, можно увидеть, что он перезаписывает COM-объект с GUID {C90250F3-4D7D-4991-9B69-A5C5BC1C2AE6}с соответствующим файломActXPrxy.dll, предназначенным для управления компонентами «ActiveX» Windows, на prxyms.dll. В то же время одним из процессов, который использует данный COM-объект, является explorer.exe. Это означает, что закрепление в системе гарантированно, поскольку без данного процесса не обходится работа на устройстве с операционной системой Windows.

Таким образом, умение детектировать данный вид атаки является важным аспектом для Blue Team. Поэтому я решила подготовить серию статей, посвященных технологии Component Object Model (COM), а также разбору атаки COM Hijacking и ее детектированию.

Сегодня я начну с основ и расскажу вам о том, что представляет собой Component Object Model. Сразу отмечу, что данная тема очень обширная, и разобрать ее подробно в одном материале не получится. В этой статье мною будут рассмотренны базовые понятия COM, которые в будущем помогут нам лучше понимать принцип работ атаки COM Hijacking. После чего я проанализирую, какие стратегии может ипользовать злоумышленник для выбора подходящего COM-объекта с целью последующей атаки.

Немного определений

COM (Component Object Model) - это стандарт, позволяющий разработчикам повторно использовать в своем коде независимые компоненты ПО. При этом им не нужно знать внутреннее устройство данных компонентов, заниматься их совместимостью на разных языках программирования и повторной компиляцией.

Также в начале статьи я уже упомянула такое понятие, как COM-объект. За ним кроется определение объекта, в котором указаны данные о нем и о наборе интерфейсов для обеспечения его функциональности. COM-объект реализуется в форматах DLL или EXE.

На основе стандарта COM выполняется очень много процессов в Windows, поэтому отключить его с целью защитится от атакующих не получится. Соответственно, атаки с помощью COM-объектов становятся лакомым кусочком для злоумышленников.

Программное обеспечение для поиска необходимого COM-объекта использует реестр, поэтому далее мы рассмотрим, какие ключи задействованы в данном процессе и можно ли их применять для проведения атаки (Спойлер: ДА!).

Представление COM-объектов в реестре

Реестр содержит информацию обо всех установленных в системе COM-объектах, которая находится в следующих ветках:

  • HKEY_LOCAL_MACHINE\Software\Classes (HKLM) - информация, применимая на локальном компьютере при любом активном пользователе;

  • HKEY_CURRENT_USER\Software\Classes (HKCU) - информация, применимая к текущему интерактивному пользователю;

  • HKEY_CLASSES_ROOT (HKCR) - виртуальный куст, объединяющий сведения из двух выше указанных веток. Объединение происходит по определенному правилу: включаются все разделы и ключи из HKCU\Software\Classes, а из HKLM\Software\Classes включаются разделы и ключи, отсутствующие HKCU\Software\Classes.

Так как архитектура 64-битной операционной системы поддерживает совместимость с x86 приложениями, существуют дополнительные ветки в реестре, представленные под спойлером:

COM-объекты в x64 архитектуре ОС для x86 приложений

Для поддержания 32-разрядных приложений в 64-разрядный операционных системах Windows используются аналогичная структура ключей, но вложены они в Wow6432Node. Например:

  • HKEY_CURRENT_USER\SOFTWARE\WOW6432Node\Classes;

  • HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Classes;

  • HKEY_CLASSES_ROOT\WOW6432Node.

Во всех выше перечисленных ветках есть подраздел CLSID, содержащий идентификаторы (GUID) класса. А дальше, в зависимости исходных данных атакующего, используются следующие подразделы:

  • InprocServer/InprocServer32 - путь к dll-библиотеке, который будет подгружаться в процесс, вызванный COM-объектом;

  • LocalServer/LocalServer32 - путь к исполняемому файлу (.exe). В данном случае COM-объект запускается в новом процессе;

  • TreatAs - перенаправляет на другой COM-объект, путем указания CLSID;

  • ProgID - текстовый идентификатор, который используется аналогично CLSID. ProgID всегда сопоставляется CLSID, и на последующих шагах используется именно он;

  • VersionIndependentProgID - аналогичен ProgID, но имеет некоторые исключения. ProgID может содержать в себе версию COM-объекта и обслуживается непосредственно кодом приложения, в то время как VersionIndependentProgID указывает единый независимый от версии идентификатор. И в ответ уже сопоставляется с CLSID последней версии объекта.

Как мы видим, существует достаточно большое разнообразие ключей реестра. Здесь может возникнуть вопрос: а как система понимает, каким именно из этих ключей воспользоваться? Дальше мы как раз и поговорим о порядке проверки и применении ключей в реестре, что позволит в будущем нам оценить все возможные варианты атаки.

Порядок проверки ключей в реестре

Согласно документации Microsoft, параметры, указанные для интерактивного пользователя в HKCU\Software\Classes, имеют приоритет над HKCR (HKLM). Если процесс запускается в интерактивном сеансе с правами администратор (т.е. уровень целостности выше среднего), и при этом UAC отключен, то применяется конфигурация, указанная в HKLM. Подробнее про UAC и методы его обхода можно почитать в статье моего коллеги. Процессы, которые не выполняются в контексте безопасности интерактивного пользователя, не используют HKCU и HKCR, а напрямую обращаются к HKEY_LOCAL_MACHINE\Software\Classes.

Итак, с приоритетом веток мы разобрались. Выбор между ProgID и CLSID тоже понятен - он зависит от обращение к COM-объекту: идет ли оно через GUID или используется текстовый идентификатор. А вот приоритет внутри CLSID между параметрами InprocServer, LocalServer и TreatAs пока не известен. Поэтому давайте проверим его на практике и для этого проведем небольшой эксперимент.

Выбор приоритета внутри CLSID

Как правило, сразу три параметра не указываются, но нам ничего не мешает сделать это. Для этого я создала два dll-файла (для ключей InprocServerи TreatAs) и exe (для ключа LocalServer) с разными MessageBox. Под спойлером представлен конфигурационный файл реестра для проведения эксперимента. В нем указаны сразу все три ключа, а также применение созданных мною соответствующих исполняемых файлов.

Конфигурация реестра

Для проведения эксперимента на устройстве применена следующая конфигурация реестра:

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{00000001-0000-0000-0000-0000FEEDACDC}]
@="ComHijacking"

[HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{00000001-0000-0000-0000-0000FEEDACDC}\InprocServer32]
@="C:\\hijacking_dll_treatas.dll"
"ThreadingModel"="Apartment"

[HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{72C34DD5-D70A-438B-8A42-98424B88AFB8}]

[HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{72C34DD5-D70A-438B-8A42-98424B88AFB8}\TreatAs]
@="{00000001-0000-0000-0000-0000FEEDACDC}"

[HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{72C34DD5-D70A-438B-8A42-98424B88AFB8}\InprocServer32]
@="C:\\hijacking_dll_inprocserver.dll"
"ThreadingModel"="Apartment"

[HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{72C34DD5-D70A-438B-8A42-98424B88AFB8}\LocalServer32]
@="C:\\hijacking_exe_localserver.exe"

[HKEY_CURRENT_USER\SOFTWARE\Classes\CLSID\{72C34DD5-D70A-438B-8A42-98424B88AFB8}\ProgID]
@="Hijack.COM"

[HKEY_CURRENT_USER\SOFTWARE\Classes\Hijack.COM]
@=""

[HKEY_CURRENT_USER\SOFTWARE\Classes\Hijack.COM\CLSID]
@="{72C34DD5-D70A-438B-8A42-98424B88AFB8}"

Далее производится вызов COM-объекта по ProgID "Hijack.COM". На рисунке 1 видно, что заданных всех выше перечисленных параметров наивысший приоритет имеет TreatAs. В свою очередь, Process Monitor показывает, что было обращение только к ключу TreatAs, остальные ключи не проверялись (рисунок 2).

Рисунок 1 - Сработал триггер, указанный в TreatAs
Рисунок 1 - Сработал триггер, указанный в TreatAs
Рисунок 2 - Process Monitor показывает обращение к TreatAs
Рисунок 2 - Process Monitor показывает обращение к TreatAs

Ниже на рисунке 3 мы видим, что если из всех заданных ключей убрать TreatAs, а оставить LocalServer32 и InprocServer32, то в текущей ситуации приоритет будет иметь InproceServer32. При этом Process Monitor показывает, что сначала был запрос к TreatAs, но так как он не задан, запрос оказался безуспешным (рисунок 4). На следующем этапе было получено значение из InprocServer32, а потом из LocalServer32, но приоритет был отдан InprocServer32.

Рисунок 3 - Сработал триггер, указанный в InprocServer32
Рисунок 3 - Сработал триггер, указанный в InprocServer32
Рисунок 4 - Process Monitor показывает порядок обращений к ключам реестра
Рисунок 4 - Process Monitor показывает порядок обращений к ключам реестра

Логично, что убрав все ключи, кроме LocalServer32обращение будет произведено именно к нему (LocalServer32)(рисунок 5) . В то же время, Process Monitor показывает, что сохраняется последовательность обращений к реестру, но к TreatAs и InprocServer32 они безуспешны.

Рисунок 5 - Сработал триггер, указанный в LocalServer32
Рисунок 5 - Сработал триггер, указанный в LocalServer32
Рисунок 6 - Process Monitor показывает порядок обращений к ключам реестра
Рисунок 6 - Process Monitor показывает порядок обращений к ключам реестра

Подводя итог нашего эксперимента, сделаем вывод, что приоритет ключей в реестре определяется в следующем порядке: TreatAs > InprocServer32 > LocalServer32.

Интересно то, что приоритет этих ключей выше приоритета рассмотренных ранее веток. Это значит, что, например, наличие ключа InprocServer32 в HKCR приоритетнее, чем ключ LocalServer32 в HKCU. То есть более привилегированный ключ в менее привилегированной ветке будет иметь приоритет над менее привилегированным ключом в более привилегированной ветке реестра. Ниже показано это выглядит в схематичном виде:

Рисунок 7 - Приоритет ключей в реестре
Рисунок 7 - Приоритет ключей в реестре

Таким образом, определив приоритет выбора ключа, по которому будет исполняться COM-объект, мы поймем, что атакующий может этим воспользоваться и переопределить COM-объект, указав в более приоритетном ключе значение, ссылающееся на вредоносный объект.

Но то, какими правами нужно обладать для изменения этих ключей, разберем далее.

Права доступа на редактирование реестра

Как видно из рисунка 8, ветка HKCU может изменяться интерактивным пользователем без прав администратора.

Рисунок 8 - Права на HKEY_CURRENT_USER\Software\Classes
Рисунок 8 - Права на HKEY_CURRENT_USER\Software\Classes

А дальше мы видим, что HKLM и HKCR требует прав локального администратора или SYSTEM для изменения (рисунки 9 и 10) . Согласно документации Microsoft, если вносить изменения в HKCR, то они буду прописываться в HKEY_LOCAL_MACHINE\Software\Classes.

Рисунок 9 - Права на HKEY_LOCAL_MACHINE\Software\Classes
Рисунок 9 - Права на HKEY_LOCAL_MACHINE\Software\Classes
Рисунок 10 - Права на HKEY_CLASSES_ROOT
Рисунок 10 - Права на HKEY_CLASSES_ROOT

В Windows существует большое количество COM-объектов, а при установке дополнительного прикладного ПО, это количество только растет.

Тогда как же злоумышленнику определиться, какой COM-объект лучше всего атаковать? Для этого мы посмотрим, каким критериям должен соответствовать выбранный COM-объект, а затем разберем стратегии, которыми может пользоваться атакующий при выборе COM-объекта для атаки.

Выбор COM-объекта для атаки

Безусловно, можно создать новый COM-объект и дальше попытаться его вызвать, но чтобы сделать этого без "лишнего шума" лучше воспользоваться существующими ключами, которые могут быть уже настроены на вызов по какому-либо триггеру. Выбранный COM-объект должен соответствовать двум критериям: первый их них - часто запускаться, а второй - не ломать логику работы приложений.

Для выбора подходящего COM-объекта существует несколько стратегий, давайте их рассмотрим поподробнее.

Часто используемые COM-объекты

Первая стратегия будет основываться на использовании такого инструмента, как Process Monitor. Суть метода заключается в том, чтобы собрать события запросов к реестру с ключами InprocServer32, LocalServer32, TreatAs, ScriptletURL, ProgID или любой ключ в разделе \Classes\ (рисунок 11), а далее оценить какие COM-объекты наиболее часто используются (рисунок 12). При этом для удобства можно выгрузить в CSV файл, а далее в EXEL посчитать статистику.
На основании полученной оценки производится выбор наиболее подходящего COM-объекта для атаки COM-hijacking. О том, как используется выбранный объект дальше, я расскажу в последующих статьях.

Рисунок 11 - Пример фильтров в Process Monitor для сбора статистика по часто используемым COM-объектам
Рисунок 11 - Пример фильтров в Process Monitor для сбора статистика по часто используемым COM-объектам
Рисунок 12 - Результат вывода Process Monitor с заданными фильтрами
Рисунок 12 - Результат вывода Process Monitor с заданными фильтрами

Так с помощью данного метода можно обнаружить следующие COM-объекты:

Легитимное ПО

GUID COM-объекта

Легитимное значение по умолчанию

Вредоносное ПО

wmiprvse.exe

{CF4CC405-E2C5-4DDD-B3CE-5E7582D8C9FA}

%systemroot%\system32\wbem\wmiutils.dll

Candiru

firefox.exe

{1f486a52-3cb1-48fd-8f50-b8dc300d9f9d}

%SystemRoot%\system32\propsys.dll

Jumplump

explorer.exe

{42aedc87-2188-41fd-b9a3-0c966feabec1}

%SystemRoot%\system32\windows.storage.dll

Дроппер rtlstat.dll APT Space Pirates

Отсутствующие в HKCU COM-объекты

Вторая стратегия также основывается на использовании Process Monitor, но в данном случае выбираются не самые популярные, а отсутствующие в HKCU COM-объекты (рисунок 13). В HKCU проверка проходит раньше, чем в HKCR. Исключение составляют процессы с высоким уровнем целостности. Это сделано для того, чтобы предотвратить повышение привилегий.

Рисунок 13 - Пример фильтров в Process Monitor для получения COM-объектов, отсутствующих в HKCU
Рисунок 13 - Пример фильтров в Process Monitor для получения COM-объектов, отсутствующих в HKCU

Полученный результат можно сохранить в CSV формате, а далее отфильтровать уникальные значения в EXEL. Или воспользоваться автоматизацией, "скормив" данный CSV-файл acCOMplice. Этот набор скриптов предназначен для изучения атаки COM-hijacking: с его помощью можно определить подходящий COM-объект для использования в атаке, а затем провести на выбранный объект атаку COM-hijacking. Так, например, функция Extract-HijackableKeysFromProcmonCSV выводит уникальные COM-объекты из CSV-файла (рисунок 14).

Рисунок 14 - Результат работы скрипта по поиску уникальных COM-объектов в csv файле
Рисунок 14 - Результат работы скрипта по поиску уникальных COM-объектов в csv файле

Использование обнаруженных COM-объектов вредоносным ПО представлено в таблице:

Легитимное ПО

GUID COM-объекта

Легитимное значение по умолчанию

Вредоносное ПО

explorer.exe

{00020424-0000-0000-c000-000000000046}

C:\Windows\System32\oleaut32.dll

Saburex.A

Фантомные COM-объекты

Третья стратегия базируется на выявлении COM-объектов, ссылающихся на несуществующие dll или exe файлы. Пример получения списка COM-объектов с отсутствующими исполняемыми файлами показан на рисунке ниже. Таким образом можно будет положить файл по требуемому пути с нужным именем и не изменять реестр. Подобные COM-объекты называются Phantom COM.

Рисунок 15 - Пример фильтра для получения списка COM-объектов, у которых отсутствует на диске исполняемый файл
Рисунок 15 - Пример фильтра для получения списка COM-объектов, у которых отсутствует на диске исполняемый файл

Такого же результата возможно добиться и с помощью утилиты acCOMplice, что наглядно отображено на рисунке 16. В функции Find-MissingLibraries осуществляется получение значений ключей реестра inprocserver и localserver в ветке HKCR\CLSID. Далее происходит проверка, на предмет существования на диске файла, полученного на предыдущем этапе из реестра. Дополнительно можно проверить права пользователя на данный файл. Если он не найден, то данный COM-объект является потенциальным для использования в атаке.

Рисунок 16 - Получение списка COM-объектов, у которых отсутствует исполняемый файл на диске, с помощью acCOMplice
Рисунок 16 - Получение списка COM-объектов, у которых отсутствует исполняемый файл на диске, с помощью acCOMplice

Если в представленной выше утилите для получения списка COM-объектов использовались запросы к реестру, то аналогичную информацию можно узнать с помощью wmi, выполнив командуgwmi Win32_COMSetting.

Использование обнаруженных COM-объектов вредоносным ПО представлено в таблице:

Легитимное ПО

GUID COM-объекта

Легитимное значение по умолчанию

Вредоносное ПО

microsoftedgeupdate.exe

<>

%ProgramFiles(x86)%\microsoft\edgeupdate\1.3.*.*\psmachine_64.dll

Trojan.Siggen17.58258

COM-объекты в запланированных задачах

Четвертый способ строится на анализе запланированных задач (ScheduledTask). Данный метод был автоматизирован с помощью powershell скрипта Get-ScheduledTaskComHandler. Он осуществляет проверку всех ScheduledTask, где в качестве обработчика назначен Custom handler (т.е. использование COM). Для этого скрипт получает описание запланированных задач в xml формате, которые располагаются по пути: C:\Windows\System32\Tasks. Далее выбираются только те задачи, у которых в качестве Actions используется ComHandler. По полученному CLSID из xml осуществляется поиск dll в реестре по пути HKCR:\CLSID\{GUID}\InprocServer32. Дополнительно происходит проверка, в каком контексте выполняется задача (InteractiveUsers, AllUsers, AnyUser) и выполняется ли задача при логоне пользователя или нет.

Рисунок 17 - Пример строк в xml файле с описанием запланированной задачи, которая использует COM в качестве
Рисунок 17 - Пример строк в xml файле с описанием запланированной задачи, которая использует COM в качестве

Просто запуск скрипта без каких-либо параметров выводит все задачи, использующие COM (рисунок 18).

Рисунок 18 - Пример вывода скрипта Get-ScheduledTaskComHandler без параметров
Рисунок 18 - Пример вывода скрипта Get-ScheduledTaskComHandler без параметров

Если использовать параметр -PersistenceLocations, то в результате мы получим список задач, которые можно использовать для закрепления в системе с помощью COM Hijacked, что видно на рисунке 19. В данном случае осуществляется дополнительная проверка того, что задача выполняется в пользовательском контексте и запускается при входе пользователя.

Рисунок 19 - Получение списка COM-объектов пригодных для закрепления в системе с помощью Get-ScheduledTaskComHandler скрипта
Рисунок 19 - Получение списка COM-объектов пригодных для закрепления в системе с помощью Get-ScheduledTaskComHandler скрипта

Данный способ покрывает сразу два этапа атаки: выбор COM-объекта и вызов вредоносного объекта при каждом входе пользователя. Атакующему остается только заменить COM-объект на вредоносный (об этом будет рассказано в последующих статьях).

Примеры COM-объектов, используемые атакующими

Также мною были проанализированы отчеты по ransomware и APT за последнее время. Опираясь на них, можно сделать вывод, что наиболее популярными COM-объектами у атакующих в данной атаке являются следующие объекты:

GUID COM-объекта

Легитимное значение по умолчанию

{BCDE0395-E52F-467C-8E3D-C4579291692E}

%SystemRoot%\System32\MMDevApi.dll

{00021401-0000-0000-C000-000000000046}

C:\Windows\SysWOW64\windows.storage.dll

{AB8902B4-09CA-4bb6-B78D-A8F59079A8D5}

C:\Windows\SysWOW64\thumbcache.dll

{CF4CC405-E2C5-4DDD-B3CE-5E7582D8C9FA}

%systemroot%\system32\wbem\wmiutils.dll

{7C857801-7381-11CF-884D-00AA004B2E24}

%systemroot%\system32\wbem\wbemsvc.dll

{ddc05a5a-351a-4e06-8eaf-54ec1bc2dcea}

%SystemRoot%\System32\ApplicationFrame.dll

{1f486a52-3cb1-48fd-8f50-b8dc300d9f9d}

%SystemRoot%\system32\propsys.dll

{4590f811-1d3a-11d0-891f-00aa004b2e24}

%SystemRoot%\system32\wbem\wbemprox.dll

{4de225bf-cf59-4cfc-85f7-68b90f185355}

%SystemRoot%\system32\wbem\wmiprvsd.dll

{F56F6FDD-AA9D-4618-A949-C1B91AF43B1A}

%SystemRoot%\System32\Actioncenter.dll

Такие COM-объекты по умолчанию присутствуют в Windows и часто вызываются легитимными процессами. Например, как можно заметить, часть из этих объектов относятся к WMI. Многие встроенные в Windows утилиты по типу tasklist.exe, systeminfo.exe и др., под капотом используют WMI и, следовательно, используют данные COM-объекты. Этот факт гарантирует атакующему, что после выполнения замены COM-объекта на вредоносный он будет вызываться в системе жертвы и при этом достаточно часто.

Заключение

В этой статье я рассмотрела базовые принципы устройства Component Object Model в Windows. Из всего выше сказанного особо важным является приоритет ключей InprocServer, LocalServer и TreatAs. Именно игра на приоритетах делает возможным проведение атаки COM-Hijacking. COM-объекты в ветке HKCU имеют больший приоритет, а также данная ветка дает права на запись для интерактивного пользователя, т.е. атакующему не требуется искать способы для повышения привилегий.

Кроме этого, мною были проанализированы четыре основных способа выбора COM-объекта, любым из которых можно воспользоваться. Уверена, данные способы не являются исчерпывающими. Но если цель атакующего - закрепление в системе, а атака COM-Hijacking относиться именно к закреплению, то важно учитывать, что выбранный COM-объект должен часто запускаться на устройстве жертвы и не ломать логику работы других процессов, использующих его.

Надеюсь данная статья оказалась для вас полезной! Если у вас появились вопросы, пишите в комментариях!

Автор: Кожушок Диана( @wildresearcher), аналитик-исследователь киберугроз в компании R-Vision

Комментарии (3)


  1. Mingun
    26.06.2023 17:39

    Это значит, что, например, наличие ключа InprocServer32 в HKCR приоритетнее, чем ключ LocalServer32 в HKCU.

    В HKCU проверка проходит раньше, чем в HKCR.

    Что за чепуха. Сами же написали, что HKCR виртуальный куст, поэтому никаких "раньше" быть не может (между HKCU и HKCR, так как первое часть второго). Это самое обычное наследование свойств. Сначала читаются свойства из HKLM, затем на них накладываются свойства (и перекрывают, а если нечего перекрывать, то просто добавляются) из HKCU. Итоговое дерево называется HKCR.


    1. wildresearcher Автор
      26.06.2023 17:39

      Верно, HKCR состоит из HKLM+HKCU. Когда вы запускаете какой-либо COM-объект, то поиск осуществляется в двух ветках по очереди: в HKCU, а затем HKCR. Если в HKCU значение не найдено, то в HKCR будет хранится значение из HKLM. То есть по сути можно заменить "InprocServer32 в HKLM приоритетнее, чем ключ LocalServer32 в HKCU." Но напрямую не осуществляется запрос в HKLM, вместо этого используется HKCR. Чтобы увидеть о чем я говорю запустите Procmon, обратитесь к какому-либо COM-объекту и вы увидите данные события в этой очередности.


      1. wildresearcher Автор
        26.06.2023 17:39

        Это значит, что, например, наличие ключа InprocServer32 в HKCR приоритетнее, чем ключ LocalServer32 в HKCU.

        Что касательно этой фразы, то исходные данные такие: есть COM-объект, у него заданы InprocServer32 в HKLM и LocalServer32 в HKCU. В HKCR тогда будут и InprocServer32, и LocalServer32. Изначально будет поиск InprocServer32 напрямую в HKCU, но там он будет не найден по понятным причинам. Далее будет осуществляться поиск InprocServer32 в HKCR(но считай в HKLM). И по итогу будет выбран именно он для использования, хотя при всем этом вHKCU был еще задан LocalServer32.