Dekkapok.Engineering

● 2026-10-29

เมื่อ AI เจาะโค้ด Bitcoin Core: ผ่า 3 ช่องโหว่ DoS และจุดเปลี่ยนของนโยบายเปิดเผยบั๊ก

#bitcoin #security #cplusplus #ai #dos #networking

เมื่อ AI เจาะโค้ด Bitcoin Core: ผ่า 3 ช่องโหว่ DoS และจุดเปลี่ยนของนโยบายเปิดเผยบั๊ก
FILE: bitcoin-core-dos-ai-vulnerabilities.md
SIZE: 56.1 KB
SHA256: 0xce68ce29
CHARS: 31882
WORDS: 2912
READ TIME: 80 min

1. The Paradox: เมื่อป้อมปราการที่แข็งแกร่งที่สุดในโลก ถูก AI เจาะด้วยพรอมต์เดียว

เป็นเวลากว่า 15 ปีที่วงการซอฟต์แวร์ระดับโลกลดความโอหังลงทุกครั้งเมื่อเอ่ยถึง Bitcoin Core

นี่ไม่ใช่แอปพลิเคชันเว็บธรรมดา และไม่ใช่ Microservice ทั่วไปที่พังแล้วสั่ง Restart Pod ใน Kubernetes ได้ แต่มันคือซอฟต์แวร์กระจายศูนย์ที่แบกรับมูลค่าทางเศรษฐกิจระดับแสนล้านดอลลาร์ โค้ดภาษา C++ ทุกบรรทัดถูก Review ซ้ำแล้วซ้ำเล่าโดยวิศวกรระบบและนักเข้ารหัสลับระดับโลก มีระบบ Automated Fuzzing ด้วย Google OSS-Fuzz ทำงานตลอด 24 ชั่วโมง และยึดหลักการขั้นเด็ดขาดว่า “หากมีบั๊กแม้แต่ไบต์เดียว เครือข่ายอาจเกิด Unintended Hard Fork หรือเงินของผู้ใช้อาจสูญหาย”

คอมมูนิตี้เชื่อมั่นมาตลอดว่า ช่องโหว่เชิงตรรกะใน State Machine ที่ซับซ้อนของเครือข่าย Peer-to-Peer (P2P) คือสิ่งที่สายตามนุษย์เท่านั้นจะตรวจจับได้ และหากมีใครค้นพบบั๊กเหล่านั้น โครงการจะใช้มาตรการ Vulnerability Embargo (การปิดข้อมูลช่องโหว่ไว้เป็นความลับ) เป็นเวลานานนับปี เพื่อให้เวลา Node Operator ทั่วโลกทยอยอัปเกรดเวอร์ชันอย่างเงียบ ๆ

🛑 จุดเปลี่ยนสำคัญในปลายเดือนตุลาคม 2026: เครื่องมือ AI Code Scanner อัตโนมัติชื่อ Project Loupe พัฒนาโดย Elias Rohrer แห่งองค์กร Spiral (ในเครือ Block) ได้สแกน Codebase ของ Bitcoin Core ผลปรากฏว่า AI ตัวนี้ขุดพบช่องโหว่ระดับ Denial of Service (DoS) และ Memory Exhaustion ถึง 3 จุด ซึ่งเปิดช่องให้ Remote Peer ปลายทางสามารถสั่ง Crash หรือทำให้เกิด Memory Exhaustion ในโหนดเหยื่อได้

การค้นพบนี้บีบให้ทีมผู้ดูแลต้องปล่อย Emergency Point Releases ครอบคลุมถึง 4 สาขาเวอร์ชัน (v29.5, v30.4, v31.2, และ v32.1) พร้อมทั้งนำไปสู่การแก้ไข Bitcoin Core Security Disclosure Policy เพื่อเปิดข้อยกเว้นฉุกเฉินสำหรับช่องโหว่ที่ “AI ค้นพบได้ง่าย”


Executive Summary: สรุปภาพรวมสำหรับผู้ดูแลโหนด (Node Operators)

ช่องโหว่หมายเลข CVEระดับความรุนแรงระบบที่ได้รับผลกระทบสาขาที่ได้รับการแพตช์คำแนะนำเร่งด่วน
TCP Urgent Data DoSCVE-2026-117350MediumWindows, macOS, BSD (Linux ปลอดภัย)29.5, 30.4, 31.2, 32.1อัปเกรดเป็น v32.1 ทันที หรือย้ายโหนดไปรันบน Linux
Pre-BIP31 Stalled PeersCVE-2026-117349Mediumทุกระบบปฏิบัติการ (All OS)29.5, 30.4, 31.2, 32.1อัปเกรดทันที; เปิด -blocksonly เพื่อลด Exposure
Undo File OverflowCVE-2026-117346LowWindows, 32-bit OS, FAT3229.5, 30.4, 31.2, 32.1ย้ายออกจาก FAT32 / 32-bit OS; อัปเกรดเป็น v32.1

💡 ข้อสังเกตเรื่อง Lifecycle: แม้ว่าจะมีแพตช์ v29.5 ออกมา แต่ทางโครงการ Bitcoin Core ได้ประกาศให้สาย v29.x สิ้นสุดอายุขัย (End-of-Life: EOL) อย่างเป็นทางการเมื่อวันที่ 27 ตุลาคม 2026 ดังนั้น คำแนะนำหลักสำหรับทุก Node Operator คือการอัปเกรดขึ้นสู่ v32.1 ซึ่งเป็นเวอร์ชันล่าสุดที่รองรับ


2. Bitcoin P2P Architecture: I/O Multiplexing และกลไก Backpressure

ในการทำงานของ Full Node โหนดจะต้องเปิด TCP Connections เชื่อมต่อไปยัง Peer ภายนอกหลายสิบจนถึงหลายร้อยโหนดพร้อมกัน เพื่อ Broadcast และ Relay ธุรกรรม (Transactions) รวมถึงบล็อกข้อมูล (Blocks) โดยสถาปัตยกรรมเครือข่ายภายในแยกส่วนระหว่าง Network Transport, P2P Message Handling, และ Consensus Validation:

