在逻辑漏洞的挖掘过程中,文件删除操作是非常重要的一个攻击点,但是人们一般都是关心 DeleteFile 函数。我最近发现了一种比较隐蔽的文件删除方法,那就是通过NtCreateFile。

FILE_SUPERSEDE

在 NtCreateFile 函数中设置 FILE_SUPERSEDE 标志位,即可达到文件删除的目的。

1
2
3
pNtCreateFile(&file_handle, GENERIC_WRITE, &obj_file,
&io_file, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_WRITE,
FILE_SUPERSEDE, FILE_NON_DIRECTORY_FILE, NULL, 0);

这是 docs 对该 flag 的解释,意思就是如果文件存在,就把它删掉,然后再创建一个新文件。

不过该 flag 只能在 NtCreateFile 上设置,而该函数一般用于驱动程序。但是驱动程序写起来比较麻烦,所以我找了一段普通的用户态程序来测试。

运行后,原 test.txt 文件将会被删除,然后会创建一个新 test.txt。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include <windows.h>
#include "Winternl.h"

NTSTATUS(WINAPI *pNtCreateFile)(PHANDLE FileHandle,
ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes,
PIO_STATUS_BLOCK IoStatusBlock, PVOID AllocationSize,
ULONG FileAttributes, ULONG ShareAccess, ULONG CreateDisposition,
ULONG CreateOptions, PVOID EaBuffer, ULONG EaLength);

VOID(WINAPI *pRtlInitUnicodeString)(PUNICODE_STRING DestinationString,
PCWSTR SourceString);

int main()
{
HANDLE pFile;
wchar_t path[] = L"\\??\\H:\\script\\test.txt";

*(FARPROC *)&pRtlInitUnicodeString =
GetProcAddress(GetModuleHandle(L"ntdll"), "RtlInitUnicodeString");

*(FARPROC *)&pNtCreateFile =
GetProcAddress(GetModuleHandle(L"ntdll"), "NtCreateFile");

UNICODE_STRING file_fname;
pRtlInitUnicodeString(&file_fname, path);

OBJECT_ATTRIBUTES obj_file;
InitializeObjectAttributes(&obj_file, &file_fname,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);

HANDLE file_handle; IO_STATUS_BLOCK io_file;
NTSTATUS ret = pNtCreateFile(&file_handle, GENERIC_WRITE, &obj_file,
&io_file, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_WRITE,
FILE_SUPERSEDE, FILE_NON_DIRECTORY_FILE, NULL, 0);

std::cout << "Hello World!\n";
}