├── .gitattributes ├── LICENSE ├── README.md ├── main.c └── main.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Lloyd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🗑️ delete-self-poc 2 | The `delete-self-poc` is a demonstration of a method to delete a locked executable or currently running file from disk. This concept was initially discovered by Jonas Lykkegaard, and I have created the proof of concept (POC) for it. Additionally, it can be used to delete locked files on disk, provided that the current calling process has the necessary permissions to access and delete them. 3 | 4 | How does this work, though - in this POC? 5 | 1. Open a HANDLE to the current running process with DELETE access. Note that only DELETE access is required. 6 | 2. Use the SetFileInformationByHandle function to rename the primary file stream, :$DATA, to :wtfbbq. 7 | 3. Close the HANDLE. 8 | 4. Open a HANDLE to the current process and set the DeleteFile flag of the FileDispositionInfo class to TRUE. 9 | 5. Close the HANDLE to trigger the file disposition. 10 | 6. Voila! The file is now gone. 11 | 12 | # Releases 13 | I have included a statically linked release within this repository, if you can't be bothered compiling the original source code. 14 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | static 4 | HANDLE 5 | ds_open_handle( 6 | PWCHAR pwPath 7 | ) 8 | { 9 | return CreateFileW(pwPath, DELETE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 10 | } 11 | 12 | static 13 | void * 14 | ds_rename_handle( 15 | HANDLE hHandle 16 | ) 17 | { 18 | LPCWSTR lpwStream = DS_STREAM_RENAME; 19 | PFILE_RENAME_INFO pfRename = (PFILE_RENAME_INFO)malloc(sizeof(FILE_RENAME_INFO) + sizeof(WCHAR) * wcslen(lpwStream)); // FILE_RENAME_INFO contains space for 1 WCHAR without NULL-byte 20 | if(pfRename == NULL) 21 | { 22 | DS_DEBUG_LOG(L"could not allocate memory"); 23 | return NULL; 24 | } 25 | RtlSecureZeroMemory(pfRename, sizeof(FILE_RENAME_INFO) + sizeof(WCHAR) * wcslen(lpwStream)); 26 | 27 | // set our FileNameLength and FileName to DS_STREAM_RENAME 28 | pfRename->FileNameLength = (DWORD)(sizeof(WCHAR) * wcslen(lpwStream)); 29 | RtlCopyMemory(pfRename->FileName, lpwStream, sizeof(WCHAR) * (wcslen(lpwStream) + 1)); 30 | 31 | BOOL fRenameOk = SetFileInformationByHandle(hHandle, FileRenameInfo, pfRename, (DWORD)(sizeof(FILE_RENAME_INFO) + sizeof(WCHAR) * wcslen(lpwStream))); 32 | if(!fRenameOk) 33 | { 34 | free(pfRename); 35 | return NULL; 36 | } 37 | return pfRename; 38 | } 39 | 40 | static 41 | BOOL 42 | ds_deposite_handle( 43 | HANDLE hHandle 44 | ) 45 | { 46 | // set FILE_DISPOSITION_INFO::DeleteFile to TRUE 47 | FILE_DISPOSITION_INFO fDelete; 48 | RtlSecureZeroMemory(&fDelete, sizeof(fDelete)); 49 | 50 | fDelete.DeleteFile = TRUE; 51 | 52 | return SetFileInformationByHandle(hHandle, FileDispositionInfo, &fDelete, sizeof(fDelete)); 53 | } 54 | 55 | int 56 | main( 57 | int argc, 58 | char** argv 59 | ) 60 | { 61 | WCHAR wcPath[MAX_PATH + 1]; 62 | RtlSecureZeroMemory(wcPath, sizeof(wcPath)); 63 | 64 | // get the path to the current running process ctx 65 | if (GetModuleFileNameW(NULL, wcPath, MAX_PATH) == 0) 66 | { 67 | DS_DEBUG_LOG(L"failed to get the current module handle"); 68 | return 0; 69 | } 70 | 71 | HANDLE hCurrent = ds_open_handle(wcPath); 72 | if (hCurrent == INVALID_HANDLE_VALUE) 73 | { 74 | DS_DEBUG_LOG(L"failed to acquire handle to current running process"); 75 | return 0; 76 | } 77 | 78 | // rename the associated HANDLE's file name 79 | DS_DEBUG_LOG(L"attempting to rename file name"); 80 | void *pfRename = ds_rename_handle(hCurrent); 81 | if (pfRename == NULL) 82 | { 83 | DS_DEBUG_LOG(L"failed to rename to stream"); 84 | return 0; 85 | } 86 | 87 | DS_DEBUG_LOG(L"successfully renamed file primary :$DATA ADS to specified stream, closing initial handle"); 88 | CloseHandle(hCurrent); 89 | free(pfRename); // free memory allocated in ds_rename_handle 90 | pfRename = NULL; 91 | 92 | // open another handle, trigger deletion on close 93 | hCurrent = ds_open_handle(wcPath); 94 | if (hCurrent == INVALID_HANDLE_VALUE) 95 | { 96 | DS_DEBUG_LOG(L"failed to reopen current module"); 97 | return 0; 98 | } 99 | 100 | if (!ds_deposite_handle(hCurrent)) 101 | { 102 | DS_DEBUG_LOG(L"failed to set delete deposition"); 103 | return 0; 104 | } 105 | 106 | // trigger the deletion deposition on hCurrent 107 | DS_DEBUG_LOG(L"closing handle to trigger deletion deposition"); 108 | CloseHandle(hCurrent); 109 | 110 | // verify we've been deleted 111 | if (PathFileExistsW(wcPath)) 112 | { 113 | DS_DEBUG_LOG(L"failed to delete copy, file still exists"); 114 | return 0; 115 | } 116 | 117 | DS_DEBUG_LOG(L"successfully deleted self from disk"); 118 | return 1; 119 | } 120 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #pragma comment(lib, "Shlwapi.lib") 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define DS_STREAM_RENAME L":wtfbbq" 11 | #define DS_DEBUG_LOG(msg) wprintf(L"[LOG] - %s\n", msg) --------------------------------------------------------------------------------