flowchart TD
    subgraph OS_NET["OS Kernel Socket Layer"]
        RAW_SOCK["Incoming TCP Socket Buffer"]
        SELECT_POLL["I/O Multiplexer (poll on Linux / select on Non-Linux)"]
    end

    subgraph P2P_ENGINE["Bitcoin Core: Network Engine (CConnman)"]
        SOCK_WAIT["Sock::WaitMany()"]
        RECV_BUF["Peer Receive Queue (vRecvMsg)"]
        BACKPRESSURE{"Queue เกินเพดาน?<br/>(fPauseRecv == true?)"}
        DROP_READ["Pause Socket Read<br/>(ถอด Sock::RECV ออกจาก WaitMany)"]
    end

    subgraph MSG_PROCESSING["P2P Message Dispatcher"]
        DISPATCH["ProcessMessages()"]
    end

    subgraph DOWNSTREAM["Downstream Validation Paths"]
        direction LR
        MEMPOOL_PATH["Mempool Relay Engine<br/>(Unconfirmed Transactions)"]
        CONSENSUS_PATH["Consensus & Chainstate<br/>(UTXO Validation & Block Connect)"]
    end

    RAW_SOCK --> SELECT_POLL
    SELECT_POLL --> SOCK_WAIT
    SOCK_WAIT --> RECV_BUF
    RECV_BUF --> BACKPRESSURE
    BACKPRESSURE -- "Queue ล้น" --> DROP_READ
    BACKPRESSURE -- "Queue ปกติ" --> DISPATCH
    DISPATCH --> MEMPOOL_PATH
    DISPATCH --> CONSENSUS_PATH

    style BACKPRESSURE fill:#2d3748,stroke:#4a5568,stroke-width:2px,color:#fff
    style DROP_READ fill:#22543d,stroke:#38a169,stroke-width:2px,color:#fff
    style RAW_SOCK fill:#1a202c,stroke:#2d3748,stroke-width:2px,color:#fff
    style MEMPOOL_PATH fill:#1e1b19,stroke:#60a5fa,stroke-width:1.5px,color:#fff
    style CONSENSUS_PATH fill:#1e1b19,stroke:#e53e3e,stroke-width:1.5px,color:#fff

กลไก Network Backpressure คืออะไร?

เมื่อ Peer ฝั่งตรงข้ามส่งข้อมูลธุรกรรมเข้ามารวดเร็วกว่าที่ CPU ของโหนดจะ Deserialization และส่งต่อไปยัง Validation Engine ได้ทัน ข้อมูลดิบจะเริ่มสะสมในคิวรับข้อมูลภายในที่ชื่อว่า vRecvMsg

หากปล่อยให้คิวนี้ขยายตัวอย่างไร้ขีดจำกัด กระบวนการ bitcoind จะกิน RAM จนเครื่องเกิด Out-of-Memory และถูก Kernel สั่ง Termination ดังนั้น Bitcoin Core จึงสร้างกลไกป้องกันตัวที่เรียกว่า Socket Backpressure:

  1. เมื่อข้อมูลใน Receive Queue (vRecvMsg) พุ่งแตะเพดานที่กำหนด โหนดจะตั้งค่าแฟล็ก pnode->fPauseRecv = true
  2. ในรอบถัดไปของการตรวจสอบเครือข่าย ฟังก์ชัน GenerateWaitSockets() จะ ถอด Event การอ่าน (Sock::RECV) ออกจาก Socket นั้น
  3. เมื่อโหนดหยุดอ่านข้อมูลจาก Socket ข้อมูลใหม่จะค้างอยู่ใน TCP Receive Window ของ OS Kernel จนกระทั่ง Window เต็ม ฝั่งผู้ส่งจะถูกระงับการส่งข้อมูลตามมาตรฐาน TCP Flow Control

ทว่า กลไก Backpressure นี้ กลับถูกเจาะทะลุได้บนระบบปฏิบัติการบางตัว เพียงเพราะความไม่เท่าเทียมกันของ System Calls โบราณ


3. ผ่าโค้ด C++ บรรทัดต่อบรรทัด: 3 ช่องโหว่ DoS และแพตช์แก้ไขระดับบิต


ช่องโหว่ที่ 1: CVE-2026-117350 — ทะลวง Backpressure ด้วย TCP Urgent Data (Medium Severity)

Root Cause: ความไม่สอดคล้องระหว่าง poll() และ select()

ในซอร์สโค้ดไฟล์ src/util/sock.cpp ตัวฟังก์ชัน Sock::WaitMany() มีหน้าที่ตรวจสอบสถานะความพร้อมของ Sockets ผ่าน I/O Multiplexing:

  • บน Linux: โค้ดคอมไพล์เพื่อใช้งาน System Call สมัยใหม่อย่าง poll() ซึ่งบริหารจัดการผ่าน struct pollfd ได้อย่างแม่นยำ
  • บน Non-Linux (Windows, macOS, BSD): โค้ดยังคงพึ่งพา System Call ดั้งเดิมอย่าง select() และบริหารจัดการผ่านบิตเซ็ต fd_set

ข้อผิดพลาดร้ายแรงเกิดขึ้นในลูปจัดเตรียม fd_set ก่อนส่งเข้าคำสั่ง select():

// 🛑 โค้ดดั้งเดิมที่มีช่องโหว่ใน src/util/sock.cpp (ก่อน PR #36665)
bool Sock::WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const
{
    fd_set readfds, writefds, exceptfds;
    FD_ZERO(&readfds);
    FD_ZERO(&writefds);
    FD_ZERO(&exceptfds);

    SOCKET maxfd = 0;
    for (const auto& [sock, events] : events_per_sock) {
        const SOCKET fd = sock->Get();
        if (fd == INVALID_SOCKET) continue;

        if (events & Sock::RECV) {
            FD_SET(fd, &readfds);
        }
        if (events & Sock::SEND) {
            FD_SET(fd, &writefds);
        }

        // 💥 จุดบกพร่อง: exceptfds ถูกลงทะเบียนให้กับ "ทุก Socket" เสมอ!
        // แม้ว่า Socket นั้นจะติด Backpressure (ไม่ได้ร้องขอ Sock::RECV) ก็ตาม
        FD_SET(fd, &exceptfds);

        if (fd > maxfd) maxfd = fd;
    }

    int ret = select(maxfd + 1, &readfds, &writefds, &exceptfds, &tv);
    // ...
}

กลไกการโจมตี (Exploitation Flow)

ตัวพารามิเตอร์ช่องที่สามของ select() คือ exceptfds (Exceptional Conditions) ในโปรโตคอล TCP/IP สิ่งที่ทำให้ Socket เกิดสถานะนี้คือการมาถึงของ TCP Urgent Data (หรือ Out-of-Band Data: MSG_OOB)

