Tấn Công Embedding Trong Hệ Thống RAG - Phần 3: "Đảo Ngược Zero-Shot - Khi Mẫu Dò Và Tìm Kiếm Chùm Lên Tiếng"
Mở đầu: Bạn đã có chìa khóa, giờ làm sao mở ổ?
Tiếp tục câu chuyện từ Phần 2.
Bạn đã có trong tay embeddings.npy - kho báu gồm 31 vector số. Bạn đã xác định được "nhà sản xuất" của chiếc khóa - mô hình all-MiniLM-L6-v2. Bạn đã dùng pipeline phân loại 3 tầng để thu hẹp từ 31 xuống chỉ 3 mục tiêu đáng tấn công. Và bạn đã trích xuất riêng Chunk #23 - mục tiêu mà chúng ta sẽ tập trung vào.
Bây giờ đến phần thú vị nhất: làm thế nào để biến một mảng số float32 trở lại thành văn bản tiếng Anh có nghĩa?
Hãy tưởng tượng bạn tìm thấy một két sắt. Bạn biết nhà sản xuất (đã xác định mô hình). Bạn có bản thiết kế của két (embedding). Nhưng bạn không có mật khẩu. Làm sao để mở nó?
Có hai cách tiếp cận, giống như hai người thợ khóa với hai phương pháp khác nhau:
Cách 1 - Dùng "sách mã mẫu": Bạn có một cuốn sách dày chứa hàng trăm nghìn mật khẩu mẫu cho các loại két khác nhau. Bạn so sánh "dấu vân tay" của từng mật khẩu mẫu với dấu vân tay của két cần mở. Mật khẩu nào có dấu vân tay giống nhất - đó là mật khẩu đúng. Nhanh, nhưng nếu cuốn sách không có mật khẩu nào giống, bạn bó tay.
Cách 2 - Mò từng số một: Bạn bắt đầu với số đầu tiên, thử từ 0 đến 9, xem số nào làm cho "dấu vân tay" gần nhất. Sau đó số thứ hai, rồi số thứ ba... Cứ thế cho đến khi có cả dãy số. Chậm hơn, tốn sức hơn, nhưng không cần cuốn sách nào cả.
Cả hai cách đều không cần huấn luyện trước (zero-shot). Cả hai đều có thể giúp bạn mở két. Nhưng tùy tình huống, bạn sẽ chọn cách phù hợp. Phần này sẽ hướng dẫn bạn cả hai.
1. Tạo ngân hàng mẫu (Template Bank) - "Cuốn sách vàng" của kẻ tấn công
1.1. Tạo cuốn sách - Tự động sinh 500.000 câu mẫu
Ý tưởng rất đơn giản: Nếu bạn có đủ các câu mẫu bao phủ mọi chủ đề có thể, một trong số chúng sẽ có embedding gần với embedding mục tiêu.
Phát hiện chủ đề - "Bắt mạch" embedding mục tiêu:
Trước khi tạo mẫu, script cần biết chunk đang nói về chủ đề gì. Làm sao để biết?
Hãy tưởng tượng bạn có 20 "thẻ đánh dấu" (anchors) cho 20 chủ đề khác nhau. Mỗi thẻ là một cụm từ đặc trưng cho chủ đề đó:
- Chủ đề "đặt lại mật khẩu": "reset your password", "login portal"
- Chủ đề "kết nối database": "connection string", "postgresql://"
- Chủ đề "khóa API": "API key", "bearer token"
Script nhúng (embed) tất cả các thẻ này, rồi tính Cosine Similarity giữa embedding của chunk mục tiêu với từng thẻ. Chủ đề nào có độ tương đồng cao nhất - đó là chủ đề của chunk. Giống như bạn đưa một mùi hương lạ cho một chú chó nghiệp vụ, nó đánh hơi và chỉ ra đúng loại hoa. Phép thuật ở đây không phải phép thuật, mà là phép toán vector đơn giản: so sánh độ giống nhau giữa các vector số.
Cụ thể hơn: Script có sẵn 20 bộ "từ khóa đại diện" (signatures) cho 20 chủ đề doanh nghiệp phổ biến. Mỗi bộ gồm 5-6 cụm từ đặc trưng. Ví dụ:
- Chủ đề "đặt lại mật khẩu" (it_password): "reset your password", "login portal", "SSO authentication"
- Chủ đề "kết nối database" (database_connection): "connection string", "postgresql://", "DATABASE_URL"
- Chủ đề "khóa API" (api_developer): "API key", "bearer token", "endpoint"
Script nhúng các từ khóa này và tính điểm tương đồng trung bình với chunk mục tiêu. Chủ đề có điểm cao nhất sẽ được chọn.
Trong trường hợp của chúng ta, chunk #23 có độ tương đồng 0.4618 với chủ đề "đặt lại mật khẩu" - cao nhất trong 20 chủ đề. Script kết luận: "Đây là chunk về đặt lại mật khẩu!"
Bước 1: Tạo file generate_templates.py (link source code) để tự động sinh 500.000 câu mẫu:

Bước 2: Sau khi đã chuẩn bị xong template bank, chúng ta hãy tải xuống một danh sách từ khóa mật khẩu để phục vụ cho giai đoạn membership inference:
attacker@rag:~$ wget -O passwords.txt https://raw.githubusercontent.com/danielmiessler/SecLists/refs/heads/master/Passwords/Common-Credentials/100k-most-used-passwords-NCSC.txt
1.2. Mở két - Dùng ngân hàng mẫu và Membership Inference
Bây giờ chúng ta đã có "cuốn sách" với 500.000 câu mẫu. Công việc tiếp theo: so sánh embedding mục tiêu với tất cả mẫu, tìm mẫu khớp nhất, và dùng suy luận thành viên để điền vào vị trí {PASSWORD}.
Bước 1: Tạo file emb_fin.py (link script):
Script này giúp tìm ra mật khẩu chính xác từ vector embedding một cách nhanh hơn, chính xác hơn và không bị đánh lừa bởi các yếu tố gây nhiễu. Nó sẽ thực hiện tìm được mật khẩu chính xác thông qua 4 bước sau:
- Chọn mẫu linh hoạt (Không chọn bừa): Thay vì chỉ lấy đại 20 câu mẫu có điểm cao nhất (dễ bị trùng lặp nội dung), script sẽ tự động điều chỉnh ngưỡng điểm để chọn ra số lượng câu mẫu phù hợp nhất với thực tế.
- Lọc bỏ các mẫu "sinh đôi" (Tăng độ khách quan): Loại bỏ các câu mẫu có nội dung quá giống nhau. Giống như việc hỏi ý kiến từ 20 người khác nhau hoàn toàn, chứ không phải hỏi 20 người là anh em cùng một nhà.
- Lọc 2 vòng (Tăng tốc độ): Chạy thử toàn bộ danh sách 100.000 mật khẩu trên một vài câu mẫu trước để tìm ra các "ứng viên sáng giá", sau đó mới đưa các ứng viên này vào vòng chung kết. Cách này giúp script chạy nhanh hơn rất nhiều.
- Chấm điểm thông minh (Tránh nhìn nhầm): Thay vì chỉ nhìn vào điểm tổng quát, script đo xem chính từ mật khẩu giúp câu mẫu giống với target đến mức nào. Điều này tránh việc một mật khẩu bị chấm điểm cao vô lý chỉ vì nó vô tình chứa từ liên quan đến ngữ cảnh (ví dụ: mật khẩu chứa từ "mega" đạt điểm cao chỉ vì tên công ty là Megacorp).
Bước 2: Chạy pipeline:
attacker@rag:~$ python3 emb_fin.py chunk_23.npy --chunk 0 --templates templates.json --wordlist passwords.txt --slots PASSWORD --default-URL https://login.megacorpone.ai --max-templates 500000
Và đây là điều kỳ diệu xảy ra. Nhưng trước khi xem kết quả, hãy tự hỏi: "Nếu là bạn, bạn sẽ đoán mật khẩu là gì?" Một từ thông thường trong danh sách? Hay một từ có cấu trúc đặc biệt?
Kết quả mong đợi:

Giải thích: Pipeline này:
- Mã hóa tất cả 500.000 mẫu và tính độ tương tự với embedding mục tiêu
- Chọn 20 mẫu đa dạng nhất có điểm cao nhất (giống như chọn 20 nhân chứng đáng tin nhất). Để tránh chọn 20 câu quá giống nhau (ví dụ cùng nội dung chỉ khác dấu câu), script tính Cosine Similarity giữa các embedding của mẫu. Hai mẫu có độ tương đồng > 0.9 bị coi là trùng lặp và bị loại bỏ. Điều này đảm bảo 20 nhân chứng đưa ra các góc nhìn độc lập.
- Với mỗi mẫu, thử từng mật khẩu trong danh sách 99.839 từ
- Các mẫu bỏ phiếu,
N0=Acc3ssthắng với 18/20 đồng thuận - Z-score 6.50 nghĩa là ứng viên chiến thắng cách ứng viên tiếp theo 6.5 độ lệch chuẩn - rất đáng tin cậy
Về cơ chế bỏ phiếu:
Với mỗi mẫu, script thử tất cả từ trong danh sách (99.839 từ), chọn từ có Cosine Similarity cao nhất với embedding mục tiêu. Mỗi mẫu sẽ "bỏ phiếu" cho một từ. Sau khi 20 mẫu bỏ phiếu, từ nào nhận được nhiều phiếu nhất sẽ là mật khẩu dự đoán.
Trong kết quả này,
N0=Acc3ssnhận được 18/20 phiếu - một sự đồng thuận rất mạnh. Con số này cao hơn nhiều so với ngẫu nhiên (kỳ vọng chỉ ~2/20 nếu mọi từ đều ngẫu nhiên), cho thấy kết quả đáng tin cậy.
Như vậy, chúng ta đã mở được két bằng Cách 1!
Bước 3: Xác minh kết quả
Để chắc chắn rằng chúng ta không bị đánh lừa bởi một kết quả trùng hợp, hãy kiểm tra file PDF gốc trong hệ thống:
attacker@rag:~$ pdftotext MC1_password_reset.pdf
attacker@rag:~$ cat MC1_password_reset.txt
Hiển thị kết quả:
IT Security - Password Reset Procedure
Please navigate to https://login.megacorpone.ai and click on "Need help signing in". The default password after resetting is N0=Acc3ss which must be changed immediately upon first login.
Khớp hoàn toàn! Mật khẩu chúng ta phục hồi từ embedding chính là mật khẩu thật trong tài liệu. Đây là bằng chứng thuyết phục rằng embedding KHÔNG phải hàm băm một chiều - thông tin vẫn có thể được khôi phục.
2. Khi "cuốn sách" không đủ tốt
Nhưng có tình huống mà ngay cả 500.000 câu mẫu cũng không đủ.
Hãy tưởng tượng câu gốc có cấu trúc bất thường: "Attention: user mwilliams must change password to N0=Acc3ss before accessing mainframe" - cấu trúc này có thể không nằm trong 500.000 mẫu của bạn.
Trong trường hợp đó, Cách 1 sẽ cho độ tin cậy "YẾU" hoặc "KHÔNG CHẮC CHẮN".
Đó là lúc Cách 2 xuất hiện.
3. Khi ngân hàng mẫu thất bại - Tìm kiếm theo chùm (Beam Search)
Nhưng nếu ngân hàng mẫu của bạn không có cấu trúc nào khớp với văn bản gốc? Ví dụ, văn bản gốc là một cấu trúc bất thường không nằm trong 500,000 mẫu bạn đã tạo?
Đó là lúc tìm kiếm theo chùm xuất hiện. Thay vì dùng mẫu có sẵn, bạn xây dựng văn bản từ đầu, token-by-token.
Ý tưởng: Bắt đầu với một token, thử nhiều token tiếp theo, giữ lại những token làm cho embedding gần với mục tiêu nhất. Lặp lại 30-40 bước.
Nghiên cứu Zero2Text (arXiv 2602.01757v2) đã chứng minh rằng cách tiếp cận này có thể phục hồi văn bản có nghĩa từ embeddings mà không cần bất kỳ huấn luyện nào. Phương pháp này sử dụng một mô hình ngôn ngữ như GPT-2 hoặc Qwen3-0.6B để đề xuất token tiếp theo, và mô hình embedding mục tiêu để chấm điểm.
Về cơ bản, beam search hoạt động như sau: Ở mỗi bước, GPT-2 đề xuất 50 token tiếp theo có khả năng nhất dựa trên ngữ cảnh hiện tại. Với mỗi đề xuất, script nhúng câu mới và tính Cosine Similarity với embedding mục tiêu. 10 câu có điểm cao nhất được giữ lại cho bước tiếp theo. Sau 27 bước, câu có điểm cao nhất được chọn làm kết quả cuối cùng. Quá trình này giống như việc bạn xếp chữ mà không cần từ điển - mỗi bước đều thử nhiều khả năng và chỉ giữ lại những khả năng tốt nhất.
3.1. Xây dựng từng chữ một
Thay vì dùng mẫu có sẵn, Cách 2 xây dựng văn bản từ đầu, token-by-token, sử dụng GPT-2 để đề xuất các token tiếp theo và embedding model để chấm điểm.
Bước 1: Tạo file zero2text_impl.py (link):
Script này giúp phục hồi toàn bộ văn bản và dữ liệu ẩn từ vector embedding khi ngân hàng mẫu thất bại, bằng cách kết hợp thuật toán Tìm kiếm theo chùm (Beam Search) với kỹ thuật Suy luận thành viên (Membership Inference).
Chức năng chính bao gồm:
- Tái tạo khung cấu trúc (Beam Search): Dùng GPT-2 kết hợp mô hình embedding mục tiêu để dựng lại câu văn gốc từ đầu theo từng token (dù câu có cấu trúc lạ hoặc chưa từng xuất hiện trong mẫu).
- Nhận diện vùng nhạy cảm (Entropy & Regex Detection): Tự động phát hiện các chuỗi ngẫu nhiên phức tạp (như mật khẩu, API Key sk-, JWT eyJ...) nhờ phân tích Shannon entropy và regex, sau đó thay thế bằng các thẻ giữ chỗ ({PASSWORD}, {API_KEY}). -Kết hợp Pipeline điền giá trị (Slot Filling Engine): Đưa khung câu vừa tái tạo vào engine của emb_fin.py để quét wordlist và khôi phục chính xác giá trị bí mật ban đầu.
Bước 2: Chạy script:
attacker@rag:~$ python3 zero2text_impl.py chunk_23.npy --chunk 0 --templates templates.json --wordlist passwords.txt --slots PASSWORD --default-URL https://login.megacorpone.ai --max-steps 40 --beam-width 10 --device cuda
Kết quả mong đợi:

Giải thích: Beam search:
- Bắt đầu với token đầu tiên, thử 100 khả năng
- Giữ lại 10 khả năng tốt nhất (beam width=10)
- Tại mỗi bước, mở rộng mỗi khả năng với token tiếp theo
- Chấm điểm bằng độ tương tự cosine với embedding mục tiêu
- Sau 27 bước, hội tụ về văn bản có cấu trúc khớp 91.6%
- Phát hiện vùng entropy cao (
N0=Acc3ss) và dùng membership inference để xác nhận
Về việc phát hiện mật khẩu: Sau khi có cấu trúc câu, script phân tích entropy (độ ngẫu nhiên) của từng từ. Mật khẩu thường có entropy cao - nhiều lớp ký tự khác nhau (chữ hoa, chữ thường, số, ký tự đặc biệt) và không có nghĩa trong từ điển. Ví dụ: "Please" có entropy thấp, trong khi "N0=Acc3ss" có entropy cao (0.723). Đây là cách script biết đâu là mật khẩu cần phục hồi.
4. So sánh hai cách tiếp cận
| Tiêu chí | Cách 1: Ngân hàng mẫu (emb_fin.py) | Cách 2: Tìm kiếm theo chùm (zero2text_impl.py) |
|---|---|---|
| Cách làm | So sánh embedding của chunk mục tiêu với 500.000 câu mẫu có sẵn. Chọn 20 câu khớp nhất, dùng Membership Inference để bỏ phiếu chọn mật khẩu. | Xây dựng lại văn bản từ đầu, token-by-token, sử dụng GPT-2 để đề xuất token tiếp theo và embedding model để chấm điểm. |
| Thời gian chạy | Vài phút (nhanh, thường 2-5 phút cho 1 chunk) | Vài phút đến vài giờ (chậm hơn, tùy thuộc số bước beam search) |
| Yêu cầu phần cứng | CPU là đủ (có thể chạy trên máy tính thông thường) | BẮT BUỘC có GPU (CUDA, tối thiểu 8GB VRAM) |
| Cần cuốn sách mẫu? | Có - cần tạo 500.000 câu mẫu trước khi tấn công | Không - xây dựng văn bản từ đầu, không cần mẫu có sẵn |
| Code phức tạp | Đơn giản - logic so sánh vector, dễ hiểu và dễ sửa | Phức tạp hơn - cần hiểu beam search, attention mechanism, quản lý GPU memory |
| Dễ dàng triển khai | Rất dễ - chỉ cần pip install sentence-transformers và chạy script |
Khó hơn - cần cài CUDA, PyTorch với GPU support, quản lý bộ nhớ GPU |
| Độ chính xác điển hình | 60-92% - phụ thuộc nhiều vào chất lượng ngân hàng mẫu | 85-96% - thường cao hơn nếu beam search hội tụ thành công |
| Độ tin cậy của kết quả | Biến động - nếu có mẫu khớp tốt → rất tin cậy; nếu không → kết quả yếu | Ổn định hơn - vì xây dựng trực tiếp từ embedding, ít phụ thuộc vào mẫu có sẵn |
| Thành công khi | Ngân hàng mẫu có câu có cấu trúc tương tự với văn bản gốc | Văn bản không quá bất thường và có độ dài < 32 token |
| Thất bại khi | Không có mẫu nào khớp cấu trúc; Mẫu khớp nhưng sai từ vựng chuyên ngành | Văn bản quá dài (>32 token); Mật khẩu hoàn toàn ngẫu nhiên không có nghĩa (entropy cực cao) |
| Chi phí vận hành | Thấp - chỉ tốn thời gian CPU và bộ nhớ RAM (~2-4GB) | Cao - tốn GPU, điện năng, thời gian, và RAM lớn (~8-16GB) |
| Khả năng debug | Dễ - có thể xem từng mẫu được chọn và điểm số của từng ứng viên | Khó - khó theo dõi quá trình beam search nếu không có logs chi tiết |
| Khả năng mở rộng | Tốt - có thể tăng số lượng mẫu lên hàng triệu mà không ảnh hưởng nhiều đến hiệu năng | Hạn chế - beam search bị giới hạn bởi GPU memory và thời gian |
| Khả năng tái sử dụng | Cao - ngân hàng mẫu có thể tái sử dụng cho nhiều chunk cùng chủ đề | Thấp - mỗi chunk cần chạy lại beam search từ đầu |
| Khi nào nên dùng | Ưu tiên dùng trước - nhanh, nhẹ, không cần GPU. Phù hợp với các chủ đề phổ biến (mật khẩu, database, API) | Dùng khi Cách 1 cho kết quả yếu - cần độ chính xác cao hơn, có sẵn GPU và thời gian |
5. Một thực tế cần nhớ
Cả hai cách đều có giới hạn:
1. Độ dài token: Cả hai đều hoạt động tốt với văn bản ngắn (32-64 token). Với văn bản dài (256-512 token), kết quả kém chính xác hơn. Tin tốt: Mật khẩu, khóa API thường ngắn.
2. Mật khẩu ngẫu nhiên: N0=Acc3ss có nghĩa, nên mô hình có thể đoán được. 1qaz2wsx3edc4rfv ngẫu nhiên thuần túy - cả hai cách đều bó tay vì mô hình coi nó như nhiễu.
3. Cần biết mô hình embedding: Cả hai cách đều yêu cầu bạn biết mô hình nào tạo ra embedding. Nếu không, hãy chờ Phần 4.
6. Tóm tắt: 3 bài học từ phần này
-
Ngân hàng mẫu là cách nhanh và đơn giản nhất. Tạo 500.000 mẫu tự động, so sánh và bỏ phiếu. Phù hợp với các chủ đề phổ biến.
-
Tìm kiếm theo chùm là cách mạnh mẽ hơn. Không cần mẫu có sẵn, xây dựng văn bản từ đầu. Nhưng chậm hơn và cần GPU.
-
Cả hai đều là zero-shot - không cần huấn luyện trước. Nhưng vẫn cần biết mô hình embedding. Nếu không, phải đợi đến Phần 4.
7. Preview Phần 4
Trong phần tiếp theo - "Đảo Ngược Có Huấn Luyện - ALGEN Và Vec2Text" - chúng ta sẽ:
- Dùng ALGEN (Few-Shot) để căn chỉnh không gian embedding mà không cần biết mô hình mục tiêu
- Kỹ thuật Canary Injection - tiêm dữ liệu vào vector store để tạo cặp căn chỉnh
- Vec2Text (Supervised) - huấn luyện với 60 giờ GPU để đạt độ chính xác cao nhất
- So sánh 4 phương pháp và hướng dẫn chọn vũ khí phù hợp
Hãy sẵn sàng cho những vũ khí hạng nặng. 🚀
"Zero-shot không có nghĩa là không chuẩn bị. Nó có nghĩa là bạn đã chuẩn bị đủ tốt để không cần huấn luyện lại."
Hẹn gặp lại ở Phần 4.
All Rights Reserved