-
Kernel-mode backdoors for Windows NT
Table of contents
1 - PREFACE
2 - OVERVIEW OF EXISTING KERNEL-MODE BACKDOORS FOR WINDOWS NT
2.1 - NTROOTKIT
2.2 - HE4HOOK
2.3 - SLANRET (IERK, BACKDOOR-ALI)
3 - OBSCURITY ON DISK, IN REGISTRY AND IN MEMORY
4 - MY VARIANT: THORNY PATH
4.1 - SHELL
4.2 - ACTIVATION AND COMMUNICATION WITH REMOTE CLIENT
4.3 - OBSCURITY ON DISK
5 - CONCLUSION
6 - FILES
-- [ 1 - Preface
This article is intended for those who know the architecture of the
Windows NT kernel and the principles of operation of NT drivers. This
article examines issues involved in the development of kernel-mode tools
for stealthy remote administration of Windows NT.
Recently there has been a tendency of extending the use of Windows NT
(2000, XP, 2003) from it's classical stronghold as home and
office OS to servers. At the same time, the outdated Windows 9x family is
replaced by the NT family. Because of this it should be evident that remote
administration tools (backdoors) and unnoticeable access tools (rootkits)
for the NT family have a certain value. Most of the published utilities
work in user-mode and can thus be detected by Antivirus tools or by manual
inspection.
It's quite another matter those works in kernel-mode: They can hide
from any user-mode program. Antivirus software will have to suplly kernel-
mode components in order to detect a kernel-mode-backdoor. Software exists
that protects against such backdoors (such as IPD, " Integrity Protection
Driver"), but it's use is not widely spread. Kernel mode backdoors are not
as widely used as they could be due to their relative complexity in comp-
arison with user-mode backdoors.
-- [ 2 - Overview of existing Kernel-Mode backdoors for Windows NT
This section briefly reviews existing kernel-mode backdoors for Windows
NT.
---- [ 2.1 - Ntrootkit
Ntrootkit (c) by Greg Hoglund and a team of free developers [ 1 ] is a
device driver for Windows NT 4.0 and 2000. It's possibilities (implemented
and potential):
- Receiving commands from a remote client. The rk_packet module contains
a simplified IP-stack, which uses free IP-address from the subnet where
the host on which Ntrootkit has been installed is situated.
It's MAC and IP addresses are hardcoded in the source. Connection with
the rootkit at that IP is carried out via a TCP connection to any port.
The available commands in rk_command.c are:
ps - list processes
help - self explainatory
buffertest, echo and debugint - for debugging purpose
hidedir - hide directory/file
hideproc - hide process (es)
sniffkeys - keyboard spy
There are also imcomplete pieces of code: Execute commands received via
a covert channel and starting a Win32-process from a driver (a hard and
complicated task).
- Encrypt all traffic using Schneier's Blowfish algorithm:
rk_blowfish.c is present, but not (yet?) used
- Self-defense (rk_defense.c) - hide protected objects (in this
case: registry keys), identified by the string " _root_ "; redirect
launched processes.
The hiding of processes, directories and files as implemented in
rk_ioman.c is done through hooking the following functions:
NtCreateFile
ZwOpenFile
ZwQueryDirectoryFile
ZwOpenKey
ZwQueryKey
ZwQueryValueKey
ZwEnumerateValueKey
ZwEnumerateKey
ZwSetValueKey
ZwCreateKey
The way to detect this rootkit:
Make direct request to filesystem driver, send IRP to it. There is
one more module that hooks file handling: rk_files.c, adopted from
filemon, but it is not used.
- Starting processes: An unfinished implementation of it can be found
in rk_command.c, another one (which is almost complete and good) is
in rk_exec.c
The implementation suffers from the fact that Zw* functions which are
normally unavailable to drivers directly are called through the system
call interface (int 0x2E), leading to problems with different versions
of the NT family as system call numbers change.
It seems like the work on Ntrootkit is very loosely coordinated: every
developer does what (s) he considers needed or urgent. Ntrootkit does
not achieve complete (or sufficient) invisibility. It creates device
named " Ntroot ", visible from User-Mode.
When using Ntrootkit for anything practical, one will need some means
of interaction with the rootkitted system. Shortly: There will be the
need for some sort of shell. Ntrootkit itself can not give out a shell
directly, although it can start a process -- the downside is that the
I/O of that process can not be redirected. One is thus forced to start
something like netcat. It's process can be hidden, but it's TC$$P-connection
will be visible. The missing redirection of I/O is a big drawback.
However, Ntrootkit development is still in progress, and it will
probably become a fully-functional tool for complete and stealthy remote
administration.
---- [ 2.2 - He4hook
This description is based on [ 2 ]. The filesystem access was hooked via
two different methods in the versions up to and including 2.15b6. Only one
of it works at one time, and in versions after 2.15b6 the first method was
removed.
Method A: hook kernel syscalls:
===============================
ZwCreateFile, ZwOpenFile - driver version 1.12 and from 1.17 to
2.15beta6
IoCreateFile - from 1.13 to 2.15beta6
ZwQueryDirectoryFile, ZwClose - before 2.15beta6
Almost all these exported functions (Zw*) have the following function
body:
mov eax, NumberFunction
lea edx, [ esp+04h ]
int 2eh; Syscall interface
The " NumberFunction " is the number of the called function in the
syscalls table (which itself can be accessed via the global variable
KeServiceDescriptorTable). This variable points to following structure:
typedef struct SystemServiceDescriptorTable
{
Ssd SystemServiceDescriptors [ 4 ];
} SSDT, *LPSSDT;
Other structures:
typedef VOID *SSTAT [ ];
typedef unsigned char SSTPT [ ];
typedef SSTAT *LPSSTAT;
typedef SSTPT *LPSSTPT;
typedef struct SystemServiceDescriptor
{
LPSSTAT lpSystemServiceTableAddressTable;
ULONG dwFirstServiceIndex;
ULONG dwSystemServiceTableNumEntries;
LPSSTPT lpSystemServiceTableParameterTable;
} SSD, *LPSSD;
The DescriptorTable pointed to by KeServiceDescriptorTable is only
accessible from kernel mode. In User-Mode, there is something called
KeServiceDescriptorTableShadow -- unfortunately it is not exported.
Base services are in
KeServiceDescriptorTable->SystemServiceDescriptors [ 0 ]
KeServiceDescriptorTableShadow->SystemServiceDescriptors [ 0 ]
KernelMode GUI services are in
KeServiceDescriptorTableShadow->SystemServiceDescriptors [ 1 ]
Other elements of that tables were free at moment when [ 2 ] was
written, in all versions up to WinNt4 (SP3-6) and Win2k build 2195.
Each element of the table is a SSID structure, which contains the
following data:
lpSystemServiceTableAddressTable - A pointer to an array of addresses
of functions that will be called if
a matching syscall is called
dwFirstServiceIndex - Start index for the first function
dwSystemServiceTableNumEntries - Number of services in table
lpSystemServiceTableParameterTable - An array of bytes specifying the
number of bytes from the stack that
will be passed through
In order to hook a system call, He4hookInv replaces the address stored in
KeServiceDescriptorTable->SystemServiceDescriptos[0].lpSystemServiceTableAddressTableIn
with a pointer to it's own table.
One can interface with He4hookInv by adding your own services to the
system call tables. He4hookInv updates both tables:
- KeServiceDescriptorTable
- KeServiceDescriptorTableShadow.
Otherwise, if it updated only KeServiceDescriptorTable, new services
would be unavailable from UserMode. To locate KeServiceDescriptorTable-
Shadow the following technique is used:
The function KeAddSystemServiceTable can be used to add services to the
kernel. It can add services to both tables. Taking into account that its
0-th descriptor is identical, it's possible, by scanning
KeAddSystemServiceTable function's code, to find the address of the shadow
table. You can see how it is done in file He4hookInv.c, function
FindShadowTable (void).
If this method fails for some reason, a hardcoded address is taken
(KeServiceDescriptorTable-0x230) as location of the shadow table. This
address has not changed since WincNt Sp3. Another problem is the search
for the correct index into the function address array. As almost all Zw*
functions have an identical first instruction (mov eax, NumberFunction),
one can get a pointer to the function number easily by adding one byte
to the address exported by ntoskrnl.exe
Method B: (for driver versions 2.11 and higher)
===============================================
The callback tables located in the DRIVER_OBJECT of the file system
drivers are patched: The IRP handlers of the needed drivers are replaced.
This includes replacing the pointers to base function handlers
(DRIVER_OBJECT->MAJORCFunction) as well as replacing pointers to the
drivers unload procedure (DRIVER_OBJECT->DRIVERCUnload).
The following functions are handled:
IRP_MJ_CREATE
IRP_MJ_CREATE_NAMED_PIPE
IRP_MJ_CREATE_MAILSLOT
IRP_MJ_DIRECTORY_CONTROL -> IRP_MN_QUERY_DIRECTORY
For a more detailed description of the redirection of file operations
refer to the source [ 2 ].
---- [ 2.3 - Slanret (IERK, Backdoor-ALI)
The source code for this is unavailable -- it was originally disco-
vered by some administrator on his network. It is a normal driver
("ierk8243.sys") which periodically causes BSODs, and is visible as a
service called " Virtual Memory Manager ".
" Slanret is technically just one component of a
root kit. It comes with a straightforward backdoor
program: a 27 kilobyte server called " Krei " that
listens on an open port and grants the hacker remote
access to the system. The Slanret component is a
seven kilobyte cloaking routine that burrows into the
system as a device driver, then accepts commands from
the server instructing it on what files or processes
to conceal. " [ 3 ]
---- [ 3. Stealth on disk, in registry and in memory
The lower the I/O interception in a rootkit is performed, the harder
it usually is to detect it's presence. One would think that a reliable
place for interception would be the low-level disk operations (read/write
sectors). This would require handling all filesystems that might be on
the hard disk though: FAT16, FAT32, NTFS.
While FAT was relatively easy to deal with (and some old DOS stealth
viruses used similar techniques) an implementation of something similar
on WinNT is a task for maniacs.
A second place to hook would be hooking dispatch functions of file-
system drivers: Patch DriverObject->MajorFunction and FastIoDispatch in
memory or patch the drivers on disk. This has the advantage of being re-
latively universal and is the method used in HE4HookInv.
A third possibility is setting a filter on a filesyste driver (FSD).
This has no advantages in comparison with the previous method, but has
the drawback of being more visible (Filemon uses this approach). The
functions Zw*, Io* can then be hooked either by manipulating the Ke-
ServiceDescriptorTable or directly patching the function body. It is
usually quite easy to detect that pointers in KeServiceDescriptorTable
point to strange locations or that the function body of a function has
changed. A filter driver is also easy to detect by calling IoGetDevice-
ObjectPointer and then checking DEVICE_OBJECT->StackSize.
All normal drivers have their own keys in the registry, namely in
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Servic es.
The abovementioned rootkits can hide registry keys, but obviously,
if the system is booted "cleanly", an administrator can see anything that
was hidden. One can also load a rootkit using ZwSetSystemInformation(
SystemLoadAndCallimage) without the need to create any registry keys. An
example of this technique can be found in [6].
A rootkit loader in a separate file is too unstealthy. It might be a
smarter move to patch that call into some executable file which is part of
the system boot. One can use any driver or user-mode program that works
with sufficient privileges, or any DLL linked to by it. One has to ask one
question though: If the newly introduced changes need to be hidden anyway,
why make two similar but differing procedures (for hiding changes to a
file as well as hiding the existance of a file) instead of limiting our-
selves to one ?
In most cases one can target null.sys. Implementing it's functionality
is as easy as "hello world", and that is why it is usually replaced with a
trojan. But if we are going to have a procedure for hiding changes to a
file, we can replace ANY driver with a trojan that will substitute the
content of the replaced file with the original content to everyone (incl-
uding the kernel). Upon startup, it will copy itself to some allocated
memory area and start a thread there.
This will make the trojan almost unnoticeable in memory: No system
utility can see the driver any more, as it is just an anonymous memory
page amongst many. We do not even need a thread, using intercepted IRP
dispatch functions of some driver (DriverObject->MajorFunction[IRP_MJ_xxx]).
We can also use IoQueueWorkItem and KeInsertQueueDpc, so no additional
threads in SYSTEM will be visible in the task manager. After this is done
the trojan can unload the driver it was started from, and reload it in a
clean (unchanged) variant. As a result, high levels of stealth will be
achieved by relatively simple means. The original content of the manipu-
lated file could for example be stored in the trojan's file after the
trojan itself.
It will then be sufficient to hook all FSD requests (IRP and FastIO)
and upon access change the position (and size of the file).
(CurrentIrpStackLocation->Parameters.*.ByteOffset)
Rest In Part 2
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
Bookmarks