sequenceDiagram
    autonumber
    actor Attacker as "Attacker (Malicious Peer)"
    participant Kernel as "Victim OS Kernel (Windows/macOS)"
    participant Select as "Sock::WaitMany (select backend)"
    participant Handler as "SocketHandlerConnected()"
    participant Mem as "RAM: CNode::vRecvMsg"

    Note over Attacker,Mem: โหนดเหยื่อตรวจพบ Receive Buffer เต็ม -> สั่ง Pause Recv
    Attacker->>Kernel: ส่งข้อมูลปกติจนชนเพดานคิว
    Select-->>Kernel: ถอน Socket ออกจาก readfds (ไม่ขออ่านแล้ว)
    Note right of Select: แต่ exceptfds ยังเฝ้าดู Socket นี้อยู่ตลอดเวลา!

    Attacker->>Kernel: ส่ง TCP Packet พร้อมตั้งค่า URG Flag (Urgent Data)
    Kernel->>Select: select() ถูกปลุก! รายงานว่า Socket มี Event ใน exceptfds
    Select->>Handler: รายงานสถานะว่า Socket มีความเคลื่อนไหว
    Handler->>Kernel: สั่งอ่านข้อมูลเพื่อเคลียร์สถานะ Urgent
    Kernel-->>Handler: คืนไบต์ข้อมูลออกมา
    Handler->>Mem: บังคับยัดข้อมูลเข้า vRecvMsg (Bypass Backpressure สำเร็จ!)
    Note over Mem: RAM ขยายตัวต่อเนื่องจนระบบเกิด Out-of-Memory
    Mem-->>Kernel: 💥 Host OOM -> Process ถูก Kernel Terminate / Crash!
  1. โหนดเปิดระบบ Backpressure: คิวรับข้อมูลเต็ม ฟังก์ชัน GenerateWaitSockets() ใน net.cpp จึง ไม่ใส่แฟล็ก Sock::RECV ให้กับ Socket ของผู้โจมตี เพื่อสั่งหยุดอ่านข้อมูล
  2. ผู้โจมตีส่งแพ็กเก็ต URG: ฝั่ง Attacker จงใจส่ง TCP Packet ที่เปิดแฟล็ก Urgent Pointer (URG) ใน TCP Header
  3. select() สะดุ้งตื่น: เนื่องจากบั๊กในลูป ตัวแปร exceptfds ยังคงเฝ้าดู Socket นี้อยู่ เมื่อมี Urgent Data เข้ามา ตัว OS จึงปลุกให้ select() คืนค่าทันที
  4. ลอบอ่านข้อมูล Bypass Backpressure: ฟังก์ชัน SocketHandlerConnected() ใน src/net.cpp เห็นว่ามี Event บน Socket จึงเรียก ReceiveMsgBytes() เพื่อดึงข้อมูลออกไปล้างสถานะ
  5. Memory Exhaustion DoS: ข้อมูลใหม่จะถูก Append เข้าสู่ vRecvMsg ซ้ำแล้วซ้ำเล่า ทำให้ขีดจำกัดหน่วยความจำล้มเหลว โหนดปลายทางบน Windows, macOS หรือ BSD จึงตกเป็นเหยื่อของการถูกสูบ RAM จน Crash ได้จากระยะไกล

