CTF Bây Giờ Là Đấu Token, Không Phải Đấu Não
Mỗi lần có ai nộp CV ghi “top 10 đội CTF toàn cầu,” tôi đều dừng lại đọc kỹ hơn. Không phải vì ấn tượng ngay, mà vì cần kiểm tra: giải nào, năm nào. Trước đây hai thông tin đó là đủ để tôi hình dung ra người này làm được gì. Cầm một binary lạ, không có tài liệu, không có hint, 48 tiếng để tìm flag - hoặc bạn hiểu thật sự, hoặc không có cửa.
Tôi không chắc kiểm tra đó còn đáng tin nữa không.
Câu chuyện trong một câu
--> // making it invisible to querySelectorAll. // // This inline script is NOT touched by Rocket Loader (no src, no type attr). // It rescues module scripts via two strategies: // 1. Query the DOM for type$="-module" + src (covers case A) // 2. Regex-parse the raw HTML for commented-out script tags (covers case B) // Dynamically-created scripts bypass Rocket Loader entirely. (function () { if (window.__markdyRescue) return; window.__markdyRescue = true; var rescued = false; function rescueModuleScripts() { if (rescued) return; rescued = true; var srcs = []; // Strategy 1: Rocket Loader kept the tag in DOM but changed the type. // type="module" → type="{uuid}-module" (still has src attribute) document.querySelectorAll('script[type$="-module"][src]').forEach(function (s) { srcs.push(s.src); }); // Strategy 2: Rocket Loader COMMENTED OUT the script tag entirely: // // These are invisible to querySelectorAll, so we parse the raw HTML. // We handle both attribute orderings (type-first or src-first). var html = document.documentElement.innerHTML; var reSrcFirst = //g; var reTypeFirst = //g; var m; while ((m = reSrcFirst.exec(html)) !== null) { srcs.push(m[1]); } while ((m = reTypeFirst.exec(html)) !== null) { srcs.push(m[1]); } // Re-inject each found src as a real module script. // Deduplicate first, then inject. Dynamically-created scripts bypass // Rocket Loader entirely. Modules with the same URL are only executed // once by the browser (cached), so re-injecting already-running scripts // is safe. var seen = {}; srcs.forEach(function (src) { if (seen[src]) return; seen[src] = true; var fix = document.createElement('script'); fix.type = 'module'; fix.src = src; document.head.appendChild(fix); }); } // Rescue when user clicks the placeholder (fallback if autoplay failed). document.addEventListener('click', function (e) { var t = e.target; if (t && typeof t.closest === 'function' && t.closest('.markdy-placeholder')) { rescueModuleScripts(); } }); // Rescue automatically after a short delay for autoplay. // Only fires if initAll() never ran (no data-markdy-init on any root). setTimeout(function () { if (document.querySelector('.markdy-root:not([data-markdy-init])')) { rescueModuleScripts(); } }, 1500); }());Kabir Acharya - từng thi đấu với TheHackersCrew và các đội top 10 thế giới, nhiều lần vô địch DownUnderCTF (giải CTF lớn nhất Úc) - viết rằng Claude Opus 4.5 và GPT-5.5 đã tự động hoá đủ phần của bảng xếp hạng CTF mở để nó không còn đo được kỹ năng bảo mật con người một cách đáng tin cậy nữa.
Tại sao bài này lên trang nhất HN
--> // making it invisible to querySelectorAll. // // This inline script is NOT touched by Rocket Loader (no src, no type attr). // It rescues module scripts via two strategies: // 1. Query the DOM for type$="-module" + src (covers case A) // 2. Regex-parse the raw HTML for commented-out script tags (covers case B) // Dynamically-created scripts bypass Rocket Loader entirely. (function () { if (window.__markdyRescue) return; window.__markdyRescue = true; var rescued = false; function rescueModuleScripts() { if (rescued) return; rescued = true; var srcs = []; // Strategy 1: Rocket Loader kept the tag in DOM but changed the type. // type="module" → type="{uuid}-module" (still has src attribute) document.querySelectorAll('script[type$="-module"][src]').forEach(function (s) { srcs.push(s.src); }); // Strategy 2: Rocket Loader COMMENTED OUT the script tag entirely: // // These are invisible to querySelectorAll, so we parse the raw HTML. // We handle both attribute orderings (type-first or src-first). var html = document.documentElement.innerHTML; var reSrcFirst = //g; var reTypeFirst = //g; var m; while ((m = reSrcFirst.exec(html)) !== null) { srcs.push(m[1]); } while ((m = reTypeFirst.exec(html)) !== null) { srcs.push(m[1]); } // Re-inject each found src as a real module script. // Deduplicate first, then inject. Dynamically-created scripts bypass // Rocket Loader entirely. Modules with the same URL are only executed // once by the browser (cached), so re-injecting already-running scripts // is safe. var seen = {}; srcs.forEach(function (src) { if (seen[src]) return; seen[src] = true; var fix = document.createElement('script'); fix.type = 'module'; fix.src = src; document.head.appendChild(fix); }); } // Rescue when user clicks the placeholder (fallback if autoplay failed). document.addEventListener('click', function (e) { var t = e.target; if (t && typeof t.closest === 'function' && t.closest('.markdy-placeholder')) { rescueModuleScripts(); } }); // Rescue automatically after a short delay for autoplay. // Only fires if initAll() never ran (no data-markdy-init on any root). setTimeout(function () { if (document.querySelector('.markdy-root:not([data-markdy-init])')) { rescueModuleScripts(); } }, 1500); }());Vì Kabir không phải người mới viết bài câu view. Anh ta ở đỉnh của game. Và anh ta mô tả cụ thể hai mốc khiến cuộc chơi thay đổi:
-
Claude Opus 4.5 - “gần như mọi thử thách độ khó trung bình, và một số thử thách khó, đều trở thành agent-solvable.” Bạn có thể spin up một Claude instance cho từng challenge qua CTFd API, để máy chạy giờ đầu, rồi chỉ cần tay vào những thứ còn lại.
-
GPT-5.5 - có thể one-shot các challenge “Insane difficulty” heap pwn trên HackTheBox. Một run được orchestrate tốt, chạy suốt 48 tiếng của một giải CTF mở, có xác suất cao tìm được flag trước bạn.
Đó không còn là “AI như công cụ hỗ trợ.” Đó là “bảng xếp hạng đang đo một phần ai có budget token nhiều hơn.”
HN tranh luận gì
--> // making it invisible to querySelectorAll. // // This inline script is NOT touched by Rocket Loader (no src, no type attr). // It rescues module scripts via two strategies: // 1. Query the DOM for type$="-module" + src (covers case A) // 2. Regex-parse the raw HTML for commented-out script tags (covers case B) // Dynamically-created scripts bypass Rocket Loader entirely. (function () { if (window.__markdyRescue) return; window.__markdyRescue = true; var rescued = false; function rescueModuleScripts() { if (rescued) return; rescued = true; var srcs = []; // Strategy 1: Rocket Loader kept the tag in DOM but changed the type. // type="module" → type="{uuid}-module" (still has src attribute) document.querySelectorAll('script[type$="-module"][src]').forEach(function (s) { srcs.push(s.src); }); // Strategy 2: Rocket Loader COMMENTED OUT the script tag entirely: // // These are invisible to querySelectorAll, so we parse the raw HTML. // We handle both attribute orderings (type-first or src-first). var html = document.documentElement.innerHTML; var reSrcFirst = //g; var reTypeFirst = //g; var m; while ((m = reSrcFirst.exec(html)) !== null) { srcs.push(m[1]); } while ((m = reTypeFirst.exec(html)) !== null) { srcs.push(m[1]); } // Re-inject each found src as a real module script. // Deduplicate first, then inject. Dynamically-created scripts bypass // Rocket Loader entirely. Modules with the same URL are only executed // once by the browser (cached), so re-injecting already-running scripts // is safe. var seen = {}; srcs.forEach(function (src) { if (seen[src]) return; seen[src] = true; var fix = document.createElement('script'); fix.type = 'module'; fix.src = src; document.head.appendChild(fix); }); } // Rescue when user clicks the placeholder (fallback if autoplay failed). document.addEventListener('click', function (e) { var t = e.target; if (t && typeof t.closest === 'function' && t.closest('.markdy-placeholder')) { rescueModuleScripts(); } }); // Rescue automatically after a short delay for autoplay. // Only fires if initAll() never ran (no data-markdy-init on any root). setTimeout(function () { if (document.querySelector('.markdy-root:not([data-markdy-init])')) { rescueModuleScripts(); } }, 1500); }());Phản biện sắc nhất đến từ copx:
“Này giống như ai đó than vãn rằng làm phụ tùng máy móc đã bị tàn phá: thợ thủ công lành nghề từng làm tay bằng dụng cụ thủ công! Giờ đám gian lận CAD/CAM/CNC đã tự động hoá toàn bộ quy trình. Đây chỉ là tư duy Luddite.”
Nghe có vẻ hợp lý. Nhưng Kabir đã phản biện sẵn bằng ví dụ cờ vua, và tôi nghĩ đó là lập luận mạnh hơn.
Máy tính chơi cờ đã giỏi hơn mọi con người từ năm 1996. Nhưng cờ vua thi đấu vẫn sống khoẻ. Magnus Carlsen vẫn là nhân vật đáng xem, prize pool vẫn thật. Lý do đơn giản: engine bị cấm trong thi đấu. Chúng được dùng để luyện tập, phân tích, bình luận - không phải để đi nước trong trận.
Hãy tưởng tượng cho mỗi kỳ thủ mang Stockfish vào bàn đấu và dùng tự do. Kết quả còn có ý nghĩa không? Có còn ai care bảng xếp hạng không? Có còn sản sinh ra Magnus Carlsen tiếp theo không?
CTF không có quy tắc đó. Không thể enforce “cấm AI” trong một giải open online. Ban tổ chức đã thử. Thất bại. Frontier model không care các trick cũ. Quy định bị bỏ qua và không thể giám sát ở quy mô lớn.
Ví dụ CNC cũng bỏ sót điểm mấu chốt về việc học. Khi CNC thay thế gia công thủ công, con đường học thay đổi nhưng vẫn tồn tại. Lập luận khó hơn trong bài là: CTF cụ thể phục vụ như một cái thang - cách để người mới đo tiến bộ thật sự và để ngành tuyển dụng tìm người tài. Khi các bậc thang bị tự động hoá, người mới có vẻ tiến bộ trong khi không học được gì.
kevinsimper có giải pháp thực tế: làm offline với laptop cung cấp sẵn, như CS2 esports chống cheat. Cách đó có thể work với finals top-tier - DEF CON đã có các format hạn chế thiết bị. Nhưng không cứu được giải open online là nơi hầu hết mọi người thật sự chơi.
Thiệt hại thực tế
--> // making it invisible to querySelectorAll. // // This inline script is NOT touched by Rocket Loader (no src, no type attr). // It rescues module scripts via two strategies: // 1. Query the DOM for type$="-module" + src (covers case A) // 2. Regex-parse the raw HTML for commented-out script tags (covers case B) // Dynamically-created scripts bypass Rocket Loader entirely. (function () { if (window.__markdyRescue) return; window.__markdyRescue = true; var rescued = false; function rescueModuleScripts() { if (rescued) return; rescued = true; var srcs = []; // Strategy 1: Rocket Loader kept the tag in DOM but changed the type. // type="module" → type="{uuid}-module" (still has src attribute) document.querySelectorAll('script[type$="-module"][src]').forEach(function (s) { srcs.push(s.src); }); // Strategy 2: Rocket Loader COMMENTED OUT the script tag entirely: // // These are invisible to querySelectorAll, so we parse the raw HTML. // We handle both attribute orderings (type-first or src-first). var html = document.documentElement.innerHTML; var reSrcFirst = //g; var reTypeFirst = //g; var m; while ((m = reSrcFirst.exec(html)) !== null) { srcs.push(m[1]); } while ((m = reTypeFirst.exec(html)) !== null) { srcs.push(m[1]); } // Re-inject each found src as a real module script. // Deduplicate first, then inject. Dynamically-created scripts bypass // Rocket Loader entirely. Modules with the same URL are only executed // once by the browser (cached), so re-injecting already-running scripts // is safe. var seen = {}; srcs.forEach(function (src) { if (seen[src]) return; seen[src] = true; var fix = document.createElement('script'); fix.type = 'module'; fix.src = src; document.head.appendChild(fix); }); } // Rescue when user clicks the placeholder (fallback if autoplay failed). document.addEventListener('click', function (e) { var t = e.target; if (t && typeof t.closest === 'function' && t.closest('.markdy-placeholder')) { rescueModuleScripts(); } }); // Rescue automatically after a short delay for autoplay. // Only fires if initAll() never ran (no data-markdy-init on any root). setTimeout(function () { if (document.querySelector('.markdy-root:not([data-markdy-init])')) { rescueModuleScripts(); } }, 1500); }());Ngoài bảng xếp hạng, Kabir liệt kê những tổn thất cụ thể:
- Plaid CTF ngừng tổ chức vào năm 2026, một trong những giải được tôn trọng nhất cộng đồng.
- Tác giả challenge mất động lực. Dành nhiều tuần xây dựng thứ gì đó tinh tế thì có ít lý do hơn nếu agent ăn nó trong vài phút.
- Tín hiệu tuyển dụng đang suy giảm. Xếp hạng CTF từng là proxy đáng tin cho kỹ năng bảo mật. Giờ nó một phần là proxy cho budget API và mức độ sẵn sàng tự động hoá.
- Con đường cho người mới đang gãy. Nếu bảng xếp hạng open bị thống trị bởi agent run, người mới bị nudge về phía paste prompt trước khi họ xây dựng được instinct mà AI đang thay thế.
Câu cuối trong bài gây đau thật sự: “Nó cũng tạo thêm không gian cho các AI shill kiếm tiền từ sự suy tàn bằng cách bán lại các wrapper tầm thường cho cộng đồng đã tạo ra training data có giá trị.”
Đọc hay bỏ qua?
--> // making it invisible to querySelectorAll. // // This inline script is NOT touched by Rocket Loader (no src, no type attr). // It rescues module scripts via two strategies: // 1. Query the DOM for type$="-module" + src (covers case A) // 2. Regex-parse the raw HTML for commented-out script tags (covers case B) // Dynamically-created scripts bypass Rocket Loader entirely. (function () { if (window.__markdyRescue) return; window.__markdyRescue = true; var rescued = false; function rescueModuleScripts() { if (rescued) return; rescued = true; var srcs = []; // Strategy 1: Rocket Loader kept the tag in DOM but changed the type. // type="module" → type="{uuid}-module" (still has src attribute) document.querySelectorAll('script[type$="-module"][src]').forEach(function (s) { srcs.push(s.src); }); // Strategy 2: Rocket Loader COMMENTED OUT the script tag entirely: // // These are invisible to querySelectorAll, so we parse the raw HTML. // We handle both attribute orderings (type-first or src-first). var html = document.documentElement.innerHTML; var reSrcFirst = //g; var reTypeFirst = //g; var m; while ((m = reSrcFirst.exec(html)) !== null) { srcs.push(m[1]); } while ((m = reTypeFirst.exec(html)) !== null) { srcs.push(m[1]); } // Re-inject each found src as a real module script. // Deduplicate first, then inject. Dynamically-created scripts bypass // Rocket Loader entirely. Modules with the same URL are only executed // once by the browser (cached), so re-injecting already-running scripts // is safe. var seen = {}; srcs.forEach(function (src) { if (seen[src]) return; seen[src] = true; var fix = document.createElement('script'); fix.type = 'module'; fix.src = src; document.head.appendChild(fix); }); } // Rescue when user clicks the placeholder (fallback if autoplay failed). document.addEventListener('click', function (e) { var t = e.target; if (t && typeof t.closest === 'function' && t.closest('.markdy-placeholder')) { rescueModuleScripts(); } }); // Rescue automatically after a short delay for autoplay. // Only fires if initAll() never ran (no data-markdy-init on any root). setTimeout(function () { if (document.querySelector('.markdy-root:not([data-markdy-init])')) { rescueModuleScripts(); } }, 1500); }());| Đọc bài gốc nếu… | Bỏ qua nếu… |
|---|---|
| Bạn từng chơi CTF nghiêm túc và muốn ai đó đặt tên cho cảm giác đang có | Bạn đã thấy AI-augmented competition là tiến hóa bình thường |
| Bạn tuyển dụng security engineer và vẫn cân CTF performance | Bạn cần giải pháp cụ thể - bài này là điếu văn, không phải playbook |
| Bạn muốn timeline chi tiết: GPT-4 → Opus 4.5 → GPT-5.5 → xong | Bạn tìm tài nguyên cho người mới (vào picoGym hoặc HackTheBox) |
Tôi rút ra gì
--> // making it invisible to querySelectorAll. // // This inline script is NOT touched by Rocket Loader (no src, no type attr). // It rescues module scripts via two strategies: // 1. Query the DOM for type$="-module" + src (covers case A) // 2. Regex-parse the raw HTML for commented-out script tags (covers case B) // Dynamically-created scripts bypass Rocket Loader entirely. (function () { if (window.__markdyRescue) return; window.__markdyRescue = true; var rescued = false; function rescueModuleScripts() { if (rescued) return; rescued = true; var srcs = []; // Strategy 1: Rocket Loader kept the tag in DOM but changed the type. // type="module" → type="{uuid}-module" (still has src attribute) document.querySelectorAll('script[type$="-module"][src]').forEach(function (s) { srcs.push(s.src); }); // Strategy 2: Rocket Loader COMMENTED OUT the script tag entirely: // // These are invisible to querySelectorAll, so we parse the raw HTML. // We handle both attribute orderings (type-first or src-first). var html = document.documentElement.innerHTML; var reSrcFirst = //g; var reTypeFirst = //g; var m; while ((m = reSrcFirst.exec(html)) !== null) { srcs.push(m[1]); } while ((m = reTypeFirst.exec(html)) !== null) { srcs.push(m[1]); } // Re-inject each found src as a real module script. // Deduplicate first, then inject. Dynamically-created scripts bypass // Rocket Loader entirely. Modules with the same URL are only executed // once by the browser (cached), so re-injecting already-running scripts // is safe. var seen = {}; srcs.forEach(function (src) { if (seen[src]) return; seen[src] = true; var fix = document.createElement('script'); fix.type = 'module'; fix.src = src; document.head.appendChild(fix); }); } // Rescue when user clicks the placeholder (fallback if autoplay failed). document.addEventListener('click', function (e) { var t = e.target; if (t && typeof t.closest === 'function' && t.closest('.markdy-placeholder')) { rescueModuleScripts(); } }); // Rescue automatically after a short delay for autoplay. // Only fires if initAll() never ran (no data-markdy-init on any root). setTimeout(function () { if (document.querySelector('.markdy-root:not([data-markdy-init])')) { rescueModuleScripts(); } }, 1500); }());Cách đây không lâu, tôi hay dùng CTF history để phân biệt người thật sự hiểu bảo mật với người chỉ đọc về nó. Đó là tín hiệu tốt vì nó đắt - tốn não, tốn thời gian, không thể mua bằng bằng cấp hay title.
Bây giờ tín hiệu đó đang rẻ đi nhanh chóng. Nó không mất hoàn toàn qua đêm, nhưng xu hướng rõ.
Điều thật sự mất không chỉ là bảng xếp hạng. Đó là cái thang - con đường mà người trẻ tò mò có thể leo từ “tôi không biết gì về bảo mật” lên “tôi tìm được lỗ hổng trong kernel.” Cái thang đó đang thiếu bậc.
Bảng xếp hạng không nói dối. Nó chỉ ngừng đo thứ nó từng đo.
Thảo luận trên Hacker News · Nguồn: kabir.au · Đăng bởi frays
Hoang Yell
Một nhà phát triển phần mềm và là người kể chuyện kỹ thuật. Tôi đọc Hacker News mỗi ngày và kể lại những câu chuyện hay nhất ở đây — bằng tiếng Việt và tiếng Anh, cho người tò mò nhưng không có thời gian.