From 8ddcd02ec95f3d32cba2ece0c1118f1303498a74 Mon Sep 17 00:00:00 2001 From: Leon Jacobs Date: Fri, 26 Aug 2016 09:19:08 +0200 Subject: [PATCH] Convert tabs to spaces --- dns_recv.py | 169 ++++++++++++++++++++++----------------- dns_send.py | 224 +++++++++++++++++++++++++++++----------------------- 2 files changed, 222 insertions(+), 171 deletions(-) diff --git a/dns_recv.py b/dns_recv.py index d485123..072be36 100644 --- a/dns_recv.py +++ b/dns_recv.py @@ -1,103 +1,126 @@ -import socket -import optparse +# The MIT License (MIT) +# +# Copyright (c) 2014-2016 Leon Jacobs +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + import getpass -from lib.FrameProcessor import ProcessFrame +import optparse +import socket -def main(ip, port, out_file, secret): +from lib.FrameProcessor import ProcessFrame - # setup the socket - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - sock.bind((ip, port)) - frameHandler = ProcessFrame() +def main(ip, port, out_file, secret): + # setup the socket + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.bind((ip, port)) - # Set the secret if we have one configured - if secret: - frameHandler.setSecret(secret) + frame_handler = ProcessFrame() - print '[INFO] Fake DNS server listening on', ip, '/', port, 'with a configured secret.' if secret else '' + # Set the secret if we have one configured + if secret: + frame_handler.set_secret(secret) - # if we have a file destination to write to, set it - if out_file: - frameHandler.setOutfile(out_file) + print '[INFO] Fake DNS server listening on', ip, '/', port, 'with a configured secret.' if secret else '' - # Start a never ending loop and receive the UDP frames in sock - # From: http://code.activestate.com/recipes/491264-mini-fake-dns-server/ - while True: + # if we have a file destination to write to, set it + if out_file: + frame_handler.set_outfile(out_file) - # read the socket - data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes + # Start a never ending loop and receive the UDP frames in sock + # From: http://code.activestate.com/recipes/491264-mini-fake-dns-server/ + while True: + # read the socket + data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes - # Determine the OPCODE for the query. - op_code = (ord(data[2]) >> 3) & 15 + # Determine the OPCODE for the query. + op_code = (ord(data[2]) >> 3) & 15 - # OPCODE 0 == Standard query http://www.networksorcery.com/enp/protocol/dns.htm#Opcode - if op_code == 0: + # OPCODE 0 == Standard query http://www.networksorcery.com/enp/protocol/dns.htm#Opcode + if op_code == 0: - # the raw packet has the name we are querying starting at byte 12 - byte_name_start=12 - byte_name_length=ord(data[byte_name_start]) - domain = '' + # the raw packet has the name we are querying starting at byte 12 + byte_name_start = 12 + byte_name_length = ord(data[byte_name_start]) + domain = '' - # set the frame to decode, as we are promarily interested in the - # first part of the question - frame_to_decode = data[byte_name_start + 1:byte_name_start + byte_name_length + 1] + # set the frame to decode, as we are promarily interested in the + # first part of the question + frame_to_decode = data[byte_name_start + 1:byte_name_start + byte_name_length + 1] - # Continue working with the rest of the request and process a response - # we will also lookup the state in ProcessFrame to determine the IP - # response we should be seeing - while byte_name_length != 0: + # Continue working with the rest of the request and process a response + # we will also lookup the state in ProcessFrame to determine the IP + # response we should be seeing + while byte_name_length != 0: + domain += data[byte_name_start + 1:byte_name_start + byte_name_length + 1] + '.' - domain += data[byte_name_start + 1:byte_name_start + byte_name_length + 1] + '.' + byte_name_start += byte_name_length + 1 + byte_name_length = ord(data[byte_name_start]) - byte_name_start+= byte_name_length + 1 - byte_name_length=ord(data[byte_name_start]) + print '[INFO] Full resource record query was for:', domain - print '[INFO] Full resource record query was for:', domain + # send the frame to the processor + frame_handler.set_data(frame_to_decode) + frame_handler.process() - # send the frame to the processor - frameHandler.setData(frame_to_decode) - frameHandler.process() + # prepare the response packet + response_packet = '' - # prepare the response packet - response_packet = '' + if domain: + response_packet += data[:2] + "\x81\x80" + response_packet += data[4:6] + data[4:6] + '\x00\x00\x00\x00' # Questions and Answers Counts + response_packet += data[12:] # Original Domain Name Question + response_packet += '\xc0\x0c' # Pointer to domain name + # Response type, ttl and resource data length -> 4 bytes + response_packet += '\x00\x01\x00\x01\x00\x00\x00\x3c\x00\x04' + response_packet += str.join('', map(lambda x: chr(int(x)), '127.0.0.1'.split('.'))) # 4bytes of IP - if domain: - response_packet+=data[:2] + "\x81\x80" - response_packet+=data[4:6] + data[4:6] + '\x00\x00\x00\x00' # Questions and Answers Counts - response_packet+=data[12:] # Original Domain Name Question - response_packet+='\xc0\x0c' # Pointer to domain name - response_packet+='\x00\x01\x00\x01\x00\x00\x00\x3c\x00\x04' # Response type, ttl and resource data length -> 4 bytes - response_packet+=str.join('',map(lambda x: chr(int(x)), '127.0.0.1'.split('.'))) # 4bytes of IP + sock.sendto(response_packet, addr) - sock.sendto(response_packet, addr) if __name__ == '__main__': - parser = optparse.OptionParser("usage: %prog -L ") - parser.add_option('-L', '--listen', dest='listen', - type='string', help='specify hostname to listen on') - parser.add_option('-p', '--port', dest='port', default=53, - type='int', help='port number to listen on (Defaults: 53)') - parser.add_option('-O', '--outfile', dest='out', default='', - type='string', help='specify a message file destination') - parser.add_option('-s', '--secret', dest='secret', default=False, - action='store_true', help='Set the secret used for the AES encryption') + parser = optparse.OptionParser("usage: %prog -L ") + parser.add_option('-L', '--listen', dest='listen', + type='string', help='specify hostname to listen on') + parser.add_option('-p', '--port', dest='port', default=53, + type='int', help='port number to listen on (Defaults: 53)') + parser.add_option('-O', '--outfile', dest='out', default='', + type='string', help='specify a message file destination') + parser.add_option('-s', '--secret', dest='secret', default=False, + action='store_true', help='Set the secret used for the AES encryption') - (options, args) = parser.parse_args() + (options, args) = parser.parse_args() - if not options.listen: - parser.error('At least a listening IP must be provided.') + if not options.listen: + parser.error('At least a listening IP must be provided.') - if options.secret: - secret = getpass.getpass(prompt='What is the secret? ') - else: - secret = None + if options.secret: + secret = getpass.getpass(prompt='What is the secret? ') + else: + secret = None - listening_ip = options.listen - listening_port = options.port - out_file = options.out + listening_ip = options.listen + listening_port = options.port + out_file = options.out - # kick off the main loop - main(listening_ip, listening_port, out_file, secret) + # kick off the main loop + main(listening_ip, listening_port, out_file, secret) diff --git a/dns_send.py b/dns_send.py index 0f99da8..33f06b7 100644 --- a/dns_send.py +++ b/dns_send.py @@ -1,132 +1,160 @@ -import dns.resolver +# The MIT License (MIT) +# +# Copyright (c) 2014-2016 Leon Jacobs +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import getpass import hashlib import optparse -import getpass + +import dns.resolver + from lib.Crypt import CryptString PAYLOADS_LENGTH = 60 # Change payload length to allow for a iterator -PAYLOADS_LENGTH = PAYLOADS_LENGTH - 4 +PAYLOADS_LENGTH -= 4 + def main(ip, port, file, identifier, xxd, secret): + with open(file) as to_send: + message = to_send.readlines() - with open(file) as to_send: - MESSAGE = to_send.readlines() + message = ''.join(message) - MESSAGE = ''.join(MESSAGE) + # based on if we have a secret, we will AES crypt it with it + if secret: + c = CryptString(secret) + message = c.encode(message) + print '[INFO] Message is encypted with the secret' - # based on if we have a secret, we will AES crypt it with it - if secret: - c = CryptString(secret) - MESSAGE = c.encode(MESSAGE) - print '[INFO] Message is encypted with the secret' + print '---START OF MESSAGE---' + print message + print '---END OF MESSAGE---' - print '---START OF MESSAGE---' - print MESSAGE - print '---END OF MESSAGE---' + # prepare the dns service + my_resolver = dns.resolver.Resolver(configure=False) + my_resolver.nameservers = [ip] + my_resolver.port = port - # prepare the dns service - my_resolver = dns.resolver.Resolver(configure=False) - my_resolver.nameservers = [ip] - my_resolver.port = port + if xxd: - if xxd: + # the payloads will not have a iterator, so add the 4 we negated earlier + # prepare the payload + payloads = [''.join(message.encode('hex')[i:i + PAYLOADS_LENGTH + 4]) for i in + range(0, len(message.encode('hex')), PAYLOADS_LENGTH + 4)] - # the payloads will not have a iterator, so add the 4 we negated earlier - # prepare the payload - payloads = [''.join(MESSAGE.encode('hex')[i:i+PAYLOADS_LENGTH + 4]) for i in range(0, len(MESSAGE.encode('hex')), PAYLOADS_LENGTH + 4)] + iteration = 0 + for payload in payloads: - iteration = 0 - for payload in payloads: + payload_to_send = payload + '.' + fake_domain - payload_to_send = payload + '.' + fake_domain + print '[INFO] Sending lookup for :', payload_to_send + answer = my_resolver.query(payload_to_send, 'A') + for res in answer: + pass - print '[INFO] Sending lookup for :', payload_to_send - answer = my_resolver.query(payload_to_send, 'A') - for res in answer: - pass + iteration += 1 + else: - iteration += 1 - else: + # prepare the payload + payloads = [''.join(message.encode('hex')[i:i + PAYLOADS_LENGTH]) for i in + range(0, len(message.encode('hex')), PAYLOADS_LENGTH)] - # prepare the payload - payloads = [''.join(MESSAGE.encode('hex')[i:i+PAYLOADS_LENGTH]) for i in range(0, len(MESSAGE.encode('hex')), PAYLOADS_LENGTH)] + # prepare the first, control payload explaining how many messages to expect... + handshake = ('0000' + str(len(payloads) + 2) + ':' + ('1' if secret else '0')).ljust(PAYLOADS_LENGTH, '0') - # prepare the first, control payload explaining how many messages to expect... - handshake = ('0000'+str(len(payloads) + 2 ) + ':' + ('1' if secret else '0')).ljust(PAYLOADS_LENGTH, '0') + # ...a message identifier... + identifier = ('0001' + (identifier.encode('hex'))).ljust(PAYLOADS_LENGTH, '0') - # ...a message identifier... - identifier = ('0001'+(identifier.encode('hex'))).ljust(PAYLOADS_LENGTH, '0') + # ...and the sha1 of the message + checksum = ('0002' + hashlib.sha1(message).hexdigest()) - # ...and the sha1 of the message - checksum = ('0002'+hashlib.sha1(MESSAGE).hexdigest()) + # lastly, a 'null' message indicating a complete transaction + complete = [''.ljust(PAYLOADS_LENGTH, '0')] - # lastly, a 'null' message indicating a complete transaction - complete = [''.ljust(PAYLOADS_LENGTH, '0')] + # add the 3 header requests to the payload and final 'null' one + payloads = [handshake, identifier, checksum] + payloads + complete - # add the 3 header requests to the payload and final 'null' one - payloads = [handshake, identifier, checksum] + payloads + complete + iteration = 0 + for payload in payloads: - iteration = 0 - for payload in payloads: + if payload.startswith('0000') or payload.startswith('0001') or payload.startswith('0002'): + payload_to_send = payload + '.' + fake_domain + else: + payload_to_send = str(iteration).rjust(4, '0') + payload + '.' + fake_domain - if payload.startswith('0000') or payload.startswith('0001') or payload.startswith('0002'): - payload_to_send = payload + '.' + fake_domain - else: - payload_to_send = str(iteration).rjust(4, '0') + payload + '.' + fake_domain + print '[INFO] Sending lookup for :', payload_to_send + answer = my_resolver.query(payload_to_send, 'A') + for res in answer: + if str(res) != '127.0.0.1': + print '[WARNING] Hmm, didnt get 127.0.0.1 as the response. Maybe you are not really talking to', \ + ip, '. We got', res + pass - print '[INFO] Sending lookup for :', payload_to_send - answer = my_resolver.query(payload_to_send, 'A') - for res in answer: - if str(res) <> '127.0.0.1': - print '[WARNING] Hmm, didnt get 127.0.0.1 as the response. Maybe you are not really talking to', UDP_IP, '. We got', res - pass + iteration += 1 - iteration += 1 + print '[INFO] Message sent in', iteration, 'requests' - print '[INFO] Message sent in', iteration, 'requests' if __name__ == '__main__': - parser = optparse.OptionParser("usage: %prog -S -F ") - parser.add_option('-S', '--server', dest='server', - type='string', help='specify dns server to send requests to') - parser.add_option('-F', '--file', dest='file', - type='string', help='specify the file to send') - parser.add_option('-I', '--indentifier', dest='ident', default='None', - type='string', help='specify a message indentifier') - parser.add_option('-X', '--xxd', dest='xxd', default=False, - action='store_true', help='Enable questions to be `xxd -r` friendly (60 chars long)') - parser.add_option('-s', '--secret', dest='secret', default=False, - action='store_true', help='Set the secret used for the AES encryption') - parser.add_option('-d', '--domain', dest='domain', default='fake.io', - type='string', help='fake zone to use for generated lookups') - parser.add_option('-p', '--port', dest='remote_port', default='53', - type='int', help='Remote listening port') - - (options, args) = parser.parse_args() - - if not options.server: - parser.error('A server IP must be provided.') - - if not options.file: - parser.error('A file to send must be specified') - - if len(options.ident.encode('hex')) > PAYLOADS_LENGTH - 4: - parser.error('The message identifier is too long.') - - if options.secret: - secret = getpass.getpass(prompt='What is the secret? ') - else: - secret = None - - server_ip = options.server - server_port = options.remote_port - file = options.file - identifier = options.ident - xxd = options.xxd - fake_domain = options.domain - - # kick off the main loop - main(server_ip, server_port, file, identifier, xxd, secret) \ No newline at end of file + parser = optparse.OptionParser("usage: %prog -S -F ") + parser.add_option('-S', '--server', dest='server', + type='string', help='specify dns server to send requests to') + parser.add_option('-F', '--file', dest='file', + type='string', help='specify the file to send') + parser.add_option('-I', '--indentifier', dest='ident', default='None', + type='string', help='specify a message indentifier') + parser.add_option('-X', '--xxd', dest='xxd', default=False, + action='store_true', help='Enable questions to be `xxd -r` friendly (60 chars long)') + parser.add_option('-s', '--secret', dest='secret', default=False, + action='store_true', help='Set the secret used for the AES encryption') + parser.add_option('-d', '--domain', dest='domain', default='fake.io', + type='string', help='fake zone to use for generated lookups') + parser.add_option('-p', '--port', dest='remote_port', default='53', + type='int', help='Remote listening port') + + (options, args) = parser.parse_args() + + if not options.server: + parser.error('A server IP must be provided.') + + if not options.file: + parser.error('A file to send must be specified') + + if len(options.ident.encode('hex')) > PAYLOADS_LENGTH - 4: + parser.error('The message identifier is too long.') + + if options.secret: + secret = getpass.getpass(prompt='What is the secret? ') + else: + secret = None + + server_ip = options.server + server_port = options.remote_port + file = options.file + identifier = options.ident + xxd = options.xxd + fake_domain = options.domain + + # kick off the main loop + main(server_ip, server_port, file, identifier, xxd, secret)