การแก้ไขในโค้ด C++ (Pull Request #36665)

Antoine Poinsot (darosior) ได้แก้ไขการลงทะเบียน Event ให้สอดคล้องกันระหว่าง Backend:

// ✅ ซอร์สโค้ดที่ได้รับการแก้ไขใน src/util/sock.cpp (PR #36665)
    for (const auto& [sock, events] : events_per_sock) {
        const SOCKET fd = sock->Get();
        if (fd == INVALID_SOCKET) continue;

        // เฝ้าดูการอ่านเฉพาะเมื่อระบบขออีเวนต์ RECV
        if (events & Sock::RECV) {
            FD_SET(fd, &readfds);
            // 💡 แก่นของแพตช์: จะเฝ้าดู Exceptional Conditions (TCP Urgent Data)
            // ก็ต่อเมื่อ Node อยู่ในสภาวะพร้อมอ่านข้อมูลปกติ (Sock::RECV) เท่านั้น!
            FD_SET(fd, &exceptfds);
        }
        if (events & Sock::SEND) {
            FD_SET(fd, &writefds);
        }
    }

💡 Technical Analysis [Source Code]: การย้าย FD_SET(fd, &exceptfds) เข้าไปอยู่ใต้เงื่อนไข if (events & Sock::RECV) ทำให้เมื่อเกิด Backpressure ตัว exceptfds จะถูกถอดออกไปด้วย แม้แฮกเกอร์จะยิง TCP Urgent Data เข้ามา OS Kernel ก็จะไม่ปลุก select() ข้อมูลจะค้างเติ่งในหน้าต่าง TCP ของ Kernel จนฝั่งผู้ส่งติดตันไปเองตามกลไก Flow Control


ช่องโหว่ที่ 2: CVE-2026-117349 — สูบ RAM แบบเงียบเชียบด้วย Pre-BIP31 Stalled Peers (Medium Severity)

กลไก Send Queue และ Ping Timeout

เมื่อโหนดต้องการส่งข้อมูลให้เพียร์ภายนอก มันจะจัดเก็บข้อมูลลงใน Send Buffer ที่ชื่อว่า vSendMsg หากเพียร์ปลายทางดึงข้อมูลช้าจนคิวนี้โตเกินขนาดที่กำหนด โหนดจะสั่งตั้งสถานะระงับการส่งชั่วคราว: pnode->fPauseSend = true

อย่างไรก็ตาม เพื่อให้เครือข่ายรับรู้สถานะร่วมกันได้ โหนดยังคงต้องนำรายการแจ้งเตือนธุรกรรมใหม่ที่เพิ่งเข้ามาใน Mempool (เรียกว่า INV messages หรือ MSG_TX) ยัดเข้าสู่คิวส่งของเพียร์รายนี้อยู่ตลอดเวลา

ตามปกติแล้ว โหนดจะมีระบบตัดการเชื่อมต่ออัตโนมัติภายใต้ข้อกำหนด BIP31 (Pong Message):

  • ทุก ๆ 2 นาที โหนดจะส่งข้อความ ping พร้อมแนบรหัสสุ่ม 64 บิต (Nonce)
  • เพียร์ปลายทางต้องส่งข้อความ pong พร้อม Nonce เดิมกลับมาภายใน 20 นาที (TIMEOUT_INTERVAL)
  • หากเพียร์นิ่งเงียบเกิน 20 นาที โหนดจะสั่ง Disconnect ทันที ซึ่งจะทำการ Free หน่วยความจำใน Send Queue ทั้งหมดทิ้ง
flowchart TD
    subgraph NORMAL_PEER["Modern Peer (BIP31 Compliant)"]
        N_CONN["Connected Peer"] --> N_STALL["เจตนาแกล้งรับข้อมูลช้า (Pause Send)"]
        N_STALL --> N_PING["ส่ง BIP31 Ping (พร้อม Nonce)"]
        N_PING --> N_CHECK{"ตอบ Pong ภายใน 20 นาทีไหม?"}
        N_CHECK -- "ไม่ตอบ" --> N_DISC["Drop Connection ทันที!<br/>(Free Memory คืน RAM สู่ระบบ)"]
        N_CHECK -- "ตอบ" --> N_DRAIN["ระบาย Buffer ตามปกติ"]
    end

    subgraph ROGUE_PEER["Malicious Exploit Peer (Fake Pre-BIP31)"]
        R_CONN["Handshake: แจ้ง Version < 60000"] --> R_STALL["ปิด TCP Window หยุดอ่านข้อมูล"]
        R_STALL --> R_PAUSE["เข้าสู่สถานะ fPauseSend = true"]
        R_PAUSE --> R_EXEMPT["⚠️ ได้รับการยกเว้นไม่ต้องตรวจ Ping Timeout!<br/>(เพราะโหนดโบราณตอบ Pong ไม่เป็น)"]
        R_EXEMPT --> R_QUEUE["Mempool Relay ยัด INV ทรานแซกชันใหม่เข้าคิวเรื่อย ๆ"]
        R_QUEUE --> R_OOM["💥 RAM คิวส่งโตสะสม 2 - 5 MB/ชม. ต่อ 1 Connection"]
    end

    style N_DISC fill:#22543d,stroke:#38a169,stroke-width:2px,color:#fff
    style R_EXEMPT fill:#742a2a,stroke:#e53e3e,stroke-width:2px,color:#fff
    style R_OOM fill:#742a2a,stroke:#e53e3e,stroke-width:2px,color:#fff

The Pre-BIP31 Compatibility Loophole

โหนดรุ่นก่อน BIP31 (ซอฟต์แวร์รุ่นเก่ากว่าเวอร์ชันโปรโตคอล 60000) ไม่รู้จักคำสั่ง Pong เพื่อป้องกันไม่ให้โหนดสมัยใหม่ตัดการเชื่อมต่อโหนดรุ่นเดอะเหล่านี้ โค้ดดั้งเดิมของ Bitcoin Core จึงได้ ยกเว้นการตรวจสอบ 20-Minute Ping Timeout ให้กับเพียร์ใดก็ตามที่ระบุตนเองว่าเป็น Pre-BIP31!

ผู้โจมตีสามารถแสวงหาประโยชน์จากข้อยกเว้นนี้ได้โดย:

  1. เชื่อมต่อไปยังโหนดเหยื่อ แล้วส่งข้อความ version ปลอมเลขโปรโตคอลให้ต่ำกว่า 60000 (เช่น ส่งเลขเวอร์ชัน 31800)
  2. เมื่อเชื่อมต่อสำเร็จ ให้หยุดอ่านข้อมูลบน TCP Socket (เช่น กำหนด TCP Window Size เป็น 0)
  3. โหนดเหยื่อจะพบว่าคิวส่งเต็ม จึงตั้งค่า pnode->fPauseSend = true
  4. แต่โหนดเหยื่อไม่สามารถ Disconnect ได้ เนื่องจากเพียร์นี้อ้างว่าเป็น Pre-BIP31 ตัวจับเวลา 20 นาทีจึงถูกยกเว้น
  5. โหนดเหยื่อจะยังคงทำหน้าที่ Mempool Relay นำข้อความ INV ของธุรกรรมใหม่ทุกรายการไปต่อท้ายคิว vSendMsg ของแฮกเกอร์อย่างไม่รู้จบ

⚠️ Resource Drain Reality Check (การวิเคราะห์อัตราการสูบ RAM):

  • ตามที่ระบุใน Official Advisory: อัตราการโตของคิวอยู่ที่สูงสุดประมาณ 2 MB ต่อชั่วโมง (Inbound Peer) และ 5 MB ต่อชั่วโมง (Outbound Peer)
  • Time-to-OOM: หากผู้โจมตีเปิด 100 Inbound Connections อัตราการสูบ RAM รวมจะอยู่ที่ประมาณ 200 MB ต่อชั่วโมง
  • การจะกิน RAM จนเต็ม 8 GB ต้องใช้เวลาต่อเนื่องประมาณ 40 ชั่วโมง (เกือบ 2 วัน) ไม่ใช่ “เพียงไม่กี่ชั่วโมง” อย่างที่มักเข้าใจผิดกัน นี่คือการโจมตีแบบ Slow-drip Denial of Service ที่จงใจสูบหน่วยความจำอย่างช้า ๆ เพื่อหลบเลี่ยงระบบตรวจจับ Network Anomaly ทั่วไป

การแก้ไขในโค้ด C++ (Pull Request #36667)

Antoine Poinsot ได้อัปเดตระบบตรวจสอบเวลาของโหนดใน src/net.cpp โดยใช้ตัวแปร m_pause_send_since เทียบกับ NodeClock:

// ✅ โครงสร้างการตรวจสอบใน src/net.cpp ตามแนวทางของ PR #36667
// บังคับใช้เกณฑ์ Timeout กับเพียร์ที่ติด fPauseSend ยาวนานผิดปกติ
void CConnman::ThreadSocketHandler()
{
    // ...
    const auto current_time = NodeClock::now();
    for (CNode* pnode : m_nodes) {
        if (pnode->fPauseSend) {
            // หากเพิ่งติด Pause Send ให้บันทึกเวลาเริ่มต้น
            if (pnode->m_pause_send_since == NodeClock::time_point{}) {
                pnode->m_pause_send_since = current_time;
            } else if (current_time - pnode->m_pause_send_since > TIMEOUT_INTERVAL) {
                // บังคับ Disconnect ทันทีที่คิวค้างเติ่งเกิน 20 นาที!
                LogPrint(BCLog::NET, "Disconnecting peer %d: send queue paused for >%d seconds\n",
                         pnode->GetId(), Ticks<std::chrono::seconds>(TIMEOUT_INTERVAL));
                pnode->fDisconnect = true;
            }
        } else {
            pnode->m_pause_send_since = NodeClock::time_point{};
        }
    }
    // ...
}

💡 Technical Analysis [Source Code]: ด้วยการตรวจเช็ก m_pause_send_since โดยตรง โหนดจะไม่สนใจว่าเพียร์ปลายทางจะรองรับ BIP31 หรือไม่ หากคิวส่งติดสถานะค้างเกิน 20 นาที เพียร์รายนั้นจะถูกตัดการเชื่อมต่อทันที


ช่องโหว่ที่ 3: CVE-2026-117346 — Undo File Overflow เกิน 4 GiB (Low Severity)

กลไก Chain Reorganization และไฟล์ rev*.dat

เมื่อเกิด Chain Reorganization (Reorg) โหนดต้อง Disconnect บล็อกเดิม ออกจาก Chain Tip แล้วสลับไปใช้บล็อกใหม่ ในการนี้ โหนดต้องอ่านข้อมูล Undo Data จากไฟล์ดิสก์ชื่อ rev*.dat เพื่อนำ UTXO ในอดีตกลับมาคืนในฐานข้อมูล:

flowchart LR
    BLOCK["ไฟล์บล็อกดิบ<br/>blk00000.dat<br/>(จำกัดไม่เกิน 128 MiB)"]
    UNDO["ไฟล์ Undo Data<br/>rev00000.dat<br/>(เดิม: ใช้ uint32_t ไม่จำกัดขนาด!)"]

    subgraph OVERFLOW["💥 การเกิด Integer Wrap ที่ 4 GiB"]
        POS["ตำแหน่ง nPos ชน 4,294,967,296 ไบต์"]
        WRAP["uint32_t วนกลับเป็น 0!"]
        OVERWRITE["เขียนข้อมูลใหม่ทับหัวไฟล์ rev*.dat เดิม"]
        TRUNCATE["ไฟล์ถูกสั่ง Truncate เหลือแค่เศษสั้น ๆ<br/>(ข้อมูล Undo ในอดีตเสียหาย)"]
    end

    UNDO --> POS
    POS --> WRAP
    WRAP --> OVERWRITE
    OVERWRITE --> TRUNCATE

    style OVERFLOW fill:#1a202c,stroke:#e53e3e,stroke-width:2px,color:#fff
    style WRAP fill:#742a2a,stroke:#e53e3e,stroke-width:2px,color:#fff
    style TRUNCATE fill:#742a2a,stroke:#e53e3e,stroke-width:2px,color:#fff

ข้อบกพร่องของตัวแปร 32-bit

  1. ไฟล์บล็อก blk*.dat ถูกจำกัดขนาดไว้ที่ 128 MiB ต่อไฟล์ แต่ไฟล์ Undo Data rev*.dat ไม่มีตัวจำกัดขนาดของตัวเอง
  2. ธุรกรรมบางประเภทสามารถสร้างข้อมูล Undo Data ขนาดใหญ่กว่าขนาดบล็อกดิบหลายเท่า
  3. ภายในโครงสร้าง FlatFilePos เดิมใช้ตัวแปร 32 บิต: unsigned int nPos; (สูงสุด 4,294,967,295 ไบต์)
  4. เมื่อขนาดของไฟล์ rev*.dat พุ่งแตะ 4 GiB ตัวแปร nPos จะเกิดปรากฏการณ์ Integer Overflow (Wrap-around กลับมาที่ 0)
  5. การเขียนข้อมูลครั้งถัดไปจะ Seek กลับไปเขียนทับหัวไฟล์ และเมื่อไฟล์บล็อกปิดตัวลง คำสั่ง TruncateFile() จะตัดขนาดไฟล์ Undo ลงมาเหลือเพียงแค่เศษที่เหลือหลังจากการ Wrap ทำลายข้อมูลย้อนสถานะในอดีตทิ้ง
  6. หากเกิด Reorg ขึ้น โหนดจะไม่สามารถอ่าน Undo Data ได้ เกิด Error Unserialize: end of data และระบบจะสั่ง AbortNode() ทันที โหนดจะต้องรันคำสั่ง -reindex ซึ่งกินเวลานานหลายวัน

ความแตกต่างด้าน Feasibility ระหว่างแพลตฟอร์ม

  • บนระบบ 64-bit Unix (Linux, macOS): การโจมตีในสภาวะจริงทำได้ยากมากในระดับที่ไม่คุ้มค่าทางเศรษฐศาสตร์ (Low Severity) เนื่องจากต้องอาศัยการขุดบล็อกต่อเนื่องกันเป็นเวลาราว 1 เดือน และต้องชนะการแข่ง Reorg 2 บล็อกในตำแหน่งไฟล์ที่แม่นยำ
  • บน Windows (แม้กระทั่ง 64-bit): ตัวแปร long บน Windows CRT และฟังก์ชัน File Seeking แบบดั้งเดิมมีข้อจำกัดที่ 2 GiB เมื่อไฟล์โตเกิน 2 GiB ระบบจะปฏิเสธคำสั่งเขียนไฟล์และเกิด Write Failure ทันที โดยไม่จำเป็นต้องรอให้เกิด Reorg!
  • บนระบบปฏิบัติการ 32-bit หรือ FAT32 Filesystem: ชนขีดจำกัดทางฟิสิกส์ทันทีที่ 2 GiB (บน 32-bit OS) หรือ 4 GiB (บน FAT32) ทำให้โหนด Crash ทันที

การแก้ไขในโค้ด C++ (Pull Request #36669)

Antoine Poinsot ได้ปรับขนาดตัวแปรใน src/flatfile.h:

// ✅ ซอร์สโค้ดที่ได้รับการแก้ไขใน src/flatfile.h (PR #36669)
struct FlatFilePos {
    int nFile;
    // 💡 ขยายขนาดตัวแปรตำแหน่ง Offset จาก 32 บิตเป็น 64 บิตอย่างสมบูรณ์
    uint64_t nPos; 

    FlatFilePos() : nFile(-1), nPos(0) {}
    FlatFilePos(int nFileIn, uint64_t nPosIn) : nFile(nFileIn), nPos(nPosIn) {}

    // การ Serialize ข้อมูลลงดิสก์ใช้ CompactSize (VarInt) 
    // เพื่อรักษาความเข้ากันได้ของ Binary Format บนดิสก์ 100% ไม่บังคับให้ผู้ใช้ต้อง Reindex ใหม่!
    SERIALIZE_METHODS(FlatFilePos, obj) { 
        READWRITE(obj.nFile, Using<CompactSizeFormatter<true>>(obj.nPos)); 
    }
};

นอกจากนี้ยังได้อัปเดต 64-bit Seek Wrapper บน Windows (_fseeki64) เพื่อให้รองรับตำแหน่งเกิน 4 GiB ได้อย่างถูกต้อง


4. Cross-Platform Vulnerability Matrix: สรุปผลกระทบแยกตามสภาพแวดล้อม

แพลตฟอร์ม / สภาพแวดล้อมCVE-2026-117350
(TCP Urgent Data DoS)
CVE-2026-117349
(Pre-BIP31 Slow Drain)
CVE-2026-117346
(Undo File Overflow)
Linux (64-bit, ext4/xfs)🛡️ Immune (ปลอดภัย)
(ใช้ poll() backend)
⚠️ Vulnerable
(หากเปิดรับการเชื่อมต่อ P2P)
🟢 Extremely Low Feasibility
(ต้องใช้ Hashpower มหาศาล + Reorg)
macOS (64-bit, APFS)🛑 Vulnerable (Crash Risk)
(ใช้ select() backend)
⚠️ Vulnerable🟢 Extremely Low Feasibility
(ต้องใช้ Hashpower มหาศาล + Reorg)
Windows (64-bit, NTFS)🛑 Vulnerable (Crash Risk)
(ใช้ select() backend)
⚠️ Vulnerable⚠️ Vulnerable at 2 GiB
(ชนขีดจำกัด 32-bit seek ใน CRT)
BSD (FreeBSD, OpenBSD)🛑 Vulnerable (Crash Risk)
(ใช้ select() backend)
⚠️ Vulnerable🟢 Extremely Low Feasibility
32-bit OS (เช่น Raspberry Pi เก่า)ขึ้นกับ OS backend⚠️ Vulnerable🛑 Fatal at 2 GiB (OS Limit)
FAT32 Filesystem (USB/SD Card)ขึ้นกับ OS backend⚠️ Vulnerable🛑 Fatal at 4 GiB (FS Limit)
Node ที่รันโหมด -blocksonly🛑 Vulnerable (ถ้าไม่ใช่ Linux)🟡 Reduced Exposure
(ไม่รับ TX INV แต่ไม่การันตี 100%)
🟢 สถานะเดิม

5. Cascading Risks: การประเมินภัยคุกคามต่อเนื่องสู่ Lightning Network

ความเสี่ยงของช่องโหว่ DoS ใน Bitcoin Core สามารถสร้างผลกระทบข้างเคียงไปยัง Lightning Network (L2) ในฐานะ Conditional Threat Model (ภัยคุกคามแบบมีเงื่อนไข):

โหนด Lightning Network ต้องพึ่งพาโหนด Bitcoin Core ในฐานะ Trust Anchor (โหนดอ้างอิงความจริงของบล็อกเชน) เพื่อเฝ้าดูว่าคู่ Channel พยายามบรอดแคสต์สถานะเก่าที่ถูกยกเลิกไปแล้ว (Revoked State) หรือไม่

ที่สำคัญคือ ในช่วงเวลาเดียวกัน ซอฟต์แวร์โหนดหลักของ Lightning Network ทุกค่าย ไม่ว่าจะเป็น Core Lightning (CLN) โดย Blockstream, Lightning Dev Kit (LDK) โดย Spiral, และ Eclair โดย ACINQ ต่างก็ต้องออกแพตช์ฉุกเฉินพร้อมกันอย่างไม่เคยปรากฏมาก่อน เพื่ออุดช่องโหว่ด้าน Channel Re-establishment, Splicing Logic, และ State Reconciliation หลังจากการเชื่อมต่อหลุด ซึ่งถูกขุดพบโดยเครื่องมือสแกน AI ของกลุ่ม Bitcoin Red Team และ Project Loupe เช่นเดียวกัน เมื่อทั้ง Base Layer และ Layer-2 ต่างเผชิญกับคลื่นการตรวจสอบของ AI ความเสี่ยงจากการที่ L1 โหนดเหยื่อดับมืดจึงกลายเป็นประเด็นที่ต้องจับตามองอย่างยิ่ง

flowchart TD
    ATTACKER["ผู้ไม่หวังดี (Malicious Counterparty)"]
    L1_NODE["Bitcoin Core Full Node (บน Windows/macOS)"]
    LN_NODE["Lightning Network Routing Node<br/>(CLN / LDK / Eclair)"]
    BTC_CHAIN["Bitcoin Blockchain"]

    ATTACKER -->|"1. ยิง DoS สั่ง Crash โหนด L1"| L1_NODE
    L1_NODE -->|"💥 โหนดดับมืด (Downtime)"| L1_NODE
    LN_NODE -.->|"2. ขาดการเชื่อมต่อ L1 ชั่วคราว"| L1_NODE
    ATTACKER -->|"3. พยายามบรอดแคสต์สถานะเก่าโกงเงิน"| BTC_CHAIN
    LN_NODE -.->|"4. ส่ง Justice Tx ได้หรือไม่ขึ้นกับ Watchtower & CSV Timeout"| BTC_CHAIN

    style L1_NODE fill:#742a2a,stroke:#e53e3e,stroke-width:2px,color:#fff
    style ATTACKER fill:#1a202c,stroke:#e53e3e,stroke-width:2px,color:#fff

⚠️ Technical Nuance on L2 Risk [Security Implications]:

  • ช่องโหว่เหล่านี้ ไม่ได้อนุญาตให้ขโมยเหรียญ Bitcoin บน On-Chain ได้โดยตรง และไม่ได้ละเมิดกฎฉันทามติ (Consensus Rules)
  • การที่คู่เปิด Channel จะโกงเงินสำเร็จในกรณีที่โหนด L1 ดับ จะต้องขึ้นอยู่กับเงื่อนไขหลายประการ:
    1. โหนด L1 ต้องดับยาวนานจนพ้นช่วงเวลาพิพาท (Dispute CSV Timeout) ซึ่งปกติกินเวลาหลายร้อยบล็อก (1 ถึง 2 สัปดาห์)
    2. โหนด Lightning ของเหยื่อไม่ได้ตั้งค่า Watchtowers ภายนอกไว้ช่วยเฝ้าดู
  • ดังนั้น นี่จึงเป็นความเสี่ยงเชิงปฏิบัติการ (Operational Risk) ที่ผู้ดูแลโหนด Routing ควรป้องกันด้วยการมี Redundant Node Monitoring

6. Policy Shift: ข้อยกเว้นใหม่ของนโยบาย Embargo ในยุค LLM

สิ่งที่น่าสนใจไม่แพ้บั๊กในโค้ดคือ “การปรับตัวของนโยบายความปลอดภัยใน Bitcoin Core”

นโยบาย Embargo ดั้งเดิม vs ความเป็นจริงในยุค AI

ในอดีต โครงการ Bitcoin Core จะใช้นโยบายกักกันข้อมูลความปลอดภัย (Vulnerability Embargo):

  • บั๊กระดับ Low จะเปิดเผยข้อมูล 2 สัปดาห์หลังปล่อยแพตช์
  • บั๊กระดับ Medium และ High จะถูกปิดเป็นความลับจนกว่าเวอร์ชันสุดท้ายที่ได้รับผลกระทบจะหมดอายุขัย (End-of-Life: EOL) ซึ่งอาจนานถึง 1 ปีเต็ม

อย่างไรก็ตาม ในยุคที่มีโมเดลภาษาขนาดใหญ่ (LLM) หากนักพัฒนาทำการพุชโค้ดแก้บั๊ก (Backport Patch) ไปยัง Git Repository สาธารณะ โมเดล AI ของผู้ไม่ประสงค์ดีสามารถทำ Semantic Diff Analysis เพื่อวิเคราะห์เจตนาของแพตช์ แล้วสร้างสคริปต์ Exploit สำหรับเวอร์ชันที่ยังไม่ได้อัปเกรดได้ในเวลาอันรวดเร็ว

ข้อยกเว้น “Easily Discoverable Vulnerabilities”

เมื่อวันที่ 22 ตุลาคม 2026 ชุมชนนักพัฒนา Bitcoin Core ได้ควบรวมการอัปเดตนโยบาย Security Disclosure Policy โดยกำหนด ข้อยกเว้นเฉพาะสำหรับช่องโหว่ที่ค้นพบได้ง่าย (Easily Discoverable Exception):

💡 The Easily Discoverable Exception [Official Policy]:

  • นโยบาย Embargo ไม่ได้ถูกยกเลิกทั้งหมด: บั๊กที่ค้นพบได้ยากและต้องใช้เทคนิควิเคราะห์ชั้นสูง ยังคงได้รับการปกป้องภายใต้กระบวนการ Embargo ตามปกติ
  • ข้อยกเว้น: หากช่องโหว่ใดถูกประเมินว่า “สามารถค้นพบได้โดยง่ายด้วยการสแกนของ AI ทั่วไป” โครงการจะก้าวข้ามระยะเวลารอคอย 1 ปี โดยดำเนินการ:
    1. เร่ง Backport แพตช์ไปยังกิ่งเวอร์ชันที่ยังดูแลอยู่ทันที
    2. ปล่อย Point Releases พร้อมกันโดยเร็วที่สุด
    3. เผยแพร่ Security Advisory สู่สาธารณะพร้อมกับการปล่อยซอฟต์แวร์ทันที (Synchronous Disclosure)

จุดบอดระดับองค์กร: เมื่อแพตช์ปล่อยเร็วกว่าฐานข้อมูล CVE (Enterprise Tracking Lag)

การเร่งปล่อย Point Releases อย่างรวดเร็วส่งผลให้เกิด ช่องว่างทางปฏิบัติการ (Operational Friction) ครั้งใหญ่สำหรับสถาบันการเงินและผู้ดูแลระบบระดับ Enterprise:

  • ระบบ Enterprise Custody และ Exchange มักพึ่งพา Automated CVE Tracking Feeds (เช่น NVD หรือ MITRE) ในการสแกนและจัดตาราง Rollout แพตช์อัตโนมัติ
  • แต่เนื่องจาก Bitcoin Core จำเป็นต้องชิงความเร็วเผยแพร่ซอฟต์แวร์ตัดหน้าแฮกเกอร์ ทำให้หมายเลข CVE (CVE-2026-117350, CVE-2026-117349, CVE-2026-117346) ยังไม่ได้รับการอนุมัติและอัปเดตเข้าสู่ฐานข้อมูลสากลในทันทีที่โค้ดปล่อยออกมา
  • ส่งผลให้เกิด หน้าต่างความมืดบอดชั่วคราว (Temporary Blind Spot) สำหรับองค์กรที่รันโหนดบน Windows หรือ macOS เพราะระบบมอนิเตอร์อัตโนมัติไม่ได้ส่งเสียงเตือน สิ่งนี้สะท้อนว่าในยุคที่โอเพนซอร์สขยับตามความเร็วของ AI ฝั่ง Enterprise IT จำเป็นต้องหันมาติดตาม Repository Releases และ Developer Mailing List โดยตรง แทนที่จะรอฐานข้อมูล CVE เพียงอย่างเดียว

แรงกดดันทางเศรษฐกิจมหภาค: $12B Options Expiry และสปอต ETF

ความรวดเร็วในการคลอดแพตช์ภายในกรอบเวลาเพียง 5 วัน (23–28 ตุลาคม 2026) ยังเกิดขึ้นท่ามกลาง สภาวะความผันผวนทางเศรษฐกิจระดับประวัติศาสตร์:

  • ตลาดคริปโตในสัปดาห์นั้นกำลังจับตาสัญญา Bitcoin Options มูลค่ามหาศาลกว่า 12,000 ล้านดอลลาร์ ($12B Expiry) ที่กำลังจะหมดอายุ ควบคู่ไปกับกระแสเงินทุนไหลเข้าอย่างหนักใน Spot ETF (เช่น กองทุน IBIT ของ BlackRock ที่มียอด Inflow หลายร้อยล้านดอลลาร์ต่อวัน) และการประชุมกำหนดอัตราดอกเบี้ยของธนาคารกลางสหรัฐฯ (FOMC)
  • หากเกิดเหตุการณ์ DoS ถล่มโหนดจนเกิด Network Disruption ในช่วงเวลาที่ตลาดมี Position มหาศาลเช่นนี้ มันอาจจุดชนวน Liquidation Cascade และทำลายความเชื่อมั่นของนักลงทุนสถาบันอย่างรุนแรง การประสานงานปิดช่องโหว่และปล่อยแพตช์พร้อมกัน 4 สาขาอย่างทันท่วงทีก่อนที่จะมีการโจมตีในโลกจริง จึงช่วยสกัดกั้น Narrative Shock ที่อาจกระทบตลาดการเงินระดับโลกได้อย่างหวุดหวิด

7. Operational Hardening: แนวทางปฏิบัติสำหรับผู้ดูแลโหนด

Step 1: อัปเกรดเป็น v32.1 เป็นค่าเริ่มต้น

ตรวจสอบเวอร์ชันปัจจุบัน:

bitcoind -version
  • คำแนะนำหลัก: อัปเกรดขึ้นสู่ v32.1
  • สำหรับองค์กรที่ยังจำเป็นต้องตรึงเวอร์ชันเก่า ให้ขยับไปยัง Point Release ล่าสุดของสายนั้น: v31.2, v30.4, หรือ v29.5 (หมายเหตุ: v29.x เข้าสู่สถานะ EOL แล้ว)

Step 2: ตรวจสอบระบบปฏิบัติการและระบบไฟล์

  • หลีกเลี่ยงการรันโหนด Production บน Windows เพื่อตัดความเสี่ยงของโค้ดพาธ select()
  • ตรวจสอบว่าไดเรกทอรี blocks ไม่ได้อยู่บน FAT32:
df -Th ~/.bitcoin/blocks

หากพบว่าเป็น vfat หรือ fat32 ให้ย้ายไปยังไดรฟ์ที่เป็น ext4, xfs, หรือ zfs เพื่อป้องกันปัญหาไฟล์ Undo เกินขีดจำกัด

Step 3: พิจารณาเปิดโหมด -blocksonly เพื่อลด Exposure

หากโหนดของคุณไม่ได้ทำหน้าที่เป็น Mining Gateway หรือ Mempool Router:

# ใน bitcoin.conf
blocksonly=1

(ช่วยลดความเสี่ยงจาก Pre-BIP31 Transaction INV Flooding ได้อย่างมีนัยสำคัญ)

Step 4: ตัวอย่างการตั้งค่า Systemd Resource Controls (Illustrative Example)

การใช้ Systemd Cgroups ช่วยควบคุมไม่ให้หน่วยความจำขยายตัวจนกระทบเซอร์วิสอื่นบนเซิร์ฟเวอร์:

# ตัวอย่างการตั้งค่าใน /etc/systemd/system/bitcoind.service
# ⚠️ ข้อควรระวัง: ปรับแต่งตัวเลขให้เหมาะสมกับ RAM จริงของเครื่องคุณ
[Unit]
Description=Bitcoin Core Daemon
After=network.target

[Service]
ExecStart=/usr/local/bin/bitcoind -daemon=0 -conf=/etc/bitcoin/bitcoin.conf
User=bitcoin
Group=bitcoin
Type=simple
Restart=on-failure
RestartSec=30s

# ตัวอย่างการจำกัดหน่วยความจำสำหรับโฮสต์ที่มี RAM ขนาด 16GB ขึ้นไป
MemoryAccounting=true
MemoryHigh=12G
MemoryMax=14G

# ข้อควรระวัง: MemoryMax จะทำให้โหนดถูก Kill เมื่อแตะเพดาน 
# ช่วยกันเครื่องค้าง แต่จะเกิด Service Disruption ชั่วคราว

LimitNOFILE=32768

[Install]
WantedBy=multi-user.target

8. Lessons for Systems Engineers: บทเรียนวิศวกรรมระบบจาก Codebase บิตคอยน์

กรณีศึกษาทั้ง 3 ช่องโหว่นี้ให้บทเรียนสำคัญสำหรับวิศวกรที่พัฒนาระบบเครือข่าย, ฐานข้อมูล, และ Distributed Systems:

  1. Cross-Platform I/O Parity: System Calls ที่มีจุดประสงค์คล้ายกัน (เช่น poll และ select) อาจมีพฤติกรรมต่อ Exceptional Conditions ที่แตกต่างกันอย่างสิ้นเชิง การทดสอบเฉพาะบน Linux อาจทำให้โค้ดพาธบน Windows/BSD กลายเป็นช่องโหว่ Zero-Day
  2. Backpressure Integrity: เมื่อระบบส่งสัญญาณระงับการอ่าน (Pause Read) การระงับนั้นต้องครอบคลุม ทุก Event Pathway ใน Event Multiplexer ไม่ใช่ระงับเฉพาะ Read Set แล้วปล่อยให้ Exception Set ลักลอบอ่านข้อมูลได้
  3. Backward Compatibility Debt: โค้ดที่เขียนขึ้นเพื่อรองรับไคลเอนต์โบราณ (เช่น Pre-BIP31) มักกลายเป็นจุดอ่อนที่สุดของระบบ เพราะมันมักได้รับการยกเว้นจากการตรวจสอบด้านความปลอดภัยสมัยใหม่
  4. Integer Width in Long-Lived Storage: การเลือกใช้ uint32_t สำหรับเก็บตำแหน่งไฟล์อาจดูเพียงพอในวันแรก แต่ในระบบที่ทำงานต่อเนื่องเป็นทศวรรษ ข้อมูลที่โตอย่างไม่จำกัดจะชนเพดาน 4 GiB เสมอ การเปลี่ยนไปใช้ 64-bit และ VarInt ตั้งแต่เนิ่น ๆ จะช่วยป้องกันความเสียหายในระดับฐานข้อมูล

9. แหล่งข้อมูลอ้างอิงระดับ Canonical (References)

  1. Official Bitcoin Core Security Advisories (October 28, 2026):
  2. Bitcoin Core GitHub Pull Requests:
  3. Research Initiatives & Attribution:
    • Project Loupe — Automated AI Vulnerability Scanner for Bitcoin by Elias Rohrer at Spiral
    • Independent Researchers & Bitcoin Red Team: Lőrinc (@LORINC), Gregory Sanders, Andrew Dorman
SYSTEM LOG: TERMINAL CURL ACCESS ONLINE

Retrieve the raw markdown file or structured JSON of this post directly in your command line:

curl -sL https://dekkapok.engineering/blogs/bitcoin-core-dos-ai-vulnerabilities.txt
curl -sL https://dekkapok.engineering/blogs/bitcoin-core-dos-ai-vulnerabilities.json

← blogs ทั้งหมด