Background
Lately, my focus has been on discovering any potential vulnerabilities in KEPServerEX. KEPServerEX is the industry’s leading connectivity platform that provides a single source of industrial automation data to all your applications. Users can connect, manage, monitor, and control diverse automation devices and software applications through one intuitive user interface.
This software employs multiple anti-debugging measures, making it challenging to discover any vulnerabilities and performing fuzzing on it. In this regard, I would like to share my perspective on the issue and my strategy for circumventing these measures.
Anti-debuger and anti attach
KEPServerEX comes with several default services, as shown in the image below:
However, when attempting to attach a debugger to the server_runtime.exe
process, it crashes instantly.
To identify the cause of the crash, I configured Windows to collect a crash dump of the process. You can learn more about how to do this here.
The collected crash dump provided me with a stack trace, which helped to identify the instructions responsible for the crash.
(185c.1460): Security check failure or stack buffer overrun - code c0000409 (first/second chance not available)
For analysis of this file, run !analyze -v
eax=00000001 ebx=028af068 ecx=00000007 edx=000001e9 esi=00000003 edi=6bde5a94
eip=76caeddb esp=028aee38 ebp=028aee4c iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
ucrtbase!abort+0x4b:
76caeddb cd29 int 29h
0:000> k
# ChildEBP RetAddr
00 028aee4c 76cae17b ucrtbase!abort+0x4b
01 028aee7c 6d9f5ee4 ucrtbase!terminate+0x3b
02 028aeef4 6d9f6405 VCRUNTIME140!FindHandler<__FrameHandler3>+0x13c [D:\a01\_work\4\s\src\vctools\crt\vcruntime\src\eh\frame.cpp @ 508]
03 028aef28 6d9fe416 VCRUNTIME140!__InternalCxxFrameHandler<__FrameHandler3>+0xf7 [D:\a01\_work\4\s\src\vctools\crt\vcruntime\src\eh\frame.cpp @ 350]
04 028aef64 76fc87a2 VCRUNTIME140!__CxxFrameHandler2+0x26 [D:\a01\_work\4\s\src\vctools\crt\vcruntime\src\eh\i386\trnsctrl.cpp @ 214]
05 028aef88 76fc8774 ntdll!ExecuteHandler2+0x26
06 028af050 76fb4e96 ntdll!ExecuteHandler+0x24
07 028af050 761ea6e2 ntdll!KiUserExceptionDispatcher+0x26
08 028af570 6d9f7586 KERNELBASE!RaiseException+0x62
09 028af5a0 6e9e10fd VCRUNTIME140!_CxxThrowException+0x66 [D:\a01\_work\4\s\src\vctools\crt\vcruntime\src\eh\throw.cpp @ 74]
WARNING: Stack unwind information not available. Following frames may be wrong.
0a 028af5e4 6bda4d6b libplatform!kepplatform::CException::Throw+0x1d
0b 028af618 6bdb74f3 thingworxinterface!QueryInterface+0x34b
0c 028af644 6bdcf882 thingworxinterface!QueryInterface+0x12ad3
0d 028af688 76c3a9ac thingworxinterface!QueryInterface+0x2ae62
0e 028af6bc 76c3a97a ucrtbase!__crt_seh_guarded_call<int>::operator()<<lambda_69a2805e680e0e292e8ba93315fe43a8>,<lambda_f03950bc5685219e0bcd2087efbe011e> &,<lambda_03fcd07e894ec930e3f35da366ca99d6> >+0x30
0f 028af6dc 6bdb9a3f ucrtbase!_execute_onexit_table+0x2a
10 028af71c 6bdba374 thingworxinterface!QueryInterface+0x1501f
11 028af728 6bdba5e5 thingworxinterface!QueryInterface+0x15954
12 028af768 6bdba683 thingworxinterface!QueryInterface+0x15bc5
13 028af77c 76fb2946 thingworxinterface!QueryInterface+0x15c63
14 028af79c 76f8dcf2 ntdll!LdrxCallInitRoutine+0x16
15 028af7e8 76f9d795 ntdll!LdrpCallInitRoutine+0x51
16 028af880 76f9d685 ntdll!LdrShutdownProcess+0xf5
17 028af950 75d14112 ntdll!RtlExitUserProcess+0xb5
18 028af964 6dfa75ae kernel32!ExitProcessImplementation+0x12
19 028af9a4 6dfa70d2 libserver!kepserver::CUniqueId::operator=+0xa07e // [1]
1a 028af9d0 6ea2362e libserver!kepserver::CUniqueId::operator=+0x9ba2
1b 028af9e4 6ea237f1 libthread!kepthread::CKEPThread::_ThreadProc+0x4e
1c 028afa0c 76c54f9f libthread!kepthread::CKEPThread::_ThreadProc+0x61
1d 028afa44 75d0fa29 ucrtbase!thread_start<unsigned int (__stdcall*)(void *),1>+0x3f
1e 028afa54 76fa7a4e kernel32!BaseThreadInitThunk+0x19
1f 028afab0 76fa7a1e ntdll!__RtlUserThreadStart+0x2f
20 028afac0 00000000 ntdll!_RtlUserThreadStart+0x1b
Upon analyzing the stack trace, I have observed that libserver.dll
is responsible for calling exit process and generating an exception. By examining the address mentioned in [1]
, it has been confirmed that it belongs to the libserver!sub_10087540
function as shown below.
void __noreturn sub_10087540()
{
NTSTATUS (__stdcall *v0)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); // esi
HANDLE CurrentProcess; // eax
NTSTATUS (__stdcall *v2)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); // esi
HANDLE v3; // eax
NTSTATUS (__stdcall *v4)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); // esi
HANDLE v5; // eax
int v6; // [esp+10h] [ebp-24h] BYREF
int v7; // [esp+14h] [ebp-20h] BYREF
int ProcessInformation; // [esp+18h] [ebp-1Ch] BYREF
CPPEH_RECORD ms_exc; // [esp+1Ch] [ebp-18h]
v0 = NtQueryInformationProcess;
if ( NtQueryInformationProcess )
{
ProcessInformation = 0;
CurrentProcess = GetCurrentProcess();
if ( !v0(CurrentProcess, ProcessDebugPort, &ProcessInformation, 4, 0) && ProcessInformation )
ExitProcess(1u);
v7 = 0;
v2 = NtQueryInformationProcess;
v3 = GetCurrentProcess();
if ( !v2(v3, ProcessDebugObjectHandle, &v7, 4, 0) && v7 )
ExitProcess(2u);
v6 = 1;
v4 = NtQueryInformationProcess;
v5 = GetCurrentProcess();
if ( !v4(v5, ProcessDebugFlags, &v6, 4, 0) && !v6 )
ExitProcess(3u);
}
ms_exc.registration.TryLevel = 0;
__asm { int 2Dh; Windows NT - debugging services: eax = type }
ExitProcess(4u);
}
Upon initial examination, it is apparent that the following methods are employed:
- The utilization of the function
ntdll!NtQueryInformationProcess
with ProcessInformationClassProcessDebugPort
,ProcessDebugObjectHandle
,ProcessDebugFlags
for the purposes of anti-debugging. You can read more with regards to this trick here for better understanding. For more information on other Anti-Debug tricks from CheckPoint, do check it out here. - The implementation of the instruction
int 2D
in order to check to if a debugger is attached to the current process.
First attempt at Bypassing of Checks
To fix this function, I plan to simply patch it by removing the bottom checks.
xor eax, eax
ret
I begin by patching the start of the libserver!sub_10087540
function with xor
and ret
.
.text:10087540 sub_10087540 proc near ; CODE XREF: sub_100836A0+27↑p
.text:10087540 ; sub_10084080+45↑p ...
.text:10087540 xor eax, eax // patch
.text:10087542 retn
.text:10087542 sub_10087540 endp
.text:10087542
.text:10087542 ; ---------------------------------------------------------------------------
.text:10087543 db 6Ah ; j
.text:10087544 db 0FEh
Checking of file signature
After patching libserver.dll
, I attempted to restart the KEPServerEX service runtime. Unfortunately, the process failed to start, as it appeared to be checking something and exiting immediately.
In order to gain more information, I utilized Procmon to observe the behavior of the process upon startup.
Based on the fact that the process failed to start after updating libserver.dll
, it is likely that it was checking the hash of the files.
To further investigate, I filtered the Process Name
to server_runtime.exe
and set the operation to ReadFile
. This allowed me to observe that the process was reading various dll and exe files within the installation directory.
By examining an event, I was able to see the stack trace indicating that the process was utilizing WinVerifyTrust
to check the files in the installation directory.
Specifically, I analyzed the code at server_runtime.exe + 0x34101
.
int __thiscall sub_33000(int this, int a2)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
v37 = a2;
pCertContext = 0;
p_pCertContext = &pCertContext;
v31 = 0;
if ( *(_DWORD *)(this + 20) || (v3 = sub_333E0(), v3 >= 0) )
{
v33.pcwszFilePath = *(LPCWSTR *)(this + 4);
pWVTData.pFile = &v33;
v33.cbStruct = 16;
v33.hFile = 0;
v33.pgKnownSubject = 0;
pgActionID.Data1 = 0xAAC56B;
*(_DWORD *)&pgActionID.Data2 = 0x11D0CD44;
*(_DWORD *)pgActionID.Data4 = 0xC000C28C;
*(_DWORD *)&pgActionID.Data4[4] = 0xEE95C24F;
v35 = 0;
pWVTData.cbStruct = 52;
pWVTData.pPolicyCallbackData = 0;
pWVTData.pSIPClientData = 0;
pWVTData.dwUIChoice = 2;
pWVTData.fdwRevocationChecks = 0;
pWVTData.dwUnionChoice = 1;
pWVTData.dwStateAction = WTD_STATEACTION_VERIFY;
pWVTData.hWVTStateData = 0;
pWVTData.pwszURLReference = 0;
pWVTData.dwProvFlags = 16;
pWVTData.dwUIContext = 0;
v4 = (kepplatform *)WinVerifyTrust(0, &pgActionID, &pWVTData); // [2]
v3 = (int)v4;
v36 = this + 8;
if ( (int)v4 < 0 )
{
v5 = kepplatform::XlatErrorCode(v4);
v6 = ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(
v40,
v5);
ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::operator=(this + 8, v6);
LOBYTE(v31) = 0;
CMFCRibbonInfo::XID::~XID(v40);
pWVTData.dwStateAction = WTD_STATEACTION_CLOSE;
WinVerifyTrust(0xFFFFFFFFh, &pgActionID, &pWVTData);
goto done;
}
pWVTData.dwStateAction = WTD_STATEACTION_CLOSE;
WinVerifyTrust(0xFFFFFFFFh, &pgActionID, &pWVTData);
...
...
}
...
...
}
Upon examination of the code provided, it is evident that the WinVerifyTrust
function is invoked at [2]
and checks the return value. According to MS, If the trust provider verifies that the subject is trusted for the specified action, the return value is zero. No other value besides zero should be considered a successful return.
As a result, modifying the return value of WinVerifyTrust
to zero is sufficient to bypass the signature check and proceed with the intended operation.
Second attempt at Bypassing of Checks
Observe the ASM code:
.text:000330A8 mov [ebp+70h+var_2C], 0
.text:000330AF mov [ebp+70h+pWVTData.cbStruct], 34h ; '4'
.text:000330B6 mov [ebp+70h+pWVTData.pPolicyCallbackData], 0
.text:000330BD mov [ebp+70h+pWVTData.pSIPClientData], 0
.text:000330C4 mov [ebp+70h+pWVTData.dwUIChoice], 2
.text:000330CB mov [ebp+70h+pWVTData.fdwRevocationChecks], 0
.text:000330D2 mov [ebp+70h+pWVTData.dwUnionChoice], 1
.text:000330D9 mov [ebp+70h+pWVTData.dwStateAction], 1
.text:000330E0 mov [ebp+70h+pWVTData.hWVTStateData], 0
.text:000330E7 mov [ebp+70h+pWVTData.pwszURLReference], 0
.text:000330EE mov [ebp+70h+pWVTData.dwProvFlags], 10h
.text:000330F5 mov [ebp+70h+pWVTData.dwUIContext], 0
.text:000330FC call WinVerifyTrust
.text:00033101 mov esi, eax // [3] =>> patch xor esi, esi
.text:00033103 lea edi, [ebx+8]
.text:00033106 mov [ebp+70h+var_28], edi
.text:00033109 test esi, esi
.text:0003310B jns short loc_33156
Upon executing WinVerifyTrust
, the resulting value of eax is subsequently moved to esi
at [3]
, where it is then verified on the esi
register.
Upon examining the events recorded in Procmon
, it was revealed that libsecure.dll
leverages the WinVerifyTrust
function to authenticate the signatures of files located in the installation directory. Additionally, I implemented the patch method as described earlier.
Hiding the Debugger
After successfully attaching to the server_runtime.exe
process, I attempted to set a breakpoint. However, upon reaching the breakpoint, the program promptly crashed. Upon examining the crash, the resulting stack trace was as follows:
eax=02dfa770 ebx=02dfa74c ecx=71c83004 edx=0237f8fc esi=02dfa7c4 edi=02dfa74c
eip=71bbbc00 esp=0237f8e8 ebp=0237f97c iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
libua!kepua::nodeids::NodeHash::operator=+0xa0:
71bbbc00 cc int 3
0:009> k
# ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0237f97c 71bf086e libua!kepua::nodeids::NodeHash::operator=+0xa0
01 0237f9e8 71b87af7 libua!kepua::types::node_id_t::isString+0x22de
02 0237fa6c 74bb9572 libua!kepua::kepuaclient::CUaClientTransportFactory::Build+0x1bb7
03 0237faa4 74bbe0d0 libsocket!kepsocket::IConnection::ProcessReadEvent+0x72
04 0237fad4 74bbdbb0 libsocket!kepsocket::CBaseSocket::UnregisterCommDiagCallback+0xe00
05 0237fb04 74f537f1 libsocket!kepsocket::CBaseSocket::UnregisterCommDiagCallback+0x8e0
06 0237fb2c 75c54f9f libthread!kepthread::CKEPThread::_ThreadProc+0x61
07 0237fb64 76e3fa29 ucrtbase!thread_start<unsigned int (__stdcall*)(void *),1>+0x3f
08 0237fb74 77717a4e kernel32!BaseThreadInitThunk+0x19
09 0237fbd0 77717a1e ntdll!__RtlUserThreadStart+0x2f
0a 0237fbe0 00000000 ntdll!_RtlUserThreadStart+0x1b
0:009> u eip
libua!kepua::nodeids::NodeHash::operator=+0xa0:
71bbbc00 cc int 3
71bbbc01 8bec mov ebp,esp
71bbbc03 6aff push 0FFFFFFFFh
71bbbc05 689f2ac171 push offset libua!kepua::CEndpointUrl::ToString+0x1119f (71c12a9f)
71bbbc0a 64a100000000 mov eax,dword ptr fs:[00000000h]
71bbbc10 50 push eax
71bbbc11 81ec9c000000 sub esp,9Ch
71bbbc17 a12830c871 mov eax,dword ptr [libua!kepua::types::status_change_notification_t::`vftable'+0x51b58 (71c83028)]
By setting the breakpoint at 71bbc00
, the debugger is unable to receive the breakpoint exception and unable to handle it. As a result, the exception remains unhandled, and if the process fails to handle it, the entire application will terminate. For further details on this issue, please refer to this resource.
In essence, it will iterate through all the threads in the target process and set the hidefromdebugger
flag wherever it exists.
KEPServerEX uses the functions present in libthread.dll
when initializing the thread. The function libthread!kepthread::CKEPThread::Start
initializes the thread and invokes the function libthread!sub_10003960
. This function sets the flag THREAD_INFORMATION_CLASS::ThreadHideFromDebugger (0x11)
through the function ntdll!NtSetInformationThread
.
bool __cdecl sub_10003960(int a1)
{
HMODULE ModuleHandleW; // eax
if ( dword_100091E0 <= *(_DWORD *)(*((_DWORD *)NtCurrentTeb()->ThreadLocalStoragePointer + TlsIndex) + 4) )
return NtSetInformationThread && !NtSetInformationThread(a1, 0x11, 0, 0);
_Init_thread_header(&dword_100091E0);
if ( dword_100091E0 != -1 )
return NtSetInformationThread && !NtSetInformationThread(a1, 0x11, 0, 0);
ModuleHandleW = GetModuleHandleW(L"ntdll.dll");
NtSetInformationThread = (int (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD))GetProcAddress(
ModuleHandleW,
"NtSetInformationThread");
_Init_thread_footer(&dword_100091E0);
return NtSetInformationThread && !NtSetInformationThread(a1, 0x11, 0, 0);
}
Third attempt at Bypassing of Checks
The method to bypass this technique is similar to the anti-debug bypass mentioned earlier. All we have to do is make a patch at the start of the libthread!sub_10003960
function.
.text:10003960
.text:10003960 loc_10003960: ; CODE XREF: sub_10001010+7↑p
.text:10003960 ; kepthread::CKEPThread::Start(bool)+CD↑p
.text:10003960 ; __unwind { // SEH_10003960
.text:10003960 xor eax, eax // patch
.text:10003962 retn
.text:10003963 ; ---------------------------------------------------------------------------
.text:10003963 push 0FFFFFFFFh
.text:10003965 push offset SEH_10003960
.text:1000396A mov eax, large fs:0
Once this is done, we can easily attach and debug the KEPServerEX process.
(18f8.1210): Break instruction exception - code 80000003 (first chance)
eax=00e41000 ebx=00000000 ecx=7775db60 edx=7775db60 esi=7775db60 edi=7775db60
eip=77724ce0 esp=0382fefc ebp=0382ff28 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
ntdll!DbgBreakPoint:
77724ce0 cc int 3
0:037> bp libua+5BC00
0:037> g
Breakpoint 0 hit
eax=7a9277e0 ebx=7a9277bc ecx=7ad73004 edx=0b17f518 esi=7a927834 edi=7a9277bc
eip=7acabc00 esp=0b17f504 ebp=0b17f598 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
libua!kepua::nodeids::NodeHash::operator=+0xa0:
7acabc00 55 push ebp
0:007> k
# ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0b17f500 7acab3b9 libua!kepua::nodeids::NodeHash::operator=+0xa0
01 0b17f598 7ace086e libua!kepua::tcp::CServer::AddEndpoint+0x1339
02 0b17f604 7ac77af7 libua!kepua::types::node_id_t::isString+0x22de
03 0b17f688 74bf9572 libua!kepua::kepuaclient::CUaClientTransportFactory::Build+0x1bb7
04 0b17f6c0 74bfe0d0 libsocket!kepsocket::IConnection::ProcessReadEvent+0x72
05 0b17f6f0 74bfdbb0 libsocket!kepsocket::CBaseSocket::UnregisterCommDiagCallback+0xe00
06 0b17f720 74a137f1 libsocket!kepsocket::CBaseSocket::UnregisterCommDiagCallback+0x8e0
07 0b17f748 75c54f9f libthread!kepthread::CKEPThread::_ThreadProc+0x61
08 0b17f780 76e3fa29 ucrtbase!thread_start<unsigned int (__stdcall*)(void *),1>+0x3f
09 0b17f790 77717a4e KERNEL32!BaseThreadInitThunk+0x19
0a 0b17f7ec 77717a1e ntdll!__RtlUserThreadStart+0x2f
0b 0b17f7fc 00000000 ntdll!_RtlUserThreadStart+0x1b
0:007> !heap -p -a 7a9277e0
address 7a9277e0 found in
_HEAP @ 5ab0000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
7a927788 0022 0000 [00] 7a9277b0 000d8 - (busy)
? libua!kepua::types::monitoring_filter_t::`vftable'+470
73bbc24a verifier!AVrfpDphNormalHeapAllocate+0x000000ba
73bba9da verifier!AVrfDebugPageHeapAllocate+0x0000036a
7778ef3e ntdll!RtlDebugAllocateHeap+0x00000039
776f7080 ntdll!RtlpAllocateHeap+0x000000f0
776f6ddc ntdll!RtlpAllocateHeapInternal+0x0000104c
776f5d7e ntdll!RtlAllocateHeap+0x0000003e
75c40166 ucrtbase!_malloc_base+0x00000026
750bfed1 mfc140u!operator new+0x00000036
7aca627f libua!kepua::security::EncryptUserPassword+0x0000169f
7aca516a libua!kepua::security::EncryptUserPassword+0x0000058a
7aca5cd8 libua!kepua::security::EncryptUserPassword+0x000010f8
74bfc468 libsocket!kepsocket::CSocketServer::_Accept+0x00000088
74bfc62f libsocket!kepsocket::CSocketServer::_DropStoppedClients+0x0000012f
74a1362e libthread!kepthread::CKEPThread::_ThreadProc+0x0000004e
0:007> p
Breakpoint 0 hit
eax=7a9277e0 ebx=7a9277bc ecx=7ad73004 edx=0b17f518 esi=7a927834 edi=7a9277bc
eip=7acabc00 esp=0b17f504 ebp=0b17f598 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
libua!kepua::nodeids::NodeHash::operator=+0xa0:
7acabc00 55 push ebp
0:007>
eax=7a9277e0 ebx=7a9277bc ecx=7ad73004 edx=0b17f518 esi=7a927834 edi=7a9277bc
eip=7acabc01 esp=0b17f500 ebp=0b17f598 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
libua!kepua::nodeids::NodeHash::operator=+0xa1:
7acabc01 8bec mov ebp,esp
0:007>
eax=7a9277e0 ebx=7a9277bc ecx=7ad73004 edx=0b17f518 esi=7a927834 edi=7a9277bc
eip=7acabc03 esp=0b17f500 ebp=0b17f500 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
libua!kepua::nodeids::NodeHash::operator=+0xa3:
7acabc03 6aff push 0FFFFFFFFh
Code Patching
import argparse
import os
#offset for KEPServerEX6-6.12.361.0
def patch(_dir):
server_runtime = os.path.join(_dir, "server_runtime.exe")
libsecure = os.path.join(_dir, "libsecure.dll")
libserver = os.path.join(_dir, "libserver.dll")
libthread = os.path.join(_dir, "libthread.dll")
#
offset_server_runtime = 0x34101 - 0x1000+0x400 #xor esi, esi
offset_libsecure = 0x16A41 - 0x1000+0x400 #xor esi, esi
offset_libserver = 0x87540 - 0x1000+0x400 #xor eax,eax ret
offset_libthread = 0x3960 - 0x1000+0x400 #xor eax,eax ret
# patch check signature file
#
with open(server_runtime, 'rb+') as f:
f.seek(offset_server_runtime)
f.write(b'\x31\xf6')
with open(libsecure, 'rb+') as f:
f.seek(offset_libsecure)
f.write(b'\x31\xf6')
# patch anti debug/attach
with open(libserver, 'rb+') as f:
f.seek(offset_libserver)
f.write(b'\x31\xC0\xC3')
#patch hidedebuger
with open(libthread, 'rb+') as f:
f.seek(offset_libthread)
f.write(b'\x31\xC0\xC3')
if __name__ == "__main__":
parser = argparse.ArgumentParser()
#
# define each option with: parser.add_argument
#
parser.add_argument("-d", "--dir", help="directory keperserver")
args = parser.parse_args() # automatically looks at sys.argv
#
# access results with: args.argumentName
#
if(args.dir != None):
patch(args.dir)
Bonus
After successfully bypassing all security measures, I was able to debug the process and take on further tasks, such as fuzzing. My focus was on the parsing of OPCs UA TCP in the server_runtime
, which is processed in the libua.dll
.
To carry out the fuzzing, I utilized the wtf fuzzer. However, my input for mutation was not effective, leading me to fail in discovering anything of interest.
During the packet receiving process, I noticed that it begins at the libua!sub_100907C0
function. It is important to note that each time the process receives a packet, it can only receive a maximum of 0xff
bytes. Therefore, when developing the harness, it is essential to hook the WS2_32!recv
function to pass the correct input sequentially.
Do check out Axel Souchet’s tool, KEPaboo, which is also meant for neutralising KEPServerEx’s anti-debugging techniques.
Conclusion
I would like to express my gratitude to all who took the time to read this piece. I want to extend a heartfelt appreciation to my team members who provided valuable assistance in the KEPServer setup and reviewed this blog post.
In conclusion, my earnest hope is that this write-up will prove to be an useful for anyone facing challenges with KEPServerEx’s anti-debugging techniques.