Kết luận cho chủ doanh nghiệp
- 1
Bốn lượt quét mù đều tìm đủ 13/13 lỗ cài sẵn và không báo nhầm đoạn mồi nào, 0 lần trên 28 lần kiểm. Trên mã tự viết cỡ một plugin, quét bằng AI dùng được làm vòng rà đầu tiên.
- 2
Hai model khác nhau ở mức nguy hiểm. Cùng một lỗ SQL, ba lượt claude-opus-5 chấm Thấp với tin cậy 35 đến 40% và nêu điều kiện lỗ thành khai thác được; lượt claude-sonnet-5 chấm Nghiêm trọng 98%. Thứ tự sửa phải đối chiếu với cấu hình máy chủ thật.
- 3
Lượt vá cho 13/13 bản vá đúng hướng, nhưng 6 bản kéo theo thay đổi ngoài plugin: cổng thanh toán, phần mềm kho, JavaScript, đường dẫn cũ, dữ liệu đã lưu. Bản vá cần chạy thử trên bản sao trước khi lên website.
Điểm chính: Claude tìm đủ 13/13 lỗ trong 4 lượt quét mù, 0 báo nhầm; chỗ cần kiểm lại là mức nguy hiểm và 6 bản vá đổi thứ ngoài plugin.
1Plugin cài sẵn 13 lỗ và 7 đoạn mồi viết đúng
Plugin đem quét tên mona-shop-demo 1.4.2, do MONA viết ngày 15/09/2026 chỉ để làm bộ test này và chưa từng chạy trên website nào. Nó làm những việc một cửa hàng online hay gắn thêm vào WordPress: khách tra đơn bằng mã đơn và số điện thoại, để lại đánh giá, tải hoá đơn, nhân viên kho tải ảnh sản phẩm lên, phần mềm kho gọi vào để cập nhật tồn. Sáu tệp PHP, 315 dòng, có chú thích tiếng Việt, có chỗ viết cẩn thận và có chỗ viết ẩu như mã thật.
MONA cài vào đó 13 lỗ, gồm 10 lỗ chính và 3 lỗ phụ. Đáp án ghi tệp, hàm và mức nguy hiểm của từng lỗ, viết xong trước lượt quét đầu tiên và để ngoài thư mục đem quét.
| Lỗ | Tệp | Hàm | Mức theo đáp án | Số lượt tìm thấy |
|---|---|---|---|---|
| Tiêm câu lệnh SQL | includes/class-orders.php | find() | Nghiêm trọng | 4/4 |
| Chèn mã vào trang (phản chiếu) | includes/class-orders.php | shortcode() | Cao | 4/4 |
| Xem đơn hàng của người khác | includes/class-orders.php | ajax_detail() | Cao | 4/4 |
| Chuyển hướng theo đường dẫn khách gửi | includes/class-orders.php | after_payment() | Trung bình | 4/4 |
| Chèn mã vào trang (lưu trong đánh giá) | includes/class-reviews.php | handle_submit() và hiển thị | Cao | 4/4 |
| Đọc file ngoài thư mục cho phép | includes/class-invoices.php | download() | Nghiêm trọng | 4/4 |
| Dựng lại đối tượng từ cookie | includes/class-invoices.php | restore_cart_snapshot() | Nghiêm trọng | 4/4 |
| Tải file tuỳ ý lên máy chủ | includes/class-import.php | ajax_upload() | Nghiêm trọng | 4/4 |
| Bắt máy chủ gọi tới địa chỉ lạ | includes/class-import.php | ajax_fetch_remote() | Trung bình | 4/4 |
| Đường REST không kiểm quyền | includes/class-sync.php | /stock | Cao | 4/4 |
| Thiếu mã chống giả mạo khi lưu cài đặt | admin/settings.php | form cài đặt | Trung bình | 4/4 |
| Thiếu mã chống giả mạo và thiếu kiểm quyền khi xoá đánh giá | admin/settings.php | admin_post_msd_clear_reviews | Cao | 4/4 |
| Khoá bí mật viết thẳng trong mã | mona-shop-demo.php | MSD_SYNC_TOKEN | Thấp đến Trung bình | 4/4 |
Phần khó của bộ test nằm ở 7 đoạn mồi. Mồi là đoạn mã nhìn giống chỗ có lỗ nhưng viết đúng: một câu SQL có nối chuỗi mà biến đã ép sang số nguyên, một hàm tải ảnh đi qua API media có giới hạn loại tệp, một đường REST có kiểm quyền đặt ngay cạnh đường REST bỏ ngỏ. Một công cụ chỉ dò từ khoá sẽ báo cả mồi, và mỗi dòng báo nhầm là thêm một việc kiểm lại vô ích cho người sửa mã.
| Mã | Tệp | Hàm | Vì sao an toàn |
|---|---|---|---|
| D1 | includes/class-orders.php | ajax_my_orders() | Có mã chống giả mạo; lọc theo người đang đăng nhập; truy vấn dùng prepare |
| D2 | includes/class-reviews.php | admin_filter_sql() | Biến nối vào SQL đã ép sang số nguyên |
| D3 | includes/class-invoices.php | invoice_path() | Đường dẫn dựng từ số nguyên; không lấy từ dữ liệu khách gửi |
| D4 | includes/class-import.php | ajax_upload_media() | Có mã chống giả mạo; kiểm quyền; tải qua API media có giới hạn loại file |
| D5 | includes/class-sync.php | /stock-report | Có kiểm quyền quản lý cửa hàng; câu SQL tĩnh |
| D6 | admin/settings.php | wp_body_open | Nội dung quản trị viên nhập được lọc bằng wp_kses_post |
| D7 | includes/class-reviews.php | handle_submit() | Có mã chống giả mạo |
Plugin được chép thành 4 bản riêng, mỗi lượt quét một bản để lượt sau không thấy vết của lượt trước. Mọi chú thích có thể mớm cho máy, kiểu ghi chú nói hàm này an toàn, bị xoá trước khi quét. Mỗi lượt chỉ được đọc đúng thư mục của mình, không sửa tệp, không ra mạng, và mỗi phát hiện phải ghi ai khai thác được, mức tin cậy, rồi tự tìm cách bác chính phát hiện đó trước khi giữ lại.
2Bốn lượt quét mù đều tìm đủ 13 lỗ, không báo nhầm mồi
Bốn lượt chạy trong ngày 15/09/2026 bằng Claude Code: ba lượt dùng claude-opus-5 và một lượt dùng claude-sonnet-5, model rẻ hơn. Mỗi lượt tốn từ 169.000 đến 184.000 token. MONA chấm từng lượt bằng tay theo đáp án, đếm lỗ tìm ra, lỗ bỏ sót và số lần báo nhầm mồi.
| Lượt | Model | Thời gian | Lỗ tìm ra | Mồi báo nhầm | Chấm lỗ SQL |
|---|---|---|---|---|---|
| Lượt 1 | claude-opus-5 | 55 giây | 13/13 | 0/7 | Thấp, tin cậy 35% |
| Lượt 2 | claude-opus-5 | 57 giây | 13/13 | 0/7 | Thấp, tin cậy 35% |
| Lượt 3 | claude-opus-5 | 50 giây | 13/13 | 0/7 | Thấp, tin cậy 40% |
| Lượt 4 | claude-sonnet-5 | 137 giây | 13/13 | 0/7 | Nghiêm trọng, tin cậy 98% |
Cả bốn lượt đều nêu đủ 13 lỗ, kể cả hai lỗ thiếu mã chống giả mạo trong trang quản trị mà MONA đoán sẽ bị sót vì chúng nằm lẫn trong mã giao diện. Không lượt nào báo nhầm mồi: 0 lần trên 28 lần kiểm. Lượt nào cũng chỉ ra lệnh xoá đánh giá vừa thiếu mã chống giả mạo vừa thiếu kiểm quyền, tức một tài khoản thường cũng gọi được.
Ba chi tiết chỉ thấy khi đọc hết bốn bản báo cáo. Cả bốn lượt cùng nêu rằng kiểm kiểu dữ liệu sau khi gọi unserialize là quá muộn, vì hàm huỷ của đối tượng đã chạy trước đó. Cả bốn nối hằng số khoá bí mật khai báo mà không dùng với đường REST đang bỏ ngỏ, nghĩa là đọc ra ý định của người viết mã. Và ba lượt Opus cùng hạ mức tin cậy của lỗ SQL, chuyện ở phần sau.
3Hai model tìm lỗ như nhau, chấm mức nguy hiểm khác nhau
Nếu chỉ nhìn cột lỗ tìm ra, hai model như nhau. Khác biệt nằm ở cột mức nguy hiểm của đúng một lỗ: câu truy vấn trong hàm find() nối thẳng mã đơn khách gõ vào SQL.
| Ba lượt claude-opus-5 | Lượt claude-sonnet-5 | |
|---|---|---|
| Mức nguy hiểm | Thấp | Nghiêm trọng |
| Mức tin cậy | 35 đến 40% | 98% |
| Lý do ghi trong báo cáo | WordPress tự thêm dấu gạch chéo vào dữ liệu gửi lên, nên ở cấu hình chuẩn dấu nháy bị thoát; lỗ thành khai thác được khi có ai thêm bước gỡ dấu gạch chéo, gọi hàm từ nguồn khác, hoặc cơ sở dữ liệu bật NO_BACKSLASH_ESCAPES | Mã đơn từ đường dẫn đi vào câu SQL mà chưa được thoát ký tự |
| Thời gian một lượt | Trung vị 55 giây | 137 giây |
| Số lần gọi công cụ | 4 | 12 |
Cả hai bản đều đúng ở chỗ hàm đó phải sửa. Chỗ lệch là thứ tự sửa. Một báo cáo 13 lỗ thì việc đầu tiên của đội kỹ thuật là xếp lỗ nào sửa trong hôm nay. Theo bản Sonnet, buổi sáng dồn vào một lỗ chưa khai thác được ở cấu hình hiện tại, trong khi lỗ đọc tệp ngoài thư mục cho phép, thứ cho người lạ tải về tệp cấu hình chứa mật khẩu cơ sở dữ liệu, nằm ngay bên dưới.
Lượt Sonnet còn chậm hơn 2,5 lần so với trung vị ba lượt Opus. Với việc rà mã trước khi bàn giao, MONA chọn model đắt hơn vì nó chấm đúng mức nguy hiểm theo cấu hình thật, số lỗ tìm ra thì hai model bằng nhau.
4Bản vá đúng hướng cả 13 lỗ, 6 bản đòi đổi thứ ngoài plugin
Sau bốn lượt quét, danh sách 13 lỗ được giao cho một lượt claude-opus-5 khác để vá, với ba điều kiện: không gỡ tính năng để né lỗi, giữ nguyên các hàm vốn viết đúng, và tự ghi lại thứ gì phải đổi theo. Lượt vá chạy 231 giây và mã đi từ 315 lên 492 dòng. MONA chấm bằng tay: đọc lại từng hàm, so với bản gốc, kiểm cú pháp cả sáu tệp bằng PHP 8.4.
Kết quả: 13/13 bản vá đúng hướng, 6 hàm an toàn giữ nguyên từng ký tự. Truy vấn đơn hàng chuyển sang $wpdb->prepare, tên tệp hoá đơn bị giam trong thư mục bằng realpath, đường REST tồn kho so khoá bằng hash_equals. Nhưng 6 bản vá kéo theo thay đổi nằm ngoài plugin, nên chép thẳng lên website đang bán hàng là có thứ ngừng chạy.
| Lỗ | Bản vá | Phải đổi thêm ở đâu |
|---|---|---|
| Chuyển hướng theo đường dẫn khách gửi | Chỉ cho chuyển về tên miền được khai báo | Phải khai thêm tên miền cổng thanh toán để khách quay về được sau khi trả tiền |
| Đường REST không kiểm quyền | So khoá bằng hash_equals; khoá bí mật chuyển ra khỏi mã nguồn | Phần mềm kho phải gửi kèm khoá; chưa sửa thì ngừng cập nhật tồn kho |
| Bắt máy chủ gọi tới địa chỉ lạ | Trả nội dung ảnh dạng base64 | Đoạn JavaScript đang gọi phải giải mã base64 |
| Thiếu mã chống giả mạo và thiếu kiểm quyền khi xoá đánh giá | Đòi mã chống giả mạo và quyền quản trị | Mọi đường dẫn cũ gọi lệnh xoá phải bọc wp_nonce_url |
| Chèn mã vào trang (lưu trong đánh giá) | Đánh giá chỉ nhận chữ thuần và xuống dòng | Đánh giá mất khả năng nhận thẻ HTML; dữ liệu cũ đã lưu cần một lượt dọn riêng |
| Đọc file ngoài thư mục cho phép | Giam tên file trong thư mục hoá đơn bằng realpath và suy quyền sở hữu | Bản vá tự ghi là đang đoán quy ước đặt tên file; nên thêm một cột trỏ file vào bảng đơn hàng |
Bản vá hoá đơn là ví dụ rõ nhất. Trong mã có hai quy ước đặt tên tệp hoá đơn mâu thuẫn nhau, và bảng đơn hàng không có cột nào trỏ tới tệp. Lượt vá chọn một cách suy quyền sở hữu, rồi tự ghi rằng nếu thực tế đặt tên theo kiểu kia thì khách sẽ bị chặn tải hoá đơn của chính mình. Đưa bản đó lên website đang chạy mà chưa thử trên bản sao là sáng hôm sau tổng đài nhận cuộc gọi hỏi vì sao không tải được hoá đơn.
5Rà mã tự viết bằng AI trước khi bàn giao, theo sáu bước
Cả năm lượt của MONA ngày 15/09/2026, bốn lượt quét và một lượt vá, tốn 8 phút 50 giây máy chạy. Phần tốn công là viết đáp án và chấm, và phần đó cho ra một con số của riêng doanh nghiệp. Sáu bước dưới đây lặp lại được với mã của bất kỳ công ty nào.
| Bước | Làm gì |
|---|---|
| Chọn mã đem rà | Plugin, theme và đoạn mã do đội trong nhà tự viết, hoặc mã nhận bàn giao từ đơn vị khác. Chỉ rà mã của chính doanh nghiệp, hoặc mã đã được bên sở hữu cho phép. |
| Cài lỗ biết trước | Nhờ người viết mã cài vài lỗ đã biết vào một bản sao và ghi đáp án ra ngoài thư mục quét. Không có đáp án thì mọi bản báo cáo đọc lên đều thuyết phục như nhau. |
| Đặt mồi | Thêm vài đoạn viết đúng mà nhìn giống lỗ, để đếm được số lần báo nhầm. |
| Quét mù nhiều lượt | Mỗi lượt một bản sao riêng, chỉ được đọc thư mục của mình, không sửa tệp, không ra mạng. |
| Chấm theo đáp án | Đếm lỗ tìm ra, lỗ bỏ sót, lần báo nhầm, rồi đối chiếu mức nguy hiểm với cấu hình máy chủ thật. |
| Vá trên bản sao | Đọc phần bản vá tự khai là phải đổi thêm ở đâu, chạy thử trên bản sao có dữ liệu mẫu, rồi mới đưa lên website đang chạy. |
Bước rà này đứng trong buổi nghiệm thu bảo mật, cùng các cửa kiểm tra ở bài về kiểm tra bảo mật trước nghiệm thu theo OWASP. Website đã chạy thì xem thêm những gì một website WordPress bị dò mỗi ngày, vì lỗ trong mã tự viết là thứ các lượt dò đó đi tìm.
6Giới hạn của phép đo
- Lỗ do MONA tự cài nên gọn hơn lỗ ngoài đời: mỗi lỗ nằm trong một hàm, không lỗ nào phải nối nhiều tệp mới thấy.
- Plugin chỉ 315 dòng. Mã của một website bán hàng thật thường lớn hơn nhiều, và tỉ lệ tìm ra ở quy mô đó chưa đo.
- Chưa có lỗ thuộc quy trình nghiệp vụ, như tính sai tiền khuyến mãi hay đặt trùng lịch, loại lỗ đọc mã khó bắt nhất.
- Bốn lượt chạy cùng một ngày, cùng một yêu cầu rà. Chưa đo lại sau khi model đổi phiên bản.
- MONA chạy Claude Code thường với yêu cầu rà bảo mật tự viết. Bài không kết luận gì về sản phẩm Claude Code Security mà Anthropic mở cho gói Enterprise và Team.
- Không quét website của khách hàng hay của MONA, và không công bố lỗ hổng của website đang chạy nào.
- Thời gian một lượt gồm cả lúc model đọc và lúc ghi báo cáo, đo trên một máy, một kết nối mạng.
7Phương pháp, dữ liệu và trích dẫn
Bộ test là MONA WP Sec Bench 1.0: plugin mona-shop-demo 1.4.2, sáu tệp PHP, 315 dòng, 13 lỗ cài sẵn và 7 đoạn mồi. Đáp án viết trước khi quét và để ngoài thư mục quét. Bốn lượt quét mù chạy ngày 15/09/2026 bằng Claude Code với cùng một yêu cầu rà bảo mật do MONA viết, mỗi lượt trên một bản sao riêng; một lượt vá chạy sau đó. MONA chấm tay từng lượt theo đáp án. Các tệp dữ liệu chép từ đáp án và bảng điểm MONA ghi trong ngày đo. Mã nguồn plugin không đăng vì nó chứa lỗ hổng cố ý.
Tải dữ liệu: Tổng quan JSON · Bốn lượt quét CSV · Mười ba lỗ CSV · Bảy mồi CSV · Vòng vá CSV
- Anthropic, Claude Code Security · công bố 20/02/2026; hơn 500 lỗ hổng, bản xem trước có giới hạn, model Claude Opus 4.6; tra 14/10/2026
- OWASP Top 10:2025 · tên các nhóm lỗi dùng khi xếp loại
- Bảng điểm và đáp án MONA WP Sec Bench 1.0 · ghi ngày 15/09/2026
Trích dẫn bài này
Nhóm kỹ thuật MONA Software (2026). Cho Claude quét lỗ hổng một plugin WordPress cài sẵn 13 lỗ. NC-31, bản 1.0. Công ty TNHH Phần mềm MONA. https://mona.software/nghien-cuu/claude-quet-lo-hong-plugin-wordpress/. Đo 15/09/2026, cập nhật 14/10/2026.
@techreport{mona_nc31_2026,
author = {{Nhóm kỹ thuật MONA Software}},
title = {Cho Claude quét lỗ hổng một plugin WordPress cài sẵn 13 lỗ},
institution = {Công ty TNHH Phần mềm MONA},
number = {NC-31, bản 1.0},
year = {2026},
month = oct,
url = {https://mona.software/nghien-cuu/claude-quet-lo-hong-plugin-wordpress/},
note = {Bài đo, đo 15/09/2026}
}














































